react-cheminfo 0.64.0 → 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 (640) 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/molecule3d/core/highlight.d.ts.map +1 -1
  137. package/lib/molecule3d/core/highlight.js +2 -8
  138. package/lib/molecule3d/core/highlight.js.map +1 -1
  139. package/lib/molecule3d/core/index.d.ts +1 -1
  140. package/lib/molecule3d/core/index.d.ts.map +1 -1
  141. package/lib/molecule3d/core/index.js +1 -1
  142. package/lib/molecule3d/core/index.js.map +1 -1
  143. package/lib/molecule3d/core/measurement.d.ts +17 -0
  144. package/lib/molecule3d/core/measurement.d.ts.map +1 -1
  145. package/lib/molecule3d/core/measurement.js +43 -0
  146. package/lib/molecule3d/core/measurement.js.map +1 -1
  147. package/lib/molecule3d/ui/drawScene.d.ts +4 -2
  148. package/lib/molecule3d/ui/drawScene.d.ts.map +1 -1
  149. package/lib/molecule3d/ui/drawScene.js +1 -1
  150. package/lib/molecule3d/ui/drawScene.js.map +1 -1
  151. package/lib/molecule3d/ui/moleculeViewer3DProps.d.ts +3 -1
  152. package/lib/molecule3d/ui/moleculeViewer3DProps.d.ts.map +1 -1
  153. package/lib/molecule3d/ui/sceneDrawer.d.ts +47 -0
  154. package/lib/molecule3d/ui/sceneDrawer.d.ts.map +1 -0
  155. package/lib/molecule3d/ui/sceneDrawer.js +103 -0
  156. package/lib/molecule3d/ui/sceneDrawer.js.map +1 -0
  157. package/lib/molecule3d/ui/useMoleculeScene.d.ts +2 -1
  158. package/lib/molecule3d/ui/useMoleculeScene.d.ts.map +1 -1
  159. package/lib/molecule3d/ui/useMoleculeScene.js +13 -22
  160. package/lib/molecule3d/ui/useMoleculeScene.js.map +1 -1
  161. package/lib/molecule3d/ui/useSharedCamera.d.ts +1 -3
  162. package/lib/molecule3d/ui/useSharedCamera.d.ts.map +1 -1
  163. package/lib/molecule3d/ui/useSharedCamera.js.map +1 -1
  164. package/lib/moleculesClient/core/index.d.ts +5 -0
  165. package/lib/moleculesClient/core/index.d.ts.map +1 -0
  166. package/lib/moleculesClient/core/index.js +3 -0
  167. package/lib/moleculesClient/core/index.js.map +1 -0
  168. package/lib/moleculesClient/core/moleculesClient.d.ts +12 -0
  169. package/lib/moleculesClient/core/moleculesClient.d.ts.map +1 -0
  170. package/lib/moleculesClient/core/moleculesClient.js +105 -0
  171. package/lib/moleculesClient/core/moleculesClient.js.map +1 -0
  172. package/lib/moleculesClient/core/moleculesClientApi.d.ts +100 -0
  173. package/lib/moleculesClient/core/moleculesClientApi.d.ts.map +1 -0
  174. package/lib/moleculesClient/core/moleculesClientApi.js +20 -0
  175. package/lib/moleculesClient/core/moleculesClientApi.js.map +1 -0
  176. package/lib/moleculesClient/core/moleculesTypes.d.ts +154 -0
  177. package/lib/moleculesClient/core/moleculesTypes.d.ts.map +1 -0
  178. package/lib/moleculesClient/core/moleculesTypes.js +2 -0
  179. package/lib/moleculesClient/core/moleculesTypes.js.map +1 -0
  180. package/lib/moleculesClient/core/requestRetry.d.ts +50 -0
  181. package/lib/moleculesClient/core/requestRetry.d.ts.map +1 -0
  182. package/lib/moleculesClient/core/requestRetry.js +108 -0
  183. package/lib/moleculesClient/core/requestRetry.js.map +1 -0
  184. package/lib/molstar/core/camera.d.ts +2 -1
  185. package/lib/molstar/core/camera.d.ts.map +1 -1
  186. package/lib/molstar/core/camera.js +3 -2
  187. package/lib/molstar/core/camera.js.map +1 -1
  188. package/lib/molstar/core/framing.d.ts +102 -0
  189. package/lib/molstar/core/framing.d.ts.map +1 -0
  190. package/lib/molstar/core/framing.js +120 -0
  191. package/lib/molstar/core/framing.js.map +1 -0
  192. package/lib/molstar/core/hover.d.ts +16 -0
  193. package/lib/molstar/core/hover.d.ts.map +1 -1
  194. package/lib/molstar/core/hover.js +14 -0
  195. package/lib/molstar/core/hover.js.map +1 -1
  196. package/lib/molstar/core/index.d.ts +4 -1
  197. package/lib/molstar/core/index.d.ts.map +1 -1
  198. package/lib/molstar/core/index.js +2 -1
  199. package/lib/molstar/core/index.js.map +1 -1
  200. package/lib/pedagogy/core/answers.d.ts +46 -0
  201. package/lib/pedagogy/core/answers.d.ts.map +1 -0
  202. package/lib/pedagogy/core/answers.js +77 -0
  203. package/lib/pedagogy/core/answers.js.map +1 -0
  204. package/lib/pedagogy/core/attempts.d.ts +86 -0
  205. package/lib/pedagogy/core/attempts.d.ts.map +1 -0
  206. package/lib/pedagogy/core/attempts.js +117 -0
  207. package/lib/pedagogy/core/attempts.js.map +1 -0
  208. package/lib/pedagogy/core/index.d.ts +8 -0
  209. package/lib/pedagogy/core/index.d.ts.map +1 -1
  210. package/lib/pedagogy/core/index.js +4 -0
  211. package/lib/pedagogy/core/index.js.map +1 -1
  212. package/lib/pedagogy/core/progressController.d.ts +64 -0
  213. package/lib/pedagogy/core/progressController.d.ts.map +1 -0
  214. package/lib/pedagogy/core/progressController.js +56 -0
  215. package/lib/pedagogy/core/progressController.js.map +1 -0
  216. package/lib/pedagogy/core/seed.d.ts +96 -0
  217. package/lib/pedagogy/core/seed.d.ts.map +1 -0
  218. package/lib/pedagogy/core/seed.js +128 -0
  219. package/lib/pedagogy/core/seed.js.map +1 -0
  220. package/lib/pedagogy/core/series.d.ts +77 -0
  221. package/lib/pedagogy/core/series.d.ts.map +1 -0
  222. package/lib/pedagogy/core/series.js +10 -0
  223. package/lib/pedagogy/core/series.js.map +1 -0
  224. package/lib/pedagogy/ui/AnswerFields.d.ts +21 -0
  225. package/lib/pedagogy/ui/AnswerFields.d.ts.map +1 -0
  226. package/lib/pedagogy/ui/AnswerFields.js +24 -0
  227. package/lib/pedagogy/ui/AnswerFields.js.map +1 -0
  228. package/lib/pedagogy/ui/ExerciseList.d.ts +82 -0
  229. package/lib/pedagogy/ui/ExerciseList.d.ts.map +1 -0
  230. package/lib/pedagogy/ui/ExerciseList.js +53 -0
  231. package/lib/pedagogy/ui/ExerciseList.js.map +1 -0
  232. package/lib/pedagogy/ui/ExerciseSeries.d.ts +95 -0
  233. package/lib/pedagogy/ui/ExerciseSeries.d.ts.map +1 -0
  234. package/lib/pedagogy/ui/ExerciseSeries.js +74 -0
  235. package/lib/pedagogy/ui/ExerciseSeries.js.map +1 -0
  236. package/lib/pedagogy/ui/QuestionCard.d.ts +64 -0
  237. package/lib/pedagogy/ui/QuestionCard.d.ts.map +1 -0
  238. package/lib/pedagogy/ui/QuestionCard.js +73 -0
  239. package/lib/pedagogy/ui/QuestionCard.js.map +1 -0
  240. package/lib/pedagogy/ui/index.d.ts +10 -0
  241. package/lib/pedagogy/ui/index.d.ts.map +1 -1
  242. package/lib/pedagogy/ui/index.js +6 -0
  243. package/lib/pedagogy/ui/index.js.map +1 -1
  244. package/lib/pedagogy/ui/seriesCounts.d.ts +17 -0
  245. package/lib/pedagogy/ui/seriesCounts.d.ts.map +1 -0
  246. package/lib/pedagogy/ui/seriesCounts.js +27 -0
  247. package/lib/pedagogy/ui/seriesCounts.js.map +1 -0
  248. package/lib/pedagogy/ui/seriesParts.d.ts +55 -0
  249. package/lib/pedagogy/ui/seriesParts.d.ts.map +1 -0
  250. package/lib/pedagogy/ui/seriesParts.js +38 -0
  251. package/lib/pedagogy/ui/seriesParts.js.map +1 -0
  252. package/lib/pedagogy/ui/useAttemptRecords.d.ts +10 -0
  253. package/lib/pedagogy/ui/useAttemptRecords.d.ts.map +1 -0
  254. package/lib/pedagogy/ui/useAttemptRecords.js +11 -0
  255. package/lib/pedagogy/ui/useAttemptRecords.js.map +1 -0
  256. package/lib/periodic/ui/PeriodicTable.d.ts.map +1 -1
  257. package/lib/periodic/ui/PeriodicTable.js +6 -3
  258. package/lib/periodic/ui/PeriodicTable.js.map +1 -1
  259. package/lib/property/core/copyableText.d.ts +14 -0
  260. package/lib/property/core/copyableText.d.ts.map +1 -0
  261. package/lib/property/core/copyableText.js +21 -0
  262. package/lib/property/core/copyableText.js.map +1 -0
  263. package/lib/property/core/index.d.ts +2 -0
  264. package/lib/property/core/index.d.ts.map +1 -0
  265. package/lib/property/core/index.js +2 -0
  266. package/lib/property/core/index.js.map +1 -0
  267. package/lib/property/ui/PropertyTable.d.ts +43 -0
  268. package/lib/property/ui/PropertyTable.d.ts.map +1 -0
  269. package/lib/property/ui/PropertyTable.js +19 -0
  270. package/lib/property/ui/PropertyTable.js.map +1 -0
  271. package/lib/property/ui/index.d.ts +3 -0
  272. package/lib/property/ui/index.d.ts.map +1 -0
  273. package/lib/property/ui/index.js +2 -0
  274. package/lib/property/ui/index.js.map +1 -0
  275. package/lib/router/core/history.d.ts +24 -3
  276. package/lib/router/core/history.d.ts.map +1 -1
  277. package/lib/router/core/history.js +55 -7
  278. package/lib/router/core/history.js.map +1 -1
  279. package/lib/router/core/index.d.ts +3 -3
  280. package/lib/router/core/index.d.ts.map +1 -1
  281. package/lib/router/core/index.js +2 -2
  282. package/lib/router/core/index.js.map +1 -1
  283. package/lib/router/core/languageRoute.d.ts +61 -0
  284. package/lib/router/core/languageRoute.d.ts.map +1 -0
  285. package/lib/router/core/languageRoute.js +96 -0
  286. package/lib/router/core/languageRoute.js.map +1 -0
  287. package/lib/router/core/siteAddresses.d.ts +23 -0
  288. package/lib/router/core/siteAddresses.d.ts.map +1 -1
  289. package/lib/router/core/siteAddresses.js +26 -0
  290. package/lib/router/core/siteAddresses.js.map +1 -1
  291. package/lib/router/core/tabRouter.d.ts +2 -108
  292. package/lib/router/core/tabRouter.d.ts.map +1 -1
  293. package/lib/router/core/tabRouter.js +38 -5
  294. package/lib/router/core/tabRouter.js.map +1 -1
  295. package/lib/router/core/tabRouterTypes.d.ts +154 -0
  296. package/lib/router/core/tabRouterTypes.d.ts.map +1 -0
  297. package/lib/router/core/tabRouterTypes.js +2 -0
  298. package/lib/router/core/tabRouterTypes.js.map +1 -0
  299. package/lib/seo/core/fitDescription.d.ts +40 -0
  300. package/lib/seo/core/fitDescription.d.ts.map +1 -0
  301. package/lib/seo/core/fitDescription.js +45 -0
  302. package/lib/seo/core/fitDescription.js.map +1 -0
  303. package/lib/seo/core/index.d.ts +7 -0
  304. package/lib/seo/core/index.d.ts.map +1 -1
  305. package/lib/seo/core/index.js +4 -0
  306. package/lib/seo/core/index.js.map +1 -1
  307. package/lib/seo/core/limits.d.ts +24 -0
  308. package/lib/seo/core/limits.d.ts.map +1 -0
  309. package/lib/seo/core/limits.js +24 -0
  310. package/lib/seo/core/limits.js.map +1 -0
  311. package/lib/seo/core/pageContent.d.ts +25 -0
  312. package/lib/seo/core/pageContent.d.ts.map +1 -0
  313. package/lib/seo/core/pageContent.js +25 -0
  314. package/lib/seo/core/pageContent.js.map +1 -0
  315. package/lib/seo/core/plainDescription.d.ts.map +1 -1
  316. package/lib/seo/core/plainDescription.js +3 -2
  317. package/lib/seo/core/plainDescription.js.map +1 -1
  318. package/lib/seo/core/routeProblems.d.ts.map +1 -1
  319. package/lib/seo/core/routeProblems.js +1 -6
  320. package/lib/seo/core/routeProblems.js.map +1 -1
  321. package/lib/seo/core/routeTable.d.ts +31 -0
  322. package/lib/seo/core/routeTable.d.ts.map +1 -0
  323. package/lib/seo/core/routeTable.js +96 -0
  324. package/lib/seo/core/routeTable.js.map +1 -0
  325. package/lib/seo/vite/index.d.ts +2 -0
  326. package/lib/seo/vite/index.d.ts.map +1 -1
  327. package/lib/seo/vite/index.js +1 -0
  328. package/lib/seo/vite/index.js.map +1 -1
  329. package/lib/seo/vite/prerender.d.ts +10 -0
  330. package/lib/seo/vite/prerender.d.ts.map +1 -1
  331. package/lib/seo/vite/prerender.js +12 -1
  332. package/lib/seo/vite/prerender.js.map +1 -1
  333. package/lib/seo/vite/routeTable.d.ts +40 -0
  334. package/lib/seo/vite/routeTable.d.ts.map +1 -0
  335. package/lib/seo/vite/routeTable.js +44 -0
  336. package/lib/seo/vite/routeTable.js.map +1 -0
  337. package/lib/server/answerAddresses.d.ts +51 -0
  338. package/lib/server/answerAddresses.d.ts.map +1 -0
  339. package/lib/server/answerAddresses.js +72 -0
  340. package/lib/server/answerAddresses.js.map +1 -0
  341. package/lib/server/cardMeta.d.ts +57 -0
  342. package/lib/server/cardMeta.d.ts.map +1 -0
  343. package/lib/server/cardMeta.js +52 -0
  344. package/lib/server/cardMeta.js.map +1 -0
  345. package/lib/server/config.d.ts +72 -0
  346. package/lib/server/config.d.ts.map +1 -0
  347. package/lib/server/config.js +67 -0
  348. package/lib/server/config.js.map +1 -0
  349. package/lib/server/envNumber.d.ts +35 -0
  350. package/lib/server/envNumber.d.ts.map +1 -0
  351. package/lib/server/envNumber.js +42 -0
  352. package/lib/server/envNumber.js.map +1 -0
  353. package/lib/server/healthRoute.d.ts +26 -0
  354. package/lib/server/healthRoute.d.ts.map +1 -0
  355. package/lib/server/healthRoute.js +22 -0
  356. package/lib/server/healthRoute.js.map +1 -0
  357. package/lib/server/listenSite.d.ts +38 -0
  358. package/lib/server/listenSite.d.ts.map +1 -0
  359. package/lib/server/listenSite.js +60 -0
  360. package/lib/server/listenSite.js.map +1 -0
  361. package/lib/server/mcpServer.d.ts +8 -0
  362. package/lib/server/mcpServer.d.ts.map +1 -0
  363. package/lib/server/mcpServer.js +11 -0
  364. package/lib/server/mcpServer.js.map +1 -0
  365. package/lib/server/repositoryVersion.d.ts +16 -0
  366. package/lib/server/repositoryVersion.d.ts.map +1 -0
  367. package/lib/server/repositoryVersion.js +33 -0
  368. package/lib/server/repositoryVersion.js.map +1 -0
  369. package/lib/server/routeOptions.d.ts +23 -0
  370. package/lib/server/routeOptions.d.ts.map +1 -0
  371. package/lib/server/routeOptions.js +20 -0
  372. package/lib/server/routeOptions.js.map +1 -0
  373. package/lib/server/routeTable.d.ts +14 -0
  374. package/lib/server/routeTable.d.ts.map +1 -0
  375. package/lib/server/routeTable.js +32 -0
  376. package/lib/server/routeTable.js.map +1 -0
  377. package/lib/server/siteView.d.ts +29 -0
  378. package/lib/server/siteView.d.ts.map +1 -0
  379. package/lib/server/siteView.js +21 -0
  380. package/lib/server/siteView.js.map +1 -0
  381. package/lib/server/spaPages.d.ts +32 -0
  382. package/lib/server/spaPages.d.ts.map +1 -0
  383. package/lib/server/spaPages.js +126 -0
  384. package/lib/server/spaPages.js.map +1 -0
  385. package/lib/server/spaSite.d.ts +30 -0
  386. package/lib/server/spaSite.d.ts.map +1 -0
  387. package/lib/server/spaSite.js +170 -0
  388. package/lib/server/spaSite.js.map +1 -0
  389. package/lib/server/spaSiteOptions.d.ts +126 -0
  390. package/lib/server/spaSiteOptions.d.ts.map +1 -0
  391. package/lib/server/spaSiteOptions.js +2 -0
  392. package/lib/server/spaSiteOptions.js.map +1 -0
  393. package/lib/server/staticSite.d.ts +9 -0
  394. package/lib/server/staticSite.d.ts.map +1 -1
  395. package/lib/server/staticSite.js +41 -19
  396. package/lib/server/staticSite.js.map +1 -1
  397. package/lib/server/toolSiteServer.d.ts +77 -0
  398. package/lib/server/toolSiteServer.d.ts.map +1 -0
  399. package/lib/server/toolSiteServer.js +58 -0
  400. package/lib/server/toolSiteServer.js.map +1 -0
  401. package/lib/server/trustProxy.d.ts +20 -0
  402. package/lib/server/trustProxy.d.ts.map +1 -0
  403. package/lib/server/trustProxy.js +32 -0
  404. package/lib/server/trustProxy.js.map +1 -0
  405. package/lib/server/types.d.ts +13 -0
  406. package/lib/server/types.d.ts.map +1 -0
  407. package/lib/server/types.js +2 -0
  408. package/lib/server/types.js.map +1 -0
  409. package/lib/server.d.ts +22 -0
  410. package/lib/server.d.ts.map +1 -1
  411. package/lib/server.js +12 -0
  412. package/lib/server.js.map +1 -1
  413. package/lib/share/core/config.d.ts +18 -0
  414. package/lib/share/core/config.d.ts.map +1 -1
  415. package/lib/share/core/config.js +33 -2
  416. package/lib/share/core/config.js.map +1 -1
  417. package/lib/share/core/index.d.ts +3 -1
  418. package/lib/share/core/index.d.ts.map +1 -1
  419. package/lib/share/core/index.js +2 -1
  420. package/lib/share/core/index.js.map +1 -1
  421. package/lib/share/core/keySetParam.d.ts +40 -0
  422. package/lib/share/core/keySetParam.d.ts.map +1 -0
  423. package/lib/share/core/keySetParam.js +60 -0
  424. package/lib/share/core/keySetParam.js.map +1 -0
  425. package/lib/share/core/vocabulary.d.ts +8 -0
  426. package/lib/share/core/vocabulary.d.ts.map +1 -1
  427. package/lib/shell/ui/ShareControl.d.ts +75 -0
  428. package/lib/shell/ui/ShareControl.d.ts.map +1 -0
  429. package/lib/shell/ui/ShareControl.js +40 -0
  430. package/lib/shell/ui/ShareControl.js.map +1 -0
  431. package/lib/shell/ui/ShellFrame.d.ts +14 -0
  432. package/lib/shell/ui/ShellFrame.d.ts.map +1 -0
  433. package/lib/shell/ui/ShellFrame.js +45 -0
  434. package/lib/shell/ui/ShellFrame.js.map +1 -0
  435. package/lib/shell/ui/SiteShell.d.ts +251 -0
  436. package/lib/shell/ui/SiteShell.d.ts.map +1 -0
  437. package/lib/shell/ui/SiteShell.js +39 -0
  438. package/lib/shell/ui/SiteShell.js.map +1 -0
  439. package/lib/shell/ui/index.d.ts +5 -0
  440. package/lib/shell/ui/index.d.ts.map +1 -0
  441. package/lib/shell/ui/index.js +3 -0
  442. package/lib/shell/ui/index.js.map +1 -0
  443. package/lib/state/core/index.d.ts +1 -0
  444. package/lib/state/core/index.d.ts.map +1 -1
  445. package/lib/state/core/index.js.map +1 -1
  446. package/lib/state/core/legacyEntries.d.ts +30 -0
  447. package/lib/state/core/legacyEntries.d.ts.map +1 -0
  448. package/lib/state/core/legacyEntries.js +51 -0
  449. package/lib/state/core/legacyEntries.js.map +1 -0
  450. package/lib/state/core/persistBucket.d.ts +26 -2
  451. package/lib/state/core/persistBucket.d.ts.map +1 -1
  452. package/lib/state/core/persistBucket.js +26 -9
  453. package/lib/state/core/persistBucket.js.map +1 -1
  454. package/lib/state/core/persistSignalBucket.d.ts +26 -0
  455. package/lib/state/core/persistSignalBucket.d.ts.map +1 -1
  456. package/lib/state/core/persistSignalBucket.js +31 -0
  457. package/lib/state/core/persistSignalBucket.js.map +1 -1
  458. package/lib/state/core/storageEntry.d.ts +15 -0
  459. package/lib/state/core/storageEntry.d.ts.map +1 -1
  460. package/lib/state/core/storageEntry.js +33 -0
  461. package/lib/state/core/storageEntry.js.map +1 -1
  462. package/lib/structure/ui/StructureExportButton.d.ts +27 -0
  463. package/lib/structure/ui/StructureExportButton.d.ts.map +1 -0
  464. package/lib/structure/ui/StructureExportButton.js +21 -0
  465. package/lib/structure/ui/StructureExportButton.js.map +1 -0
  466. package/lib/structure/ui/index.d.ts +4 -0
  467. package/lib/structure/ui/index.d.ts.map +1 -1
  468. package/lib/structure/ui/index.js +2 -0
  469. package/lib/structure/ui/index.js.map +1 -1
  470. package/lib/structure/ui/useStructureDraft.d.ts +32 -0
  471. package/lib/structure/ui/useStructureDraft.d.ts.map +1 -0
  472. package/lib/structure/ui/useStructureDraft.js +20 -0
  473. package/lib/structure/ui/useStructureDraft.js.map +1 -0
  474. package/lib/ui.d.ts +2 -0
  475. package/lib/ui.d.ts.map +1 -1
  476. package/lib/ui.js +2 -0
  477. package/lib/ui.js.map +1 -1
  478. package/lib/worker/core/index.d.ts +1 -1
  479. package/lib/worker/core/index.d.ts.map +1 -1
  480. package/lib/worker/core/messages.d.ts +4 -2
  481. package/lib/worker/core/messages.d.ts.map +1 -1
  482. package/lib/worker/core/messages.js.map +1 -1
  483. package/lib/worker/core/oclResourcesUrl.d.ts +16 -0
  484. package/lib/worker/core/oclResourcesUrl.d.ts.map +1 -0
  485. package/lib/worker/core/oclResourcesUrl.js +19 -0
  486. package/lib/worker/core/oclResourcesUrl.js.map +1 -0
  487. package/lib/worker/core/serveWorkerRequests.d.ts +8 -0
  488. package/lib/worker/core/serveWorkerRequests.d.ts.map +1 -1
  489. package/lib/worker/core/serveWorkerRequests.js +15 -2
  490. package/lib/worker/core/serveWorkerRequests.js.map +1 -1
  491. package/lib/worker/core/workerChannel.d.ts +3 -4
  492. package/lib/worker/core/workerChannel.d.ts.map +1 -1
  493. package/lib/worker/core/workerChannel.js +72 -67
  494. package/lib/worker/core/workerChannel.js.map +1 -1
  495. package/lib/worker/core/workerChannelParts.d.ts +17 -6
  496. package/lib/worker/core/workerChannelParts.d.ts.map +1 -1
  497. package/lib/worker/core/workerChannelParts.js +20 -4
  498. package/lib/worker/core/workerChannelParts.js.map +1 -1
  499. package/lib/worker/core/workerChannelTypes.d.ts +46 -2
  500. package/lib/worker/core/workerChannelTypes.d.ts.map +1 -1
  501. package/lib/worker/core/workerSlot.d.ts +66 -0
  502. package/lib/worker/core/workerSlot.d.ts.map +1 -0
  503. package/lib/worker/core/workerSlot.js +107 -0
  504. package/lib/worker/core/workerSlot.js.map +1 -0
  505. package/package.json +16 -1
  506. package/src/card/core/cardAddress.ts +100 -0
  507. package/src/card/core/cardPageUrl.ts +97 -0
  508. package/src/card/core/index.ts +6 -0
  509. package/src/card/core/panelRoom.ts +66 -0
  510. package/src/card/ui/CardDetailsButton.tsx +104 -0
  511. package/src/card/ui/CardFigure.tsx +86 -0
  512. package/src/card/ui/CardFrame.tsx +46 -0
  513. package/src/card/ui/CopyTableButton.tsx +81 -0
  514. package/src/card/ui/cardTooltip.ts +18 -0
  515. package/src/card/ui/embed.ts +27 -0
  516. package/src/card/ui/fitCardToFrame.ts +76 -0
  517. package/src/card/ui/holdRoomForPanels.ts +83 -0
  518. package/src/card/ui/index.ts +14 -0
  519. package/src/card/ui/openPanels.ts +106 -0
  520. package/src/card.ts +13 -0
  521. package/src/conformers.ts +4 -0
  522. package/src/core.ts +5 -0
  523. package/src/fetch/core/apiError.ts +65 -0
  524. package/src/fetch/core/fetchJson.ts +129 -0
  525. package/src/fetch/core/index.ts +4 -0
  526. package/src/formula/core/formulaText.ts +96 -0
  527. package/src/formula/core/index.ts +20 -0
  528. package/src/formula/core/normalizeFormula.ts +50 -0
  529. package/src/formula/core/readFormula.ts +211 -0
  530. package/src/formula/ui/CopyableFormula.tsx +51 -0
  531. package/src/formula/ui/FormulaInput.tsx +50 -0
  532. package/src/formula/ui/QuestionFormula.tsx +20 -0
  533. package/src/formula/ui/index.ts +6 -0
  534. package/src/formula.ts +11 -0
  535. package/src/hooks/ui/index.ts +1 -0
  536. package/src/hooks/ui/keyTargets.ts +5 -3
  537. package/src/hooks/ui/useAsync.ts +50 -14
  538. package/src/locales/de.json +37 -0
  539. package/src/locales/en.json +37 -0
  540. package/src/locales/es.json +37 -0
  541. package/src/locales/fr.json +37 -0
  542. package/src/locales/it.json +37 -0
  543. package/src/molecule3d/core/highlight.ts +2 -8
  544. package/src/molecule3d/core/index.ts +1 -0
  545. package/src/molecule3d/core/measurement.ts +47 -0
  546. package/src/molecule3d/ui/drawScene.ts +13 -2
  547. package/src/molecule3d/ui/moleculeViewer3DProps.ts +3 -1
  548. package/src/molecule3d/ui/sceneDrawer.ts +132 -0
  549. package/src/molecule3d/ui/useMoleculeScene.ts +16 -26
  550. package/src/molecule3d/ui/useSharedCamera.ts +1 -3
  551. package/src/moleculesClient/core/index.ts +19 -0
  552. package/src/moleculesClient/core/moleculesClient.ts +175 -0
  553. package/src/moleculesClient/core/moleculesClientApi.ts +120 -0
  554. package/src/moleculesClient/core/moleculesTypes.ts +163 -0
  555. package/src/moleculesClient/core/requestRetry.ts +118 -0
  556. package/src/molstar/core/camera.ts +3 -2
  557. package/src/molstar/core/framing.ts +200 -0
  558. package/src/molstar/core/hover.ts +26 -0
  559. package/src/molstar/core/index.ts +21 -1
  560. package/src/pedagogy/core/answers.ts +94 -0
  561. package/src/pedagogy/core/attempts.ts +205 -0
  562. package/src/pedagogy/core/index.ts +36 -0
  563. package/src/pedagogy/core/progressController.ts +116 -0
  564. package/src/pedagogy/core/seed.ts +177 -0
  565. package/src/pedagogy/core/series.ts +82 -0
  566. package/src/pedagogy/ui/AnswerFields.tsx +63 -0
  567. package/src/pedagogy/ui/ExerciseList.tsx +202 -0
  568. package/src/pedagogy/ui/ExerciseSeries.tsx +250 -0
  569. package/src/pedagogy/ui/QuestionCard.tsx +231 -0
  570. package/src/pedagogy/ui/index.ts +18 -0
  571. package/src/pedagogy/ui/seriesCounts.ts +34 -0
  572. package/src/pedagogy/ui/seriesParts.tsx +127 -0
  573. package/src/pedagogy/ui/useAttemptRecords.ts +16 -0
  574. package/src/periodic/ui/PeriodicTable.tsx +6 -3
  575. package/src/property/core/copyableText.ts +20 -0
  576. package/src/property/core/index.ts +1 -0
  577. package/src/property/ui/PropertyTable.tsx +83 -0
  578. package/src/property/ui/index.ts +2 -0
  579. package/src/router/core/history.ts +61 -7
  580. package/src/router/core/index.ts +3 -2
  581. package/src/router/core/languageRoute.ts +136 -0
  582. package/src/router/core/siteAddresses.ts +38 -0
  583. package/src/router/core/tabRouter.ts +59 -117
  584. package/src/router/core/tabRouterTypes.ts +158 -0
  585. package/src/seo/core/fitDescription.ts +74 -0
  586. package/src/seo/core/index.ts +17 -0
  587. package/src/seo/core/limits.ts +27 -0
  588. package/src/seo/core/pageContent.ts +42 -0
  589. package/src/seo/core/plainDescription.ts +4 -2
  590. package/src/seo/core/routeProblems.ts +1 -7
  591. package/src/seo/core/routeTable.ts +103 -0
  592. package/src/seo/vite/index.ts +2 -0
  593. package/src/seo/vite/prerender.ts +26 -1
  594. package/src/seo/vite/routeTable.ts +79 -0
  595. package/src/server/answerAddresses.ts +92 -0
  596. package/src/server/cardMeta.ts +88 -0
  597. package/src/server/config.ts +125 -0
  598. package/src/server/envNumber.ts +65 -0
  599. package/src/server/healthRoute.ts +42 -0
  600. package/src/server/listenSite.ts +90 -0
  601. package/src/server/mcpServer.ts +14 -0
  602. package/src/server/repositoryVersion.ts +33 -0
  603. package/src/server/routeOptions.ts +26 -0
  604. package/src/server/routeTable.ts +35 -0
  605. package/src/server/siteView.ts +42 -0
  606. package/src/server/spaPages.ts +179 -0
  607. package/src/server/spaSite.ts +216 -0
  608. package/src/server/spaSiteOptions.ts +133 -0
  609. package/src/server/staticSite.ts +65 -35
  610. package/src/server/toolSiteServer.ts +127 -0
  611. package/src/server/trustProxy.ts +32 -0
  612. package/src/server/types.ts +25 -0
  613. package/src/server.ts +40 -0
  614. package/src/share/core/config.ts +45 -2
  615. package/src/share/core/index.ts +4 -0
  616. package/src/share/core/keySetParam.ts +92 -0
  617. package/src/share/core/vocabulary.ts +8 -0
  618. package/src/shell/ui/ShareControl.tsx +150 -0
  619. package/src/shell/ui/ShellFrame.tsx +180 -0
  620. package/src/shell/ui/SiteShell.tsx +297 -0
  621. package/src/shell/ui/index.ts +9 -0
  622. package/src/state/core/index.ts +1 -0
  623. package/src/state/core/legacyEntries.ts +76 -0
  624. package/src/state/core/persistBucket.ts +50 -10
  625. package/src/state/core/persistSignalBucket.ts +52 -0
  626. package/src/state/core/storageEntry.ts +32 -0
  627. package/src/structure/ui/StructureExportButton.tsx +57 -0
  628. package/src/structure/ui/index.ts +4 -0
  629. package/src/structure/ui/useStructureDraft.ts +47 -0
  630. package/src/ui.ts +2 -0
  631. package/src/worker/core/index.ts +1 -0
  632. package/src/worker/core/messages.ts +4 -3
  633. package/src/worker/core/oclResourcesUrl.ts +21 -0
  634. package/src/worker/core/serveWorkerRequests.ts +30 -2
  635. package/src/worker/core/workerChannel.ts +74 -74
  636. package/src/worker/core/workerChannelParts.ts +33 -13
  637. package/src/worker/core/workerChannelTypes.ts +47 -2
  638. package/src/worker/core/workerSlot.ts +181 -0
  639. package/styles/card.css +145 -0
  640. package/styles/chrome.css +248 -0
@@ -0,0 +1,103 @@
1
+ /**
2
+ * The route table a build writes beside its page, for the server that hands the
3
+ * page out: it titles every address from it, lists it in `sitemap.xml` and links
4
+ * it from the crawl path. The app's own table is the one thing that knows the
5
+ * pages, so there is one table rather than two that drift.
6
+ */
7
+
8
+ import type { RouteMeta } from './routes.ts';
9
+ import { assertRoutes } from './routes.ts';
10
+
11
+ /** The file the table is written to, next to `index.html`. */
12
+ export const ROUTE_TABLE_FILE = 'routes.json';
13
+
14
+ /** A field of a route the table can carry. */
15
+ export type RouteField = keyof RouteMeta;
16
+
17
+ /** Every field of a route, in the order they are written. */
18
+ export const ROUTE_FIELDS: readonly RouteField[] = [
19
+ 'path',
20
+ 'title',
21
+ 'description',
22
+ 'short',
23
+ 'note',
24
+ 'indexed',
25
+ 'prefix',
26
+ ];
27
+
28
+ /** The fields a route cannot be read without. */
29
+ const REQUIRED_FIELDS: readonly RouteField[] = ['path', 'title', 'description'];
30
+
31
+ /**
32
+ * Write a route table as the file a server reads back.
33
+ * @param routes - Every address the site answers.
34
+ * @param fields - The fields written; `path`, `title` and `description` are
35
+ * always written, since a route cannot be read without them.
36
+ * @returns The JSON document, ending with a newline.
37
+ */
38
+ export function serializeRouteTable(
39
+ routes: readonly RouteMeta[],
40
+ fields: readonly RouteField[] = ROUTE_FIELDS,
41
+ ): string {
42
+ const written = new Set<RouteField>([...REQUIRED_FIELDS, ...fields]);
43
+ const order = ROUTE_FIELDS.filter((field) => written.has(field));
44
+ const table = routes.map((route) => {
45
+ const entry: Record<string, unknown> = {};
46
+ for (const field of order) {
47
+ if (route[field] !== undefined) entry[field] = route[field];
48
+ }
49
+ return entry;
50
+ });
51
+ return `${JSON.stringify(table, null, 2)}\n`;
52
+ }
53
+
54
+ /**
55
+ * Read a route table back, refusing anything the build did not write: an empty
56
+ * table, an entry missing its path, title or description, a field of the wrong
57
+ * type, or a table `assertRoutes` refuses.
58
+ * @param value - The parsed JSON.
59
+ * @returns The routes, carrying only the fields a route has, or `undefined`
60
+ * when the value is not a route table.
61
+ */
62
+ export function parseRouteTable(value: unknown): RouteMeta[] | undefined {
63
+ if (!Array.isArray(value) || value.length === 0) return undefined;
64
+ const routes: RouteMeta[] = [];
65
+ for (const entry of value) {
66
+ const route = readRoute(entry);
67
+ if (route === undefined) return undefined;
68
+ routes.push(route);
69
+ }
70
+ try {
71
+ assertRoutes(routes);
72
+ } catch {
73
+ return undefined;
74
+ }
75
+ return routes;
76
+ }
77
+
78
+ function readRoute(value: unknown): RouteMeta | undefined {
79
+ if (typeof value !== 'object' || value === null) return undefined;
80
+ const entry = value as Record<string, unknown>;
81
+ const { path, title, description, short, note, indexed, prefix } = entry;
82
+ if (
83
+ typeof path !== 'string' ||
84
+ typeof title !== 'string' ||
85
+ typeof description !== 'string' ||
86
+ !isOptional(short, 'string') ||
87
+ !isOptional(note, 'string') ||
88
+ !isOptional(indexed, 'boolean') ||
89
+ !isOptional(prefix, 'boolean')
90
+ ) {
91
+ return undefined;
92
+ }
93
+ const route: RouteMeta = { path, title, description };
94
+ if (typeof short === 'string') route.short = short;
95
+ if (typeof note === 'string') route.note = note;
96
+ if (typeof indexed === 'boolean') route.indexed = indexed;
97
+ if (typeof prefix === 'boolean') route.prefix = prefix;
98
+ return route;
99
+ }
100
+
101
+ function isOptional(value: unknown, type: 'string' | 'boolean'): boolean {
102
+ return value === undefined || typeof value === type;
103
+ }
@@ -2,3 +2,5 @@ export type { OgCardOptions } from './ogCard.ts';
2
2
  export { OG_HEIGHT, OG_WIDTH, ogCardHtml } from './ogCard.ts';
3
3
  export type { PrerenderOptions } from './prerender.ts';
4
4
  export { cheminfoPrerender } from './prerender.ts';
5
+ export type { RouteTableOptions } from './routeTable.ts';
6
+ export { cheminfoRouteTable } from './routeTable.ts';
@@ -36,6 +36,8 @@ import { withLanguagePath } from '../../language/core/languagePath.ts';
36
36
  import { trimTrailingSlash } from '../../router/core/address.ts';
37
37
  import type { NoscriptText } from '../core/noscript.ts';
38
38
  import { noscriptIndex } from '../core/noscript.ts';
39
+ import type { PageContentRecord } from '../core/pageContent.ts';
40
+ import { pageContentOf } from '../core/pageContent.ts';
39
41
  import { pageHeadTags } from '../core/pageMeta.ts';
40
42
  import type { PageContent } from '../core/pageProse.ts';
41
43
  import type { RobotsDisallow } from '../core/robots.ts';
@@ -124,6 +126,15 @@ export interface PrerenderOptions {
124
126
  * @default undefined — every page carries the menu alone
125
127
  */
126
128
  content?: (route: RouteMeta, language: Language) => PageContent | undefined;
129
+ /**
130
+ * The pages written for, by path — or a function giving them per language —
131
+ * for a site that would otherwise write `content` only to look a path up in
132
+ * them. Every other address then says the name and the sentence its route
133
+ * is indexed under, so none ships the bare menu. `content`, when given, is
134
+ * read instead.
135
+ * @default undefined — `content` alone decides
136
+ */
137
+ pages?: PageContentRecord;
127
138
  /**
128
139
  * Every language the site is written in, the default one included.
129
140
  *
@@ -306,7 +317,7 @@ function crawlPathOf(
306
317
  const noscript =
307
318
  typeof declared === 'function' ? declared(language) : declared;
308
319
  if (noscript === false) return '';
309
- const content = options.content?.(route, language);
320
+ const content = contentOf(options, route, language);
310
321
  const { routes: listed, ...prose } = noscript === true ? {} : noscript;
311
322
  // The crawl path of a translated page links to that language's addresses: a
312
323
  // French page listing the English ones is a crawler's only route out of it,
@@ -351,3 +362,17 @@ function withHtmlLang(template: string, language: Language): string {
351
362
  `<html$<attributes> lang="${language}"`,
352
363
  );
353
364
  }
365
+
366
+ // What a page says for itself: the site's own function when it wrote one, and
367
+ // otherwise the page it wrote for that path, or the route's name and sentence.
368
+ function contentOf(
369
+ options: PrerenderOptions,
370
+ route: RouteMeta,
371
+ language: Language,
372
+ ): PageContent | undefined {
373
+ const { content, pages } = options;
374
+ if (content !== undefined) return content(route, language);
375
+ return pages === undefined
376
+ ? undefined
377
+ : pageContentOf(pages)(route, language);
378
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Write the route table beside the page a server hands out.
3
+ *
4
+ * A site with a backend titles every address it answers, lists it in
5
+ * `sitemap.xml` and links it from the crawl path — and the app's own route table
6
+ * is the one thing that knows those addresses. So the build writes it next to
7
+ * `index.html`, from the very table the app reads, and `readRouteTable` in
8
+ * `react-cheminfo/server` reads it back: a page added to the site is indexed
9
+ * without anybody remembering to.
10
+ */
11
+
12
+ import { writeFileSync } from 'node:fs';
13
+ import { join, resolve } from 'node:path';
14
+
15
+ import type { Plugin } from 'vite';
16
+
17
+ import type { RouteField } from '../core/routeTable.ts';
18
+ import {
19
+ ROUTE_FIELDS,
20
+ ROUTE_TABLE_FILE,
21
+ serializeRouteTable,
22
+ } from '../core/routeTable.ts';
23
+ import type { RouteMeta } from '../core/routes.ts';
24
+ import { assertRoutes } from '../core/routes.ts';
25
+
26
+ /** Options of {@link cheminfoRouteTable}. */
27
+ export interface RouteTableOptions {
28
+ /** Every address the site answers, each with its title and description. */
29
+ routes: readonly RouteMeta[];
30
+ /**
31
+ * The fields written for each route. `path`, `title` and `description` are
32
+ * always written: a server cannot read a route without them.
33
+ * @default every field of a route
34
+ */
35
+ fields?: readonly RouteField[];
36
+ /**
37
+ * The file written in the build.
38
+ * @default 'routes.json'
39
+ */
40
+ fileName?: string;
41
+ }
42
+
43
+ /**
44
+ * The Vite plugin writing the route table.
45
+ *
46
+ * The table is author configuration, so it is checked when the build starts —
47
+ * an address written twice, or one climbing out of the site, fails the build
48
+ * rather than shipping two sitemap entries for one page.
49
+ * @param options - The table, and which fields it carries.
50
+ * @returns The Vite plugin.
51
+ */
52
+ export function cheminfoRouteTable(options: RouteTableOptions): Plugin {
53
+ const {
54
+ routes,
55
+ fields = ROUTE_FIELDS,
56
+ fileName = ROUTE_TABLE_FILE,
57
+ } = options;
58
+ let root = process.cwd();
59
+ let outDir = 'dist';
60
+
61
+ return {
62
+ name: 'cheminfo-route-table',
63
+ apply: 'build',
64
+ configResolved(config) {
65
+ root = config.root;
66
+ outDir = config.build.outDir;
67
+ },
68
+ buildStart() {
69
+ assertRoutes(routes);
70
+ },
71
+ writeBundle(output) {
72
+ const directory = output.dir ?? resolve(root, outDir);
73
+ writeFileSync(
74
+ join(directory, fileName),
75
+ serializeRouteTable(routes, fields),
76
+ );
77
+ },
78
+ };
79
+ }
@@ -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
+ }