react-cheminfo 0.75.0 → 0.77.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 (342) 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/copySvg.d.ts +12 -0
  19. package/lib/download/core/copySvg.d.ts.map +1 -0
  20. package/lib/download/core/copySvg.js +38 -0
  21. package/lib/download/core/copySvg.js.map +1 -0
  22. package/lib/download/core/downloadFigure.d.ts +12 -1
  23. package/lib/download/core/downloadFigure.d.ts.map +1 -1
  24. package/lib/download/core/downloadFigure.js +21 -0
  25. package/lib/download/core/downloadFigure.js.map +1 -1
  26. package/lib/download/core/figureDpi.d.ts +59 -0
  27. package/lib/download/core/figureDpi.d.ts.map +1 -0
  28. package/lib/download/core/figureDpi.js +75 -0
  29. package/lib/download/core/figureDpi.js.map +1 -0
  30. package/lib/download/core/figurePng.d.ts +4 -1
  31. package/lib/download/core/figurePng.d.ts.map +1 -1
  32. package/lib/download/core/figurePng.js +11 -2
  33. package/lib/download/core/figurePng.js.map +1 -1
  34. package/lib/download/core/index.d.ts +5 -1
  35. package/lib/download/core/index.d.ts.map +1 -1
  36. package/lib/download/core/index.js +4 -1
  37. package/lib/download/core/index.js.map +1 -1
  38. package/lib/download/core/pngDpi.d.ts +22 -0
  39. package/lib/download/core/pngDpi.d.ts.map +1 -0
  40. package/lib/download/core/pngDpi.js +79 -0
  41. package/lib/download/core/pngDpi.js.map +1 -0
  42. package/lib/download/ui/FigureDownloadPanel.d.ts +13 -0
  43. package/lib/download/ui/FigureDownloadPanel.d.ts.map +1 -1
  44. package/lib/download/ui/FigureDownloadPanel.js +18 -34
  45. package/lib/download/ui/FigureDownloadPanel.js.map +1 -1
  46. package/lib/download/ui/figureDownloadHint.d.ts +5 -0
  47. package/lib/download/ui/figureDownloadHint.d.ts.map +1 -1
  48. package/lib/download/ui/figureDownloadHint.js +8 -0
  49. package/lib/download/ui/figureDownloadHint.js.map +1 -1
  50. package/lib/download/ui/figureDownloadOptions.d.ts +84 -0
  51. package/lib/download/ui/figureDownloadOptions.d.ts.map +1 -0
  52. package/lib/download/ui/figureDownloadOptions.js +2 -0
  53. package/lib/download/ui/figureDownloadOptions.js.map +1 -0
  54. package/lib/download/ui/resolutionChoices.d.ts +27 -0
  55. package/lib/download/ui/resolutionChoices.d.ts.map +1 -0
  56. package/lib/download/ui/resolutionChoices.js +48 -0
  57. package/lib/download/ui/resolutionChoices.js.map +1 -0
  58. package/lib/download/ui/useFigureActions.d.ts +4 -1
  59. package/lib/download/ui/useFigureActions.d.ts.map +1 -1
  60. package/lib/download/ui/useFigureActions.js +17 -8
  61. package/lib/download/ui/useFigureActions.js.map +1 -1
  62. package/lib/download/ui/useFigureDownload.d.ts +2 -55
  63. package/lib/download/ui/useFigureDownload.d.ts.map +1 -1
  64. package/lib/download/ui/useFigureDownload.js +45 -13
  65. package/lib/download/ui/useFigureDownload.js.map +1 -1
  66. package/lib/ecosystem/core/sites.d.ts.map +1 -1
  67. package/lib/ecosystem/core/sites.js +14 -0
  68. package/lib/ecosystem/core/sites.js.map +1 -1
  69. package/lib/ecosystem/core/types.d.ts +1 -1
  70. package/lib/ecosystem/core/types.d.ts.map +1 -1
  71. package/lib/ecosystem/ui/glyphs.d.ts.map +1 -1
  72. package/lib/ecosystem/ui/glyphs.js +3 -0
  73. package/lib/ecosystem/ui/glyphs.js.map +1 -1
  74. package/lib/i18n/core/chromeCatalog.d.ts +1 -1
  75. package/lib/i18n/core/chromeCatalog.d.ts.map +1 -1
  76. package/lib/ir/core/bandAssignments.d.ts.map +1 -1
  77. package/lib/ir/core/bandAssignments.js +27 -0
  78. package/lib/ir/core/bandAssignments.js.map +1 -1
  79. package/lib/ir/ui/BandRow.d.ts +4 -4
  80. package/lib/ir/ui/BandRow.d.ts.map +1 -1
  81. package/lib/ir/ui/BandRow.js +41 -14
  82. package/lib/ir/ui/BandRow.js.map +1 -1
  83. package/lib/locales/de.json +32 -1
  84. package/lib/locales/en.json +32 -1
  85. package/lib/locales/es.json +32 -1
  86. package/lib/locales/fr.json +32 -1
  87. package/lib/locales/it.json +32 -1
  88. package/lib/molecule3d/ui/Molecule3DStage.d.ts +4 -33
  89. package/lib/molecule3d/ui/Molecule3DStage.d.ts.map +1 -1
  90. package/lib/molecule3d/ui/Molecule3DStage.js +3 -37
  91. package/lib/molecule3d/ui/Molecule3DStage.js.map +1 -1
  92. package/lib/osiris/core/barGeometry.d.ts +42 -0
  93. package/lib/osiris/core/barGeometry.d.ts.map +1 -0
  94. package/lib/osiris/core/barGeometry.js +46 -0
  95. package/lib/osiris/core/barGeometry.js.map +1 -0
  96. package/lib/osiris/core/computeProperties.d.ts +42 -0
  97. package/lib/osiris/core/computeProperties.d.ts.map +1 -0
  98. package/lib/osiris/core/computeProperties.js +115 -0
  99. package/lib/osiris/core/computeProperties.js.map +1 -0
  100. package/lib/osiris/core/constants.d.ts +70 -0
  101. package/lib/osiris/core/constants.d.ts.map +1 -0
  102. package/lib/osiris/core/constants.js +99 -0
  103. package/lib/osiris/core/constants.js.map +1 -0
  104. package/lib/osiris/core/fitness.d.ts +32 -0
  105. package/lib/osiris/core/fitness.d.ts.map +1 -0
  106. package/lib/osiris/core/fitness.js +48 -0
  107. package/lib/osiris/core/fitness.js.map +1 -0
  108. package/lib/osiris/core/index.d.ts +29 -0
  109. package/lib/osiris/core/index.d.ts.map +1 -0
  110. package/lib/osiris/core/index.js +19 -0
  111. package/lib/osiris/core/index.js.map +1 -0
  112. package/lib/osiris/core/poolSize.d.ts +17 -0
  113. package/lib/osiris/core/poolSize.d.ts.map +1 -0
  114. package/lib/osiris/core/poolSize.js +22 -0
  115. package/lib/osiris/core/poolSize.js.map +1 -0
  116. package/lib/osiris/core/poolTypes.d.ts +140 -0
  117. package/lib/osiris/core/poolTypes.d.ts.map +1 -0
  118. package/lib/osiris/core/poolTypes.js +12 -0
  119. package/lib/osiris/core/poolTypes.js.map +1 -0
  120. package/lib/osiris/core/predictIdCode.d.ts +39 -0
  121. package/lib/osiris/core/predictIdCode.d.ts.map +1 -0
  122. package/lib/osiris/core/predictIdCode.js +54 -0
  123. package/lib/osiris/core/predictIdCode.js.map +1 -0
  124. package/lib/osiris/core/predictProtocol.d.ts +67 -0
  125. package/lib/osiris/core/predictProtocol.d.ts.map +1 -0
  126. package/lib/osiris/core/predictProtocol.js +29 -0
  127. package/lib/osiris/core/predictProtocol.js.map +1 -0
  128. package/lib/osiris/core/predictionPool.d.ts +23 -0
  129. package/lib/osiris/core/predictionPool.d.ts.map +1 -0
  130. package/lib/osiris/core/predictionPool.js +186 -0
  131. package/lib/osiris/core/predictionPool.js.map +1 -0
  132. package/lib/osiris/core/predictionQueue.d.ts +46 -0
  133. package/lib/osiris/core/predictionQueue.d.ts.map +1 -0
  134. package/lib/osiris/core/predictionQueue.js +57 -0
  135. package/lib/osiris/core/predictionQueue.js.map +1 -0
  136. package/lib/osiris/core/predictors.d.ts +62 -0
  137. package/lib/osiris/core/predictors.d.ts.map +1 -0
  138. package/lib/osiris/core/predictors.js +100 -0
  139. package/lib/osiris/core/predictors.js.map +1 -0
  140. package/lib/osiris/core/propertyKeys.d.ts +32 -0
  141. package/lib/osiris/core/propertyKeys.d.ts.map +1 -0
  142. package/lib/osiris/core/propertyKeys.js +44 -0
  143. package/lib/osiris/core/propertyKeys.js.map +1 -0
  144. package/lib/osiris/core/riskAppearance.d.ts +60 -0
  145. package/lib/osiris/core/riskAppearance.d.ts.map +1 -0
  146. package/lib/osiris/core/riskAppearance.js +59 -0
  147. package/lib/osiris/core/riskAppearance.js.map +1 -0
  148. package/lib/osiris/core/riskDetail.d.ts +22 -0
  149. package/lib/osiris/core/riskDetail.d.ts.map +1 -0
  150. package/lib/osiris/core/riskDetail.js +86 -0
  151. package/lib/osiris/core/riskDetail.js.map +1 -0
  152. package/lib/osiris/core/riskDetailRequest.d.ts +31 -0
  153. package/lib/osiris/core/riskDetailRequest.d.ts.map +1 -0
  154. package/lib/osiris/core/riskDetailRequest.js +49 -0
  155. package/lib/osiris/core/riskDetailRequest.js.map +1 -0
  156. package/lib/osiris/core/riskFindings.d.ts +26 -0
  157. package/lib/osiris/core/riskFindings.d.ts.map +1 -0
  158. package/lib/osiris/core/riskFindings.js +79 -0
  159. package/lib/osiris/core/riskFindings.js.map +1 -0
  160. package/lib/osiris/core/runPredictionJob.d.ts +20 -0
  161. package/lib/osiris/core/runPredictionJob.d.ts.map +1 -0
  162. package/lib/osiris/core/runPredictionJob.js +43 -0
  163. package/lib/osiris/core/runPredictionJob.js.map +1 -0
  164. package/lib/osiris/core/scales.d.ts +70 -0
  165. package/lib/osiris/core/scales.d.ts.map +1 -0
  166. package/lib/osiris/core/scales.js +185 -0
  167. package/lib/osiris/core/scales.js.map +1 -0
  168. package/lib/osiris/core/types.d.ts +91 -0
  169. package/lib/osiris/core/types.d.ts.map +1 -0
  170. package/lib/osiris/core/types.js +19 -0
  171. package/lib/osiris/core/types.js.map +1 -0
  172. package/lib/osiris/core/valuation.d.ts +28 -0
  173. package/lib/osiris/core/valuation.d.ts.map +1 -0
  174. package/lib/osiris/core/valuation.js +39 -0
  175. package/lib/osiris/core/valuation.js.map +1 -0
  176. package/lib/osiris/ui/PropertyBar.d.ts +27 -0
  177. package/lib/osiris/ui/PropertyBar.d.ts.map +1 -0
  178. package/lib/osiris/ui/PropertyBar.js +27 -0
  179. package/lib/osiris/ui/PropertyBar.js.map +1 -0
  180. package/lib/osiris/ui/PropertyPanel.d.ts +91 -0
  181. package/lib/osiris/ui/PropertyPanel.d.ts.map +1 -0
  182. package/lib/osiris/ui/PropertyPanel.js +77 -0
  183. package/lib/osiris/ui/PropertyPanel.js.map +1 -0
  184. package/lib/osiris/ui/PropertyRows.d.ts +40 -0
  185. package/lib/osiris/ui/PropertyRows.d.ts.map +1 -0
  186. package/lib/osiris/ui/PropertyRows.js +23 -0
  187. package/lib/osiris/ui/PropertyRows.js.map +1 -0
  188. package/lib/osiris/ui/RiskFindings.d.ts +43 -0
  189. package/lib/osiris/ui/RiskFindings.d.ts.map +1 -0
  190. package/lib/osiris/ui/RiskFindings.js +51 -0
  191. package/lib/osiris/ui/RiskFindings.js.map +1 -0
  192. package/lib/osiris/ui/RiskPanel.d.ts +46 -0
  193. package/lib/osiris/ui/RiskPanel.d.ts.map +1 -0
  194. package/lib/osiris/ui/RiskPanel.js +32 -0
  195. package/lib/osiris/ui/RiskPanel.js.map +1 -0
  196. package/lib/osiris/ui/RiskSquare.d.ts +51 -0
  197. package/lib/osiris/ui/RiskSquare.d.ts.map +1 -0
  198. package/lib/osiris/ui/RiskSquare.js +23 -0
  199. package/lib/osiris/ui/RiskSquare.js.map +1 -0
  200. package/lib/osiris/ui/index.d.ts +15 -0
  201. package/lib/osiris/ui/index.d.ts.map +1 -0
  202. package/lib/osiris/ui/index.js +8 -0
  203. package/lib/osiris/ui/index.js.map +1 -0
  204. package/lib/osiris/ui/usePredictions.d.ts +81 -0
  205. package/lib/osiris/ui/usePredictions.d.ts.map +1 -0
  206. package/lib/osiris/ui/usePredictions.js +102 -0
  207. package/lib/osiris/ui/usePredictions.js.map +1 -0
  208. package/lib/osiris-ui.d.ts +11 -0
  209. package/lib/osiris-ui.d.ts.map +1 -0
  210. package/lib/osiris-ui.js +11 -0
  211. package/lib/osiris-ui.js.map +1 -0
  212. package/lib/osiris.d.ts +17 -0
  213. package/lib/osiris.d.ts.map +1 -0
  214. package/lib/osiris.js +17 -0
  215. package/lib/osiris.js.map +1 -0
  216. package/lib/panel/ui/FilterInput.d.ts.map +1 -1
  217. package/lib/panel/ui/FilterInput.js +3 -1
  218. package/lib/panel/ui/FilterInput.js.map +1 -1
  219. package/lib/panel/ui/SidePanelItem.d.ts +39 -0
  220. package/lib/panel/ui/SidePanelItem.d.ts.map +1 -0
  221. package/lib/panel/ui/SidePanelItem.js +63 -0
  222. package/lib/panel/ui/SidePanelItem.js.map +1 -0
  223. package/lib/panel/ui/SidePanelStack.d.ts +11 -10
  224. package/lib/panel/ui/SidePanelStack.d.ts.map +1 -1
  225. package/lib/panel/ui/SidePanelStack.js +22 -15
  226. package/lib/panel/ui/SidePanelStack.js.map +1 -1
  227. package/lib/panel/ui/SpectrumColorSwatch.d.ts.map +1 -1
  228. package/lib/panel/ui/SpectrumColorSwatch.js +4 -2
  229. package/lib/panel/ui/SpectrumColorSwatch.js.map +1 -1
  230. package/lib/panel/ui/usePanelFolds.d.ts +35 -0
  231. package/lib/panel/ui/usePanelFolds.d.ts.map +1 -0
  232. package/lib/panel/ui/usePanelFolds.js +73 -0
  233. package/lib/panel/ui/usePanelFolds.js.map +1 -0
  234. package/lib/shared/ui/FullscreenStage.d.ts +43 -0
  235. package/lib/shared/ui/FullscreenStage.d.ts.map +1 -0
  236. package/lib/shared/ui/FullscreenStage.js +38 -0
  237. package/lib/shared/ui/FullscreenStage.js.map +1 -0
  238. package/lib/shared/ui/canFillScreen.d.ts +7 -0
  239. package/lib/shared/ui/canFillScreen.d.ts.map +1 -0
  240. package/lib/shared/ui/canFillScreen.js +9 -0
  241. package/lib/shared/ui/canFillScreen.js.map +1 -0
  242. package/lib/shared/ui/index.d.ts +3 -0
  243. package/lib/shared/ui/index.d.ts.map +1 -1
  244. package/lib/shared/ui/index.js +2 -0
  245. package/lib/shared/ui/index.js.map +1 -1
  246. package/lib/split/ui/splitPanes.d.ts +3 -3
  247. package/lib/split/ui/splitPanes.js +1 -1
  248. package/lib/structure/core/structureNotations.d.ts +8 -1
  249. package/lib/structure/core/structureNotations.d.ts.map +1 -1
  250. package/lib/structure/core/structureNotations.js +14 -8
  251. package/lib/structure/core/structureNotations.js.map +1 -1
  252. package/lib/structure/ui/CardStructure.d.ts +43 -0
  253. package/lib/structure/ui/CardStructure.d.ts.map +1 -0
  254. package/lib/structure/ui/CardStructure.js +28 -0
  255. package/lib/structure/ui/CardStructure.js.map +1 -0
  256. package/lib/structure/ui/StructureExportPanel.d.ts +8 -0
  257. package/lib/structure/ui/StructureExportPanel.d.ts.map +1 -1
  258. package/lib/structure/ui/StructureExportPanel.js +9 -4
  259. package/lib/structure/ui/StructureExportPanel.js.map +1 -1
  260. package/lib/structure/ui/StructurePicturePane.d.ts +1 -1
  261. package/lib/structure/ui/StructurePicturePane.d.ts.map +1 -1
  262. package/lib/structure/ui/StructurePicturePane.js +5 -2
  263. package/lib/structure/ui/StructurePicturePane.js.map +1 -1
  264. package/lib/structure/ui/index.d.ts +3 -0
  265. package/lib/structure/ui/index.d.ts.map +1 -1
  266. package/lib/structure/ui/index.js +1 -0
  267. package/lib/structure/ui/index.js.map +1 -1
  268. package/package.json +4 -1
  269. package/src/card/core/cardAddress.ts +39 -3
  270. package/src/card/core/cardPageUrl.ts +10 -3
  271. package/src/card/core/index.ts +7 -1
  272. package/src/credits/core/credits.ts +40 -0
  273. package/src/download/core/copySvg.ts +40 -0
  274. package/src/download/core/downloadFigure.ts +26 -1
  275. package/src/download/core/figureDpi.ts +94 -0
  276. package/src/download/core/figurePng.ts +11 -2
  277. package/src/download/core/index.ts +14 -1
  278. package/src/download/core/pngDpi.ts +84 -0
  279. package/src/download/ui/FigureDownloadPanel.tsx +41 -51
  280. package/src/download/ui/figureDownloadHint.ts +13 -0
  281. package/src/download/ui/figureDownloadOptions.ts +85 -0
  282. package/src/download/ui/resolutionChoices.ts +67 -0
  283. package/src/download/ui/useFigureActions.ts +24 -9
  284. package/src/download/ui/useFigureDownload.tsx +57 -71
  285. package/src/ecosystem/core/sites.ts +14 -0
  286. package/src/ecosystem/core/types.ts +2 -1
  287. package/src/ecosystem/ui/glyphs.tsx +20 -0
  288. package/src/ir/core/bandAssignments.ts +27 -0
  289. package/src/ir/ui/BandRow.tsx +84 -26
  290. package/src/locales/de.json +32 -1
  291. package/src/locales/en.json +32 -1
  292. package/src/locales/es.json +32 -1
  293. package/src/locales/fr.json +32 -1
  294. package/src/locales/it.json +32 -1
  295. package/src/molecule3d/ui/Molecule3DStage.tsx +4 -88
  296. package/src/osiris/core/barGeometry.ts +74 -0
  297. package/src/osiris/core/computeProperties.ts +175 -0
  298. package/src/osiris/core/constants.ts +110 -0
  299. package/src/osiris/core/fitness.ts +59 -0
  300. package/src/osiris/core/index.ts +78 -0
  301. package/src/osiris/core/poolSize.ts +24 -0
  302. package/src/osiris/core/poolTypes.ts +140 -0
  303. package/src/osiris/core/predictIdCode.ts +69 -0
  304. package/src/osiris/core/predictProtocol.ts +78 -0
  305. package/src/osiris/core/predictionPool.ts +237 -0
  306. package/src/osiris/core/predictionQueue.ts +89 -0
  307. package/src/osiris/core/predictors.ts +142 -0
  308. package/src/osiris/core/propertyKeys.ts +54 -0
  309. package/src/osiris/core/riskAppearance.ts +86 -0
  310. package/src/osiris/core/riskDetail.ts +120 -0
  311. package/src/osiris/core/riskDetailRequest.ts +64 -0
  312. package/src/osiris/core/riskFindings.ts +92 -0
  313. package/src/osiris/core/runPredictionJob.ts +49 -0
  314. package/src/osiris/core/scales.ts +236 -0
  315. package/src/osiris/core/types.ts +103 -0
  316. package/src/osiris/core/valuation.ts +44 -0
  317. package/src/osiris/ui/PropertyBar.tsx +77 -0
  318. package/src/osiris/ui/PropertyPanel.tsx +227 -0
  319. package/src/osiris/ui/PropertyRows.tsx +92 -0
  320. package/src/osiris/ui/RiskFindings.tsx +137 -0
  321. package/src/osiris/ui/RiskPanel.tsx +81 -0
  322. package/src/osiris/ui/RiskSquare.tsx +108 -0
  323. package/src/osiris/ui/index.ts +17 -0
  324. package/src/osiris/ui/usePredictions.ts +194 -0
  325. package/src/osiris-ui.ts +11 -0
  326. package/src/osiris.ts +17 -0
  327. package/src/panel/ui/FilterInput.tsx +4 -1
  328. package/src/panel/ui/SidePanelItem.tsx +162 -0
  329. package/src/panel/ui/SidePanelStack.tsx +29 -36
  330. package/src/panel/ui/SpectrumColorSwatch.tsx +5 -3
  331. package/src/panel/ui/usePanelFolds.ts +103 -0
  332. package/src/shared/ui/FullscreenStage.tsx +96 -0
  333. package/src/shared/ui/canFillScreen.ts +8 -0
  334. package/src/shared/ui/index.ts +6 -0
  335. package/src/split/ui/splitPanes.tsx +4 -4
  336. package/src/structure/core/structureNotations.ts +25 -8
  337. package/src/structure/ui/CardStructure.tsx +76 -0
  338. package/src/structure/ui/StructureExportPanel.tsx +23 -4
  339. package/src/structure/ui/StructurePicturePane.tsx +14 -1
  340. package/src/structure/ui/index.ts +6 -0
  341. package/styles/chrome.css +3 -3
  342. package/styles/osiris.css +339 -0
@@ -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
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * What is still to be predicted, in the order it will be.
3
+ *
4
+ * Arrival order by default, because that is the order the reader read the file
5
+ * in. On top of it sits a short list of keys to answer first — the rows
6
+ * actually on screen — which is re-laid every time the reader scrolls or
7
+ * focuses a row. A key named as wanted that has since been answered or removed
8
+ * is skipped rather than looked up again.
9
+ */
10
+
11
+ import type { PredictionInput } from './poolTypes.ts';
12
+
13
+ /** The molecules waiting, and which of them to take next. */
14
+ export interface PredictionQueue {
15
+ /** How many are waiting. */
16
+ readonly size: number;
17
+ /**
18
+ * Whether one is waiting.
19
+ * @param key - The row's key.
20
+ * @returns True when it is still queued.
21
+ */
22
+ has: (key: string) => boolean;
23
+ /**
24
+ * Queue one molecule, unless its key is already waiting.
25
+ * @param input - The molecule.
26
+ * @returns True when it was added.
27
+ */
28
+ add: (input: PredictionInput) => boolean;
29
+ /**
30
+ * Take the next molecule to predict: the first still-queued key of the wanted
31
+ * list, else the one that has waited longest.
32
+ * @returns The molecule, or `undefined` when nothing is waiting.
33
+ */
34
+ take: () => PredictionInput | undefined;
35
+ /**
36
+ * Replace the list of keys to answer first.
37
+ * @param keys - The keys, most wanted first.
38
+ */
39
+ prioritize: (keys: readonly string[]) => void;
40
+ /** Forget everything waiting. */
41
+ clear: () => void;
42
+ }
43
+
44
+ /**
45
+ * A queue of molecules to predict.
46
+ * @returns The queue, empty.
47
+ */
48
+ export function createPredictionQueue(): PredictionQueue {
49
+ const waiting = new Map<string, PredictionInput>();
50
+ let wanted: readonly string[] = [];
51
+ let cursor = 0;
52
+
53
+ return {
54
+ get size() {
55
+ return waiting.size;
56
+ },
57
+ has: (key) => waiting.has(key),
58
+ add(input) {
59
+ if (waiting.has(input.key)) return false;
60
+ waiting.set(input.key, input);
61
+ return true;
62
+ },
63
+ take() {
64
+ while (cursor < wanted.length) {
65
+ const key = wanted[cursor] as string;
66
+ cursor += 1;
67
+ const next = waiting.get(key);
68
+ if (next !== undefined) {
69
+ waiting.delete(key);
70
+ return next;
71
+ }
72
+ }
73
+ const first = waiting.keys().next();
74
+ if (first.done === true) return undefined;
75
+ const next = waiting.get(first.value) as PredictionInput;
76
+ waiting.delete(first.value);
77
+ return next;
78
+ },
79
+ prioritize(keys) {
80
+ wanted = [...keys];
81
+ cursor = 0;
82
+ },
83
+ clear() {
84
+ waiting.clear();
85
+ wanted = [];
86
+ cursor = 0;
87
+ },
88
+ };
89
+ }