react-cheminfo 0.76.0 → 0.78.0

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 (389) hide show
  1. package/README.md +141 -31
  2. package/lib/card/core/cardAddress.d.ts +19 -2
  3. package/lib/card/core/cardAddress.d.ts.map +1 -1
  4. package/lib/card/core/cardAddress.js +35 -2
  5. package/lib/card/core/cardAddress.js.map +1 -1
  6. package/lib/card/core/cardPageUrl.d.ts +7 -1
  7. package/lib/card/core/cardPageUrl.d.ts.map +1 -1
  8. package/lib/card/core/cardPageUrl.js +4 -2
  9. package/lib/card/core/cardPageUrl.js.map +1 -1
  10. package/lib/card/core/index.d.ts +1 -1
  11. package/lib/card/core/index.d.ts.map +1 -1
  12. package/lib/card/core/index.js +1 -1
  13. package/lib/card/core/index.js.map +1 -1
  14. package/lib/credits/core/credits.d.ts +30 -0
  15. package/lib/credits/core/credits.d.ts.map +1 -1
  16. package/lib/credits/core/credits.js +35 -0
  17. package/lib/credits/core/credits.js.map +1 -1
  18. package/lib/download/core/downloadFigure.d.ts +12 -1
  19. package/lib/download/core/downloadFigure.d.ts.map +1 -1
  20. package/lib/download/core/downloadFigure.js +21 -0
  21. package/lib/download/core/downloadFigure.js.map +1 -1
  22. package/lib/download/core/figureDpi.d.ts +59 -0
  23. package/lib/download/core/figureDpi.d.ts.map +1 -0
  24. package/lib/download/core/figureDpi.js +75 -0
  25. package/lib/download/core/figureDpi.js.map +1 -0
  26. package/lib/download/core/figurePng.d.ts +4 -1
  27. package/lib/download/core/figurePng.d.ts.map +1 -1
  28. package/lib/download/core/figurePng.js +11 -2
  29. package/lib/download/core/figurePng.js.map +1 -1
  30. package/lib/download/core/index.d.ts +4 -1
  31. package/lib/download/core/index.d.ts.map +1 -1
  32. package/lib/download/core/index.js +3 -1
  33. package/lib/download/core/index.js.map +1 -1
  34. package/lib/download/core/pngDpi.d.ts +22 -0
  35. package/lib/download/core/pngDpi.d.ts.map +1 -0
  36. package/lib/download/core/pngDpi.js +79 -0
  37. package/lib/download/core/pngDpi.js.map +1 -0
  38. package/lib/download/ui/FigureDownloadPanel.d.ts +13 -0
  39. package/lib/download/ui/FigureDownloadPanel.d.ts.map +1 -1
  40. package/lib/download/ui/FigureDownloadPanel.js +18 -34
  41. package/lib/download/ui/FigureDownloadPanel.js.map +1 -1
  42. package/lib/download/ui/figureDownloadHint.d.ts +5 -0
  43. package/lib/download/ui/figureDownloadHint.d.ts.map +1 -1
  44. package/lib/download/ui/figureDownloadHint.js +8 -0
  45. package/lib/download/ui/figureDownloadHint.js.map +1 -1
  46. package/lib/download/ui/figureDownloadOptions.d.ts +84 -0
  47. package/lib/download/ui/figureDownloadOptions.d.ts.map +1 -0
  48. package/lib/download/ui/figureDownloadOptions.js +2 -0
  49. package/lib/download/ui/figureDownloadOptions.js.map +1 -0
  50. package/lib/download/ui/resolutionChoices.d.ts +27 -0
  51. package/lib/download/ui/resolutionChoices.d.ts.map +1 -0
  52. package/lib/download/ui/resolutionChoices.js +48 -0
  53. package/lib/download/ui/resolutionChoices.js.map +1 -0
  54. package/lib/download/ui/useFigureActions.d.ts +4 -1
  55. package/lib/download/ui/useFigureActions.d.ts.map +1 -1
  56. package/lib/download/ui/useFigureActions.js +17 -8
  57. package/lib/download/ui/useFigureActions.js.map +1 -1
  58. package/lib/download/ui/useFigureDownload.d.ts +2 -55
  59. package/lib/download/ui/useFigureDownload.d.ts.map +1 -1
  60. package/lib/download/ui/useFigureDownload.js +45 -13
  61. package/lib/download/ui/useFigureDownload.js.map +1 -1
  62. package/lib/ecosystem/core/sites.d.ts.map +1 -1
  63. package/lib/ecosystem/core/sites.js +14 -0
  64. package/lib/ecosystem/core/sites.js.map +1 -1
  65. package/lib/ecosystem/core/types.d.ts +1 -1
  66. package/lib/ecosystem/core/types.d.ts.map +1 -1
  67. package/lib/ecosystem/ui/glyphs.d.ts.map +1 -1
  68. package/lib/ecosystem/ui/glyphs.js +3 -0
  69. package/lib/ecosystem/ui/glyphs.js.map +1 -1
  70. package/lib/host/core/connectHost.js +1 -1
  71. package/lib/host/core/connectHost.js.map +1 -1
  72. package/lib/host/core/connectTool.d.ts +103 -0
  73. package/lib/host/core/connectTool.d.ts.map +1 -0
  74. package/lib/host/core/connectTool.js +169 -0
  75. package/lib/host/core/connectTool.js.map +1 -0
  76. package/lib/host/core/hostOutputs.d.ts +16 -3
  77. package/lib/host/core/hostOutputs.d.ts.map +1 -1
  78. package/lib/host/core/hostOutputs.js +38 -4
  79. package/lib/host/core/hostOutputs.js.map +1 -1
  80. package/lib/host/core/index.d.ts +8 -2
  81. package/lib/host/core/index.d.ts.map +1 -1
  82. package/lib/host/core/index.js +4 -1
  83. package/lib/host/core/index.js.map +1 -1
  84. package/lib/host/core/kinds.d.ts +31 -3
  85. package/lib/host/core/kinds.d.ts.map +1 -1
  86. package/lib/host/core/kinds.js +35 -4
  87. package/lib/host/core/kinds.js.map +1 -1
  88. package/lib/host/core/messageValues.d.ts +29 -0
  89. package/lib/host/core/messageValues.d.ts.map +1 -0
  90. package/lib/host/core/messageValues.js +57 -0
  91. package/lib/host/core/messageValues.js.map +1 -0
  92. package/lib/host/core/protocol.d.ts +2 -5
  93. package/lib/host/core/protocol.d.ts.map +1 -1
  94. package/lib/host/core/protocol.js +3 -37
  95. package/lib/host/core/protocol.js.map +1 -1
  96. package/lib/host/core/resultKinds.d.ts +74 -0
  97. package/lib/host/core/resultKinds.d.ts.map +1 -0
  98. package/lib/host/core/resultKinds.js +40 -0
  99. package/lib/host/core/resultKinds.js.map +1 -0
  100. package/lib/host/core/toolMessages.d.ts +24 -0
  101. package/lib/host/core/toolMessages.d.ts.map +1 -0
  102. package/lib/host/core/toolMessages.js +136 -0
  103. package/lib/host/core/toolMessages.js.map +1 -0
  104. package/lib/host/ui/index.d.ts +2 -0
  105. package/lib/host/ui/index.d.ts.map +1 -1
  106. package/lib/host/ui/index.js +1 -0
  107. package/lib/host/ui/index.js.map +1 -1
  108. package/lib/host/ui/useFramedTool.d.ts +74 -0
  109. package/lib/host/ui/useFramedTool.d.ts.map +1 -0
  110. package/lib/host/ui/useFramedTool.js +125 -0
  111. package/lib/host/ui/useFramedTool.js.map +1 -0
  112. package/lib/i18n/core/chromeCatalog.d.ts +1 -1
  113. package/lib/i18n/core/chromeCatalog.d.ts.map +1 -1
  114. package/lib/locales/de.json +30 -1
  115. package/lib/locales/en.json +30 -1
  116. package/lib/locales/es.json +30 -1
  117. package/lib/locales/fr.json +30 -1
  118. package/lib/locales/it.json +30 -1
  119. package/lib/molecule3d/ui/Molecule3DStage.d.ts +4 -33
  120. package/lib/molecule3d/ui/Molecule3DStage.d.ts.map +1 -1
  121. package/lib/molecule3d/ui/Molecule3DStage.js +3 -37
  122. package/lib/molecule3d/ui/Molecule3DStage.js.map +1 -1
  123. package/lib/molecule3d/ui/MoleculeCanvas3D.d.ts.map +1 -1
  124. package/lib/molecule3d/ui/MoleculeCanvas3D.js +10 -2
  125. package/lib/molecule3d/ui/MoleculeCanvas3D.js.map +1 -1
  126. package/lib/molecule3d/ui/exportMoleculeImage.d.ts +10 -0
  127. package/lib/molecule3d/ui/exportMoleculeImage.d.ts.map +1 -1
  128. package/lib/molecule3d/ui/exportMoleculeImage.js +17 -0
  129. package/lib/molecule3d/ui/exportMoleculeImage.js.map +1 -1
  130. package/lib/molecule3d/ui/index.d.ts +1 -1
  131. package/lib/molecule3d/ui/index.d.ts.map +1 -1
  132. package/lib/molecule3d/ui/moleculeViewer3DProps.d.ts +23 -0
  133. package/lib/molecule3d/ui/moleculeViewer3DProps.d.ts.map +1 -1
  134. package/lib/molecule3d/ui/useImageExport.d.ts +3 -0
  135. package/lib/molecule3d/ui/useImageExport.d.ts.map +1 -1
  136. package/lib/molecule3d/ui/useImageExport.js +21 -3
  137. package/lib/molecule3d/ui/useImageExport.js.map +1 -1
  138. package/lib/osiris/core/barGeometry.d.ts +42 -0
  139. package/lib/osiris/core/barGeometry.d.ts.map +1 -0
  140. package/lib/osiris/core/barGeometry.js +46 -0
  141. package/lib/osiris/core/barGeometry.js.map +1 -0
  142. package/lib/osiris/core/computeProperties.d.ts +42 -0
  143. package/lib/osiris/core/computeProperties.d.ts.map +1 -0
  144. package/lib/osiris/core/computeProperties.js +115 -0
  145. package/lib/osiris/core/computeProperties.js.map +1 -0
  146. package/lib/osiris/core/constants.d.ts +70 -0
  147. package/lib/osiris/core/constants.d.ts.map +1 -0
  148. package/lib/osiris/core/constants.js +99 -0
  149. package/lib/osiris/core/constants.js.map +1 -0
  150. package/lib/osiris/core/fitness.d.ts +32 -0
  151. package/lib/osiris/core/fitness.d.ts.map +1 -0
  152. package/lib/osiris/core/fitness.js +48 -0
  153. package/lib/osiris/core/fitness.js.map +1 -0
  154. package/lib/osiris/core/index.d.ts +29 -0
  155. package/lib/osiris/core/index.d.ts.map +1 -0
  156. package/lib/osiris/core/index.js +19 -0
  157. package/lib/osiris/core/index.js.map +1 -0
  158. package/lib/osiris/core/poolSize.d.ts +17 -0
  159. package/lib/osiris/core/poolSize.d.ts.map +1 -0
  160. package/lib/osiris/core/poolSize.js +22 -0
  161. package/lib/osiris/core/poolSize.js.map +1 -0
  162. package/lib/osiris/core/poolTypes.d.ts +140 -0
  163. package/lib/osiris/core/poolTypes.d.ts.map +1 -0
  164. package/lib/osiris/core/poolTypes.js +12 -0
  165. package/lib/osiris/core/poolTypes.js.map +1 -0
  166. package/lib/osiris/core/predictIdCode.d.ts +39 -0
  167. package/lib/osiris/core/predictIdCode.d.ts.map +1 -0
  168. package/lib/osiris/core/predictIdCode.js +54 -0
  169. package/lib/osiris/core/predictIdCode.js.map +1 -0
  170. package/lib/osiris/core/predictProtocol.d.ts +67 -0
  171. package/lib/osiris/core/predictProtocol.d.ts.map +1 -0
  172. package/lib/osiris/core/predictProtocol.js +29 -0
  173. package/lib/osiris/core/predictProtocol.js.map +1 -0
  174. package/lib/osiris/core/predictionPool.d.ts +23 -0
  175. package/lib/osiris/core/predictionPool.d.ts.map +1 -0
  176. package/lib/osiris/core/predictionPool.js +186 -0
  177. package/lib/osiris/core/predictionPool.js.map +1 -0
  178. package/lib/osiris/core/predictionQueue.d.ts +46 -0
  179. package/lib/osiris/core/predictionQueue.d.ts.map +1 -0
  180. package/lib/osiris/core/predictionQueue.js +57 -0
  181. package/lib/osiris/core/predictionQueue.js.map +1 -0
  182. package/lib/osiris/core/predictors.d.ts +62 -0
  183. package/lib/osiris/core/predictors.d.ts.map +1 -0
  184. package/lib/osiris/core/predictors.js +100 -0
  185. package/lib/osiris/core/predictors.js.map +1 -0
  186. package/lib/osiris/core/propertyKeys.d.ts +32 -0
  187. package/lib/osiris/core/propertyKeys.d.ts.map +1 -0
  188. package/lib/osiris/core/propertyKeys.js +44 -0
  189. package/lib/osiris/core/propertyKeys.js.map +1 -0
  190. package/lib/osiris/core/riskAppearance.d.ts +60 -0
  191. package/lib/osiris/core/riskAppearance.d.ts.map +1 -0
  192. package/lib/osiris/core/riskAppearance.js +59 -0
  193. package/lib/osiris/core/riskAppearance.js.map +1 -0
  194. package/lib/osiris/core/riskDetail.d.ts +22 -0
  195. package/lib/osiris/core/riskDetail.d.ts.map +1 -0
  196. package/lib/osiris/core/riskDetail.js +86 -0
  197. package/lib/osiris/core/riskDetail.js.map +1 -0
  198. package/lib/osiris/core/riskDetailRequest.d.ts +31 -0
  199. package/lib/osiris/core/riskDetailRequest.d.ts.map +1 -0
  200. package/lib/osiris/core/riskDetailRequest.js +49 -0
  201. package/lib/osiris/core/riskDetailRequest.js.map +1 -0
  202. package/lib/osiris/core/riskFindings.d.ts +26 -0
  203. package/lib/osiris/core/riskFindings.d.ts.map +1 -0
  204. package/lib/osiris/core/riskFindings.js +79 -0
  205. package/lib/osiris/core/riskFindings.js.map +1 -0
  206. package/lib/osiris/core/runPredictionJob.d.ts +20 -0
  207. package/lib/osiris/core/runPredictionJob.d.ts.map +1 -0
  208. package/lib/osiris/core/runPredictionJob.js +43 -0
  209. package/lib/osiris/core/runPredictionJob.js.map +1 -0
  210. package/lib/osiris/core/scales.d.ts +70 -0
  211. package/lib/osiris/core/scales.d.ts.map +1 -0
  212. package/lib/osiris/core/scales.js +185 -0
  213. package/lib/osiris/core/scales.js.map +1 -0
  214. package/lib/osiris/core/types.d.ts +91 -0
  215. package/lib/osiris/core/types.d.ts.map +1 -0
  216. package/lib/osiris/core/types.js +19 -0
  217. package/lib/osiris/core/types.js.map +1 -0
  218. package/lib/osiris/core/valuation.d.ts +28 -0
  219. package/lib/osiris/core/valuation.d.ts.map +1 -0
  220. package/lib/osiris/core/valuation.js +39 -0
  221. package/lib/osiris/core/valuation.js.map +1 -0
  222. package/lib/osiris/ui/PropertyBar.d.ts +27 -0
  223. package/lib/osiris/ui/PropertyBar.d.ts.map +1 -0
  224. package/lib/osiris/ui/PropertyBar.js +27 -0
  225. package/lib/osiris/ui/PropertyBar.js.map +1 -0
  226. package/lib/osiris/ui/PropertyPanel.d.ts +91 -0
  227. package/lib/osiris/ui/PropertyPanel.d.ts.map +1 -0
  228. package/lib/osiris/ui/PropertyPanel.js +77 -0
  229. package/lib/osiris/ui/PropertyPanel.js.map +1 -0
  230. package/lib/osiris/ui/PropertyRows.d.ts +40 -0
  231. package/lib/osiris/ui/PropertyRows.d.ts.map +1 -0
  232. package/lib/osiris/ui/PropertyRows.js +23 -0
  233. package/lib/osiris/ui/PropertyRows.js.map +1 -0
  234. package/lib/osiris/ui/RiskFindings.d.ts +43 -0
  235. package/lib/osiris/ui/RiskFindings.d.ts.map +1 -0
  236. package/lib/osiris/ui/RiskFindings.js +51 -0
  237. package/lib/osiris/ui/RiskFindings.js.map +1 -0
  238. package/lib/osiris/ui/RiskPanel.d.ts +46 -0
  239. package/lib/osiris/ui/RiskPanel.d.ts.map +1 -0
  240. package/lib/osiris/ui/RiskPanel.js +32 -0
  241. package/lib/osiris/ui/RiskPanel.js.map +1 -0
  242. package/lib/osiris/ui/RiskSquare.d.ts +51 -0
  243. package/lib/osiris/ui/RiskSquare.d.ts.map +1 -0
  244. package/lib/osiris/ui/RiskSquare.js +23 -0
  245. package/lib/osiris/ui/RiskSquare.js.map +1 -0
  246. package/lib/osiris/ui/index.d.ts +15 -0
  247. package/lib/osiris/ui/index.d.ts.map +1 -0
  248. package/lib/osiris/ui/index.js +8 -0
  249. package/lib/osiris/ui/index.js.map +1 -0
  250. package/lib/osiris/ui/usePredictions.d.ts +81 -0
  251. package/lib/osiris/ui/usePredictions.d.ts.map +1 -0
  252. package/lib/osiris/ui/usePredictions.js +102 -0
  253. package/lib/osiris/ui/usePredictions.js.map +1 -0
  254. package/lib/osiris-ui.d.ts +11 -0
  255. package/lib/osiris-ui.d.ts.map +1 -0
  256. package/lib/osiris-ui.js +11 -0
  257. package/lib/osiris-ui.js.map +1 -0
  258. package/lib/osiris.d.ts +17 -0
  259. package/lib/osiris.d.ts.map +1 -0
  260. package/lib/osiris.js +17 -0
  261. package/lib/osiris.js.map +1 -0
  262. package/lib/panel/ui/FilterInput.d.ts.map +1 -1
  263. package/lib/panel/ui/FilterInput.js +3 -1
  264. package/lib/panel/ui/FilterInput.js.map +1 -1
  265. package/lib/panel/ui/SidePanelItem.d.ts +39 -0
  266. package/lib/panel/ui/SidePanelItem.d.ts.map +1 -0
  267. package/lib/panel/ui/SidePanelItem.js +63 -0
  268. package/lib/panel/ui/SidePanelItem.js.map +1 -0
  269. package/lib/panel/ui/SidePanelStack.d.ts +11 -10
  270. package/lib/panel/ui/SidePanelStack.d.ts.map +1 -1
  271. package/lib/panel/ui/SidePanelStack.js +22 -15
  272. package/lib/panel/ui/SidePanelStack.js.map +1 -1
  273. package/lib/panel/ui/SpectrumColorSwatch.d.ts.map +1 -1
  274. package/lib/panel/ui/SpectrumColorSwatch.js +4 -2
  275. package/lib/panel/ui/SpectrumColorSwatch.js.map +1 -1
  276. package/lib/panel/ui/usePanelFolds.d.ts +35 -0
  277. package/lib/panel/ui/usePanelFolds.d.ts.map +1 -0
  278. package/lib/panel/ui/usePanelFolds.js +73 -0
  279. package/lib/panel/ui/usePanelFolds.js.map +1 -0
  280. package/lib/shared/ui/FullscreenStage.d.ts +43 -0
  281. package/lib/shared/ui/FullscreenStage.d.ts.map +1 -0
  282. package/lib/shared/ui/FullscreenStage.js +38 -0
  283. package/lib/shared/ui/FullscreenStage.js.map +1 -0
  284. package/lib/shared/ui/canFillScreen.d.ts +7 -0
  285. package/lib/shared/ui/canFillScreen.d.ts.map +1 -0
  286. package/lib/shared/ui/canFillScreen.js +9 -0
  287. package/lib/shared/ui/canFillScreen.js.map +1 -0
  288. package/lib/shared/ui/index.d.ts +3 -0
  289. package/lib/shared/ui/index.d.ts.map +1 -1
  290. package/lib/shared/ui/index.js +2 -0
  291. package/lib/shared/ui/index.js.map +1 -1
  292. package/lib/split/ui/splitPanes.d.ts +3 -3
  293. package/lib/split/ui/splitPanes.js +1 -1
  294. package/lib/structure/core/structureNotations.d.ts +8 -1
  295. package/lib/structure/core/structureNotations.d.ts.map +1 -1
  296. package/lib/structure/core/structureNotations.js +14 -8
  297. package/lib/structure/core/structureNotations.js.map +1 -1
  298. package/lib/structure/ui/StructureExportPanel.d.ts +8 -0
  299. package/lib/structure/ui/StructureExportPanel.d.ts.map +1 -1
  300. package/lib/structure/ui/StructureExportPanel.js +9 -4
  301. package/lib/structure/ui/StructureExportPanel.js.map +1 -1
  302. package/lib/structure/ui/index.d.ts +1 -0
  303. package/lib/structure/ui/index.d.ts.map +1 -1
  304. package/lib/structure/ui/index.js.map +1 -1
  305. package/package.json +4 -1
  306. package/src/card/core/cardAddress.ts +39 -3
  307. package/src/card/core/cardPageUrl.ts +10 -3
  308. package/src/card/core/index.ts +7 -1
  309. package/src/credits/core/credits.ts +40 -0
  310. package/src/download/core/downloadFigure.ts +26 -1
  311. package/src/download/core/figureDpi.ts +94 -0
  312. package/src/download/core/figurePng.ts +11 -2
  313. package/src/download/core/index.ts +13 -1
  314. package/src/download/core/pngDpi.ts +84 -0
  315. package/src/download/ui/FigureDownloadPanel.tsx +41 -51
  316. package/src/download/ui/figureDownloadHint.ts +13 -0
  317. package/src/download/ui/figureDownloadOptions.ts +85 -0
  318. package/src/download/ui/resolutionChoices.ts +67 -0
  319. package/src/download/ui/useFigureActions.ts +24 -9
  320. package/src/download/ui/useFigureDownload.tsx +57 -71
  321. package/src/ecosystem/core/sites.ts +14 -0
  322. package/src/ecosystem/core/types.ts +2 -1
  323. package/src/ecosystem/ui/glyphs.tsx +20 -0
  324. package/src/host/core/connectHost.ts +1 -1
  325. package/src/host/core/connectTool.ts +285 -0
  326. package/src/host/core/hostOutputs.ts +47 -5
  327. package/src/host/core/index.ts +20 -1
  328. package/src/host/core/kinds.ts +53 -8
  329. package/src/host/core/messageValues.ts +58 -0
  330. package/src/host/core/protocol.ts +5 -37
  331. package/src/host/core/resultKinds.ts +65 -0
  332. package/src/host/core/toolMessages.ts +160 -0
  333. package/src/host/ui/index.ts +6 -0
  334. package/src/host/ui/useFramedTool.ts +213 -0
  335. package/src/locales/de.json +30 -1
  336. package/src/locales/en.json +30 -1
  337. package/src/locales/es.json +30 -1
  338. package/src/locales/fr.json +30 -1
  339. package/src/locales/it.json +30 -1
  340. package/src/molecule3d/ui/Molecule3DStage.tsx +4 -88
  341. package/src/molecule3d/ui/MoleculeCanvas3D.tsx +10 -1
  342. package/src/molecule3d/ui/exportMoleculeImage.ts +22 -0
  343. package/src/molecule3d/ui/index.ts +4 -1
  344. package/src/molecule3d/ui/moleculeViewer3DProps.ts +24 -0
  345. package/src/molecule3d/ui/useImageExport.ts +28 -2
  346. package/src/osiris/core/barGeometry.ts +74 -0
  347. package/src/osiris/core/computeProperties.ts +175 -0
  348. package/src/osiris/core/constants.ts +110 -0
  349. package/src/osiris/core/fitness.ts +59 -0
  350. package/src/osiris/core/index.ts +78 -0
  351. package/src/osiris/core/poolSize.ts +24 -0
  352. package/src/osiris/core/poolTypes.ts +140 -0
  353. package/src/osiris/core/predictIdCode.ts +69 -0
  354. package/src/osiris/core/predictProtocol.ts +78 -0
  355. package/src/osiris/core/predictionPool.ts +237 -0
  356. package/src/osiris/core/predictionQueue.ts +89 -0
  357. package/src/osiris/core/predictors.ts +142 -0
  358. package/src/osiris/core/propertyKeys.ts +54 -0
  359. package/src/osiris/core/riskAppearance.ts +86 -0
  360. package/src/osiris/core/riskDetail.ts +120 -0
  361. package/src/osiris/core/riskDetailRequest.ts +64 -0
  362. package/src/osiris/core/riskFindings.ts +92 -0
  363. package/src/osiris/core/runPredictionJob.ts +49 -0
  364. package/src/osiris/core/scales.ts +236 -0
  365. package/src/osiris/core/types.ts +103 -0
  366. package/src/osiris/core/valuation.ts +44 -0
  367. package/src/osiris/ui/PropertyBar.tsx +77 -0
  368. package/src/osiris/ui/PropertyPanel.tsx +227 -0
  369. package/src/osiris/ui/PropertyRows.tsx +92 -0
  370. package/src/osiris/ui/RiskFindings.tsx +137 -0
  371. package/src/osiris/ui/RiskPanel.tsx +81 -0
  372. package/src/osiris/ui/RiskSquare.tsx +108 -0
  373. package/src/osiris/ui/index.ts +17 -0
  374. package/src/osiris/ui/usePredictions.ts +194 -0
  375. package/src/osiris-ui.ts +11 -0
  376. package/src/osiris.ts +17 -0
  377. package/src/panel/ui/FilterInput.tsx +4 -1
  378. package/src/panel/ui/SidePanelItem.tsx +162 -0
  379. package/src/panel/ui/SidePanelStack.tsx +29 -36
  380. package/src/panel/ui/SpectrumColorSwatch.tsx +5 -3
  381. package/src/panel/ui/usePanelFolds.ts +103 -0
  382. package/src/shared/ui/FullscreenStage.tsx +96 -0
  383. package/src/shared/ui/canFillScreen.ts +8 -0
  384. package/src/shared/ui/index.ts +6 -0
  385. package/src/split/ui/splitPanes.tsx +4 -4
  386. package/src/structure/core/structureNotations.ts +25 -8
  387. package/src/structure/ui/StructureExportPanel.tsx +23 -4
  388. package/src/structure/ui/index.ts +4 -0
  389. package/styles/osiris.css +339 -0
@@ -0,0 +1,59 @@
1
+ /**
2
+ * How good one predicted number is, from 0 to 1, and where it sits on its bar.
3
+ *
4
+ * The fitness is what the green-to-red ramp reads; the position is what the
5
+ * bar's length reads. They are deliberately two numbers: a molecular weight of
6
+ * 780 is nearly the full width of its bar and nearly the worst fitness, while
7
+ * a drug-likeness of 0 is halfway along its bar and exactly half fit.
8
+ */
9
+
10
+ import { clamp } from '../../format/core/clamp.ts';
11
+
12
+ import type { PropertyKey } from './propertyKeys.ts';
13
+ import { propertyScale } from './scales.ts';
14
+ import { getValuation } from './valuation.ts';
15
+
16
+ /**
17
+ * How well a value sits in its property's preferred band, from 0 (as bad as
18
+ * the ramp goes) to 1 (inside the band).
19
+ *
20
+ * The drug score is already that fitness — it is the product of the four
21
+ * valuations `DrugScoreCalculator` computes — so it is returned as it stands
22
+ * rather than valued a second time.
23
+ * @param key - Which of the ten properties.
24
+ * @param value - The predicted number, or `null` when the predictor could not answer.
25
+ * @returns The fitness, or `null` for an unknown value.
26
+ */
27
+ export function propertyFitness(
28
+ key: PropertyKey,
29
+ value: number | null,
30
+ ): number | null {
31
+ if (value === null || !Number.isFinite(value)) return null;
32
+ const scale = propertyScale(key);
33
+ if (scale.halfWidth === null) return clamp(value, 0, 1);
34
+ return getValuation(
35
+ value,
36
+ scale.preferredMin,
37
+ scale.preferredMax,
38
+ scale.halfWidth,
39
+ );
40
+ }
41
+
42
+ /**
43
+ * Where a value sits along its bar, from 0 at the left end to 1 at the right.
44
+ *
45
+ * Clamped, because a range is a typical spread rather than a bound: cholesterol
46
+ * runs past the solubility axis and still has to draw.
47
+ * @param key - Which of the ten properties.
48
+ * @param value - The predicted number, or `null`.
49
+ * @returns The position, or `null` for an unknown value.
50
+ */
51
+ export function propertyPosition(
52
+ key: PropertyKey,
53
+ value: number | null,
54
+ ): number | null {
55
+ if (value === null || !Number.isFinite(value)) return null;
56
+ const { rangeMin, rangeMax } = propertyScale(key);
57
+ if (rangeMax === rangeMin) return 0;
58
+ return clamp((value - rangeMin) / (rangeMax - rangeMin), 0, 1);
59
+ }
@@ -0,0 +1,78 @@
1
+ export type { BarGeometry } from './barGeometry.ts';
2
+ export { barGeometry } from './barGeometry.ts';
3
+ export type { ComputePropertiesOptions } from './computeProperties.ts';
4
+ export {
5
+ EMPTY_MOLECULE_MESSAGE,
6
+ computeProperties,
7
+ } from './computeProperties.ts';
8
+ export {
9
+ DRUG_SCORE_RISK_FACTORS,
10
+ NO_FRAGMENT_MATCHED,
11
+ RISK_LABELS,
12
+ RISK_LEVELS,
13
+ RISK_LEVEL_LABELS,
14
+ RISK_TYPE_CODES,
15
+ UNKNOWN_NUMBER,
16
+ UNKNOWN_SURFACE_AREA,
17
+ riskCodeOf,
18
+ riskLevelOf,
19
+ } from './constants.ts';
20
+ export { propertyFitness, propertyPosition } from './fitness.ts';
21
+ export { MAX_POOL_SIZE, predictionPoolSize } from './poolSize.ts';
22
+ export type {
23
+ PredictionInput,
24
+ PredictionOutcome,
25
+ PredictionPool,
26
+ PredictionPoolOptions,
27
+ PredictionProgress,
28
+ } from './poolTypes.ts';
29
+ export {
30
+ UNREADABLE_ID_CODE_MESSAGE,
31
+ predictIdCode,
32
+ riskDetailOfIdCode,
33
+ } from './predictIdCode.ts';
34
+ export type {
35
+ PredictRequest,
36
+ PredictResponse,
37
+ PropertiesRequest,
38
+ RiskDetailRequest,
39
+ } from './predictProtocol.ts';
40
+ export { isPredictRequest } from './predictProtocol.ts';
41
+ export { createPredictionPool } from './predictionPool.ts';
42
+ export type { PredictionQueue } from './predictionQueue.ts';
43
+ export { createPredictionQueue } from './predictionQueue.ts';
44
+ export type { OsirisPredictors, ResourceSource } from './predictors.ts';
45
+ export {
46
+ PredictorsUnavailableError,
47
+ loadPredictors,
48
+ resetPredictors,
49
+ } from './predictors.ts';
50
+ export type { PropertyKey } from './propertyKeys.ts';
51
+ export { PROPERTY_KEYS, isPropertyKey, propertyValue } from './propertyKeys.ts';
52
+ export type { RiskAppearance, RiskTone } from './riskAppearance.ts';
53
+ export {
54
+ KNOWN_MOLECULE_NOTE,
55
+ PENDING_RISK_LABEL,
56
+ RISK_CAVEAT,
57
+ riskAppearance,
58
+ } from './riskAppearance.ts';
59
+ export { riskDetail } from './riskDetail.ts';
60
+ export { parseRiskFindings } from './riskFindings.ts';
61
+ export { runPredictionJob } from './runPredictionJob.ts';
62
+ export type {
63
+ PropertyScale,
64
+ PropertyThreshold,
65
+ ScaleSource,
66
+ } from './scales.ts';
67
+ export { PROPERTY_SCALES, propertyScale } from './scales.ts';
68
+ export type {
69
+ DetailEntry,
70
+ OsirisProperties,
71
+ RiskDetail,
72
+ RiskFinding,
73
+ RiskFindingKind,
74
+ RiskLevel,
75
+ RiskType,
76
+ } from './types.ts';
77
+ export { RISK_TYPES } from './types.ts';
78
+ export { getValuation } from './valuation.ts';
@@ -0,0 +1,24 @@
1
+ /**
2
+ * How many workers to run.
3
+ *
4
+ * One fewer than the machine's cores, so the thread that draws the table keeps
5
+ * one to itself, and never more than eight: measured scaling is 2.45x on two
6
+ * workers and 4.57x on four, and each worker costs its own copy of OpenChemLib
7
+ * and of the 398 kB resource tables.
8
+ */
9
+
10
+ import { clamp } from '../../format/core/clamp.ts';
11
+
12
+ /** The most workers the pool will ever run. */
13
+ export const MAX_POOL_SIZE = 8;
14
+
15
+ /**
16
+ * How many workers a pool should run here.
17
+ * @param requested - What the caller asked for, or `undefined` to read the machine.
18
+ * @returns A count between 1 and {@link MAX_POOL_SIZE}.
19
+ */
20
+ export function predictionPoolSize(requested?: number): number {
21
+ const cores = globalThis.navigator?.hardwareConcurrency;
22
+ const wanted = requested ?? (typeof cores === 'number' ? cores - 1 : 1);
23
+ return Math.trunc(clamp(wanted, 1, MAX_POOL_SIZE, 1));
24
+ }
@@ -0,0 +1,140 @@
1
+ /**
2
+ * What a page asks the prediction pool, and what it hears back.
3
+ *
4
+ * A prediction costs 210 to 380 ms of one thread, so a set of any size is
5
+ * answered a molecule at a time rather than all at once: a row appears as soon
6
+ * as it is read and fills in when its worker returns. Everything here is shaped
7
+ * around that — a per-result callback rather than a promise over the set, a
8
+ * progress figure, a cancel that works, and a way to say which rows the reader
9
+ * can actually see so those are computed first.
10
+ */
11
+
12
+ import type { WorkerLike } from '../../worker/core/messages.ts';
13
+
14
+ import type { PredictRequest, PredictResponse } from './predictProtocol.ts';
15
+ import type { ResourceSource } from './predictors.ts';
16
+ import type { OsirisProperties, RiskDetail, RiskType } from './types.ts';
17
+
18
+ /** One molecule to predict. */
19
+ export interface PredictionInput {
20
+ /**
21
+ * The caller's key for the row, echoed on the answer. It is the row's read
22
+ * position, never its idCode: a real list holds the same structure twice
23
+ * under two names, and both rows have to fill in.
24
+ */
25
+ key: string;
26
+ /** The idCode, with the editor's atom coordinates after a space or without. */
27
+ idCode: string;
28
+ /**
29
+ * The name the source carried.
30
+ * @default the canonical SMILES
31
+ */
32
+ label?: string;
33
+ }
34
+
35
+ /** What came back for one molecule. */
36
+ export type PredictionOutcome =
37
+ | { key: string; ok: true; properties: OsirisProperties }
38
+ | { key: string; ok: false; message: string };
39
+
40
+ /** How far the pool has got. */
41
+ export interface PredictionProgress {
42
+ /** Molecules answered, whether predicted or refused. */
43
+ done: number;
44
+ /** Molecules accepted since the run started. */
45
+ total: number;
46
+ /** Molecules still queued or still being worked on. */
47
+ pending: number;
48
+ /** Whether anything is still to come. */
49
+ running: boolean;
50
+ }
51
+
52
+ /** How a pool is built, and who it reports to. */
53
+ export interface PredictionPoolOptions {
54
+ /**
55
+ * Where the workers register OpenChemLib's resources from — the absolute URL
56
+ * of the site's own `openchemlib-resources.json`, resolved on the main
57
+ * thread. See {@link ResourceSource}.
58
+ */
59
+ resources: ResourceSource;
60
+ /**
61
+ * How many workers to run. Clamped to 1 to 8.
62
+ * @default one fewer than the machine's cores, which measured 2.45x on two workers and 4.57x on four
63
+ */
64
+ size?: number;
65
+ /**
66
+ * Makes one worker, which serves `runPredictionJob` for the requests
67
+ * `isPredictRequest` accepts. Only called where the page has a `Worker` at
68
+ * all. The site writes it because a bundler resolves a worker's address
69
+ * against the module naming it: written in this package, the URL would point
70
+ * inside `node_modules` rather than at a chunk the site's build emitted.
71
+ */
72
+ createWorker: () => WorkerLike;
73
+ /**
74
+ * The same job on this thread, used where there is no `Worker` — Node, a
75
+ * unit test.
76
+ * @default `runPredictionJob`, the same function the worker serves
77
+ */
78
+ runInProcess?: (request: PredictRequest) => Promise<PredictResponse>;
79
+ /**
80
+ * Called once per molecule, as each one comes back.
81
+ * @default undefined
82
+ */
83
+ onResult?: (outcome: PredictionOutcome) => void;
84
+ /**
85
+ * Called whenever the count of answered molecules changes.
86
+ * @default undefined
87
+ */
88
+ onProgress?: (progress: PredictionProgress) => void;
89
+ /**
90
+ * Called once when the resource tables did not load. Every answer from every
91
+ * worker would be `unknown`, so the run stops and nothing more is dispatched.
92
+ * @default undefined
93
+ */
94
+ onUnavailable?: (message: string) => void;
95
+ /**
96
+ * Milliseconds one molecule may take before it is given up on.
97
+ * @default 60000
98
+ */
99
+ timeoutMs?: number;
100
+ }
101
+
102
+ /** A pool of workers answering one molecule at a time, each. */
103
+ export interface PredictionPool {
104
+ /** How many workers it runs. */
105
+ readonly size: number;
106
+ /** How far it has got. */
107
+ readonly progress: PredictionProgress;
108
+ /**
109
+ * Add molecules to predict. One already queued or in flight is ignored, so a
110
+ * page may resubmit its whole set whenever the set changes.
111
+ * @param molecules - The molecules.
112
+ */
113
+ submit: (molecules: readonly PredictionInput[]) => void;
114
+ /**
115
+ * Say which rows to answer first — the ones the reader can see. Molecules
116
+ * already being worked on are left alone; only what is still queued moves.
117
+ * @param keys - The keys, most wanted first.
118
+ */
119
+ prioritize: (keys: readonly string[]) => void;
120
+ /**
121
+ * Why one risk came back as it did. Jumps the queue, because it is a click.
122
+ * A {@link PredictionPool.cancel} is about the set, not about the click, so
123
+ * an explanation it drops is asked for again.
124
+ * @param idCode - The molecule, coordinates included or not.
125
+ * @param risk - Which of the four risks to explain.
126
+ * @returns The findings, with the matched atoms and bonds of every fragment.
127
+ * @throws {Error} When even the second ask was dropped, with a sentence to show the reader.
128
+ */
129
+ riskDetail: (idCode: string, risk: RiskType) => Promise<RiskDetail>;
130
+ /**
131
+ * Drop everything queued and stop reporting. The molecule each worker is
132
+ * already on cannot be interrupted — the library call is synchronous — so it
133
+ * finishes and its answer is thrown away, within about 400 ms. A
134
+ * {@link PredictionPool.submit} made in the same turn is still dispatched, on
135
+ * the slots those abandoned answers free.
136
+ */
137
+ cancel: () => void;
138
+ /** Cancel, and end every worker. */
139
+ terminate: () => void;
140
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Prediction from the only thing worth keeping per row.
3
+ *
4
+ * A row never holds an `OCL.Molecule` — ten thousand of them cost 198 MB — so
5
+ * what travels to a worker is the idCode, and the molecule is rebuilt there in
6
+ * a quarter of a millisecond against the 380 ms the prediction itself costs.
7
+ */
8
+
9
+ import { splitIdCode } from '../../structure/core/editorValue.ts';
10
+
11
+ import type { ComputePropertiesOptions } from './computeProperties.ts';
12
+ import { computeProperties } from './computeProperties.ts';
13
+ import type { OsirisPredictors } from './predictors.ts';
14
+ import { riskDetail } from './riskDetail.ts';
15
+ import type { OsirisProperties, RiskDetail, RiskType } from './types.ts';
16
+
17
+ /**
18
+ * What is said when an idCode cannot be read.
19
+ *
20
+ * An idCode is written by a program, not typed by a chemist, so there is no
21
+ * position to point at and no parser reason worth repeating: OpenChemLib
22
+ * answers a malformed one with `Cannot set properties of undefined`, which
23
+ * names nothing anyone can act on. Worse, it does not always throw — `zzzz`
24
+ * reads as a 117-atom molecule — so this is the floor, not the whole guard.
25
+ */
26
+ export const UNREADABLE_ID_CODE_MESSAGE = 'This structure could not be read.';
27
+
28
+ /**
29
+ * Predict everything the tool shows, from an idCode.
30
+ * @param value - The idCode, with the editor's atom coordinates after a space or without them.
31
+ * @param predictors - OpenChemLib with its resources registered and checked.
32
+ * @param options - See {@link ComputePropertiesOptions}.
33
+ * @returns The ten numbers, the four risks, and what to call the molecule.
34
+ * @throws {Error} When the idCode cannot be read, or describes no atoms.
35
+ */
36
+ export function predictIdCode(
37
+ value: string,
38
+ predictors: OsirisPredictors,
39
+ options: ComputePropertiesOptions = {},
40
+ ): OsirisProperties {
41
+ return computeProperties(read(value, predictors), predictors, options);
42
+ }
43
+
44
+ /**
45
+ * Why one risk came back as it did, from an idCode.
46
+ * @param value - The idCode, coordinates included or not.
47
+ * @param risk - Which of the four risks to explain.
48
+ * @param predictors - OpenChemLib with its resources registered and checked.
49
+ * @returns The findings, with the matched atoms and bonds of every fragment.
50
+ * @throws {Error} When the idCode cannot be read.
51
+ */
52
+ export function riskDetailOfIdCode(
53
+ value: string,
54
+ risk: RiskType,
55
+ predictors: OsirisPredictors,
56
+ ): RiskDetail {
57
+ return riskDetail(read(value, predictors), risk, predictors);
58
+ }
59
+
60
+ function read(value: string, predictors: OsirisPredictors) {
61
+ const { idCode, coordinates } = splitIdCode(value);
62
+ try {
63
+ return coordinates === undefined
64
+ ? predictors.Molecule.fromIDCode(idCode)
65
+ : predictors.Molecule.fromIDCode(idCode, coordinates);
66
+ } catch (error) {
67
+ throw new Error(UNREADABLE_ID_CODE_MESSAGE, { cause: error });
68
+ }
69
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * What the page asks a prediction worker, and what it answers.
3
+ *
4
+ * One molecule per request. Two of the three answers are ordinary results; the
5
+ * third, `unavailable`, is how a worker says its resource tables did not load.
6
+ * That one travels as a *successful* answer on purpose: a rejection would be
7
+ * indistinguishable from the ordinary failure of one unreadable structure,
8
+ * whereas this one means every answer from every worker is worthless and the
9
+ * run must stop and say so.
10
+ */
11
+
12
+ import type { ResourceSource } from './predictors.ts';
13
+ import type { OsirisProperties, RiskDetail, RiskType } from './types.ts';
14
+
15
+ /** What every request carries, whichever job it asks for. */
16
+ interface BaseRequest {
17
+ /**
18
+ * Where the worker registers OpenChemLib's resources from. In a browser this
19
+ * is always the absolute URL of the site's own `openchemlib-resources.json`,
20
+ * resolved on the main thread: a worker has no `document`, so it cannot read
21
+ * the deployment's mount path itself. The parsed tables are accepted as well,
22
+ * for the in-process fallback where there is no worker to post them to.
23
+ */
24
+ resources: ResourceSource;
25
+ }
26
+
27
+ /** Predict everything the tool shows for one molecule. */
28
+ export interface PropertiesRequest extends BaseRequest {
29
+ kind: 'properties';
30
+ /** The caller's key for the row, echoed back so answers can arrive in any order. */
31
+ key: string;
32
+ /** The idCode, with the editor's atom coordinates after a space or without. */
33
+ idCode: string;
34
+ /**
35
+ * The name the source carried.
36
+ * @default the canonical SMILES
37
+ */
38
+ label?: string;
39
+ }
40
+
41
+ /** Explain why one risk came back as it did. */
42
+ export interface RiskDetailRequest extends BaseRequest {
43
+ kind: 'riskDetail';
44
+ /** The idCode, coordinates included or not. */
45
+ idCode: string;
46
+ /** Which of the four risks to explain. */
47
+ risk: RiskType;
48
+ }
49
+
50
+ /** One job for a prediction worker. */
51
+ export type PredictRequest = PropertiesRequest | RiskDetailRequest;
52
+
53
+ /** What a prediction worker answers. */
54
+ export type PredictResponse =
55
+ | { kind: 'properties'; key: string; properties: OsirisProperties }
56
+ | { kind: 'riskDetail'; detail: RiskDetail }
57
+ | { kind: 'unavailable'; message: string };
58
+
59
+ /**
60
+ * Whether a message is a job this worker handles, so anything else is refused
61
+ * rather than read as one.
62
+ * @param value - Whatever was posted.
63
+ * @returns True when it is a well-formed request.
64
+ */
65
+ export function isPredictRequest(value: unknown): value is PredictRequest {
66
+ if (typeof value !== 'object' || value === null) return false;
67
+ const request = value as {
68
+ kind?: unknown;
69
+ resources?: unknown;
70
+ idCode?: unknown;
71
+ key?: unknown;
72
+ risk?: unknown;
73
+ };
74
+ if (typeof request.idCode !== 'string') return false;
75
+ if (request.resources === undefined) return false;
76
+ if (request.kind === 'properties') return typeof request.key === 'string';
77
+ return request.kind === 'riskDetail' && typeof request.risk === 'string';
78
+ }
@@ -0,0 +1,237 @@
1
+ /**
2
+ * A pool of prediction workers, answering one molecule at a time each.
3
+ *
4
+ * `createWorkerChannel` from `react-cheminfo/core` does the per-worker half —
5
+ * request identifiers so answers match callers whatever order they arrive in,
6
+ * timeouts, cancellation, and the in-process fallback where a page has no
7
+ * `Worker` at all. What it does not do is spread work over several workers, so
8
+ * that is what this adds: one channel per worker, a queue the pool owns, and at
9
+ * most one outstanding job per channel, which is what makes reordering possible
10
+ * at all. A channel asked for everything at once would post everything at once,
11
+ * and the row the reader is looking at would be answered forty-eighth.
12
+ */
13
+
14
+ import { errorMessage } from '../../error/core/toError.ts';
15
+ import { CancelledRequestError } from '../../worker/core/errors.ts';
16
+ import { createWorkerChannel } from '../../worker/core/workerChannel.ts';
17
+ import type { WorkerChannel } from '../../worker/core/workerChannelTypes.ts';
18
+
19
+ import { predictionPoolSize } from './poolSize.ts';
20
+ import type {
21
+ PredictionInput,
22
+ PredictionPool,
23
+ PredictionPoolOptions,
24
+ PredictionProgress,
25
+ } from './poolTypes.ts';
26
+ import type { PredictRequest, PredictResponse } from './predictProtocol.ts';
27
+ import { createPredictionQueue } from './predictionQueue.ts';
28
+ import { PredictorsUnavailableError } from './predictors.ts';
29
+ import { requestRiskDetail } from './riskDetailRequest.ts';
30
+ import { runPredictionJob } from './runPredictionJob.ts';
31
+ import type { RiskDetail, RiskType } from './types.ts';
32
+
33
+ type Channel = WorkerChannel<PredictRequest, PredictResponse>;
34
+
35
+ interface Slot {
36
+ channel: Channel;
37
+ busy: boolean;
38
+ }
39
+
40
+ /**
41
+ * Start a pool of prediction workers.
42
+ *
43
+ * Workers are created lazily by the channels, on their first job, so a page
44
+ * that never predicts anything never loads OpenChemLib.
45
+ * @param options - See {@link PredictionPoolOptions}.
46
+ * @returns The pool.
47
+ */
48
+ export function createPredictionPool(
49
+ options: PredictionPoolOptions,
50
+ ): PredictionPool {
51
+ const {
52
+ resources,
53
+ onResult,
54
+ onProgress,
55
+ onUnavailable,
56
+ createWorker,
57
+ runInProcess,
58
+ timeoutMs = 60_000,
59
+ } = options;
60
+ const size = predictionPoolSize(options.size);
61
+ const run = runInProcess ?? runPredictionJob;
62
+
63
+ const slots: Slot[] = [];
64
+ for (let index = 0; index < size; index++) {
65
+ slots.push({
66
+ channel: createWorkerChannel<PredictRequest, PredictResponse>(
67
+ createWorker,
68
+ {
69
+ name: `osiris-prediction-${index}`,
70
+ defaultTimeoutMs: timeoutMs,
71
+ runInProcess: run,
72
+ },
73
+ ),
74
+ busy: false,
75
+ });
76
+ }
77
+
78
+ const queue = createPredictionQueue();
79
+ const inFlight = new Set<string>();
80
+ let generation = 0;
81
+ let done = 0;
82
+ let total = 0;
83
+ let running = false;
84
+ let terminated = false;
85
+ let unavailable: string | null = null;
86
+
87
+ function progress(): PredictionProgress {
88
+ return { done, total, pending: queue.size + inFlight.size, running };
89
+ }
90
+
91
+ function report(): void {
92
+ onProgress?.(progress());
93
+ }
94
+
95
+ function pump(): void {
96
+ if (unavailable !== null) return;
97
+ for (const slot of slots) {
98
+ if (slot.busy) continue;
99
+ const next = queue.take();
100
+ if (next === undefined) break;
101
+ dispatch(slot, next);
102
+ }
103
+ }
104
+
105
+ function dispatch(slot: Slot, input: PredictionInput): void {
106
+ const era = generation;
107
+ slot.busy = true;
108
+ inFlight.add(input.key);
109
+ const request: PredictRequest = {
110
+ kind: 'properties',
111
+ resources,
112
+ key: input.key,
113
+ idCode: input.idCode,
114
+ ...(input.label === undefined ? {} : { label: input.label }),
115
+ };
116
+ slot.channel.request(request).then(
117
+ (response) => {
118
+ settle(slot, input, era, response, null);
119
+ },
120
+ (error: unknown) => {
121
+ settle(slot, input, era, null, error);
122
+ },
123
+ );
124
+ }
125
+
126
+ function settle(
127
+ slot: Slot,
128
+ input: PredictionInput,
129
+ era: number,
130
+ response: PredictResponse | null,
131
+ error: unknown,
132
+ ): void {
133
+ slot.busy = false;
134
+ inFlight.delete(input.key);
135
+
136
+ // An answer from a cancelled run is not counted and not reported, but the
137
+ // slot it just freed is still dispatched from: a submit made in the same
138
+ // turn as the cancel found every slot busy and queued its molecules here.
139
+ if (era === generation) {
140
+ if (response?.kind === 'unavailable') {
141
+ stop(response.message);
142
+ return;
143
+ }
144
+ if (response?.kind === 'properties') {
145
+ done += 1;
146
+ onResult?.({
147
+ key: input.key,
148
+ ok: true,
149
+ properties: response.properties,
150
+ });
151
+ } else if (error !== null && !(error instanceof CancelledRequestError)) {
152
+ done += 1;
153
+ onResult?.({ key: input.key, ok: false, message: errorMessage(error) });
154
+ }
155
+ }
156
+
157
+ pump();
158
+ if (queue.size === 0 && inFlight.size === 0) running = false;
159
+ report();
160
+ }
161
+
162
+ function clear(): void {
163
+ generation += 1;
164
+ queue.clear();
165
+ inFlight.clear();
166
+ running = false;
167
+ for (const slot of slots) slot.channel.cancel();
168
+ }
169
+
170
+ function stop(message: string): void {
171
+ clear();
172
+ unavailable = message;
173
+ report();
174
+ onUnavailable?.(message);
175
+ }
176
+
177
+ function leastBusy(): Slot {
178
+ let best = slots[0] as Slot;
179
+ for (const slot of slots) {
180
+ if (slot.channel.pendingCount < best.channel.pendingCount) best = slot;
181
+ }
182
+ return best;
183
+ }
184
+
185
+ return {
186
+ size,
187
+ get progress() {
188
+ return progress();
189
+ },
190
+
191
+ submit(molecules) {
192
+ if (unavailable !== null) return;
193
+ if (!running && queue.size === 0 && inFlight.size === 0) {
194
+ done = 0;
195
+ total = 0;
196
+ }
197
+ for (const input of molecules) {
198
+ if (inFlight.has(input.key)) continue;
199
+ if (queue.add(input)) total += 1;
200
+ }
201
+ running = queue.size > 0 || inFlight.size > 0;
202
+ pump();
203
+ report();
204
+ },
205
+
206
+ prioritize(keys) {
207
+ queue.prioritize(keys);
208
+ },
209
+
210
+ async riskDetail(idCode: string, risk: RiskType): Promise<RiskDetail> {
211
+ const response = await requestRiskDetail(
212
+ () => leastBusy().channel,
213
+ { kind: 'riskDetail', resources, idCode, risk },
214
+ () => !terminated && unavailable === null,
215
+ );
216
+ if (response.kind === 'unavailable') {
217
+ stop(response.message);
218
+ throw new PredictorsUnavailableError(response.message);
219
+ }
220
+ if (response.kind !== 'riskDetail') {
221
+ throw new Error('The worker answered something other than a detail.');
222
+ }
223
+ return response.detail;
224
+ },
225
+
226
+ cancel() {
227
+ clear();
228
+ report();
229
+ },
230
+
231
+ terminate() {
232
+ terminated = true;
233
+ clear();
234
+ for (const slot of slots) slot.channel.terminate();
235
+ },
236
+ };
237
+ }