react-cheminfo 0.64.1 → 0.65.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 (604) hide show
  1. package/README.md +282 -0
  2. package/lib/card/core/cardAddress.d.ts +70 -0
  3. package/lib/card/core/cardAddress.d.ts.map +1 -0
  4. package/lib/card/core/cardAddress.js +59 -0
  5. package/lib/card/core/cardAddress.js.map +1 -0
  6. package/lib/card/core/cardPageUrl.d.ts +49 -0
  7. package/lib/card/core/cardPageUrl.d.ts.map +1 -0
  8. package/lib/card/core/cardPageUrl.js +59 -0
  9. package/lib/card/core/cardPageUrl.js.map +1 -0
  10. package/lib/card/core/index.d.ts +7 -0
  11. package/lib/card/core/index.d.ts.map +1 -0
  12. package/lib/card/core/index.js +4 -0
  13. package/lib/card/core/index.js.map +1 -0
  14. package/lib/card/core/panelRoom.d.ts +40 -0
  15. package/lib/card/core/panelRoom.d.ts.map +1 -0
  16. package/lib/card/core/panelRoom.js +40 -0
  17. package/lib/card/core/panelRoom.js.map +1 -0
  18. package/lib/card/ui/CardDetailsButton.d.ts +38 -0
  19. package/lib/card/ui/CardDetailsButton.d.ts.map +1 -0
  20. package/lib/card/ui/CardDetailsButton.js +50 -0
  21. package/lib/card/ui/CardDetailsButton.js.map +1 -0
  22. package/lib/card/ui/CardFigure.d.ts +51 -0
  23. package/lib/card/ui/CardFigure.d.ts.map +1 -0
  24. package/lib/card/ui/CardFigure.js +27 -0
  25. package/lib/card/ui/CardFigure.js.map +1 -0
  26. package/lib/card/ui/CardFrame.d.ts +30 -0
  27. package/lib/card/ui/CardFrame.d.ts.map +1 -0
  28. package/lib/card/ui/CardFrame.js +20 -0
  29. package/lib/card/ui/CardFrame.js.map +1 -0
  30. package/lib/card/ui/CopyTableButton.d.ts +37 -0
  31. package/lib/card/ui/CopyTableButton.d.ts.map +1 -0
  32. package/lib/card/ui/CopyTableButton.js +19 -0
  33. package/lib/card/ui/CopyTableButton.js.map +1 -0
  34. package/lib/card/ui/cardTooltip.d.ts +14 -0
  35. package/lib/card/ui/cardTooltip.d.ts.map +1 -0
  36. package/lib/card/ui/cardTooltip.js +15 -0
  37. package/lib/card/ui/cardTooltip.js.map +1 -0
  38. package/lib/card/ui/embed.d.ts +15 -0
  39. package/lib/card/ui/embed.d.ts.map +1 -0
  40. package/lib/card/ui/embed.js +24 -0
  41. package/lib/card/ui/embed.js.map +1 -0
  42. package/lib/card/ui/fitCardToFrame.d.ts +31 -0
  43. package/lib/card/ui/fitCardToFrame.d.ts.map +1 -0
  44. package/lib/card/ui/fitCardToFrame.js +66 -0
  45. package/lib/card/ui/fitCardToFrame.js.map +1 -0
  46. package/lib/card/ui/holdRoomForPanels.d.ts +16 -0
  47. package/lib/card/ui/holdRoomForPanels.d.ts.map +1 -0
  48. package/lib/card/ui/holdRoomForPanels.js +76 -0
  49. package/lib/card/ui/holdRoomForPanels.js.map +1 -0
  50. package/lib/card/ui/index.d.ts +11 -0
  51. package/lib/card/ui/index.d.ts.map +1 -0
  52. package/lib/card/ui/index.js +7 -0
  53. package/lib/card/ui/index.js.map +1 -0
  54. package/lib/card/ui/openPanels.d.ts +25 -0
  55. package/lib/card/ui/openPanels.d.ts.map +1 -0
  56. package/lib/card/ui/openPanels.js +85 -0
  57. package/lib/card/ui/openPanels.js.map +1 -0
  58. package/lib/card.d.ts +13 -0
  59. package/lib/card.d.ts.map +1 -0
  60. package/lib/card.js +13 -0
  61. package/lib/card.js.map +1 -0
  62. package/lib/conformers.d.ts +1 -0
  63. package/lib/conformers.d.ts.map +1 -1
  64. package/lib/conformers.js +1 -0
  65. package/lib/conformers.js.map +1 -1
  66. package/lib/core.d.ts +4 -0
  67. package/lib/core.d.ts.map +1 -1
  68. package/lib/core.js +5 -0
  69. package/lib/core.js.map +1 -1
  70. package/lib/fetch/core/apiError.d.ts +56 -0
  71. package/lib/fetch/core/apiError.d.ts.map +1 -0
  72. package/lib/fetch/core/apiError.js +43 -0
  73. package/lib/fetch/core/apiError.js.map +1 -0
  74. package/lib/fetch/core/fetchJson.d.ts +47 -0
  75. package/lib/fetch/core/fetchJson.d.ts.map +1 -0
  76. package/lib/fetch/core/fetchJson.js +109 -0
  77. package/lib/fetch/core/fetchJson.js.map +1 -0
  78. package/lib/fetch/core/index.d.ts +5 -0
  79. package/lib/fetch/core/index.d.ts.map +1 -0
  80. package/lib/fetch/core/index.js +3 -0
  81. package/lib/fetch/core/index.js.map +1 -0
  82. package/lib/formula/core/formulaText.d.ts +46 -0
  83. package/lib/formula/core/formulaText.d.ts.map +1 -0
  84. package/lib/formula/core/formulaText.js +74 -0
  85. package/lib/formula/core/formulaText.js.map +1 -0
  86. package/lib/formula/core/index.d.ts +6 -0
  87. package/lib/formula/core/index.d.ts.map +1 -0
  88. package/lib/formula/core/index.js +4 -0
  89. package/lib/formula/core/index.js.map +1 -0
  90. package/lib/formula/core/normalizeFormula.d.ts +22 -0
  91. package/lib/formula/core/normalizeFormula.d.ts.map +1 -0
  92. package/lib/formula/core/normalizeFormula.js +46 -0
  93. package/lib/formula/core/normalizeFormula.js.map +1 -0
  94. package/lib/formula/core/readFormula.d.ts +73 -0
  95. package/lib/formula/core/readFormula.d.ts.map +1 -0
  96. package/lib/formula/core/readFormula.js +132 -0
  97. package/lib/formula/core/readFormula.js.map +1 -0
  98. package/lib/formula/ui/CopyableFormula.d.ts +31 -0
  99. package/lib/formula/ui/CopyableFormula.d.ts.map +1 -0
  100. package/lib/formula/ui/CopyableFormula.js +19 -0
  101. package/lib/formula/ui/CopyableFormula.js.map +1 -0
  102. package/lib/formula/ui/FormulaInput.d.ts +29 -0
  103. package/lib/formula/ui/FormulaInput.d.ts.map +1 -0
  104. package/lib/formula/ui/FormulaInput.js +16 -0
  105. package/lib/formula/ui/FormulaInput.js.map +1 -0
  106. package/lib/formula/ui/QuestionFormula.d.ts +14 -0
  107. package/lib/formula/ui/QuestionFormula.d.ts.map +1 -0
  108. package/lib/formula/ui/QuestionFormula.js +13 -0
  109. package/lib/formula/ui/QuestionFormula.js.map +1 -0
  110. package/lib/formula/ui/index.d.ts +7 -0
  111. package/lib/formula/ui/index.d.ts.map +1 -0
  112. package/lib/formula/ui/index.js +4 -0
  113. package/lib/formula/ui/index.js.map +1 -0
  114. package/lib/formula.d.ts +11 -0
  115. package/lib/formula.d.ts.map +1 -0
  116. package/lib/formula.js +11 -0
  117. package/lib/formula.js.map +1 -0
  118. package/lib/hooks/ui/index.d.ts +1 -0
  119. package/lib/hooks/ui/index.d.ts.map +1 -1
  120. package/lib/hooks/ui/index.js.map +1 -1
  121. package/lib/hooks/ui/keyTargets.d.ts +4 -2
  122. package/lib/hooks/ui/keyTargets.d.ts.map +1 -1
  123. package/lib/hooks/ui/keyTargets.js +5 -3
  124. package/lib/hooks/ui/keyTargets.js.map +1 -1
  125. package/lib/hooks/ui/useAsync.d.ts +20 -5
  126. package/lib/hooks/ui/useAsync.d.ts.map +1 -1
  127. package/lib/hooks/ui/useAsync.js +29 -14
  128. package/lib/hooks/ui/useAsync.js.map +1 -1
  129. package/lib/i18n/core/chromeCatalog.d.ts +1 -1
  130. package/lib/i18n/core/chromeCatalog.d.ts.map +1 -1
  131. package/lib/locales/de.json +37 -0
  132. package/lib/locales/en.json +37 -0
  133. package/lib/locales/es.json +37 -0
  134. package/lib/locales/fr.json +37 -0
  135. package/lib/locales/it.json +37 -0
  136. package/lib/moleculesClient/core/index.d.ts +5 -0
  137. package/lib/moleculesClient/core/index.d.ts.map +1 -0
  138. package/lib/moleculesClient/core/index.js +3 -0
  139. package/lib/moleculesClient/core/index.js.map +1 -0
  140. package/lib/moleculesClient/core/moleculesClient.d.ts +12 -0
  141. package/lib/moleculesClient/core/moleculesClient.d.ts.map +1 -0
  142. package/lib/moleculesClient/core/moleculesClient.js +105 -0
  143. package/lib/moleculesClient/core/moleculesClient.js.map +1 -0
  144. package/lib/moleculesClient/core/moleculesClientApi.d.ts +100 -0
  145. package/lib/moleculesClient/core/moleculesClientApi.d.ts.map +1 -0
  146. package/lib/moleculesClient/core/moleculesClientApi.js +20 -0
  147. package/lib/moleculesClient/core/moleculesClientApi.js.map +1 -0
  148. package/lib/moleculesClient/core/moleculesTypes.d.ts +154 -0
  149. package/lib/moleculesClient/core/moleculesTypes.d.ts.map +1 -0
  150. package/lib/moleculesClient/core/moleculesTypes.js +2 -0
  151. package/lib/moleculesClient/core/moleculesTypes.js.map +1 -0
  152. package/lib/moleculesClient/core/requestRetry.d.ts +50 -0
  153. package/lib/moleculesClient/core/requestRetry.d.ts.map +1 -0
  154. package/lib/moleculesClient/core/requestRetry.js +108 -0
  155. package/lib/moleculesClient/core/requestRetry.js.map +1 -0
  156. package/lib/molstar/core/camera.d.ts +2 -1
  157. package/lib/molstar/core/camera.d.ts.map +1 -1
  158. package/lib/molstar/core/camera.js +3 -2
  159. package/lib/molstar/core/camera.js.map +1 -1
  160. package/lib/molstar/core/framing.d.ts +102 -0
  161. package/lib/molstar/core/framing.d.ts.map +1 -0
  162. package/lib/molstar/core/framing.js +120 -0
  163. package/lib/molstar/core/framing.js.map +1 -0
  164. package/lib/molstar/core/hover.d.ts +16 -0
  165. package/lib/molstar/core/hover.d.ts.map +1 -1
  166. package/lib/molstar/core/hover.js +14 -0
  167. package/lib/molstar/core/hover.js.map +1 -1
  168. package/lib/molstar/core/index.d.ts +4 -1
  169. package/lib/molstar/core/index.d.ts.map +1 -1
  170. package/lib/molstar/core/index.js +2 -1
  171. package/lib/molstar/core/index.js.map +1 -1
  172. package/lib/pedagogy/core/answers.d.ts +46 -0
  173. package/lib/pedagogy/core/answers.d.ts.map +1 -0
  174. package/lib/pedagogy/core/answers.js +77 -0
  175. package/lib/pedagogy/core/answers.js.map +1 -0
  176. package/lib/pedagogy/core/attempts.d.ts +86 -0
  177. package/lib/pedagogy/core/attempts.d.ts.map +1 -0
  178. package/lib/pedagogy/core/attempts.js +117 -0
  179. package/lib/pedagogy/core/attempts.js.map +1 -0
  180. package/lib/pedagogy/core/index.d.ts +8 -0
  181. package/lib/pedagogy/core/index.d.ts.map +1 -1
  182. package/lib/pedagogy/core/index.js +4 -0
  183. package/lib/pedagogy/core/index.js.map +1 -1
  184. package/lib/pedagogy/core/progressController.d.ts +64 -0
  185. package/lib/pedagogy/core/progressController.d.ts.map +1 -0
  186. package/lib/pedagogy/core/progressController.js +56 -0
  187. package/lib/pedagogy/core/progressController.js.map +1 -0
  188. package/lib/pedagogy/core/seed.d.ts +96 -0
  189. package/lib/pedagogy/core/seed.d.ts.map +1 -0
  190. package/lib/pedagogy/core/seed.js +128 -0
  191. package/lib/pedagogy/core/seed.js.map +1 -0
  192. package/lib/pedagogy/core/series.d.ts +77 -0
  193. package/lib/pedagogy/core/series.d.ts.map +1 -0
  194. package/lib/pedagogy/core/series.js +10 -0
  195. package/lib/pedagogy/core/series.js.map +1 -0
  196. package/lib/pedagogy/ui/AnswerFields.d.ts +21 -0
  197. package/lib/pedagogy/ui/AnswerFields.d.ts.map +1 -0
  198. package/lib/pedagogy/ui/AnswerFields.js +24 -0
  199. package/lib/pedagogy/ui/AnswerFields.js.map +1 -0
  200. package/lib/pedagogy/ui/ExerciseList.d.ts +82 -0
  201. package/lib/pedagogy/ui/ExerciseList.d.ts.map +1 -0
  202. package/lib/pedagogy/ui/ExerciseList.js +53 -0
  203. package/lib/pedagogy/ui/ExerciseList.js.map +1 -0
  204. package/lib/pedagogy/ui/ExerciseSeries.d.ts +95 -0
  205. package/lib/pedagogy/ui/ExerciseSeries.d.ts.map +1 -0
  206. package/lib/pedagogy/ui/ExerciseSeries.js +74 -0
  207. package/lib/pedagogy/ui/ExerciseSeries.js.map +1 -0
  208. package/lib/pedagogy/ui/QuestionCard.d.ts +64 -0
  209. package/lib/pedagogy/ui/QuestionCard.d.ts.map +1 -0
  210. package/lib/pedagogy/ui/QuestionCard.js +73 -0
  211. package/lib/pedagogy/ui/QuestionCard.js.map +1 -0
  212. package/lib/pedagogy/ui/index.d.ts +10 -0
  213. package/lib/pedagogy/ui/index.d.ts.map +1 -1
  214. package/lib/pedagogy/ui/index.js +6 -0
  215. package/lib/pedagogy/ui/index.js.map +1 -1
  216. package/lib/pedagogy/ui/seriesCounts.d.ts +17 -0
  217. package/lib/pedagogy/ui/seriesCounts.d.ts.map +1 -0
  218. package/lib/pedagogy/ui/seriesCounts.js +27 -0
  219. package/lib/pedagogy/ui/seriesCounts.js.map +1 -0
  220. package/lib/pedagogy/ui/seriesParts.d.ts +55 -0
  221. package/lib/pedagogy/ui/seriesParts.d.ts.map +1 -0
  222. package/lib/pedagogy/ui/seriesParts.js +38 -0
  223. package/lib/pedagogy/ui/seriesParts.js.map +1 -0
  224. package/lib/pedagogy/ui/useAttemptRecords.d.ts +10 -0
  225. package/lib/pedagogy/ui/useAttemptRecords.d.ts.map +1 -0
  226. package/lib/pedagogy/ui/useAttemptRecords.js +11 -0
  227. package/lib/pedagogy/ui/useAttemptRecords.js.map +1 -0
  228. package/lib/periodic/ui/PeriodicTable.d.ts.map +1 -1
  229. package/lib/periodic/ui/PeriodicTable.js +6 -3
  230. package/lib/periodic/ui/PeriodicTable.js.map +1 -1
  231. package/lib/property/core/copyableText.d.ts +14 -0
  232. package/lib/property/core/copyableText.d.ts.map +1 -0
  233. package/lib/property/core/copyableText.js +21 -0
  234. package/lib/property/core/copyableText.js.map +1 -0
  235. package/lib/property/core/index.d.ts +2 -0
  236. package/lib/property/core/index.d.ts.map +1 -0
  237. package/lib/property/core/index.js +2 -0
  238. package/lib/property/core/index.js.map +1 -0
  239. package/lib/property/ui/PropertyTable.d.ts +43 -0
  240. package/lib/property/ui/PropertyTable.d.ts.map +1 -0
  241. package/lib/property/ui/PropertyTable.js +19 -0
  242. package/lib/property/ui/PropertyTable.js.map +1 -0
  243. package/lib/property/ui/index.d.ts +3 -0
  244. package/lib/property/ui/index.d.ts.map +1 -0
  245. package/lib/property/ui/index.js +2 -0
  246. package/lib/property/ui/index.js.map +1 -0
  247. package/lib/router/core/history.d.ts +24 -3
  248. package/lib/router/core/history.d.ts.map +1 -1
  249. package/lib/router/core/history.js +55 -7
  250. package/lib/router/core/history.js.map +1 -1
  251. package/lib/router/core/index.d.ts +3 -3
  252. package/lib/router/core/index.d.ts.map +1 -1
  253. package/lib/router/core/index.js +2 -2
  254. package/lib/router/core/index.js.map +1 -1
  255. package/lib/router/core/languageRoute.d.ts +61 -0
  256. package/lib/router/core/languageRoute.d.ts.map +1 -0
  257. package/lib/router/core/languageRoute.js +96 -0
  258. package/lib/router/core/languageRoute.js.map +1 -0
  259. package/lib/router/core/siteAddresses.d.ts +23 -0
  260. package/lib/router/core/siteAddresses.d.ts.map +1 -1
  261. package/lib/router/core/siteAddresses.js +26 -0
  262. package/lib/router/core/siteAddresses.js.map +1 -1
  263. package/lib/router/core/tabRouter.d.ts +2 -108
  264. package/lib/router/core/tabRouter.d.ts.map +1 -1
  265. package/lib/router/core/tabRouter.js +38 -5
  266. package/lib/router/core/tabRouter.js.map +1 -1
  267. package/lib/router/core/tabRouterTypes.d.ts +154 -0
  268. package/lib/router/core/tabRouterTypes.d.ts.map +1 -0
  269. package/lib/router/core/tabRouterTypes.js +2 -0
  270. package/lib/router/core/tabRouterTypes.js.map +1 -0
  271. package/lib/seo/core/fitDescription.d.ts +40 -0
  272. package/lib/seo/core/fitDescription.d.ts.map +1 -0
  273. package/lib/seo/core/fitDescription.js +45 -0
  274. package/lib/seo/core/fitDescription.js.map +1 -0
  275. package/lib/seo/core/index.d.ts +7 -0
  276. package/lib/seo/core/index.d.ts.map +1 -1
  277. package/lib/seo/core/index.js +4 -0
  278. package/lib/seo/core/index.js.map +1 -1
  279. package/lib/seo/core/limits.d.ts +24 -0
  280. package/lib/seo/core/limits.d.ts.map +1 -0
  281. package/lib/seo/core/limits.js +24 -0
  282. package/lib/seo/core/limits.js.map +1 -0
  283. package/lib/seo/core/pageContent.d.ts +25 -0
  284. package/lib/seo/core/pageContent.d.ts.map +1 -0
  285. package/lib/seo/core/pageContent.js +25 -0
  286. package/lib/seo/core/pageContent.js.map +1 -0
  287. package/lib/seo/core/plainDescription.d.ts.map +1 -1
  288. package/lib/seo/core/plainDescription.js +3 -2
  289. package/lib/seo/core/plainDescription.js.map +1 -1
  290. package/lib/seo/core/routeProblems.d.ts.map +1 -1
  291. package/lib/seo/core/routeProblems.js +1 -6
  292. package/lib/seo/core/routeProblems.js.map +1 -1
  293. package/lib/seo/core/routeTable.d.ts +31 -0
  294. package/lib/seo/core/routeTable.d.ts.map +1 -0
  295. package/lib/seo/core/routeTable.js +96 -0
  296. package/lib/seo/core/routeTable.js.map +1 -0
  297. package/lib/seo/vite/index.d.ts +2 -0
  298. package/lib/seo/vite/index.d.ts.map +1 -1
  299. package/lib/seo/vite/index.js +1 -0
  300. package/lib/seo/vite/index.js.map +1 -1
  301. package/lib/seo/vite/prerender.d.ts +10 -0
  302. package/lib/seo/vite/prerender.d.ts.map +1 -1
  303. package/lib/seo/vite/prerender.js +12 -1
  304. package/lib/seo/vite/prerender.js.map +1 -1
  305. package/lib/seo/vite/routeTable.d.ts +40 -0
  306. package/lib/seo/vite/routeTable.d.ts.map +1 -0
  307. package/lib/seo/vite/routeTable.js +44 -0
  308. package/lib/seo/vite/routeTable.js.map +1 -0
  309. package/lib/server/answerAddresses.d.ts +51 -0
  310. package/lib/server/answerAddresses.d.ts.map +1 -0
  311. package/lib/server/answerAddresses.js +72 -0
  312. package/lib/server/answerAddresses.js.map +1 -0
  313. package/lib/server/cardMeta.d.ts +57 -0
  314. package/lib/server/cardMeta.d.ts.map +1 -0
  315. package/lib/server/cardMeta.js +52 -0
  316. package/lib/server/cardMeta.js.map +1 -0
  317. package/lib/server/config.d.ts +72 -0
  318. package/lib/server/config.d.ts.map +1 -0
  319. package/lib/server/config.js +67 -0
  320. package/lib/server/config.js.map +1 -0
  321. package/lib/server/envNumber.d.ts +35 -0
  322. package/lib/server/envNumber.d.ts.map +1 -0
  323. package/lib/server/envNumber.js +42 -0
  324. package/lib/server/envNumber.js.map +1 -0
  325. package/lib/server/healthRoute.d.ts +26 -0
  326. package/lib/server/healthRoute.d.ts.map +1 -0
  327. package/lib/server/healthRoute.js +22 -0
  328. package/lib/server/healthRoute.js.map +1 -0
  329. package/lib/server/listenSite.d.ts +38 -0
  330. package/lib/server/listenSite.d.ts.map +1 -0
  331. package/lib/server/listenSite.js +60 -0
  332. package/lib/server/listenSite.js.map +1 -0
  333. package/lib/server/mcpServer.d.ts +8 -0
  334. package/lib/server/mcpServer.d.ts.map +1 -0
  335. package/lib/server/mcpServer.js +11 -0
  336. package/lib/server/mcpServer.js.map +1 -0
  337. package/lib/server/repositoryVersion.d.ts +16 -0
  338. package/lib/server/repositoryVersion.d.ts.map +1 -0
  339. package/lib/server/repositoryVersion.js +33 -0
  340. package/lib/server/repositoryVersion.js.map +1 -0
  341. package/lib/server/routeOptions.d.ts +23 -0
  342. package/lib/server/routeOptions.d.ts.map +1 -0
  343. package/lib/server/routeOptions.js +20 -0
  344. package/lib/server/routeOptions.js.map +1 -0
  345. package/lib/server/routeTable.d.ts +14 -0
  346. package/lib/server/routeTable.d.ts.map +1 -0
  347. package/lib/server/routeTable.js +32 -0
  348. package/lib/server/routeTable.js.map +1 -0
  349. package/lib/server/siteView.d.ts +29 -0
  350. package/lib/server/siteView.d.ts.map +1 -0
  351. package/lib/server/siteView.js +21 -0
  352. package/lib/server/siteView.js.map +1 -0
  353. package/lib/server/spaPages.d.ts +32 -0
  354. package/lib/server/spaPages.d.ts.map +1 -0
  355. package/lib/server/spaPages.js +126 -0
  356. package/lib/server/spaPages.js.map +1 -0
  357. package/lib/server/spaSite.d.ts +30 -0
  358. package/lib/server/spaSite.d.ts.map +1 -0
  359. package/lib/server/spaSite.js +170 -0
  360. package/lib/server/spaSite.js.map +1 -0
  361. package/lib/server/spaSiteOptions.d.ts +126 -0
  362. package/lib/server/spaSiteOptions.d.ts.map +1 -0
  363. package/lib/server/spaSiteOptions.js +2 -0
  364. package/lib/server/spaSiteOptions.js.map +1 -0
  365. package/lib/server/staticSite.d.ts +9 -0
  366. package/lib/server/staticSite.d.ts.map +1 -1
  367. package/lib/server/staticSite.js +41 -19
  368. package/lib/server/staticSite.js.map +1 -1
  369. package/lib/server/toolSiteServer.d.ts +77 -0
  370. package/lib/server/toolSiteServer.d.ts.map +1 -0
  371. package/lib/server/toolSiteServer.js +58 -0
  372. package/lib/server/toolSiteServer.js.map +1 -0
  373. package/lib/server/trustProxy.d.ts +20 -0
  374. package/lib/server/trustProxy.d.ts.map +1 -0
  375. package/lib/server/trustProxy.js +32 -0
  376. package/lib/server/trustProxy.js.map +1 -0
  377. package/lib/server/types.d.ts +13 -0
  378. package/lib/server/types.d.ts.map +1 -0
  379. package/lib/server/types.js +2 -0
  380. package/lib/server/types.js.map +1 -0
  381. package/lib/server.d.ts +22 -0
  382. package/lib/server.d.ts.map +1 -1
  383. package/lib/server.js +12 -0
  384. package/lib/server.js.map +1 -1
  385. package/lib/share/core/config.d.ts +18 -0
  386. package/lib/share/core/config.d.ts.map +1 -1
  387. package/lib/share/core/config.js +33 -2
  388. package/lib/share/core/config.js.map +1 -1
  389. package/lib/share/core/index.d.ts +3 -1
  390. package/lib/share/core/index.d.ts.map +1 -1
  391. package/lib/share/core/index.js +2 -1
  392. package/lib/share/core/index.js.map +1 -1
  393. package/lib/share/core/keySetParam.d.ts +40 -0
  394. package/lib/share/core/keySetParam.d.ts.map +1 -0
  395. package/lib/share/core/keySetParam.js +60 -0
  396. package/lib/share/core/keySetParam.js.map +1 -0
  397. package/lib/share/core/vocabulary.d.ts +8 -0
  398. package/lib/share/core/vocabulary.d.ts.map +1 -1
  399. package/lib/shell/ui/ShareControl.d.ts +75 -0
  400. package/lib/shell/ui/ShareControl.d.ts.map +1 -0
  401. package/lib/shell/ui/ShareControl.js +40 -0
  402. package/lib/shell/ui/ShareControl.js.map +1 -0
  403. package/lib/shell/ui/ShellFrame.d.ts +14 -0
  404. package/lib/shell/ui/ShellFrame.d.ts.map +1 -0
  405. package/lib/shell/ui/ShellFrame.js +45 -0
  406. package/lib/shell/ui/ShellFrame.js.map +1 -0
  407. package/lib/shell/ui/SiteShell.d.ts +251 -0
  408. package/lib/shell/ui/SiteShell.d.ts.map +1 -0
  409. package/lib/shell/ui/SiteShell.js +39 -0
  410. package/lib/shell/ui/SiteShell.js.map +1 -0
  411. package/lib/shell/ui/index.d.ts +5 -0
  412. package/lib/shell/ui/index.d.ts.map +1 -0
  413. package/lib/shell/ui/index.js +3 -0
  414. package/lib/shell/ui/index.js.map +1 -0
  415. package/lib/state/core/index.d.ts +1 -0
  416. package/lib/state/core/index.d.ts.map +1 -1
  417. package/lib/state/core/index.js.map +1 -1
  418. package/lib/state/core/legacyEntries.d.ts +30 -0
  419. package/lib/state/core/legacyEntries.d.ts.map +1 -0
  420. package/lib/state/core/legacyEntries.js +51 -0
  421. package/lib/state/core/legacyEntries.js.map +1 -0
  422. package/lib/state/core/persistBucket.d.ts +26 -2
  423. package/lib/state/core/persistBucket.d.ts.map +1 -1
  424. package/lib/state/core/persistBucket.js +26 -9
  425. package/lib/state/core/persistBucket.js.map +1 -1
  426. package/lib/state/core/persistSignalBucket.d.ts +26 -0
  427. package/lib/state/core/persistSignalBucket.d.ts.map +1 -1
  428. package/lib/state/core/persistSignalBucket.js +31 -0
  429. package/lib/state/core/persistSignalBucket.js.map +1 -1
  430. package/lib/state/core/storageEntry.d.ts +15 -0
  431. package/lib/state/core/storageEntry.d.ts.map +1 -1
  432. package/lib/state/core/storageEntry.js +33 -0
  433. package/lib/state/core/storageEntry.js.map +1 -1
  434. package/lib/structure/ui/StructureExportButton.d.ts +27 -0
  435. package/lib/structure/ui/StructureExportButton.d.ts.map +1 -0
  436. package/lib/structure/ui/StructureExportButton.js +21 -0
  437. package/lib/structure/ui/StructureExportButton.js.map +1 -0
  438. package/lib/structure/ui/index.d.ts +4 -0
  439. package/lib/structure/ui/index.d.ts.map +1 -1
  440. package/lib/structure/ui/index.js +2 -0
  441. package/lib/structure/ui/index.js.map +1 -1
  442. package/lib/structure/ui/useStructureDraft.d.ts +32 -0
  443. package/lib/structure/ui/useStructureDraft.d.ts.map +1 -0
  444. package/lib/structure/ui/useStructureDraft.js +20 -0
  445. package/lib/structure/ui/useStructureDraft.js.map +1 -0
  446. package/lib/ui.d.ts +2 -0
  447. package/lib/ui.d.ts.map +1 -1
  448. package/lib/ui.js +2 -0
  449. package/lib/ui.js.map +1 -1
  450. package/lib/worker/core/index.d.ts +1 -1
  451. package/lib/worker/core/index.d.ts.map +1 -1
  452. package/lib/worker/core/messages.d.ts +4 -2
  453. package/lib/worker/core/messages.d.ts.map +1 -1
  454. package/lib/worker/core/messages.js.map +1 -1
  455. package/lib/worker/core/oclResourcesUrl.d.ts +16 -0
  456. package/lib/worker/core/oclResourcesUrl.d.ts.map +1 -0
  457. package/lib/worker/core/oclResourcesUrl.js +19 -0
  458. package/lib/worker/core/oclResourcesUrl.js.map +1 -0
  459. package/lib/worker/core/serveWorkerRequests.d.ts +8 -0
  460. package/lib/worker/core/serveWorkerRequests.d.ts.map +1 -1
  461. package/lib/worker/core/serveWorkerRequests.js +15 -2
  462. package/lib/worker/core/serveWorkerRequests.js.map +1 -1
  463. package/lib/worker/core/workerChannel.d.ts +3 -4
  464. package/lib/worker/core/workerChannel.d.ts.map +1 -1
  465. package/lib/worker/core/workerChannel.js +72 -67
  466. package/lib/worker/core/workerChannel.js.map +1 -1
  467. package/lib/worker/core/workerChannelParts.d.ts +17 -6
  468. package/lib/worker/core/workerChannelParts.d.ts.map +1 -1
  469. package/lib/worker/core/workerChannelParts.js +20 -4
  470. package/lib/worker/core/workerChannelParts.js.map +1 -1
  471. package/lib/worker/core/workerChannelTypes.d.ts +46 -2
  472. package/lib/worker/core/workerChannelTypes.d.ts.map +1 -1
  473. package/lib/worker/core/workerSlot.d.ts +66 -0
  474. package/lib/worker/core/workerSlot.d.ts.map +1 -0
  475. package/lib/worker/core/workerSlot.js +107 -0
  476. package/lib/worker/core/workerSlot.js.map +1 -0
  477. package/package.json +16 -1
  478. package/src/card/core/cardAddress.ts +100 -0
  479. package/src/card/core/cardPageUrl.ts +97 -0
  480. package/src/card/core/index.ts +6 -0
  481. package/src/card/core/panelRoom.ts +66 -0
  482. package/src/card/ui/CardDetailsButton.tsx +104 -0
  483. package/src/card/ui/CardFigure.tsx +86 -0
  484. package/src/card/ui/CardFrame.tsx +46 -0
  485. package/src/card/ui/CopyTableButton.tsx +81 -0
  486. package/src/card/ui/cardTooltip.ts +18 -0
  487. package/src/card/ui/embed.ts +27 -0
  488. package/src/card/ui/fitCardToFrame.ts +76 -0
  489. package/src/card/ui/holdRoomForPanels.ts +83 -0
  490. package/src/card/ui/index.ts +14 -0
  491. package/src/card/ui/openPanels.ts +106 -0
  492. package/src/card.ts +13 -0
  493. package/src/conformers.ts +4 -0
  494. package/src/core.ts +5 -0
  495. package/src/fetch/core/apiError.ts +65 -0
  496. package/src/fetch/core/fetchJson.ts +129 -0
  497. package/src/fetch/core/index.ts +4 -0
  498. package/src/formula/core/formulaText.ts +96 -0
  499. package/src/formula/core/index.ts +20 -0
  500. package/src/formula/core/normalizeFormula.ts +50 -0
  501. package/src/formula/core/readFormula.ts +211 -0
  502. package/src/formula/ui/CopyableFormula.tsx +51 -0
  503. package/src/formula/ui/FormulaInput.tsx +50 -0
  504. package/src/formula/ui/QuestionFormula.tsx +20 -0
  505. package/src/formula/ui/index.ts +6 -0
  506. package/src/formula.ts +11 -0
  507. package/src/hooks/ui/index.ts +1 -0
  508. package/src/hooks/ui/keyTargets.ts +5 -3
  509. package/src/hooks/ui/useAsync.ts +50 -14
  510. package/src/locales/de.json +37 -0
  511. package/src/locales/en.json +37 -0
  512. package/src/locales/es.json +37 -0
  513. package/src/locales/fr.json +37 -0
  514. package/src/locales/it.json +37 -0
  515. package/src/moleculesClient/core/index.ts +19 -0
  516. package/src/moleculesClient/core/moleculesClient.ts +175 -0
  517. package/src/moleculesClient/core/moleculesClientApi.ts +120 -0
  518. package/src/moleculesClient/core/moleculesTypes.ts +163 -0
  519. package/src/moleculesClient/core/requestRetry.ts +118 -0
  520. package/src/molstar/core/camera.ts +3 -2
  521. package/src/molstar/core/framing.ts +200 -0
  522. package/src/molstar/core/hover.ts +26 -0
  523. package/src/molstar/core/index.ts +21 -1
  524. package/src/pedagogy/core/answers.ts +94 -0
  525. package/src/pedagogy/core/attempts.ts +205 -0
  526. package/src/pedagogy/core/index.ts +36 -0
  527. package/src/pedagogy/core/progressController.ts +116 -0
  528. package/src/pedagogy/core/seed.ts +177 -0
  529. package/src/pedagogy/core/series.ts +82 -0
  530. package/src/pedagogy/ui/AnswerFields.tsx +63 -0
  531. package/src/pedagogy/ui/ExerciseList.tsx +202 -0
  532. package/src/pedagogy/ui/ExerciseSeries.tsx +250 -0
  533. package/src/pedagogy/ui/QuestionCard.tsx +231 -0
  534. package/src/pedagogy/ui/index.ts +18 -0
  535. package/src/pedagogy/ui/seriesCounts.ts +34 -0
  536. package/src/pedagogy/ui/seriesParts.tsx +127 -0
  537. package/src/pedagogy/ui/useAttemptRecords.ts +16 -0
  538. package/src/periodic/ui/PeriodicTable.tsx +6 -3
  539. package/src/property/core/copyableText.ts +20 -0
  540. package/src/property/core/index.ts +1 -0
  541. package/src/property/ui/PropertyTable.tsx +83 -0
  542. package/src/property/ui/index.ts +2 -0
  543. package/src/router/core/history.ts +61 -7
  544. package/src/router/core/index.ts +3 -2
  545. package/src/router/core/languageRoute.ts +136 -0
  546. package/src/router/core/siteAddresses.ts +38 -0
  547. package/src/router/core/tabRouter.ts +59 -117
  548. package/src/router/core/tabRouterTypes.ts +158 -0
  549. package/src/seo/core/fitDescription.ts +74 -0
  550. package/src/seo/core/index.ts +17 -0
  551. package/src/seo/core/limits.ts +27 -0
  552. package/src/seo/core/pageContent.ts +42 -0
  553. package/src/seo/core/plainDescription.ts +4 -2
  554. package/src/seo/core/routeProblems.ts +1 -7
  555. package/src/seo/core/routeTable.ts +103 -0
  556. package/src/seo/vite/index.ts +2 -0
  557. package/src/seo/vite/prerender.ts +26 -1
  558. package/src/seo/vite/routeTable.ts +79 -0
  559. package/src/server/answerAddresses.ts +92 -0
  560. package/src/server/cardMeta.ts +88 -0
  561. package/src/server/config.ts +125 -0
  562. package/src/server/envNumber.ts +65 -0
  563. package/src/server/healthRoute.ts +42 -0
  564. package/src/server/listenSite.ts +90 -0
  565. package/src/server/mcpServer.ts +14 -0
  566. package/src/server/repositoryVersion.ts +33 -0
  567. package/src/server/routeOptions.ts +26 -0
  568. package/src/server/routeTable.ts +35 -0
  569. package/src/server/siteView.ts +42 -0
  570. package/src/server/spaPages.ts +179 -0
  571. package/src/server/spaSite.ts +216 -0
  572. package/src/server/spaSiteOptions.ts +133 -0
  573. package/src/server/staticSite.ts +65 -35
  574. package/src/server/toolSiteServer.ts +127 -0
  575. package/src/server/trustProxy.ts +32 -0
  576. package/src/server/types.ts +25 -0
  577. package/src/server.ts +40 -0
  578. package/src/share/core/config.ts +45 -2
  579. package/src/share/core/index.ts +4 -0
  580. package/src/share/core/keySetParam.ts +92 -0
  581. package/src/share/core/vocabulary.ts +8 -0
  582. package/src/shell/ui/ShareControl.tsx +150 -0
  583. package/src/shell/ui/ShellFrame.tsx +180 -0
  584. package/src/shell/ui/SiteShell.tsx +297 -0
  585. package/src/shell/ui/index.ts +9 -0
  586. package/src/state/core/index.ts +1 -0
  587. package/src/state/core/legacyEntries.ts +76 -0
  588. package/src/state/core/persistBucket.ts +50 -10
  589. package/src/state/core/persistSignalBucket.ts +52 -0
  590. package/src/state/core/storageEntry.ts +32 -0
  591. package/src/structure/ui/StructureExportButton.tsx +57 -0
  592. package/src/structure/ui/index.ts +4 -0
  593. package/src/structure/ui/useStructureDraft.ts +47 -0
  594. package/src/ui.ts +2 -0
  595. package/src/worker/core/index.ts +1 -0
  596. package/src/worker/core/messages.ts +4 -3
  597. package/src/worker/core/oclResourcesUrl.ts +21 -0
  598. package/src/worker/core/serveWorkerRequests.ts +30 -2
  599. package/src/worker/core/workerChannel.ts +74 -74
  600. package/src/worker/core/workerChannelParts.ts +33 -13
  601. package/src/worker/core/workerChannelTypes.ts +47 -2
  602. package/src/worker/core/workerSlot.ts +181 -0
  603. package/styles/card.css +145 -0
  604. package/styles/chrome.css +248 -0
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Where an answer of a site's tool server points: the static site people
3
+ * share, which an answer's `link` names, and this server, whose copy of the
4
+ * same pages a chat frames.
5
+ *
6
+ * The two are written however the deployment writes them — an origin, an
7
+ * origin and a mount, with or without a trailing slash — and a page is named
8
+ * from the site's own root, `/titration` and `titration` alike: every helper
9
+ * joins the two with exactly one slash, so `https://www.cheminfo.org/surge`
10
+ * and `https://www.cheminfo.org/surge/` both put `/generator` at
11
+ * `https://www.cheminfo.org/surge/generator`.
12
+ */
13
+
14
+ import { trimTrailingSlash } from '../router/core/address.ts';
15
+ import { joinBasePath } from '../router/core/basePath.ts';
16
+
17
+ /** Where the pages of an answer are served, as a deployment writes them. */
18
+ export interface AnswerAddressesInput {
19
+ /** The static site an answer links to, the address people share. */
20
+ siteUrl: string | URL;
21
+ /** This server, as a browser reaches it: the view frames its pages. */
22
+ publicUrl: string | URL;
23
+ }
24
+
25
+ /** Where the pages of an answer are served, checked and written alike. */
26
+ export interface AnswerAddresses {
27
+ /** The static site, ending in `/`: `https://equilibrium.cheminfo.org/`. */
28
+ readonly siteUrl: string;
29
+ /** This server, ending in `/`: `https://equilibrium.c6h6.org/`. */
30
+ readonly publicUrl: string;
31
+ }
32
+
33
+ /**
34
+ * Check the two addresses of a tool server's answers, and write them alike:
35
+ * absolute, `http` or `https`, without a query or a fragment, ending in `/`.
36
+ * @param input - The site and this server, as the deployment gives them.
37
+ * @returns The two addresses.
38
+ * @throws {Error} When either is not an http or https address.
39
+ */
40
+ export function answerAddresses(input: AnswerAddressesInput): AnswerAddresses {
41
+ return {
42
+ siteUrl: baseAddress(input.siteUrl, 'siteUrl'),
43
+ publicUrl: baseAddress(input.publicUrl, 'publicUrl'),
44
+ };
45
+ }
46
+
47
+ /**
48
+ * The origins a view may frame: this server's, which a chat frames, and the
49
+ * shared site's, which a link opens. A server that is the site gives one.
50
+ * @param addresses - The two addresses.
51
+ * @returns Each origin once, this server's first.
52
+ */
53
+ export function viewOrigins(addresses: AnswerAddressesInput): string[] {
54
+ return [
55
+ ...new Set([
56
+ new URL(addresses.publicUrl).origin,
57
+ new URL(addresses.siteUrl).origin,
58
+ ]),
59
+ ];
60
+ }
61
+
62
+ /**
63
+ * A page under a base address, joined with exactly one slash.
64
+ * @param base - An origin, or an origin and a mount, with or without its
65
+ * trailing slash.
66
+ * @param page - A page from the site's own root, with or without its leading
67
+ * slash, and with its query: `/titration?ind=phenolphthalein`.
68
+ * @returns The page's absolute address.
69
+ */
70
+ export function joinAddress(base: string | URL, page: string): string {
71
+ return `${trimTrailingSlash(String(base))}${joinBasePath('', page)}`;
72
+ }
73
+
74
+ /**
75
+ * One address of a deployment, checked.
76
+ * @param value - The address.
77
+ * @param name - What it is, for the error.
78
+ * @returns The address without its query or fragment, ending in `/`.
79
+ */
80
+ function baseAddress(value: string | URL, name: string): string {
81
+ const text = String(value).trim();
82
+ if (!URL.canParse(text)) {
83
+ throw new Error(`${name} is not an address: ${text}`);
84
+ }
85
+ const url = new URL(text);
86
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') {
87
+ throw new Error(`${name} is not an http or https address: ${text}`);
88
+ }
89
+ url.search = '';
90
+ url.hash = '';
91
+ return url.href.endsWith('/') ? url.href : `${url.href}/`;
92
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * What a tool server's answer carries for a chat to show it: the link to the
3
+ * site's page people share, and the `meta` naming that page framed on this
4
+ * server — its card inline, the page itself over the full screen.
5
+ */
6
+
7
+ import type { CardPageOptions } from '../card/core/cardPageUrl.ts';
8
+ import { cardPageUrl, framedPageUrl } from '../card/core/cardPageUrl.ts';
9
+ import { formatQueryString } from '../router/core/query.ts';
10
+
11
+ import type { AnswerAddressesInput } from './answerAddresses.ts';
12
+ import { joinAddress } from './answerAddresses.ts';
13
+ import { mcpServer } from './mcpServer.ts';
14
+
15
+ /** How {@link cardMeta} writes the card and the page. */
16
+ export interface CardMetaOptions extends CardPageOptions {
17
+ /**
18
+ * The longest card address an answer carries. Browsers take far more, but a
19
+ * proxy reading the request line stops at 8 to 16 KB; past this length the
20
+ * answer carries no card, and the chat draws the answer itself.
21
+ * @default 8000
22
+ */
23
+ maxLength?: number;
24
+ }
25
+
26
+ /** The page an answer is shown on. */
27
+ export interface PageAnswer {
28
+ /** The page on the static site people share: what `result.link` says. */
29
+ link: string;
30
+ /**
31
+ * The answer's `meta`: the card on this server, framed inline, and the same
32
+ * page whole for when the reader asks for more. Undefined when the card's
33
+ * address would be longer than `maxLength`.
34
+ */
35
+ meta: Record<string, unknown> | undefined;
36
+ }
37
+
38
+ /**
39
+ * What an answer carries for the view to show a page of this server: inline
40
+ * its card, drawing only the answer; over the full screen the page itself.
41
+ * Both are without the site's chrome, and without the parts `hide` names.
42
+ * @param addresses - Where the site and this server are; only this server's
43
+ * is read.
44
+ * @param page - The page from the site's own root, with its own query and
45
+ * without `embed`, `hide` or `card`: `/titration?ind=phenolphthalein`.
46
+ * @param options - The parts left out, what the card draws, and the longest
47
+ * card address carried.
48
+ * @returns The answer's `meta`, or undefined when the card's address is too
49
+ * long to carry.
50
+ */
51
+ export function cardMeta(
52
+ addresses: AnswerAddressesInput,
53
+ page: string,
54
+ options: CardMetaOptions = {},
55
+ ): Record<string, unknown> | undefined {
56
+ const { maxLength = 8000, ...written } = options;
57
+ const framed = joinAddress(addresses.publicUrl, page);
58
+ const card = cardPageUrl(framed, written);
59
+ if (card.length > maxLength) return undefined;
60
+ return mcpServer().embedMeta(card, { page: framedPageUrl(framed, written) });
61
+ }
62
+
63
+ /**
64
+ * A page with its query: the link people share, on the static site, and the
65
+ * `meta` the view frames this server's copy of it with. The query is written
66
+ * by `formatQueryString`, so a `+` is escaped, a comma stays a comma, and a
67
+ * parameter left `undefined` or empty is not written: the page reads its own
68
+ * default.
69
+ * @param addresses - Where the site and this server are.
70
+ * @param path - The page, from the site's own root: `/titration`.
71
+ * @param params - Its query, in the order it is written.
72
+ * @param options - The parts left out, what the card draws, and the longest
73
+ * card address carried.
74
+ * @returns The link and the meta.
75
+ */
76
+ export function pageAnswer(
77
+ addresses: AnswerAddressesInput,
78
+ path: string,
79
+ params: Readonly<Record<string, string | undefined>> = {},
80
+ options: CardMetaOptions = {},
81
+ ): PageAnswer {
82
+ const query = formatQueryString(params);
83
+ const page = query === '' ? path : `${path}?${query}`;
84
+ return {
85
+ link: joinAddress(addresses.siteUrl, page),
86
+ meta: cardMeta(addresses, page, options),
87
+ };
88
+ }
@@ -0,0 +1,125 @@
1
+ import { siteById } from '../ecosystem/core/lookup.ts';
2
+ import type { SiteId } from '../ecosystem/core/sites.ts';
3
+ import { siteUrl } from '../ecosystem/core/sites.ts';
4
+ import { normalizeBasePath } from '../router/core/basePath.ts';
5
+
6
+ import { readEnvNumber } from './envNumber.ts';
7
+ import { parseTrustProxy } from './trustProxy.ts';
8
+
9
+ /** An environment, as `process.env` holds it. */
10
+ export type ServerEnvironment = Readonly<Record<string, string | undefined>>;
11
+
12
+ /** What a site's server knows before it reads its environment. */
13
+ export interface ServerConfigOptions {
14
+ /** The site, as `ECOSYSTEM_SITES` names it: its address is the default `SITE_URL`. */
15
+ siteId: SiteId;
16
+ /** The port derived from the site's creation date, the one compose publishes. */
17
+ port: number;
18
+ /**
19
+ * The build served when `SERVER_ROOT` names none, usually
20
+ * `join(import.meta.dirname, '../dist')`.
21
+ * @default 'dist' - resolved from the directory the process runs in
22
+ */
23
+ root?: string;
24
+ }
25
+
26
+ /** What a site's server is started with, every default filled in. */
27
+ export interface ServerConfig {
28
+ /** Where it listens: `PORT`, or the site's own port. */
29
+ port: number;
30
+ /** The interface it listens on: `HOST`, or every interface, as a container needs. */
31
+ host: string;
32
+ /** `TRACKING_SCRIPT`, verbatim; `undefined` loads nothing. */
33
+ trackingScript: string | undefined;
34
+ /**
35
+ * `SITE_URL`: the static site people share, which every answer links to,
36
+ * ending in `/`.
37
+ */
38
+ siteUrl: string;
39
+ /**
40
+ * `PUBLIC_URL`: this server's own address, whose pages a chat frames, ending
41
+ * in `/`.
42
+ */
43
+ publicUrl: string;
44
+ /** `TRUST_PROXY`: the reverse proxies whose `X-Forwarded-For` is believed. */
45
+ trustProxy: boolean | string;
46
+ /** `SERVER_ROOT`: the build it serves. */
47
+ root: string;
48
+ /**
49
+ * `BASE_PATH`: where the deployment is mounted behind a proxy that takes the
50
+ * path off, `/osiris`, or `''` on a host of its own.
51
+ */
52
+ basePath: string;
53
+ }
54
+
55
+ /**
56
+ * Read a site's server configuration from its environment, once, at startup.
57
+ *
58
+ * A malformed value refuses to start rather than handing out links that lead
59
+ * nowhere. Both addresses keep their path — a site may be mounted under one,
60
+ * and its pages are written relative to it — and lose their query and their
61
+ * fragment; each ends in `/`.
62
+ *
63
+ * const config = readServerConfig(process.env, { siteId: 'osiris', port: 10_620 });
64
+ * @param env - The environment, e.g. `process.env`.
65
+ * @param options - The site and its port.
66
+ * @returns The configuration.
67
+ * @throws {Error} When `PORT`, `SITE_URL` or `PUBLIC_URL` cannot be read, or
68
+ * `TRUST_PROXY` is a hop count.
69
+ */
70
+ export function readServerConfig(
71
+ env: ServerEnvironment,
72
+ options: ServerConfigOptions,
73
+ ): ServerConfig {
74
+ const port = readEnvNumber(env, 'PORT', options.port, {
75
+ min: 1,
76
+ max: 65_535,
77
+ integer: true,
78
+ });
79
+ return {
80
+ port,
81
+ host: env.HOST?.trim() || '0.0.0.0',
82
+ trackingScript: env.TRACKING_SCRIPT?.trim() || undefined,
83
+ siteUrl: readHttpAddress(
84
+ env,
85
+ 'SITE_URL',
86
+ siteUrl(siteById(options.siteId)),
87
+ ),
88
+ publicUrl: readHttpAddress(env, 'PUBLIC_URL', `http://localhost:${port}/`),
89
+ trustProxy: parseTrustProxy(env.TRUST_PROXY),
90
+ root: env.SERVER_ROOT?.trim() || options.root || 'dist',
91
+ basePath: normalizeBasePath(env.BASE_PATH ?? ''),
92
+ };
93
+ }
94
+
95
+ /**
96
+ * Read an http or https address from the environment, the way a link is built
97
+ * on it: its path kept and ended with `/`, its query and its fragment dropped.
98
+ * @param env - The environment, e.g. `process.env`.
99
+ * @param name - The variable, e.g. `SITE_URL`.
100
+ * @param fallback - The address when the variable is unset or blank.
101
+ * @returns The address, ending in `/`.
102
+ * @throws {Error} When the value, or the fallback, is not an http or https
103
+ * address.
104
+ */
105
+ export function readHttpAddress(
106
+ env: ServerEnvironment,
107
+ name: string,
108
+ fallback: string,
109
+ ): string {
110
+ const text = env[name]?.trim() || fallback;
111
+ const message = `${name} must be an http(s) address, not "${text}".`;
112
+ let url: URL;
113
+ try {
114
+ url = new URL(text);
115
+ } catch (error) {
116
+ throw new Error(message, { cause: error });
117
+ }
118
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') {
119
+ throw new Error(message);
120
+ }
121
+ url.search = '';
122
+ url.hash = '';
123
+ if (!url.pathname.endsWith('/')) url.pathname = `${url.pathname}/`;
124
+ return url.href;
125
+ }
@@ -0,0 +1,65 @@
1
+ /** The bounds a number read from the environment must keep. */
2
+ export interface ReadEnvNumberOptions {
3
+ /**
4
+ * The smallest value accepted.
5
+ * @default undefined - no lower bound
6
+ */
7
+ min?: number;
8
+ /**
9
+ * The largest value accepted.
10
+ * @default undefined - no upper bound
11
+ */
12
+ max?: number;
13
+ /**
14
+ * Whether the value must be a whole number.
15
+ * @default false
16
+ */
17
+ integer?: boolean;
18
+ }
19
+
20
+ /**
21
+ * Read a number from the environment.
22
+ *
23
+ * The compose files forward every variable as `${NAME:-}`, so a deployment that
24
+ * sets nothing hands the process an empty string rather than no variable — and
25
+ * `Number('')` is 0, which turned an indexer's interval into a busy loop. Unset,
26
+ * empty and blank all mean the fallback here. A value that says something but is
27
+ * not an accepted number refuses to start rather than running on a guess.
28
+ * @param env - The environment, e.g. `process.env`.
29
+ * @param name - The variable, e.g. `INDEX_INTERVAL`.
30
+ * @param fallback - The value when the variable says nothing.
31
+ * @param options - The bounds the value must keep.
32
+ * @returns The number written, or the fallback.
33
+ * @throws {Error} When the value is not a finite number, or breaks a bound.
34
+ */
35
+ export function readEnvNumber(
36
+ env: Readonly<Record<string, string | undefined>>,
37
+ name: string,
38
+ fallback: number,
39
+ options: ReadEnvNumberOptions = {},
40
+ ): number {
41
+ const text = env[name]?.trim();
42
+ if (!text) return fallback;
43
+ const value = Number(text);
44
+ const { min, max, integer = false } = options;
45
+ if (
46
+ !Number.isFinite(value) ||
47
+ (integer && !Number.isInteger(value)) ||
48
+ (min !== undefined && value < min) ||
49
+ (max !== undefined && value > max)
50
+ ) {
51
+ throw new Error(`${name} must be ${expected(options)}, not "${text}".`);
52
+ }
53
+ return value;
54
+ }
55
+
56
+ function expected(options: ReadEnvNumberOptions): string {
57
+ const { min, max, integer = false } = options;
58
+ const kind = integer ? 'an integer' : 'a number';
59
+ if (min !== undefined && max !== undefined) {
60
+ return `${kind} from ${min} to ${max}`;
61
+ }
62
+ if (min !== undefined) return `${kind} of at least ${min}`;
63
+ if (max !== undefined) return `${kind} of at most ${max}`;
64
+ return kind;
65
+ }
@@ -0,0 +1,42 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+
3
+ import { ALLOW_ORIGIN, HIDDEN } from './routeOptions.ts';
4
+
5
+ /** Options of {@link healthRoute}. */
6
+ export interface HealthRouteOptions {
7
+ /**
8
+ * More fields for the answer, read locally and cheaply: a probe every 30 s
9
+ * must never wait on another service, or a dependency that is down rolls back
10
+ * a good release. `status` is always `ok`, whatever this returns.
11
+ * @default undefined - the answer is `{ status: 'ok' }`
12
+ */
13
+ extra?: () => Record<string, unknown> | Promise<Record<string, unknown>>;
14
+ }
15
+
16
+ /**
17
+ * Answer `GET /health`, what the container's health check and the server's
18
+ * deploy script probe before they keep a new container.
19
+ *
20
+ * It is at the root, never versioned, because it probes the process rather than
21
+ * the API contract. It logs nothing, or a probe every 30 s fills the log; it is
22
+ * kept out of the API's documentation; and it is open to any origin, like
23
+ * everything else a site serves.
24
+ *
25
+ * await healthRoute(app);
26
+ * @param fastify - The server.
27
+ * @param options - Extra fields for the answer.
28
+ */
29
+ export async function healthRoute(
30
+ fastify: FastifyInstance,
31
+ options: HealthRouteOptions = {},
32
+ ): Promise<void> {
33
+ const { extra } = options;
34
+ fastify.get(
35
+ '/health',
36
+ { logLevel: 'silent', schema: HIDDEN },
37
+ async (_request, reply) => {
38
+ const fields = extra === undefined ? {} : await extra();
39
+ return reply.header(ALLOW_ORIGIN, '*').send({ ...fields, status: 'ok' });
40
+ },
41
+ );
42
+ }
@@ -0,0 +1,90 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+
3
+ /** Options of {@link listenSite}. */
4
+ export interface ListenSiteOptions {
5
+ /** The port to listen on. */
6
+ port: number;
7
+ /**
8
+ * The interface to listen on.
9
+ * @default '0.0.0.0' - every interface, as a container needs
10
+ */
11
+ host?: string;
12
+ /**
13
+ * Run once the server has closed on a signal: stop what the server does not
14
+ * own itself, such as a scheduler or a write queue started beside it.
15
+ * @default undefined - nothing more is stopped
16
+ */
17
+ onClose?: () => void | Promise<void>;
18
+ /**
19
+ * The signals that close the server.
20
+ * @default ['SIGTERM', 'SIGINT'] - what `docker stop` and a terminal send
21
+ */
22
+ signals?: readonly NodeJS.Signals[];
23
+ }
24
+
25
+ /**
26
+ * Listen, and close cleanly when the container is stopped.
27
+ *
28
+ * On the first signal the server closes — its `onClose` hooks stop the worker
29
+ * pools it owns — then `onClose` runs. A close that fails is logged and leaves
30
+ * `process.exitCode` at 1 rather than surfacing as an unhandled rejection, and
31
+ * the process ends on its own once nothing is left running. A server that
32
+ * cannot listen is logged, closed and reported the same way.
33
+ *
34
+ * await listenSite(app, { port: config.port, host: config.host });
35
+ * @param app - The server, built and not yet listening.
36
+ * @param options - Where to listen, and what else to stop.
37
+ * @throws {Error} When the server cannot listen, after it has been closed.
38
+ */
39
+ export async function listenSite(
40
+ app: FastifyInstance,
41
+ options: ListenSiteOptions,
42
+ ): Promise<void> {
43
+ const {
44
+ port,
45
+ host = '0.0.0.0',
46
+ onClose,
47
+ signals = ['SIGTERM', 'SIGINT'],
48
+ } = options;
49
+
50
+ const stop = () => {
51
+ for (const signal of signals) process.off(signal, stop);
52
+ void shutDown(app, onClose);
53
+ };
54
+ for (const signal of signals) process.once(signal, stop);
55
+
56
+ try {
57
+ await app.listen({ port, host });
58
+ } catch (error) {
59
+ for (const signal of signals) process.off(signal, stop);
60
+ app.log.error(error, `the server could not listen on ${host}:${port}`);
61
+ process.exitCode = 1;
62
+ await shutDown(app, onClose);
63
+ throw error;
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Close the server, then run what else has to stop, reporting a failure of
69
+ * either through the log and the exit code.
70
+ * @param app - The server.
71
+ * @param onClose - What else to stop.
72
+ */
73
+ async function shutDown(
74
+ app: FastifyInstance,
75
+ onClose: (() => void | Promise<void>) | undefined,
76
+ ): Promise<void> {
77
+ try {
78
+ await app.close();
79
+ } catch (error) {
80
+ app.log.error(error, 'the server did not close cleanly');
81
+ process.exitCode = 1;
82
+ }
83
+ if (onClose === undefined) return;
84
+ try {
85
+ await onClose();
86
+ } catch (error) {
87
+ app.log.error(error, 'what runs beside the server did not stop cleanly');
88
+ process.exitCode = 1;
89
+ }
90
+ }
@@ -0,0 +1,14 @@
1
+ import { createRequire } from 'node:module';
2
+
3
+ import type * as McpServer from 'cheminfo-mcp-server';
4
+
5
+ const require = createRequire(import.meta.url);
6
+
7
+ /**
8
+ * `cheminfo-mcp-server`, an optional peer, loaded when a helper first needs
9
+ * it, so `react-cheminfo/server` imports on a site that serves no tools.
10
+ * @returns The module.
11
+ */
12
+ export function mcpServer(): typeof McpServer {
13
+ return require('cheminfo-mcp-server') as typeof McpServer;
14
+ }
@@ -0,0 +1,33 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+
4
+ import { UNRELEASED_VERSION } from '../build/core/buildInfo.ts';
5
+ import { findRepositoryRoot } from '../build/vite/buildInfo.ts';
6
+
7
+ /**
8
+ * The release a server's repository is at, as release-please writes it.
9
+ *
10
+ * Read from the `package.json` at the repository root, found by the same
11
+ * climbing rule `cheminfoBuildInfo` follows: a `backend` or `server` directory
12
+ * keeps the `0.0.0` it was scaffolded with, or none at all, while the root is
13
+ * the manifest a release bumps. A manifest that cannot be read is not a reason
14
+ * to refuse to start, so it reads as unreleased instead.
15
+ *
16
+ * const version = readRepositoryVersion(import.meta.dirname);
17
+ * @param fromDirectory - Where to start climbing, usually the server's own
18
+ * directory.
19
+ * @returns The version, or `'0.0.0'` when the root manifest carries none.
20
+ */
21
+ export function readRepositoryVersion(fromDirectory: string): string {
22
+ const root = findRepositoryRoot(fromDirectory);
23
+ let manifest: unknown;
24
+ try {
25
+ manifest = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'));
26
+ } catch {
27
+ return UNRELEASED_VERSION;
28
+ }
29
+ const version = (manifest as { version?: unknown } | null)?.version;
30
+ return typeof version === 'string' && version !== ''
31
+ ? version
32
+ : UNRELEASED_VERSION;
33
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * What every route the serving helpers register shares: how it is cached, who
3
+ * may read it, and that it stays out of the API's documentation.
4
+ */
5
+
6
+ import type { FastifySchema } from 'fastify';
7
+
8
+ /** A page is checked on every visit: it names the bundles of its own build. */
9
+ export const PAGE_CACHE = 'no-cache';
10
+
11
+ /** A bundle is named by its content, so it never changes under its name. */
12
+ export const BUNDLE_CACHE = 'public, max-age=31536000, immutable';
13
+
14
+ /**
15
+ * The header that opens an answer to every origin. Every file of a build is
16
+ * public, and a page framed by a sandboxed view runs under an opaque origin,
17
+ * where everything it fetches from its own site is cross-origin: the static
18
+ * hosts open every response, and so do these servers.
19
+ */
20
+ export const ALLOW_ORIGIN = 'access-control-allow-origin';
21
+
22
+ /** Kept out of the API's documentation (`@fastify/swagger` reads `hide`). */
23
+ export const HIDDEN: FastifySchema & { hide: boolean } = { hide: true };
24
+
25
+ /** The addresses an API and its documentation answer, never a page. */
26
+ export const DEFAULT_RESERVED: readonly string[] = ['/v1/', '/docs'];
@@ -0,0 +1,35 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+
4
+ import { ROUTE_TABLE_FILE, parseRouteTable } from '../seo/core/routeTable.ts';
5
+ import type { RouteMeta } from '../seo/core/routes.ts';
6
+
7
+ /**
8
+ * Every address the frontend routes itself, from the `routes.json` its build
9
+ * wrote beside the page (`cheminfoRouteTable` in `react-cheminfo/vite`).
10
+ *
11
+ * The pages that exist whatever the build wrote are the fallback, so a server
12
+ * started before the frontend was built, or against a build of an older shape,
13
+ * still titles them.
14
+ * @param root - The directory holding the built frontend.
15
+ * @param fallback - The routes when the build wrote no table this can read.
16
+ * @returns The routes, the home page first.
17
+ */
18
+ export function readRouteTable(
19
+ root: string,
20
+ fallback: readonly RouteMeta[],
21
+ ): RouteMeta[] {
22
+ let text: string;
23
+ try {
24
+ text = readFileSync(join(root, ROUTE_TABLE_FILE), 'utf8');
25
+ } catch {
26
+ return [...fallback];
27
+ }
28
+ let parsed: unknown;
29
+ try {
30
+ parsed = JSON.parse(text);
31
+ } catch {
32
+ return [...fallback];
33
+ }
34
+ return parseRouteTable(parsed) ?? [...fallback];
35
+ }
@@ -0,0 +1,42 @@
1
+ import type { AppResource } from 'cheminfo-mcp-server';
2
+
3
+ import type { AnswerAddressesInput } from './answerAddresses.ts';
4
+ import { viewOrigins } from './answerAddresses.ts';
5
+ import { mcpServer } from './mcpServer.ts';
6
+
7
+ /** What {@link siteView} names, and where its pages come from. */
8
+ export interface SiteViewOptions {
9
+ /** Its address, which the tools name in `ui.resourceUri`: `ui://lcao/page`. */
10
+ uri: string;
11
+ /** For people: the site's host, `lcao.cheminfo.org`. */
12
+ name: string;
13
+ /** For people: what the card shows inline, and the page over the screen. */
14
+ description: string;
15
+ /** Where the site and this server are: the view frames both, and no other. */
16
+ addresses: AnswerAddressesInput;
17
+ /**
18
+ * The height of the frame, in pixels, for a page that cannot report its
19
+ * own.
20
+ * @default undefined - the page reports its height, as `CardFrame` does
21
+ */
22
+ height?: number;
23
+ }
24
+
25
+ /**
26
+ * The MCP App showing an answer on the site's own page: inline the answer's
27
+ * card, the page itself over the full screen, framed from this server or from
28
+ * the static site. One per tool server, named by every tool whose answer
29
+ * carries a `meta` from `cardMeta` or `pageAnswer`.
30
+ * @param options - Its address, its name, and the two addresses it frames.
31
+ * @returns The resource, for `createToolRoutes({ resources })`.
32
+ */
33
+ export function siteView(options: SiteViewOptions): AppResource {
34
+ const { uri, name, description, addresses, height } = options;
35
+ return mcpServer().embedView({
36
+ uri,
37
+ name,
38
+ description,
39
+ origins: viewOrigins(addresses),
40
+ height,
41
+ });
42
+ }