@rabbitio/ui-kit 1.0.0-beta.9 → 1.0.0-beta.91

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 (369) hide show
  1. package/.gitlab-ci.yml +29 -0
  2. package/.husky/commit-msg +19 -0
  3. package/.husky/pre-push +1 -0
  4. package/CHANGELOG.md +0 -0
  5. package/README.md +27 -18
  6. package/coverage/base.css +224 -0
  7. package/coverage/block-navigation.js +87 -0
  8. package/coverage/clover.xml +16765 -0
  9. package/coverage/coverage-final.json +116 -0
  10. package/coverage/favicon.png +0 -0
  11. package/coverage/index.html +1001 -0
  12. package/coverage/prettify.css +1 -0
  13. package/coverage/prettify.js +2 -0
  14. package/coverage/rabbit-ui-kit/index.html +116 -0
  15. package/coverage/rabbit-ui-kit/index.js.html +88 -0
  16. package/coverage/rabbit-ui-kit/src/common-apis/adapters/analyticsAdapters/googleAnalyticsAdapter.js.html +148 -0
  17. package/coverage/rabbit-ui-kit/src/common-apis/adapters/analyticsAdapters/index.html +146 -0
  18. package/coverage/rabbit-ui-kit/src/common-apis/adapters/analyticsAdapters/metrikaAdapter.js.html +169 -0
  19. package/coverage/rabbit-ui-kit/src/common-apis/adapters/analyticsAdapters/mixpanelAdapter.js.html +199 -0
  20. package/coverage/rabbit-ui-kit/src/common-apis/adapters/axiosAdapter.js.html +190 -0
  21. package/coverage/rabbit-ui-kit/src/common-apis/adapters/index.html +131 -0
  22. package/coverage/rabbit-ui-kit/src/common-apis/adapters/qrUtils.js.html +139 -0
  23. package/coverage/rabbit-ui-kit/src/common-apis/external-apis/apiGroups.js.html +250 -0
  24. package/coverage/rabbit-ui-kit/src/common-apis/external-apis/emailAPI.js.html +133 -0
  25. package/coverage/rabbit-ui-kit/src/common-apis/external-apis/index.html +146 -0
  26. package/coverage/rabbit-ui-kit/src/common-apis/external-apis/ipAddressProviders.js.html +352 -0
  27. package/coverage/rabbit-ui-kit/src/common-apis/globalConstants.jsx.html +94 -0
  28. package/coverage/rabbit-ui-kit/src/common-apis/index.html +116 -0
  29. package/coverage/rabbit-ui-kit/src/common-apis/models/blockchain.js.html +115 -0
  30. package/coverage/rabbit-ui-kit/src/common-apis/models/coin.js.html +829 -0
  31. package/coverage/rabbit-ui-kit/src/common-apis/models/index.html +146 -0
  32. package/coverage/rabbit-ui-kit/src/common-apis/models/protocol.js.html +100 -0
  33. package/coverage/rabbit-ui-kit/src/common-apis/services/fiatCurrenciesService.js.html +544 -0
  34. package/coverage/rabbit-ui-kit/src/common-apis/services/index.html +116 -0
  35. package/coverage/rabbit-ui-kit/src/common-apis/utils/amountUtils.js.html +1162 -0
  36. package/coverage/rabbit-ui-kit/src/common-apis/utils/cache.js.html +811 -0
  37. package/coverage/rabbit-ui-kit/src/common-apis/utils/errorUtils.js.html +211 -0
  38. package/coverage/rabbit-ui-kit/src/common-apis/utils/index.html +191 -0
  39. package/coverage/rabbit-ui-kit/src/common-apis/utils/logging/index.html +131 -0
  40. package/coverage/rabbit-ui-kit/src/common-apis/utils/logging/logger.js.html +208 -0
  41. package/coverage/rabbit-ui-kit/src/common-apis/utils/logging/logsStorage.js.html +268 -0
  42. package/coverage/rabbit-ui-kit/src/common-apis/utils/postponeExecution.js.html +118 -0
  43. package/coverage/rabbit-ui-kit/src/common-apis/utils/rabbitTicker.js.html +157 -0
  44. package/coverage/rabbit-ui-kit/src/common-apis/utils/safeStringify.js.html +235 -0
  45. package/coverage/rabbit-ui-kit/src/index.html +116 -0
  46. package/coverage/rabbit-ui-kit/src/index.js.html +376 -0
  47. package/coverage/rabbit-ui-kit/src/robust-api-caller/cacheAndConcurrentRequestsResolver.js.html +1570 -0
  48. package/coverage/rabbit-ui-kit/src/robust-api-caller/cachedRobustExternalApiCallerService.js.html +526 -0
  49. package/coverage/rabbit-ui-kit/src/robust-api-caller/cancelProcessing.js.html +172 -0
  50. package/coverage/rabbit-ui-kit/src/robust-api-caller/concurrentCalculationsMetadataHolder.js.html +310 -0
  51. package/coverage/rabbit-ui-kit/src/robust-api-caller/externalApiProvider.js.html +553 -0
  52. package/coverage/rabbit-ui-kit/src/robust-api-caller/externalServicesStatsCollector.js.html +319 -0
  53. package/coverage/rabbit-ui-kit/src/robust-api-caller/index.html +206 -0
  54. package/coverage/rabbit-ui-kit/src/robust-api-caller/robustExternalAPICallerService.js.html +997 -0
  55. package/coverage/rabbit-ui-kit/src/swaps-lib/external-apis/exolixSwapProvider.js.html +1825 -0
  56. package/coverage/rabbit-ui-kit/src/swaps-lib/external-apis/index.html +161 -0
  57. package/coverage/rabbit-ui-kit/src/swaps-lib/external-apis/letsExchangeSwapProvider.js.html +1618 -0
  58. package/coverage/rabbit-ui-kit/src/swaps-lib/external-apis/swapProvider.js.html +1819 -0
  59. package/coverage/rabbit-ui-kit/src/swaps-lib/external-apis/swapspaceSwapProvider.js.html +1861 -0
  60. package/coverage/rabbit-ui-kit/src/swaps-lib/models/baseSwapCreationInfo.js.html +319 -0
  61. package/coverage/rabbit-ui-kit/src/swaps-lib/models/existingSwap.js.html +514 -0
  62. package/coverage/rabbit-ui-kit/src/swaps-lib/models/existingSwapWithFiatData.js.html +529 -0
  63. package/coverage/rabbit-ui-kit/src/swaps-lib/models/index.html +176 -0
  64. package/coverage/rabbit-ui-kit/src/swaps-lib/models/partner.js.html +166 -0
  65. package/coverage/rabbit-ui-kit/src/swaps-lib/models/swapProviderCoinInfo.js.html +331 -0
  66. package/coverage/rabbit-ui-kit/src/swaps-lib/services/index.html +116 -0
  67. package/coverage/rabbit-ui-kit/src/swaps-lib/services/publicSwapService.js.html +1555 -0
  68. package/coverage/rabbit-ui-kit/src/swaps-lib/utils/index.html +116 -0
  69. package/coverage/rabbit-ui-kit/src/swaps-lib/utils/swapUtils.js.html +670 -0
  70. package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/arrowIcon.jsx.html +124 -0
  71. package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/arrowTosca.jsx.html +127 -0
  72. package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/arrowWhite.jsx.html +127 -0
  73. package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/darkRectangle.jsx.html +106 -0
  74. package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/determinedError.jsx.html +439 -0
  75. package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/failedValidationIcon.jsx.html +202 -0
  76. package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/index.html +281 -0
  77. package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/infoIcon.jsx.html +133 -0
  78. package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/messageIcon.jsx.html +346 -0
  79. package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/noticeQuestionIcon.jsx.html +247 -0
  80. package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/successfulValidationIcon.jsx.html +163 -0
  81. package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/supportDialogImage.jsx.html +268 -0
  82. package/coverage/rabbit-ui-kit/src/ui-kit/assets/wrappedImages/walletIcon.jsx.html +151 -0
  83. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/AssetIcon/AssetIcon.jsx.html +256 -0
  84. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/AssetIcon/index.html +116 -0
  85. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/AssetSelection/AssetSelection.jsx.html +289 -0
  86. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/AssetSelection/index.html +116 -0
  87. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/BackgroundTitle/BackgroundTitle.jsx.html +187 -0
  88. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/BackgroundTitle/index.html +116 -0
  89. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/InformationMessage/InformationMessage.jsx.html +238 -0
  90. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/InformationMessage/index.html +116 -0
  91. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/Input/Input.jsx.html +634 -0
  92. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/Input/index.html +116 -0
  93. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/LoadingDots/LoadingDots.jsx.html +196 -0
  94. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/LoadingDots/index.html +116 -0
  95. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/NoticeIcon/NoticeIcon.jsx.html +277 -0
  96. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/NoticeIcon/index.html +116 -0
  97. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/QrCode/QrCode.jsx.html +217 -0
  98. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/QrCode/index.html +116 -0
  99. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/RateSelector/RateSelector.jsx.html +175 -0
  100. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/RateSelector/index.html +116 -0
  101. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/SupportChat/SupportChat.jsx.html +217 -0
  102. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/SupportChat/index.html +116 -0
  103. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/Textarea/Textarea.jsx.html +529 -0
  104. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/Textarea/index.html +116 -0
  105. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/TitleBox/TitleBox.jsx.html +508 -0
  106. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/TitleBox/index.html +116 -0
  107. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/Tooltip/Tooltip.jsx.html +316 -0
  108. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/Tooltip/index.html +116 -0
  109. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/TwoLinesOfText/LinesOfText.jsx.html +313 -0
  110. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/TwoLinesOfText/index.html +116 -0
  111. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/Validation/Validation.jsx.html +202 -0
  112. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/Validation/index.html +116 -0
  113. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/buttons/Button/Button.jsx.html +712 -0
  114. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/buttons/Button/index.html +116 -0
  115. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/buttons/Close/Close.jsx.html +259 -0
  116. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/buttons/Close/index.html +116 -0
  117. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/buttons/LinkButton/LinkButton.jsx.html +421 -0
  118. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/buttons/LinkButton/index.html +116 -0
  119. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/buttons/RadioButtonWithText/RadioButtonWithText.jsx.html +415 -0
  120. package/coverage/rabbit-ui-kit/src/ui-kit/components/atoms/buttons/RadioButtonWithText/index.html +116 -0
  121. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/AmountInput/AmountInput.jsx.html +1429 -0
  122. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/AmountInput/index.html +116 -0
  123. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/CoinPicker/CoinPicker.jsx.html +1474 -0
  124. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/CoinPicker/index.html +116 -0
  125. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/ColoredNotice/ColoredNotice.jsx.html +211 -0
  126. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/ColoredNotice/index.html +116 -0
  127. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/LineWithIconLink/LineWithIconLink.jsx.html +190 -0
  128. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/LineWithIconLink/index.html +116 -0
  129. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/LogoCarousel/LogoCarousel.jsx.html +307 -0
  130. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/LogoCarousel/index.html +116 -0
  131. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/SearchableCoinsList/SearchableCoinsList.jsx.html +496 -0
  132. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/SearchableCoinsList/index.html +116 -0
  133. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/TitledLineWithIconLink/TitledLineWithIconLink.jsx.html +181 -0
  134. package/coverage/rabbit-ui-kit/src/ui-kit/components/molecules/TitledLineWithIconLink/index.html +116 -0
  135. package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/CoinPickerDialogStep/CoinPickerDialogStep.jsx.html +283 -0
  136. package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/CoinPickerDialogStep/index.html +116 -0
  137. package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/Dialog/Dialog.jsx.html +1567 -0
  138. package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/Dialog/DialogButtons/DialogButtons.jsx.html +481 -0
  139. package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/Dialog/DialogButtons/index.html +116 -0
  140. package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/Dialog/DialogStep/DialogStep.jsx.html +1747 -0
  141. package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/Dialog/DialogStep/index.html +116 -0
  142. package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/Dialog/index.html +116 -0
  143. package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/SwapForm/SwapForm.jsx.html +4207 -0
  144. package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/SwapForm/index.html +116 -0
  145. package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/WaitlistSubscription/WaitlistSubscription.jsx.html +559 -0
  146. package/coverage/rabbit-ui-kit/src/ui-kit/components/organisms/WaitlistSubscription/index.html +116 -0
  147. package/coverage/rabbit-ui-kit/src/ui-kit/components/templates/DeterminedErrorDialogStep/DeterminedErrorDialogStep.jsx.html +316 -0
  148. package/coverage/rabbit-ui-kit/src/ui-kit/components/templates/DeterminedErrorDialogStep/index.html +116 -0
  149. package/coverage/rabbit-ui-kit/src/ui-kit/hooks/index.html +146 -0
  150. package/coverage/rabbit-ui-kit/src/ui-kit/hooks/useCallHandlingErrors.js.html +151 -0
  151. package/coverage/rabbit-ui-kit/src/ui-kit/hooks/useIsHydrated.js.html +121 -0
  152. package/coverage/rabbit-ui-kit/src/ui-kit/hooks/useReferredState.js.html +157 -0
  153. package/coverage/rabbit-ui-kit/src/ui-kit/utils/index.html +161 -0
  154. package/coverage/rabbit-ui-kit/src/ui-kit/utils/inputValueProviders.js.html +235 -0
  155. package/coverage/rabbit-ui-kit/src/ui-kit/utils/textUtils.js.html +139 -0
  156. package/coverage/rabbit-ui-kit/src/ui-kit/utils/uiUtils.js.html +121 -0
  157. package/coverage/rabbit-ui-kit/src/ui-kit/utils/urlQueryUtils.js.html +271 -0
  158. package/coverage/rabbit-ui-kit/stories/atoms/BackgroundTitle.stories.jsx.html +202 -0
  159. package/coverage/rabbit-ui-kit/stories/atoms/LinesOfText.stories.jsx.html +283 -0
  160. package/coverage/rabbit-ui-kit/stories/atoms/LoadingDots.stories.jsx.html +226 -0
  161. package/coverage/rabbit-ui-kit/stories/atoms/QrCode.stories.jsx.html +175 -0
  162. package/coverage/rabbit-ui-kit/stories/atoms/RateSelector.stories.jsx.html +136 -0
  163. package/coverage/rabbit-ui-kit/stories/atoms/Validation.stories.jsx.html +178 -0
  164. package/coverage/rabbit-ui-kit/stories/atoms/buttons/Button.stories.jsx.html +883 -0
  165. package/coverage/rabbit-ui-kit/stories/atoms/buttons/Close.stories.jsx.html +211 -0
  166. package/coverage/rabbit-ui-kit/stories/atoms/buttons/LinkButton.stories.jsx.html +301 -0
  167. package/coverage/rabbit-ui-kit/stories/atoms/buttons/index.html +146 -0
  168. package/coverage/rabbit-ui-kit/stories/atoms/index.html +191 -0
  169. package/coverage/rabbit-ui-kit/stories/molecules/AmountInput.stories.jsx.html +289 -0
  170. package/coverage/rabbit-ui-kit/stories/molecules/CoinPicker.stories.jsx.html +322 -0
  171. package/coverage/rabbit-ui-kit/stories/molecules/ColoredNotice.stories.jsx.html +178 -0
  172. package/coverage/rabbit-ui-kit/stories/molecules/LineWithIconLink.stories.jsx.html +154 -0
  173. package/coverage/rabbit-ui-kit/stories/molecules/LogoCarousel.stories.jsx.html +235 -0
  174. package/coverage/rabbit-ui-kit/stories/molecules/TitledLineWithIconLink.stories.jsx.html +160 -0
  175. package/coverage/rabbit-ui-kit/stories/molecules/index.html +191 -0
  176. package/coverage/rabbit-ui-kit/stories/organisms/Dialog/Dialog.stories.jsx.html +523 -0
  177. package/coverage/rabbit-ui-kit/stories/organisms/Dialog/DialogButtons/DialogButtons.stories.jsx.html +328 -0
  178. package/coverage/rabbit-ui-kit/stories/organisms/Dialog/DialogButtons/index.html +116 -0
  179. package/coverage/rabbit-ui-kit/stories/organisms/Dialog/DialogStep/DialogStep.stories.jsx.html +337 -0
  180. package/coverage/rabbit-ui-kit/stories/organisms/Dialog/DialogStep/index.html +116 -0
  181. package/coverage/rabbit-ui-kit/stories/organisms/Dialog/index.html +116 -0
  182. package/coverage/rabbit-ui-kit/stories/organisms/WaitlistSubscription.stories.jsx.html +151 -0
  183. package/coverage/rabbit-ui-kit/stories/organisms/index.html +116 -0
  184. package/coverage/rabbit-ui-kit/stories/stubs/coins.jsx.html +6880 -0
  185. package/coverage/rabbit-ui-kit/stories/stubs/exampleContent.jsx.html +145 -0
  186. package/coverage/rabbit-ui-kit/stories/stubs/index.html +131 -0
  187. package/coverage/rabbit-ui-kit/stories/templates/DeterminedErrorDialogStep.stories.jsx.html +190 -0
  188. package/coverage/rabbit-ui-kit/stories/templates/index.html +116 -0
  189. package/coverage/sort-arrow-sprite.png +0 -0
  190. package/coverage/sorter.js +196 -0
  191. package/dist/global.css +197 -0
  192. package/dist/global.css.map +1 -0
  193. package/dist/index.cjs +12451 -25
  194. package/dist/index.cjs.map +1 -1
  195. package/dist/index.css +2339 -8491
  196. package/dist/index.css.map +1 -1
  197. package/dist/index.modern.js +9597 -26
  198. package/dist/index.modern.js.map +1 -1
  199. package/dist/index.module.js +12357 -27
  200. package/dist/index.module.js.map +1 -1
  201. package/dist/index.umd.js +12442 -29
  202. package/dist/index.umd.js.map +1 -1
  203. package/package.json +35 -9
  204. package/raw +1000 -0
  205. package/src/common-apis/adapters/analyticsAdapters/googleAnalyticsAdapter.js +21 -0
  206. package/src/common-apis/adapters/analyticsAdapters/metrikaAdapter.js +28 -0
  207. package/src/common-apis/adapters/analyticsAdapters/mixpanelAdapter.js +38 -0
  208. package/src/common-apis/adapters/axiosAdapter.js +35 -0
  209. package/src/common-apis/adapters/qrUtils.js +18 -0
  210. package/src/common-apis/external-apis/apiGroups.js +55 -0
  211. package/src/common-apis/external-apis/emailAPI.js +16 -0
  212. package/src/common-apis/external-apis/ipAddressProviders.js +89 -0
  213. package/src/common-apis/globalConstants.jsx +3 -0
  214. package/src/common-apis/models/blockchain.js +10 -0
  215. package/src/common-apis/models/coin.js +248 -0
  216. package/src/common-apis/models/protocol.js +5 -0
  217. package/src/{common → common-apis/services}/fiatCurrenciesService.js +4 -12
  218. package/src/common-apis/tests/integration/external-apis/ipAddressProviders/getClientIpAddress.test.js +12 -0
  219. package/src/common-apis/tests/units/utils/amountUtils/composeRateText.test.js +152 -0
  220. package/src/{common → common-apis/utils}/amountUtils.js +72 -136
  221. package/src/common-apis/utils/cache.js +242 -0
  222. package/src/{common → common-apis/utils}/errorUtils.js +15 -0
  223. package/src/common-apis/utils/logging/logger.js +41 -0
  224. package/src/common-apis/utils/logging/logsStorage.js +61 -0
  225. package/src/common-apis/utils/postponeExecution.js +11 -0
  226. package/src/common-apis/utils/rabbitTicker.js +24 -0
  227. package/src/common-apis/utils/safeStringify.js +50 -0
  228. package/src/index.js +96 -9
  229. package/src/robust-api-caller/cacheAndConcurrentRequestsResolver.js +495 -0
  230. package/src/robust-api-caller/cachedRobustExternalApiCallerService.js +147 -0
  231. package/src/robust-api-caller/cancelProcessing.js +29 -0
  232. package/src/robust-api-caller/concurrentCalculationsMetadataHolder.js +75 -0
  233. package/src/robust-api-caller/externalApiProvider.js +156 -0
  234. package/src/robust-api-caller/externalServicesStatsCollector.js +78 -0
  235. package/src/robust-api-caller/robustExternalAPICallerService.js +304 -0
  236. package/src/robust-api-caller/tests/robustExternalAPICallerService/robustExternalAPICallerService/callExternalAPI/_performCallAttempt.test.js +533 -0
  237. package/src/robust-api-caller/tests/robustExternalAPICallerService/robustExternalAPICallerService/callExternalAPI/callExternalAPI.test.js +532 -0
  238. package/src/robust-api-caller/tests/robustExternalAPICallerService/robustExternalAPICallerService/constructor.test.js +19 -0
  239. package/src/swaps-lib/external-apis/exolixSwapProvider.js +580 -0
  240. package/src/swaps-lib/external-apis/letsExchangeSwapProvider.js +511 -0
  241. package/src/swaps-lib/external-apis/swapProvider.js +578 -0
  242. package/src/swaps-lib/external-apis/swapspaceSwapProvider.js +592 -0
  243. package/src/swaps-lib/models/baseSwapCreationInfo.js +78 -0
  244. package/src/swaps-lib/models/existingSwap.js +143 -0
  245. package/src/swaps-lib/models/existingSwapWithFiatData.js +148 -0
  246. package/src/swaps-lib/models/partner.js +27 -0
  247. package/src/swaps-lib/models/swapProviderCoinInfo.js +82 -0
  248. package/src/swaps-lib/services/publicSwapService.js +490 -0
  249. package/src/swaps-lib/test/external-apis/exolixSwapProvider/_fetchSupportedCurrenciesIfNeeded.test.js +34 -0
  250. package/src/swaps-lib/test/external-apis/exolixSwapProvider/createSwap.test.js +1043 -0
  251. package/src/swaps-lib/test/external-apis/exolixSwapProvider/getSwapInfo.test.js +611 -0
  252. package/src/swaps-lib/test/external-apis/swapProvider/getAllSupportedCurrencies.test.js +63 -0
  253. package/src/swaps-lib/test/external-apis/swapProvider/getDepositCurrencies.test.js +73 -0
  254. package/src/swaps-lib/test/external-apis/swapProvider/getWithdrawalCurrencies.test.js +102 -0
  255. package/src/swaps-lib/test/external-apis/swapProvider/removeProtocolNameFromCoinName.test.js +152 -0
  256. package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/_fetchSupportedCurrenciesIfNeeded.test.js +536 -0
  257. package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/createSwap.test.js +1214 -0
  258. package/src/swaps-lib/test/external-apis/swapspaceSwapProvider/getSwapInfo.test.js +1707 -0
  259. package/src/swaps-lib/test/utils/swapUtils/safeHandleRequestsLimitExceeding.test.js +80 -0
  260. package/src/swaps-lib/utils/swapUtils.js +195 -0
  261. package/{styles → src/ui-kit/assets/styles}/_functions.scss +5 -0
  262. package/{styles → src/ui-kit/assets/styles}/_mixins.scss +2 -2
  263. package/{styles → src/ui-kit/assets/styles}/_placeholder.scss +3 -3
  264. package/{styles → src/ui-kit/assets/styles}/_variables.scss +17 -15
  265. package/src/ui-kit/assets/styles/fonts/NunitoSans-Bold.ttf +0 -0
  266. package/src/ui-kit/assets/styles/fonts/NunitoSans-ExtraBold.ttf +0 -0
  267. package/src/ui-kit/assets/styles/fonts/NunitoSans-Light.ttf +0 -0
  268. package/src/ui-kit/assets/styles/fonts/NunitoSans-Regular.ttf +0 -0
  269. package/src/ui-kit/assets/styles/fonts/NunitoSans-SemiBold.ttf +0 -0
  270. package/src/ui-kit/assets/styles/global.scss +171 -0
  271. package/src/ui-kit/assets/styles/index.scss +10 -0
  272. package/src/ui-kit/assets/wrappedImages/arrowIcon.jsx +13 -0
  273. package/src/ui-kit/assets/wrappedImages/arrowTosca.jsx +14 -0
  274. package/src/ui-kit/assets/wrappedImages/arrowWhite.jsx +14 -0
  275. package/src/ui-kit/assets/wrappedImages/darkRectangle.jsx +7 -0
  276. package/src/ui-kit/assets/wrappedImages/determinedError.jsx +118 -0
  277. package/src/ui-kit/assets/wrappedImages/failedValidationIcon.jsx +39 -0
  278. package/src/ui-kit/assets/wrappedImages/infoIcon.jsx +16 -0
  279. package/src/ui-kit/assets/wrappedImages/messageIcon.jsx +87 -0
  280. package/src/ui-kit/assets/wrappedImages/noticeQuestionIcon.jsx +54 -0
  281. package/src/ui-kit/assets/wrappedImages/successfulValidationIcon.jsx +26 -0
  282. package/src/ui-kit/assets/wrappedImages/supportDialogImage.jsx +61 -0
  283. package/src/ui-kit/assets/wrappedImages/walletIcon.jsx +22 -0
  284. package/src/ui-kit/components/atoms/AssetIcon/AssetIcon.jsx +57 -0
  285. package/src/{components → ui-kit/components}/atoms/AssetIcon/asset-icon.module.scss +1 -1
  286. package/src/ui-kit/components/atoms/AssetSelection/AssetSelection.jsx +68 -0
  287. package/src/ui-kit/components/atoms/AssetSelection/asset-selection.module.scss +56 -0
  288. package/src/ui-kit/components/atoms/BackgroundTitle/BackgroundTitle.jsx +34 -0
  289. package/src/ui-kit/components/atoms/BackgroundTitle/background-title.module.scss +52 -0
  290. package/src/ui-kit/components/atoms/InformationMessage/InformationMessage.jsx +51 -0
  291. package/src/ui-kit/components/atoms/InformationMessage/information-message.module.scss +38 -0
  292. package/src/ui-kit/components/atoms/Input/Input.jsx +183 -0
  293. package/src/ui-kit/components/atoms/Input/input.module.scss +107 -0
  294. package/src/{components → ui-kit/components}/atoms/LoadingDots/LoadingDots.jsx +8 -28
  295. package/src/{components → ui-kit/components}/atoms/LoadingDots/LoadingDots.module.scss +3 -2
  296. package/src/ui-kit/components/atoms/NoticeIcon/NoticeIcon.jsx +64 -0
  297. package/src/ui-kit/components/atoms/NoticeIcon/notice-icon.module.scss +14 -0
  298. package/src/ui-kit/components/atoms/QrCode/QrCode.jsx +44 -0
  299. package/src/ui-kit/components/atoms/QrCode/qr-code.module.scss +15 -0
  300. package/src/ui-kit/components/atoms/RateSelector/RateSelector.jsx +30 -0
  301. package/src/ui-kit/components/atoms/RateSelector/rate-selector.module.scss +47 -0
  302. package/src/{components → ui-kit/components}/atoms/SupportChat/SupportChat.jsx +5 -1
  303. package/src/ui-kit/components/atoms/Textarea/Textarea.jsx +148 -0
  304. package/src/ui-kit/components/atoms/Textarea/textarea.module.scss +71 -0
  305. package/src/ui-kit/components/atoms/TitleBox/TitleBox.jsx +141 -0
  306. package/src/ui-kit/components/atoms/TitleBox/title-box.module.scss +32 -0
  307. package/src/ui-kit/components/atoms/Tooltip/Tooltip.jsx +77 -0
  308. package/src/ui-kit/components/atoms/Tooltip/tooltip.module.scss +237 -0
  309. package/src/ui-kit/components/atoms/TwoLinesOfText/LinesOfText.jsx +76 -0
  310. package/src/ui-kit/components/atoms/TwoLinesOfText/lines-of-text.module.scss +65 -0
  311. package/src/ui-kit/components/atoms/Validation/Validation.jsx +39 -0
  312. package/src/ui-kit/components/atoms/Validation/validation.module.scss +19 -0
  313. package/src/{components → ui-kit/components}/atoms/buttons/Button/Button.jsx +24 -50
  314. package/src/{components → ui-kit/components}/atoms/buttons/Button/Button.module.scss +1 -1
  315. package/src/ui-kit/components/atoms/buttons/Close/Close.jsx +58 -0
  316. package/src/ui-kit/components/atoms/buttons/Close/close.module.scss +75 -0
  317. package/src/ui-kit/components/atoms/buttons/LinkButton/LinkButton.jsx +112 -0
  318. package/src/ui-kit/components/atoms/buttons/LinkButton/link-button.module.scss +49 -0
  319. package/src/ui-kit/components/atoms/buttons/RadioButtonWithText/RadioButtonWithText.jsx +110 -0
  320. package/src/ui-kit/components/atoms/buttons/RadioButtonWithText/radio-button-with-text.module.scss +86 -0
  321. package/src/ui-kit/components/molecules/AmountInput/AmountInput.jsx +448 -0
  322. package/src/ui-kit/components/molecules/AmountInput/amount-input.module.scss +233 -0
  323. package/src/ui-kit/components/molecules/CoinPicker/CoinPicker.jsx +463 -0
  324. package/src/ui-kit/components/molecules/CoinPicker/coin-picker.module.scss +207 -0
  325. package/src/ui-kit/components/molecules/ColoredNotice/ColoredNotice.jsx +42 -0
  326. package/src/ui-kit/components/molecules/ColoredNotice/colored-notice.module.scss +20 -0
  327. package/src/ui-kit/components/molecules/LineWithIconLink/LineWithIconLink.jsx +35 -0
  328. package/src/ui-kit/components/molecules/LineWithIconLink/line-with-icon-link.module.scss +25 -0
  329. package/src/ui-kit/components/molecules/LogoCarousel/LogoCarousel.jsx +74 -0
  330. package/src/ui-kit/components/molecules/LogoCarousel/logo-carousel.module.scss +106 -0
  331. package/src/ui-kit/components/molecules/SearchableCoinsList/SearchableCoinsList.jsx +137 -0
  332. package/src/ui-kit/components/molecules/TitledLineWithIconLink/TitledLineWithIconLink.jsx +32 -0
  333. package/src/ui-kit/components/organisms/CoinPickerDialogStep/CoinPickerDialogStep.jsx +66 -0
  334. package/src/ui-kit/components/organisms/Dialog/Dialog.jsx +494 -0
  335. package/src/ui-kit/components/organisms/Dialog/DialogButtons/DialogButtons.jsx +132 -0
  336. package/src/ui-kit/components/organisms/Dialog/DialogButtons/dialog-buttons.module.scss +25 -0
  337. package/src/ui-kit/components/organisms/Dialog/DialogStep/DialogStep.jsx +554 -0
  338. package/src/ui-kit/components/organisms/Dialog/DialogStep/dialog-step.module.scss +381 -0
  339. package/src/ui-kit/components/organisms/Dialog/dialog.module.scss +226 -0
  340. package/src/ui-kit/components/organisms/SwapForm/SwapForm.jsx +1374 -0
  341. package/src/ui-kit/components/organisms/SwapForm/swap-form.module.scss +134 -0
  342. package/src/ui-kit/components/organisms/WaitlistSubscription/WaitlistSubscription.jsx +158 -0
  343. package/src/ui-kit/components/templates/DeterminedErrorDialogStep/DeterminedErrorDialogStep.jsx +77 -0
  344. package/src/ui-kit/hooks/useCallHandlingErrors.js +22 -0
  345. package/src/ui-kit/hooks/useIsHydrated.js +12 -0
  346. package/src/ui-kit/hooks/useReferredState.js +24 -0
  347. package/src/ui-kit/tests/utils/inputValueProviders/provideFormatOfFloatValueByInputString.test.js +146 -0
  348. package/src/ui-kit/tests/utils/urlQueryUtils/getQueryParameterValues.test.js +65 -0
  349. package/src/ui-kit/tests/utils/urlQueryUtils/saveQueryParameterAndValues.test.js +104 -0
  350. package/src/ui-kit/utils/inputValueProviders.js +50 -0
  351. package/src/ui-kit/utils/textUtils.js +18 -0
  352. package/src/ui-kit/utils/uiUtils.js +12 -0
  353. package/src/ui-kit/utils/urlQueryUtils.js +62 -0
  354. package/stories/font.scss +40 -0
  355. package/stories/stubs/coins.jsx +2266 -0
  356. package/stories/stubs/exampleContent.jsx +20 -0
  357. package/src/components/atoms/AssetIcon/AssetIcon.jsx +0 -55
  358. package/styles/_global-classes.scss +0 -433
  359. package/styles/fonts/NunitoSans-Bold.ttf +0 -0
  360. package/styles/fonts/NunitoSans-ExtraBold.ttf +0 -0
  361. package/styles/fonts/NunitoSans-Light.ttf +0 -0
  362. package/styles/fonts/NunitoSans-Regular.ttf +0 -0
  363. package/styles/fonts/NunitoSans-SemiBold.ttf +0 -0
  364. package/styles/global-styles-index.scss +0 -74
  365. package/styles/index.scss +0 -33
  366. /package/{styles → src/ui-kit/assets/styles}/colors/_light-colors.scss +0 -0
  367. /package/{styles → src/ui-kit/assets/styles}/colors/_solid-colors.scss +0 -0
  368. /package/{styles → src/ui-kit/assets/styles}/size/_margin-size.scss +0 -0
  369. /package/{styles → src/ui-kit/assets/styles}/size/_padding-size.scss +0 -0
@@ -0,0 +1,495 @@
1
+ import { v4 } from "uuid";
2
+
3
+ import { improveAndRethrow } from "../common-apis/utils/errorUtils.js";
4
+ import { Logger } from "../common-apis/utils/logging/logger.js";
5
+
6
+ /**
7
+ * This util helps to avoid duplicated calls to a shared resource.
8
+ * It tracks is there currently active calculation for the specific cache id and make all other requests
9
+ * with the same cache id waiting for this active calculation to be finished. When the calculation ends
10
+ * the resolver allows all the waiting requesters to get the data from cache and start their own calculations.
11
+ *
12
+ * This class should be instantiated inside some other service where you need to request some resource concurrently.
13
+ * Rules:
14
+ * 1. When you need to make a request inside your main service call 'getCachedOrWaitForCachedOrAcquireLock'
15
+ * on the instance of this class and await for the result. If the flag allowing to start calculation is true
16
+ * then you can request data inside your main service. Otherwise you should use the cached data as an another
17
+ * requester just finished the most resent requesting and there is actual data in the cache that
18
+ * is returned to you here.
19
+ * 1.1 Also you can acquire a lock directly if you don't want to get cached data. Use the corresponding method 'acquireLock'.
20
+ *
21
+ * 2. If you start requesting (when you successfully acquired the lock) then after receiving the result of your
22
+ * requesting you should call the 'saveCachedData' so the retrieved data will appear in the cache.
23
+ *
24
+ * 3. If you successfully acquired the lock then you should after calling the 'saveCachedData' call
25
+ * the 'releaseLock' - this is mandatory to release the lock and allow other requesters to perform their requests.
26
+ * WARNING: If for any reason you forget to call this method then this class instance will wait perpetually for
27
+ * the lock releasing and all your attempts to request the data will constantly fail. So usually call it
28
+ * inside the 'finally' block.
29
+ *
30
+ * TODO: [tests, critical++] add unit tests - massively used logic and can produce sophisticated concurrency bugs
31
+ */
32
+ export class CacheAndConcurrentRequestsResolver {
33
+ /**
34
+ * @param bio {string} unique identifier for the exact service
35
+ * @param cache {Cache} cache
36
+ * @param cacheTtl {number|null} time to live for cache ms. 0 or null means the cache cannot expire
37
+ * @param [maxCallAttemptsToWaitForAlreadyRunningRequest=100] {number} number of request allowed to do waiting for
38
+ * result before we fail the original request. Use custom value only if you need to make the attempts count
39
+ * and polling interval changes.
40
+ * @param [timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished=1000] {number}
41
+ * timeout ms for polling for a result. if you change maxCallAttemptsToWaitForAlreadyRunningRequest
42
+ * then this parameter maybe also require the custom value.
43
+ * @param [removeExpiredCacheAutomatically=true] {boolean}
44
+ */
45
+ constructor(
46
+ bio,
47
+ cache,
48
+ cacheTtl,
49
+ removeExpiredCacheAutomatically = true,
50
+ maxCallAttemptsToWaitForAlreadyRunningRequest = 100,
51
+ timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished = 1000
52
+ ) {
53
+ if (cacheTtl != null && cacheTtl < timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished * 2) {
54
+ /*
55
+ * During the lifetime of this service e.g. if the data is being retrieved slowly we can get
56
+ * RACE CONDITION when we constantly retrieve data and during retrieval it is expired, so we are trying
57
+ * to retrieve it again and again.
58
+ * We have a protection mechanism that we will wait no more than
59
+ * maxCallAttemptsToWaitForAlreadyRunningRequest * timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished
60
+ * but this additional check is aimed to reduce potential loading time for some requests.
61
+ */
62
+ throw new Error(
63
+ `DEV: Wrong parameters passed to construct ${bio} - TTL ${cacheTtl} should be 2 times greater than ${timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished}`
64
+ );
65
+ }
66
+ this._bio = bio;
67
+ this._cache = cache;
68
+ this._cacheTtlMs = cacheTtl != null ? cacheTtl : null;
69
+ this._maxExecutionTimeMs =
70
+ maxCallAttemptsToWaitForAlreadyRunningRequest *
71
+ timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished;
72
+ this._removeExpiredCacheAutomatically = removeExpiredCacheAutomatically;
73
+ this._requestsManager = new ManagerOfRequestsToTheSameResource(
74
+ bio,
75
+ maxCallAttemptsToWaitForAlreadyRunningRequest,
76
+ timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished
77
+ );
78
+ }
79
+
80
+ /**
81
+ * When using this service this is the major method you should call to get data by cache id.
82
+ * This method checks is there cached data and ether
83
+ * - returns you flag that you can start requesting data from the shared resource
84
+ * - or if there is already started calculation waits until it is finished (removed from this service)
85
+ * and returns you the retrieved data
86
+ * - or just returns you the cached data
87
+ *
88
+ * 'canStartDataRetrieval' equal true means that the lock was acquired, and you should manually call 'saveCachedData'
89
+ * if needed and then 'releaseLock' to mark this calculation as finished so other
90
+ * requesters can take their share of the resource.
91
+ *
92
+ * @param cacheId {string}
93
+ * @return {Promise<({
94
+ * canStartDataRetrieval: true,
95
+ * cachedData: any,
96
+ * lockId: string
97
+ * }|{
98
+ * canStartDataRetrieval: false,
99
+ * cachedData: any
100
+ * })>}
101
+ */
102
+ async getCachedOrWaitForCachedOrAcquireLock(cacheId) {
103
+ try {
104
+ const startedAtTimestamp = Date.now();
105
+ let cached = this._cache.get(cacheId);
106
+ let cachedDataBackupIsPresentButExpired = null;
107
+ if (cached != null && !this._removeExpiredCacheAutomatically) {
108
+ const lastUpdateTimestamp = this._cache.getLastUpdateTimestamp(cacheId);
109
+ if ((lastUpdateTimestamp ?? 0) + this._cacheTtlMs < Date.now()) {
110
+ /*
111
+ * Here we are manually clearing 'cached' value retrieved from cache to force the data loading.
112
+ * But we save its value first to the backup variable to be able to return this value if ongoing
113
+ * requesting fails.
114
+ */
115
+ cachedDataBackupIsPresentButExpired = cached;
116
+ cached = null;
117
+ }
118
+ }
119
+ let calculationId = null;
120
+ let isRetrievedCacheExpired = true;
121
+ let isWaitingForActiveCalculationSucceeded;
122
+ let weStillHaveSomeTimeToProceedExecution = true;
123
+ while (
124
+ calculationId == null &&
125
+ cached == null &&
126
+ isRetrievedCacheExpired &&
127
+ weStillHaveSomeTimeToProceedExecution
128
+ ) {
129
+ const result = await this._requestsManager.startCalculationOrWaitForActiveToFinish(cacheId);
130
+ calculationId = typeof result === "string" ? result : null;
131
+ isWaitingForActiveCalculationSucceeded = typeof result === "boolean" ? result : null;
132
+ cached = this._cache.get(cacheId);
133
+ isRetrievedCacheExpired = isWaitingForActiveCalculationSucceeded && cached == null;
134
+ weStillHaveSomeTimeToProceedExecution = Date.now() - startedAtTimestamp < this._maxExecutionTimeMs;
135
+ }
136
+ if (calculationId) {
137
+ return {
138
+ canStartDataRetrieval: true,
139
+ cachedData: cached ?? cachedDataBackupIsPresentButExpired,
140
+ lockId: calculationId,
141
+ };
142
+ }
143
+
144
+ return {
145
+ canStartDataRetrieval: false,
146
+ cachedData: cached ?? cachedDataBackupIsPresentButExpired,
147
+ };
148
+ } catch (e) {
149
+ improveAndRethrow(e, `${this._bio}.getCachedOrWaitForCachedOrAcquireLock`);
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Returns just the current cache value for the given id.
155
+ * Doesn't wait for the active calculation, doesn't acquire lock, just retrieves the current cache as it is.
156
+ *
157
+ * @param cacheId {string}
158
+ * @return {any}
159
+ */
160
+ getCached(cacheId) {
161
+ try {
162
+ return this._cache.get(cacheId);
163
+ } catch (e) {
164
+ improveAndRethrow(e, "getCached");
165
+ }
166
+ }
167
+
168
+ _getTtl() {
169
+ return this._removeExpiredCacheAutomatically ? this._cacheTtlMs : null;
170
+ }
171
+
172
+ /**
173
+ * Directly acquires the lock despite on cached data availability.
174
+ * So if this method returns result === true you can start the data retrieval.
175
+ *
176
+ * @param cacheId {string}
177
+ * @return {Promise<{ result: true, lockId: string }|{ result: false }>}
178
+ */
179
+ async acquireLock(cacheId) {
180
+ try {
181
+ return await this._requestsManager.acquireLock(cacheId);
182
+ } catch (e) {
183
+ improveAndRethrow(e, "acquireLock");
184
+ }
185
+ }
186
+
187
+ /**
188
+ * This method should be called only if you acquired a lock successfully.
189
+ *
190
+ * If the current lock id is not equal to the passed one the passed data will be ignored.
191
+ * Or you can do the synchronous data merging on your side and pass the
192
+ * wasDataMergedSynchronouslyWithMostRecentCacheState=true so your data will be stored
193
+ * despite on the lockId.
194
+ * WARNING: you should do this only if you are sure you perform the synchronous update.
195
+ *
196
+ * @param cacheId {string}
197
+ * @param lockId {string}
198
+ * @param data {any}
199
+ * @param [sessionDependentData=true] {boolean}
200
+ * @param [wasDataMergedSynchronouslyWithMostRecentCacheState=false]
201
+ */
202
+ saveCachedData(
203
+ cacheId,
204
+ lockId,
205
+ data,
206
+ sessionDependentData = true,
207
+ wasDataMergedSynchronouslyWithMostRecentCacheState = false
208
+ ) {
209
+ try {
210
+ if (
211
+ wasDataMergedSynchronouslyWithMostRecentCacheState ||
212
+ this._requestsManager.isTheLockActiveOne(cacheId, lockId)
213
+ ) {
214
+ /* We save passed data only if the <caller> has the currently acquired lockId.
215
+ * If the passed lockId is not the active one it means that other code cleared/stopped the lock
216
+ * acquired by the <caller> recently due to some urgent/more prior changes.
217
+ *
218
+ * But we allow user to pass the 'wasDataMergedSynchronouslyWithMostRecentCacheState' flag
219
+ * that tells us that the user had taken the most recent cache value and merged his new data
220
+ * with that cached value (AFTER possibly performing async data retrieval). This means that we
221
+ * can ignore the fact that his lockId is no more relevant and save the passed data
222
+ * as it is synchronously merged with the most recent cached data. (Synchronously merged means that
223
+ * the lost update cannot occur during the merge time as JS execute the synchronous functions\
224
+ * till the end).
225
+ */
226
+ if (sessionDependentData) {
227
+ this._cache.putSessionDependentData(cacheId, data, this._getTtl());
228
+ } else {
229
+ this._cache.put(cacheId, data, this._getTtl());
230
+ }
231
+ }
232
+ } catch (e) {
233
+ improveAndRethrow(e, `${this._bio}.saveCachedData`);
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Should be called then and only then if you successfully acquired a lock with the lock id.
239
+ *
240
+ * @param cacheId {string}
241
+ * @param lockId {string}
242
+ */
243
+ releaseLock(cacheId, lockId) {
244
+ try {
245
+ if (this._requestsManager.isTheLockActiveOne(cacheId, lockId)) {
246
+ this._requestsManager.finishActiveCalculation(cacheId);
247
+ }
248
+ } catch (e) {
249
+ improveAndRethrow(e, `${this._bio}.releaseLock`);
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Actualized currently present cached data by key. Applies the provided function to the cached data.
255
+ *
256
+ * @param cacheId {string} id of cache entry
257
+ * @param synchronousCurrentCacheProcessor (function|null} synchronous function accepting cache entry. Should return
258
+ * an object in following format:
259
+ * {
260
+ * isModified: boolean,
261
+ * data: any
262
+ * }
263
+ * the flag signals whether data was changed during the processing or not
264
+ * @param [sessionDependent=true] {boolean} whether to mark the cache entry as session-dependent
265
+ */
266
+ actualizeCachedData(cacheId, synchronousCurrentCacheProcessor, sessionDependent = true) {
267
+ try {
268
+ const cached = this._cache.get(cacheId);
269
+ const result = synchronousCurrentCacheProcessor(cached);
270
+ if (result?.isModified && result?.data != null) {
271
+ if (sessionDependent) {
272
+ this._cache.putSessionDependentData(cacheId, result?.data, this._getTtl());
273
+ } else {
274
+ this._cache.put(cacheId, result?.data, this._getTtl());
275
+ }
276
+
277
+ /* Here we call the lock releasing to ensure the currently active calculation will be ignored.
278
+ * This is needed to ensure no 'lost update'.
279
+ * Lost update can occur if we change data in this method and after that some calculation finishes
280
+ * having the earlier data as its base to calculate its data set result. And the earlier data
281
+ * has no changes applied inside this method, so we will lose them.
282
+ *
283
+ * This is not so good solution: ideally, we should acquire lock before performing any data updating.
284
+ * But the goal of this method is to provide an instant ability to update the cached data.
285
+ * And if we start acquiring the lock here the data update can be postponed significantly.
286
+ * And this kills the desired nature of this method.
287
+ * So we better lose some data retrieval (means abusing the resource a bit) than lose
288
+ * the instant update expected after this method execution.
289
+ */
290
+ this._requestsManager.finishActiveCalculation(cacheId);
291
+ }
292
+ } catch (e) {
293
+ improveAndRethrow(e, `${this._bio}.actualizeCachedData`);
294
+ }
295
+ }
296
+
297
+ invalidate(key) {
298
+ this._cache.invalidate(key);
299
+ this._requestsManager.finishActiveCalculation(key);
300
+ }
301
+
302
+ invalidateContaining(keyPart) {
303
+ this._cache.invalidateContaining(keyPart);
304
+ this._requestsManager.finishAllActiveCalculations(keyPart);
305
+ }
306
+
307
+ markAsExpiredButDontRemove(key) {
308
+ if (this._removeExpiredCacheAutomatically) {
309
+ this._cache.markCacheItemAsExpiredButDontRemove(key, this._cacheTtlMs);
310
+ } else {
311
+ this._cache.setLastUpdateTimestamp(key, Date.now() - this._cacheTtlMs - 1);
312
+ }
313
+ this._requestsManager.finishAllActiveCalculations(key);
314
+ }
315
+ }
316
+
317
+ /**
318
+ * Util class to control access to a resource when it can be called in parallel for the same result.
319
+ * (E.g. getting today coins-fiat rates from some API).
320
+ */
321
+ class ManagerOfRequestsToTheSameResource {
322
+ /**
323
+ * @param bio {string} resource-related identifier for logging
324
+ * @param [maxPollsCount=100] {number} max number of attempts to wait when waiting for a lock acquisition
325
+ * @param [timeoutDuration=1000] {number} timeout between the polls for a lock acquisition
326
+ */
327
+ constructor(bio, maxPollsCount = 100, timeoutDuration = 1000) {
328
+ this.bio = bio;
329
+ this.maxPollsCount = maxPollsCount;
330
+ this.timeoutDuration = timeoutDuration;
331
+ this._activeCalculationsIds = new Map();
332
+ this._nextCalculationIds = new Map();
333
+ }
334
+
335
+ /**
336
+ * If there is no active calculation just creates uuid and returns it.
337
+ * If there is active calculation waits until it removed from the active calculation uuid variable.
338
+ *
339
+ * @param requestHash {string}
340
+ * @return {Promise<string|boolean>} returns uuid of new active calculation or true if waiting for active
341
+ * calculation succeed or false if max attempts count exceeded
342
+ */
343
+ async startCalculationOrWaitForActiveToFinish(requestHash) {
344
+ try {
345
+ const activeCalculationIdForHash = this._activeCalculationsIds.get(requestHash);
346
+ if (activeCalculationIdForHash == null) {
347
+ const id = v4();
348
+ this._activeCalculationsIds.set(requestHash, id);
349
+ return id;
350
+ }
351
+
352
+ return await this._waitForCalculationIdToFinish(requestHash, activeCalculationIdForHash, 0);
353
+ } catch (e) {
354
+ Logger.logError(e, `startCalculationOrWaitForActiveToFinish_${this.bio}`);
355
+ }
356
+
357
+ return null;
358
+ }
359
+
360
+ /**
361
+ * Acquires lock to the resource by the provided hash.
362
+ *
363
+ * @param requestHash {string}
364
+ * @return {Promise<{ result: true, lockId: string }|{ result: false }>} result is true if the lock is successfully
365
+ * acquired, false if the max allowed time to wait for acquisition expired or any unexpected error occurs
366
+ * during the waiting.
367
+ */
368
+ async acquireLock(requestHash) {
369
+ try {
370
+ const activeId = this._activeCalculationsIds.get(requestHash);
371
+ const nextId = v4();
372
+ if (activeId == null) {
373
+ this._activeCalculationsIds.set(requestHash, nextId);
374
+ return { result: true, lockId: nextId };
375
+ }
376
+
377
+ const currentNext = this._nextCalculationIds.get(requestHash) ?? [];
378
+ currentNext.push(nextId);
379
+ this._nextCalculationIds.set(requestHash, currentNext);
380
+
381
+ const waitingResult = await this._waitForCalculationIdToFinish(requestHash, activeId, 0, nextId);
382
+ return {
383
+ result: waitingResult,
384
+ lockId: waitingResult ? nextId : undefined,
385
+ };
386
+ } catch (e) {
387
+ improveAndRethrow(e, "acquireLock");
388
+ }
389
+ }
390
+
391
+ /**
392
+ * Clears active calculation id.
393
+ * WARNING: if you forget to call this method the start* one will perform maxPollsCount attempts before finishing
394
+ * @param requestHash {string} hash of request. Helps to distinct the request for the same resource but
395
+ * having different request parameters and hold a dedicated calculation id per this hash
396
+ */
397
+ finishActiveCalculation(requestHash = "default") {
398
+ try {
399
+ this._activeCalculationsIds.delete(requestHash);
400
+ const next = this._nextCalculationIds.get(requestHash) ?? [];
401
+ if (next.length) {
402
+ this._activeCalculationsIds.set(requestHash, next[0]);
403
+ this._nextCalculationIds.set(requestHash, next.slice(1));
404
+ }
405
+ } catch (e) {
406
+ improveAndRethrow(e, "finishActiveCalculation");
407
+ }
408
+ }
409
+
410
+ finishAllActiveCalculations(keyPart = "") {
411
+ try {
412
+ Array.from(this._activeCalculationsIds.keys()).forEach(hash => {
413
+ if (typeof hash === "string" && new RegExp(keyPart).test(hash)) {
414
+ this.finishActiveCalculation(hash);
415
+ }
416
+ });
417
+ } catch (e) {
418
+ improveAndRethrow(e, "finishAllActiveCalculations");
419
+ }
420
+ }
421
+
422
+ /**
423
+ * @param requestHash {string}
424
+ * @param lockId {string}
425
+ * @return {boolean}
426
+ */
427
+ isTheLockActiveOne(requestHash, lockId) {
428
+ try {
429
+ return this._activeCalculationsIds.get(requestHash) === lockId;
430
+ } catch (e) {
431
+ improveAndRethrow(e, "isTheLockActiveOne");
432
+ }
433
+ }
434
+
435
+ /**
436
+ * @param requestHash {string}
437
+ * @param activeCalculationId {string|null}
438
+ * @param [attemptIndex=0] {number}
439
+ * @param waitForCalculationId {string|null} if you want to wait for an exact id to appear as active then pass this parameter
440
+ * @return {Promise<boolean>} true
441
+ * - if the given calculation id is no more an active one
442
+ * - or it is equal to waitForCalculationId
443
+ * false
444
+ * - if waiting period exceeds the max allowed waiting time or unexpected error occurs
445
+ * @private
446
+ */
447
+ async _waitForCalculationIdToFinish(
448
+ requestHash,
449
+ activeCalculationId,
450
+ attemptIndex = 0,
451
+ waitForCalculationId = null
452
+ ) {
453
+ try {
454
+ if (attemptIndex + 1 > this.maxPollsCount) {
455
+ // Max number of polls for active calculation id change is achieved. So we return false.
456
+ return false;
457
+ }
458
+
459
+ const currentId = this._activeCalculationsIds.get(requestHash);
460
+ if (waitForCalculationId == null ? currentId !== activeCalculationId : currentId === waitForCalculationId) {
461
+ /* We return true depending on the usage of this function:
462
+ * 1. if there is calculation id that we should wait for to become an active then we return true only
463
+ * if this id becomes the active one.
464
+ *
465
+ * Theoretically we can fail to wait for the desired calculation id. This can be caused by wrong use of
466
+ * this service or by any other mistakes/errors. But this waiting function will return false anyway if
467
+ * the number of polls done exceeds the max allowed.
468
+ *
469
+ * 2. if we just wait for the currently active calculation id to be finished then we return true
470
+ * when we notice that the current active id differs from the original passed into this function.
471
+ */
472
+ return true;
473
+ } else {
474
+ /* The original calculation id is still the active one, so we are scheduling a new attempt to check
475
+ * whether the active calculation id changed or not in timeoutDuration milliseconds.
476
+ */
477
+ const it = this;
478
+ return new Promise((resolve, reject) => {
479
+ setTimeout(function () {
480
+ try {
481
+ resolve(
482
+ it._waitForCalculationIdToFinish(requestHash, activeCalculationId, attemptIndex + 1)
483
+ );
484
+ } catch (e) {
485
+ reject(e);
486
+ }
487
+ }, this.timeoutDuration);
488
+ });
489
+ }
490
+ } catch (e) {
491
+ Logger.logError(e, "_waitForCalculationIdToFinish", "Failed to wait for active calculation id change.");
492
+ return false;
493
+ }
494
+ }
495
+ }
@@ -0,0 +1,147 @@
1
+ import Hashes from "jshashes";
2
+
3
+ import { improveAndRethrow } from "../common-apis/utils/errorUtils.js";
4
+ import { safeStringify } from "../common-apis/utils/safeStringify.js";
5
+ import { Logger } from "../common-apis/utils/logging/logger.js";
6
+
7
+ import { RobustExternalAPICallerService } from "./robustExternalAPICallerService.js";
8
+ import { CacheAndConcurrentRequestsResolver } from "./cacheAndConcurrentRequestsResolver.js";
9
+
10
+ /**
11
+ * Extended edit of RobustExternalApiCallerService supporting cache and management of concurrent requests
12
+ * to the same resource.
13
+ * TODO: [tests, critical] Massively used logic
14
+ */
15
+ export class CachedRobustExternalApiCallerService {
16
+ /**
17
+ * @param bio {string} unique service identifier
18
+ * @param cache {Cache} cache instance
19
+ * @param providersData {ExternalApiProvider[]} array of providers
20
+ * @param [cacheTtlMs=10000] {number} time to live for cache ms
21
+ * @param [maxCallAttemptsToWaitForAlreadyRunningRequest=50] {number} see details in CacheAndConcurrentRequestsResolver
22
+ * @param [timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished=3000] {number} see details in CacheAndConcurrentRequestsResolver
23
+ * @param [removeExpiredCacheAutomatically=true] {boolean} whether to remove cached data automatically when ttl exceeds
24
+ * @param [mergeCachedAndNewlyRetrievedData=null] {function} function accepting cached data, newly retrieved data and id field name for list items
25
+ * and merging them. use if needed
26
+ */
27
+ constructor(
28
+ bio,
29
+ cache,
30
+ providersData,
31
+ cacheTtlMs = 10000,
32
+ removeExpiredCacheAutomatically = true,
33
+ mergeCachedAndNewlyRetrievedData = null,
34
+ maxCallAttemptsToWaitForAlreadyRunningRequest = 100,
35
+ timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished = 1000
36
+ ) {
37
+ this._provider = new RobustExternalAPICallerService(`cached_${bio}`, providersData, Logger.logError);
38
+ this._cacheTtlMs = cacheTtlMs;
39
+ this._cahceAndRequestsResolver = new CacheAndConcurrentRequestsResolver(
40
+ bio,
41
+ cache,
42
+ cacheTtlMs,
43
+ removeExpiredCacheAutomatically,
44
+ maxCallAttemptsToWaitForAlreadyRunningRequest,
45
+ timeoutBetweenAttemptsToCheckWhetherAlreadyRunningRequestFinished
46
+ );
47
+ this._cahceIds = [];
48
+ this._mergeCachedAndNewlyRetrievedData = mergeCachedAndNewlyRetrievedData;
49
+ }
50
+
51
+ /**
52
+ * Calls the external API or returns data from cache. Just waits if the same data already requested.
53
+ *
54
+ * @param parametersValues {array} array of values of the parameters for URL query string [and/or body]
55
+ * @param timeoutMS {number} http timeout to wait for response. If provider has its specific timeout value then it is used
56
+ * @param [cancelToken] {object|undefined} axios token to force-cancel requests from high-level code
57
+ * @param [attemptsCount] {number|undefined} number of attempts to be performed
58
+ * @param [customHashFunctionForParams] {function|undefined} function without params calculating the hash to be
59
+ * added to bio of the service to compose a unique parameters-specific cache id
60
+ * @param [doNotFailForNowData] {boolean|undefined} pass true if you do not want us to throw an error if we retrieved null data from all the providers
61
+ * @return {Promise<any>} resolving to retrieved data (or array of results if specific provider requires
62
+ * several requests. NOTE: we flatten nested arrays - results of each separate request done for the specific provider)
63
+ * @throws Error if requests to all providers are failed
64
+ */
65
+ async callExternalAPICached(
66
+ parametersValues = [],
67
+ timeoutMS = 3500,
68
+ cancelToken = null,
69
+ attemptsCount = 1,
70
+ customHashFunctionForParams = null,
71
+ doNotFailForNowData = false
72
+ ) {
73
+ const loggerSource = `${this._provider.bio}.callExternalAPICached`;
74
+ let cacheId;
75
+ let result;
76
+ try {
77
+ cacheId = this._calculateCacheId(parametersValues, customHashFunctionForParams);
78
+ result = await this._cahceAndRequestsResolver.getCachedOrWaitForCachedOrAcquireLock(cacheId);
79
+ if (!result?.canStartDataRetrieval) {
80
+ return result?.cachedData;
81
+ }
82
+
83
+ let data = await this._provider.callExternalAPI(
84
+ parametersValues,
85
+ timeoutMS,
86
+ cancelToken,
87
+ attemptsCount,
88
+ doNotFailForNowData
89
+ );
90
+
91
+ const canPerformMerge = typeof this._mergeCachedAndNewlyRetrievedData === "function";
92
+ if (canPerformMerge) {
93
+ const mostRecentCached = this._cahceAndRequestsResolver.getCached(cacheId);
94
+ data = this._mergeCachedAndNewlyRetrievedData(mostRecentCached, data, parametersValues);
95
+ }
96
+ if (data != null) {
97
+ this._cahceAndRequestsResolver.saveCachedData(cacheId, result?.lockId, data, true, canPerformMerge);
98
+ this._cahceIds.indexOf(cacheId) < 0 && this._cahceIds.push(cacheId);
99
+ }
100
+
101
+ return data;
102
+ } catch (e) {
103
+ improveAndRethrow(e, loggerSource);
104
+ } finally {
105
+ this._cahceAndRequestsResolver.releaseLock(cacheId, result?.lockId);
106
+ }
107
+ }
108
+
109
+ invalidateCaches() {
110
+ this._cahceIds.forEach(key => this._cahceAndRequestsResolver.invalidate(key));
111
+ }
112
+
113
+ actualizeCachedData(
114
+ params,
115
+ synchronousCurrentCacheProcessor,
116
+ customHashFunctionForParams = null,
117
+ sessionDependent = true,
118
+ actualizedAtTimestamp
119
+ ) {
120
+ const cacheId = this._calculateCacheId(params, customHashFunctionForParams);
121
+ this._cahceAndRequestsResolver.actualizeCachedData(cacheId, synchronousCurrentCacheProcessor, sessionDependent);
122
+ }
123
+
124
+ markCacheAsExpiredButDontRemove(parametersValues, customHashFunctionForParams) {
125
+ try {
126
+ this._cahceAndRequestsResolver.markAsExpiredButDontRemove(
127
+ this._calculateCacheId(parametersValues, customHashFunctionForParams)
128
+ );
129
+ } catch (e) {
130
+ improveAndRethrow(e, "markCacheAsExpiredButDontRemove");
131
+ }
132
+ }
133
+
134
+ _calculateCacheId(parametersValues, customHashFunctionForParams = null) {
135
+ try {
136
+ const hash =
137
+ typeof customHashFunctionForParams === "function"
138
+ ? customHashFunctionForParams(parametersValues)
139
+ : !parametersValues
140
+ ? ""
141
+ : new Hashes.SHA512().hex(safeStringify(parametersValues));
142
+ return `${this._provider.bio}-${hash}`;
143
+ } catch (e) {
144
+ improveAndRethrow(e, this._provider.bio + "_calculateCacheId");
145
+ }
146
+ }
147
+ }
@@ -0,0 +1,29 @@
1
+ import axios from "axios";
2
+
3
+ /**
4
+ * Utils class needed to perform cancelling of axios request inside some process.
5
+ * Provides cancel state and axios token for HTTP requests
6
+ */
7
+ export class CancelProcessing {
8
+ constructor() {
9
+ this._cancelToken = axios.CancelToken.source();
10
+ this._isCanceled = false;
11
+ }
12
+
13
+ cancel() {
14
+ this._isCanceled = true;
15
+ this._cancelToken.cancel();
16
+ }
17
+
18
+ isCanceled() {
19
+ return this._isCanceled;
20
+ }
21
+
22
+ getToken() {
23
+ return this._cancelToken.token;
24
+ }
25
+
26
+ static instance() {
27
+ return new CancelProcessing();
28
+ }
29
+ }