@underpostnet/cyberia 3.2.90 → 3.3.73

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 (490) hide show
  1. package/.dockerignore +1 -0
  2. package/.env.example +16 -0
  3. package/.github/workflows/coverall.cyberia.ci.yml +107 -0
  4. package/.github/workflows/cyberia-client.cd.yml +6 -18
  5. package/.github/workflows/cyberia-server.cd.yml +6 -18
  6. package/.github/workflows/docker-image.cyberia-client.ci.yml +8 -5
  7. package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +8 -3
  8. package/.github/workflows/docker-image.cyberia-server.ci.yml +8 -5
  9. package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +8 -5
  10. package/.github/workflows/docker-image.engine-cyberia.ci.yml +15 -1
  11. package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +15 -1
  12. package/.github/workflows/engine-cyberia.cd.yml +6 -78
  13. package/.github/workflows/engine-cyberia.ci.yml +4 -5
  14. package/.github/workflows/ghpkg.ci.yml +42 -21
  15. package/.github/workflows/hardhat.ci.yml +12 -11
  16. package/.github/workflows/npmpkg.ci.yml +17 -15
  17. package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -16
  18. package/.github/workflows/pwa-microservices-template-test.ci.yml +2 -2
  19. package/.github/workflows/release.cd.yml +2 -9
  20. package/.prettierignore +1 -0
  21. package/AGENTS.md +47 -0
  22. package/CHANGELOG.md +581 -2417
  23. package/CLI-HELP.md +416 -127
  24. package/Dockerfile +25 -9
  25. package/Dockerfile.dev +25 -9
  26. package/Dockerfile.test +4 -4
  27. package/README.md +21 -1
  28. package/bin/build.js +70 -43
  29. package/bin/build.template.js +8 -3
  30. package/bin/cyberia.js +372 -135
  31. package/bin/deploy.js +18 -104
  32. package/bin/index.js +372 -135
  33. package/compose.env +16 -0
  34. package/conf.js +93 -4
  35. package/deploy/cyberia-client/deploy.sh +47 -0
  36. package/deploy/cyberia-client/package.sh +18 -0
  37. package/deploy/cyberia-client/state.sh +14 -0
  38. package/deploy/cyberia-server/deploy.sh +47 -0
  39. package/deploy/cyberia-server/package.sh +18 -0
  40. package/deploy/cyberia-server/state.sh +14 -0
  41. package/deploy/dd-cyberia/deploy.sh +22 -0
  42. package/deploy/dd-cyberia/init.sh +55 -0
  43. package/deploy/dd-cyberia/package.sh +17 -0
  44. package/deploy/dd-cyberia/state.sh +14 -0
  45. package/deploy/dd-cyberia/sync-deploy.sh +116 -0
  46. package/deploy/lib/github-actions-logging.sh +594 -0
  47. package/deploy/lib/host.sh +185 -0
  48. package/deploy/lib/state.sh +62 -0
  49. package/deploy/pwa-microservices-template/deploy.sh +45 -0
  50. package/deploy/release/deploy.sh +32 -0
  51. package/deployment.yaml +7 -2
  52. package/docker-compose.yml +31 -37
  53. package/examples/static-page/README.md +19 -19
  54. package/examples/static-page/ssr-components/CustomPage.js +2 -2
  55. package/hardhat/hardhat.config.js +1 -6
  56. package/hardhat/package-lock.json +104 -43
  57. package/hardhat/package.json +3 -3
  58. package/manifests/alertmanager/deployment.yaml +81 -0
  59. package/manifests/blackbox-exporter/deployment.yaml +75 -0
  60. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +36 -7
  61. package/manifests/cronjobs/dd-cron/dd-cron-vultr.yaml +77 -0
  62. package/manifests/deployment/dd-cyberia-development/deployment.yaml +7 -2
  63. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  64. package/manifests/grafana/deployment.yaml +26 -7
  65. package/manifests/grafana/kustomization.yaml +1 -0
  66. package/manifests/grafana/service.yaml +6 -1
  67. package/manifests/mariadb/pv.yaml +1 -0
  68. package/manifests/mongodb/statefulset.yaml +9 -2
  69. package/manifests/mysql/pv-pvc.yaml +14 -1
  70. package/manifests/postgresql/pv.yaml +12 -1
  71. package/manifests/postgresql/pvc.yaml +1 -0
  72. package/manifests/prometheus/deployment.yaml +18 -22
  73. package/package.json +83 -10
  74. package/scripts/audit-selinux.sh +64 -0
  75. package/scripts/event-monitor.sh +56 -0
  76. package/scripts/gpu-diag.sh +0 -0
  77. package/scripts/ip-info.sh +0 -0
  78. package/scripts/k3s-node-setup.sh +30 -14
  79. package/scripts/kubeadm-node-setup.sh +40 -31
  80. package/scripts/lxd-vm-setup.sh +0 -0
  81. package/scripts/maas-nat-firewalld.sh +0 -0
  82. package/scripts/maas-setup.sh +8 -8
  83. package/scripts/nat-iptables.sh +2 -0
  84. package/scripts/rhel-grpc-setup.sh +0 -0
  85. package/scripts/rocky-kickstart.sh +25 -9
  86. package/scripts/selinux-normalize.sh +907 -0
  87. package/scripts/shutdown-machine.sh +843 -0
  88. package/scripts/test-monitor.sh +3 -3
  89. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +1 -1
  90. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +2 -2
  91. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +2 -2
  92. package/src/api/core/core.controller.js +1 -1
  93. package/src/api/core/core.router.js +2 -2
  94. package/src/api/core/core.service.js +2 -2
  95. package/src/api/crypto/crypto.controller.js +1 -1
  96. package/src/api/crypto/crypto.service.js +1 -1
  97. package/src/api/cyberia-action/cyberia-action.controller.js +1 -1
  98. package/src/api/cyberia-action/cyberia-action.router.js +1 -1
  99. package/src/api/cyberia-action/cyberia-action.service.js +2 -2
  100. package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +1 -1
  101. package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +1 -1
  102. package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +2 -2
  103. package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +1 -1
  104. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +1 -1
  105. package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +2 -2
  106. package/src/api/cyberia-entity/cyberia-entity.controller.js +1 -1
  107. package/src/api/cyberia-entity/cyberia-entity.router.js +1 -1
  108. package/src/api/cyberia-entity/cyberia-entity.service.js +2 -2
  109. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +1 -1
  110. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +1 -1
  111. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +2 -2
  112. package/src/api/cyberia-instance/cyberia-fallback-capture.js +466 -0
  113. package/src/api/cyberia-instance/cyberia-fallback-default-items.js +63 -0
  114. package/src/api/cyberia-instance/cyberia-fallback-world.js +16 -1
  115. package/src/api/cyberia-instance/cyberia-instance-map.service.js +1 -1
  116. package/src/api/cyberia-instance/cyberia-instance.controller.js +4 -1
  117. package/src/api/cyberia-instance/cyberia-instance.router.js +15 -2
  118. package/src/api/cyberia-instance/cyberia-instance.service.js +45 -2
  119. package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +1 -1
  120. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +1 -0
  121. package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +1 -1
  122. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +2 -2
  123. package/src/api/cyberia-map/cyberia-map.controller.js +1 -1
  124. package/src/api/cyberia-map/cyberia-map.router.js +1 -1
  125. package/src/api/cyberia-map/cyberia-map.service.js +2 -2
  126. package/src/api/cyberia-quest/cyberia-quest.controller.js +1 -1
  127. package/src/api/cyberia-quest/cyberia-quest.router.js +1 -1
  128. package/src/api/cyberia-quest/cyberia-quest.service.js +2 -2
  129. package/src/api/cyberia-quest-progress/cyberia-quest-progress.controller.js +1 -1
  130. package/src/api/cyberia-quest-progress/cyberia-quest-progress.router.js +1 -1
  131. package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +2 -2
  132. package/src/api/cyberia-saga/cyberia-saga.controller.js +1 -1
  133. package/src/api/cyberia-saga/cyberia-saga.router.js +1 -1
  134. package/src/api/cyberia-saga/cyberia-saga.service.js +2 -2
  135. package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +16 -29
  136. package/src/api/cyberia-skill/cyberia-skill.controller.js +1 -1
  137. package/src/api/cyberia-skill/cyberia-skill.router.js +1 -1
  138. package/src/api/cyberia-skill/cyberia-skill.service.js +2 -2
  139. package/src/api/default/default.controller.js +1 -1
  140. package/src/api/default/default.router.js +1 -1
  141. package/src/api/default/default.service.js +2 -2
  142. package/src/api/document/document.controller.js +1 -1
  143. package/src/api/document/document.router.js +1 -1
  144. package/src/api/document/document.service.js +2 -2
  145. package/src/api/file/file.controller.js +1 -1
  146. package/src/api/file/file.router.js +1 -1
  147. package/src/api/file/file.service.js +2 -2
  148. package/src/api/instance/instance.controller.js +1 -1
  149. package/src/api/instance/instance.router.js +1 -1
  150. package/src/api/instance/instance.service.js +2 -2
  151. package/src/api/ipfs/ipfs.controller.js +1 -1
  152. package/src/api/ipfs/ipfs.router.js +2 -2
  153. package/src/api/ipfs/ipfs.service.js +2 -2
  154. package/src/api/object-layer/object-layer.controller.js +1 -1
  155. package/src/api/object-layer/object-layer.model.js +204 -3
  156. package/src/api/object-layer/object-layer.router.js +2 -2
  157. package/src/api/object-layer/object-layer.service.js +4 -17
  158. package/src/api/object-layer-render-frames/object-layer-render-frames.controller.js +1 -1
  159. package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +1 -1
  160. package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +2 -2
  161. package/src/api/test/test.controller.js +1 -1
  162. package/src/api/test/test.service.js +1 -1
  163. package/src/api/user/guest.service.js +3 -3
  164. package/src/api/user/user.controller.js +1 -1
  165. package/src/api/user/user.router.js +2 -2
  166. package/src/api/user/user.service.js +4 -4
  167. package/src/api.js +5 -9
  168. package/src/cli/app.js +391 -0
  169. package/src/cli/baremetal.js +30 -36
  170. package/src/cli/client.js +306 -0
  171. package/src/cli/cloud-init.js +3 -3
  172. package/src/cli/cluster.js +242 -135
  173. package/src/cli/db.js +126 -67
  174. package/src/cli/deploy.js +158 -60
  175. package/src/cli/docker-compose.js +5 -5
  176. package/src/cli/domains.js +184 -0
  177. package/src/cli/dotenv-store.js +143 -0
  178. package/src/cli/event.js +2281 -0
  179. package/src/cli/fs.js +3 -3
  180. package/src/cli/host.js +672 -0
  181. package/src/cli/image.js +4 -7
  182. package/src/cli/index.js +426 -134
  183. package/src/cli/ipfs.js +34 -30
  184. package/src/cli/kickstart.js +1 -1
  185. package/src/cli/kubectl.js +218 -41
  186. package/src/cli/lxd.js +3 -3
  187. package/src/cli/monitor.js +1359 -26
  188. package/src/cli/package.js +90 -0
  189. package/src/cli/release.js +73 -34
  190. package/src/cli/repository.js +564 -367
  191. package/src/cli/run.js +362 -654
  192. package/src/cli/secrets.js +2158 -964
  193. package/src/cli/ssh.js +651 -331
  194. package/src/cli/state.js +394 -0
  195. package/src/cli/static.js +2 -2
  196. package/src/cli/system.js +26 -13
  197. package/src/cli/test.js +199 -125
  198. package/src/cli/vultr.js +623 -0
  199. package/src/cli/wireguard.js +2930 -0
  200. package/src/client/components/core/CalendarCore.js +53 -96
  201. package/src/client/components/core/Docs.js +258 -211
  202. package/src/client/components/core/FileExplorer.js +0 -21
  203. package/src/client/components/core/PanelForm.js +4 -4
  204. package/src/client/components/core/Repository.js +56 -0
  205. package/src/client/components/core/Translate.js +7 -0
  206. package/src/client/components/core/{FullScreen.js → ViewModeController.js} +42 -39
  207. package/src/client/components/core/Wallet.js +0 -11
  208. package/src/client/components/core/Worker.js +7 -1
  209. package/src/client/components/cryptokoyn/SettingsCryptokoyn.js +2 -2
  210. package/src/client/components/cyberia/FallbackWorldEngineCyberia.js +368 -0
  211. package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +35 -1
  212. package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +4 -0
  213. package/src/client/components/cyberia-portal/SettingsCyberiaPortal.js +2 -2
  214. package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +4 -0
  215. package/src/client/components/default/AppShellDefault.js +1 -1
  216. package/src/client/components/default/SettingsDefault.js +2 -2
  217. package/src/client/components/itemledger/SettingsItemledger.js +2 -2
  218. package/src/client/components/underpost/AppShellUnderpost.js +3 -2
  219. package/src/client/components/underpost/SettingsUnderpost.js +2 -2
  220. package/src/client/public/cyberia-docs/ARCHITECTURE.md +17 -3
  221. package/src/client/public/cyberia-docs/CYBERIA-CLI.md +32 -5
  222. package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +1 -1
  223. package/src/client/public/cyberia-docs/CYBERIA.md +21 -1
  224. package/src/client/public/cyberia-docs/HARDHAT-MODULE.md +23 -6
  225. package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
  226. package/src/client/public/cyberia-docs/WHITE-PAPER.md +1 -1
  227. package/src/client/public/default/android-chrome-144x144.png +0 -0
  228. package/src/client/public/default/android-chrome-192x192.png +0 -0
  229. package/src/client/public/default/android-chrome-256x256.png +0 -0
  230. package/src/client/public/default/android-chrome-36x36.png +0 -0
  231. package/src/client/public/default/android-chrome-384x384.png +0 -0
  232. package/src/client/public/default/android-chrome-48x48.png +0 -0
  233. package/src/client/public/default/android-chrome-512x512.png +0 -0
  234. package/src/client/public/default/android-chrome-72x72.png +0 -0
  235. package/src/client/public/default/android-chrome-96x96.png +0 -0
  236. package/src/client/public/default/apple-touch-icon-1024x1024.png +0 -0
  237. package/src/client/public/default/apple-touch-icon-114x114.png +0 -0
  238. package/src/client/public/default/apple-touch-icon-120x120.png +0 -0
  239. package/src/client/public/default/apple-touch-icon-144x144.png +0 -0
  240. package/src/client/public/default/apple-touch-icon-152x152.png +0 -0
  241. package/src/client/public/default/apple-touch-icon-167x167.png +0 -0
  242. package/src/client/public/default/apple-touch-icon-180x180.png +0 -0
  243. package/src/client/public/default/apple-touch-icon-57x57.png +0 -0
  244. package/src/client/public/default/apple-touch-icon-60x60.png +0 -0
  245. package/src/client/public/default/apple-touch-icon-72x72.png +0 -0
  246. package/src/client/public/default/apple-touch-icon-76x76.png +0 -0
  247. package/src/client/public/default/apple-touch-icon-precomposed.png +0 -0
  248. package/src/client/public/default/apple-touch-icon.png +0 -0
  249. package/src/client/public/default/apple-touch-startup-image-1125x2436.png +0 -0
  250. package/src/client/public/default/apple-touch-startup-image-1136x640.png +0 -0
  251. package/src/client/public/default/apple-touch-startup-image-1170x2532.png +0 -0
  252. package/src/client/public/default/apple-touch-startup-image-1179x2556.png +0 -0
  253. package/src/client/public/default/apple-touch-startup-image-1242x2208.png +0 -0
  254. package/src/client/public/default/apple-touch-startup-image-1242x2688.png +0 -0
  255. package/src/client/public/default/apple-touch-startup-image-1284x2778.png +0 -0
  256. package/src/client/public/default/apple-touch-startup-image-1290x2796.png +0 -0
  257. package/src/client/public/default/apple-touch-startup-image-1334x750.png +0 -0
  258. package/src/client/public/default/apple-touch-startup-image-1488x2266.png +0 -0
  259. package/src/client/public/default/apple-touch-startup-image-1536x2048.png +0 -0
  260. package/src/client/public/default/apple-touch-startup-image-1620x2160.png +0 -0
  261. package/src/client/public/default/apple-touch-startup-image-1640x2160.png +0 -0
  262. package/src/client/public/default/apple-touch-startup-image-1668x2224.png +0 -0
  263. package/src/client/public/default/apple-touch-startup-image-1668x2388.png +0 -0
  264. package/src/client/public/default/apple-touch-startup-image-1792x828.png +0 -0
  265. package/src/client/public/default/apple-touch-startup-image-2048x1536.png +0 -0
  266. package/src/client/public/default/apple-touch-startup-image-2048x2732.png +0 -0
  267. package/src/client/public/default/apple-touch-startup-image-2160x1620.png +0 -0
  268. package/src/client/public/default/apple-touch-startup-image-2160x1640.png +0 -0
  269. package/src/client/public/default/apple-touch-startup-image-2208x1242.png +0 -0
  270. package/src/client/public/default/apple-touch-startup-image-2224x1668.png +0 -0
  271. package/src/client/public/default/apple-touch-startup-image-2266x1488.png +0 -0
  272. package/src/client/public/default/apple-touch-startup-image-2388x1668.png +0 -0
  273. package/src/client/public/default/apple-touch-startup-image-2436x1125.png +0 -0
  274. package/src/client/public/default/apple-touch-startup-image-2532x1170.png +0 -0
  275. package/src/client/public/default/apple-touch-startup-image-2556x1179.png +0 -0
  276. package/src/client/public/default/apple-touch-startup-image-2688x1242.png +0 -0
  277. package/src/client/public/default/apple-touch-startup-image-2732x2048.png +0 -0
  278. package/src/client/public/default/apple-touch-startup-image-2778x1284.png +0 -0
  279. package/src/client/public/default/apple-touch-startup-image-2796x1290.png +0 -0
  280. package/src/client/public/default/apple-touch-startup-image-640x1136.png +0 -0
  281. package/src/client/public/default/apple-touch-startup-image-750x1334.png +0 -0
  282. package/src/client/public/default/apple-touch-startup-image-828x1792.png +0 -0
  283. package/src/client/public/default/assets/background/white.jpg +0 -0
  284. package/src/client/public/default/browserconfig.xml +1 -1
  285. package/src/client/public/default/favicon-16x16.png +0 -0
  286. package/src/client/public/default/favicon-32x32.png +0 -0
  287. package/src/client/public/default/favicon-48x48.png +0 -0
  288. package/src/client/public/default/favicon.ico +0 -0
  289. package/src/client/public/default/manifest.webmanifest +5 -5
  290. package/src/client/public/default/mstile-144x144.png +0 -0
  291. package/src/client/public/default/mstile-150x150.png +0 -0
  292. package/src/client/public/default/mstile-310x150.png +0 -0
  293. package/src/client/public/default/mstile-310x310.png +0 -0
  294. package/src/client/public/default/mstile-70x70.png +0 -0
  295. package/src/client/public/default/yandex-browser-50x50.png +0 -0
  296. package/src/client/public/default/yandex-browser-manifest.json +1 -1
  297. package/src/client/services/cyberia-instance/cyberia-instance.service.js +40 -0
  298. package/src/client/services/object-layer/object-layer.management.js +4 -4
  299. package/src/client/ssr/RootDocument.js +1 -1
  300. package/src/client/ssr/body/404.js +1 -1
  301. package/src/client/ssr/body/500.js +1 -1
  302. package/src/client/ssr/body/CacheControl.js +1 -1
  303. package/src/client/ssr/body/CyberiaDefaultSplashScreen.js +1 -1
  304. package/src/client/ssr/body/DefaultSplashScreen.js +1 -1
  305. package/src/client/ssr/body/SwaggerDarkMode.js +1 -1
  306. package/src/client/ssr/body/UnderpostDefaultSplashScreen.js +1 -1
  307. package/src/client/ssr/head/CryptokoynScripts.js +1 -1
  308. package/src/client/ssr/head/Css.js +1 -1
  309. package/src/client/ssr/head/CyberiaPortalScripts.js +1 -1
  310. package/src/client/ssr/head/DefaultScripts.js +1 -1
  311. package/src/client/ssr/head/ItemledgerScripts.js +1 -1
  312. package/src/client/ssr/head/Microdata.js +1 -1
  313. package/src/client/ssr/head/Production.js +1 -1
  314. package/src/client/ssr/head/Pwa.js +1 -1
  315. package/src/client/ssr/head/PwaItemledger.js +1 -1
  316. package/src/client/ssr/head/Seo.js +1 -1
  317. package/src/client/ssr/head/UnderpostScripts.js +1 -1
  318. package/src/client/ssr/mailer/DefaultRecoverEmail.js +1 -1
  319. package/src/client/ssr/mailer/DefaultVerifyEmail.js +1 -1
  320. package/src/client/ssr/views/Cyberia404.js +7 -5
  321. package/src/client/ssr/views/CyberiaServerMetrics.js +1 -1
  322. package/src/client/ssr/views/Maintenance.js +1 -1
  323. package/src/client/ssr/views/NoNetworkConnection.js +1 -1
  324. package/src/client/ssr/views/Test.js +1 -1
  325. package/src/client/sw/core.sw.js +20 -14
  326. package/src/client-builder/client-build-docs.js +61 -46
  327. package/src/client-builder/client-build-live.js +2 -2
  328. package/src/client-builder/client-build.js +58 -44
  329. package/src/client-builder/client-dev-server.js +3 -3
  330. package/src/client-builder/client-icons.js +2 -2
  331. package/src/client-builder/ssr.js +3 -3
  332. package/src/client.build.js +3 -6
  333. package/src/client.dev.js +3 -6
  334. package/src/db/DataBaseProvider.js +186 -3
  335. package/src/db/mariadb/MariaDB.js +3 -2
  336. package/src/db/mongo/MongoBootstrap.js +273 -101
  337. package/src/db/mongo/MongooseDB.js +11 -8
  338. package/src/{server/valkey.js → db/valkey/Valkey.js} +5 -4
  339. package/src/grpc/cyberia/grpc-server.js +1 -1
  340. package/src/index.js +103 -23
  341. package/src/mailer/EmailRender.js +2 -2
  342. package/src/mailer/MailerInterceptor.js +87 -0
  343. package/src/mailer/MailerProvider.js +50 -6
  344. package/src/projects/cyberia/atlas-sprite-sheet-generator.js +1 -1
  345. package/src/projects/cyberia/besu-genesis-generator.js +3 -3
  346. package/src/projects/cyberia/catalog-cyberia.js +54 -6
  347. package/src/projects/cyberia/gemini-client.js +1 -1
  348. package/src/projects/cyberia/generate-saga.js +15 -24
  349. package/src/projects/cyberia/hot-reload-trigger.js +1 -1
  350. package/src/projects/cyberia/instance-data.js +22 -3
  351. package/src/projects/cyberia/ipfs-client.js +4 -4
  352. package/src/projects/cyberia/map-preview-generator.js +1 -1
  353. package/src/projects/cyberia/object-layer.js +12 -22
  354. package/src/projects/cyberia/semantic-layer-generator.js +1 -1
  355. package/src/projects/underpost/catalog-underpost.js +13 -2
  356. package/src/proxy.js +4 -7
  357. package/src/runtime/cyberia-client/Dockerfile +9 -4
  358. package/src/runtime/cyberia-client/Dockerfile.dev +8 -2
  359. package/src/runtime/cyberia-server/Dockerfile +9 -4
  360. package/src/runtime/cyberia-server/Dockerfile.dev +8 -2
  361. package/src/runtime/engine-cyberia/Dockerfile +25 -9
  362. package/src/runtime/engine-cyberia/Dockerfile.dev +25 -9
  363. package/src/runtime/engine-cyberia/Dockerfile.test +4 -4
  364. package/src/runtime/engine-cyberia/compose.env +16 -0
  365. package/src/runtime/engine-cyberia/docker-compose.yml +31 -37
  366. package/src/runtime/express/Express.js +32 -27
  367. package/src/runtime/lampp/Lampp.js +22 -70
  368. package/src/runtime/nginx/Nginx.js +1 -1
  369. package/src/runtime/wp/Wp.js +16 -48
  370. package/src/server/{catalog.js → build/catalog.js} +15 -5
  371. package/src/server/build/coverage.js +117 -0
  372. package/src/server/build/execution.js +402 -0
  373. package/src/server/build/package.js +465 -0
  374. package/src/server/build/testing.js +528 -0
  375. package/src/server/{dns.js → network/dns.js} +241 -14
  376. package/src/server/network/forward-proxy.js +541 -0
  377. package/src/server/{middlewares.js → network/middlewares.js} +59 -4
  378. package/src/server/network/node-capability.js +98 -0
  379. package/src/server/{peer.js → network/peer.js} +3 -3
  380. package/src/server/{proxy.js → network/proxy.js} +7 -13
  381. package/src/server/network/router.js +356 -0
  382. package/src/server/{tls.js → network/tls.js} +2 -2
  383. package/src/server/network/underpost-compression.js +186 -0
  384. package/src/server/{underpost-gateway.js → network/underpost-gateway.js} +26 -16
  385. package/src/server/{underpost-ingress.js → network/underpost-ingress.js} +19 -3
  386. package/src/server/{cri.js → ops/cri.js} +2 -2
  387. package/src/server/ops/cron.js +949 -0
  388. package/src/server/ops/event-notification.js +284 -0
  389. package/src/server/{logger.js → ops/logger.js} +108 -23
  390. package/src/server/ops/monitoring.js +1724 -0
  391. package/src/server/ops/systemd.js +259 -0
  392. package/src/server/{conf.js → runtime/conf.js} +521 -520
  393. package/src/server/runtime/config-scope.js +215 -0
  394. package/src/server/runtime/environment.js +149 -0
  395. package/src/server/{process.js → runtime/process.js} +153 -13
  396. package/src/server/{runtime-status.js → runtime/runtime-status.js} +155 -16
  397. package/src/server/{runtime.js → runtime/runtime.js} +12 -8
  398. package/src/server/{start.js → runtime/start.js} +128 -39
  399. package/src/server/{auth.js → security/auth.js} +7 -8
  400. package/src/server/security/container-storage.js +382 -0
  401. package/src/server/{crypto.js → security/crypto.js} +1 -1
  402. package/src/server/security/selinux.js +185 -0
  403. package/src/server/{backup.js → storage/backup.js} +17 -8
  404. package/src/server/{data-query.js → storage/data-query.js} +1 -1
  405. package/src/server/{downloader.js → storage/downloader.js} +2 -2
  406. package/src/server/storage/repository.js +68 -0
  407. package/src/server.js +4 -7
  408. package/src/ws/IoInterface.js +1 -1
  409. package/src/ws/IoServer.js +1 -1
  410. package/src/ws/core/core.ws.connection.js +1 -1
  411. package/src/ws/core/core.ws.emit.js +1 -1
  412. package/src/ws/core/core.ws.server.js +1 -1
  413. package/src/ws/default/default.ws.connection.js +1 -1
  414. package/src/ws/default/default.ws.emit.js +1 -1
  415. package/src/ws/default/default.ws.server.js +1 -1
  416. package/test/e2e/event-e2e-public-ingress-down.js +45 -0
  417. package/test/e2e/event-e2e-wireguard-server-down.js +38 -0
  418. package/test/e2e/event-e2e-wireguard-spoke-down.js +40 -0
  419. package/test/{api.test.js → integration/app/api.test.js} +33 -14
  420. package/test/integration/app/cyberia/cyberia-cli-plain-reads.test.js +30 -0
  421. package/test/{cyberia-instance-conf-defaults.test.js → integration/app/cyberia/cyberia-instance-conf-defaults.test.js} +2 -1
  422. package/test/integration/app/cyberia/cyberia-load.test.js +467 -0
  423. package/test/integration/app/cyberia/fallback-world-capture.test.js +253 -0
  424. package/test/integration/app/cyberia/object-layer-item-id.test.js +327 -0
  425. package/test/{shape-generator.test.js → integration/app/cyberia/shape-generator.test.js} +1 -1
  426. package/test/integration/infra/1-security/config-scope.test.js +305 -0
  427. package/test/integration/infra/1-security/container-storage.test.js +356 -0
  428. package/test/integration/infra/1-security/in-pod-cli-surface.test.js +86 -0
  429. package/test/integration/infra/1-security/secret-onboarding.test.js +930 -0
  430. package/test/integration/infra/1-security/selinux.test.js +69 -0
  431. package/test/{sops-secret-store.test.js → integration/infra/1-security/sops-secret-store.test.js} +415 -61
  432. package/test/integration/infra/1-security/systemd-service.test.js +70 -0
  433. package/test/integration/infra/1-security/underpost-config-secret.test.js +367 -0
  434. package/test/integration/infra/2-network/dns-firewall.test.js +566 -0
  435. package/test/integration/infra/2-network/node-capability.test.js +182 -0
  436. package/test/integration/infra/2-network/wireguard-cli.test.js +1163 -0
  437. package/test/integration/infra/2-network/wireguard-edge.test.js +1731 -0
  438. package/test/{cluster-instances.test.js → integration/infra/3-cluster/cluster-instances.test.js} +11 -15
  439. package/test/{deploy-node-placement.test.js → integration/infra/3-cluster/deploy-node-placement.test.js} +3 -3
  440. package/test/integration/infra/3-cluster/docker-compose-stack.test.js +313 -0
  441. package/test/integration/infra/4-ingress/deploy-routes.test.js +379 -0
  442. package/test/integration/infra/4-ingress/gateway-static-assets.test.js +453 -0
  443. package/test/{instance-traffic-plan.test.js → integration/infra/4-ingress/instance-traffic-plan.test.js} +10 -6
  444. package/test/{underpost-gateway.test.js → integration/infra/4-ingress/underpost-gateway.test.js} +47 -6
  445. package/test/{underpost-ingress.test.js → integration/infra/4-ingress/underpost-ingress.test.js} +56 -4
  446. package/test/integration/infra/5-observability/cron-jobs.test.js +652 -0
  447. package/test/{deploy-monitor.test.js → integration/infra/5-observability/deploy-monitor.test.js} +29 -31
  448. package/test/integration/infra/5-observability/event-notification.test.js +197 -0
  449. package/test/integration/infra/5-observability/event-remediation.test.js +502 -0
  450. package/test/integration/infra/5-observability/event-targets.test.js +1174 -0
  451. package/test/integration/infra/5-observability/monitoring-stack.test.js +902 -0
  452. package/test/support/shell-harness.js +73 -0
  453. package/test/unit/build-template.test.js +97 -0
  454. package/test/unit/build-workflow-offline.test.js +132 -0
  455. package/test/unit/catalog.test.js +71 -0
  456. package/test/unit/client-build-docs.test.js +334 -0
  457. package/test/unit/conf-loading.test.js +303 -0
  458. package/test/unit/conf-resolution.test.js +979 -0
  459. package/test/unit/coverage-artifact.test.js +130 -0
  460. package/test/{crypto.test.js → unit/crypto.test.js} +1 -1
  461. package/test/unit/deploy-env-resolution.test.js +38 -0
  462. package/test/unit/deploy-log-table.test.js +447 -0
  463. package/test/unit/execution-profiles.test.js +219 -0
  464. package/test/unit/fleet-sync-source.test.js +149 -0
  465. package/test/unit/logger-redaction.test.js +112 -0
  466. package/test/unit/package.test.js +403 -0
  467. package/test/unit/prepare-host.test.js +299 -0
  468. package/test/unit/process-environment.test.js +18 -0
  469. package/test/unit/propagation-message.test.js +78 -0
  470. package/test/unit/redeploy-plan.test.js +24 -0
  471. package/test/unit/release-bump.test.js +474 -0
  472. package/test/unit/ssh-output-redaction.test.js +28 -0
  473. package/test/unit/start-options.test.js +46 -0
  474. package/test/unit/test-tiers.test.js +193 -0
  475. package/vitest.config.js +58 -0
  476. package/.nycrc +0 -9
  477. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +0 -48
  478. package/manifests/mongodb/pv-pvc.yaml +0 -59
  479. package/scripts/link-local-underpost-cli.sh +0 -6
  480. package/src/api/user/user.build.js +0 -16
  481. package/src/cli/env.js +0 -177
  482. package/src/client/components/core/Blockchain.js +0 -41
  483. package/src/client/components/core/Blog.js +0 -9
  484. package/src/client/components/core/KeyboardAvoidance.js +0 -145
  485. package/src/client/public/default/assets/background/dark.jpg +0 -0
  486. package/src/client/public/default/assets/background/white0-min.jpg +0 -0
  487. package/src/client/public/default/assets/background/white0.jpg +0 -0
  488. package/src/client/public/doc/favicon.ico +0 -0
  489. package/src/client/public/doc/sitemap +0 -148
  490. package/src/server/cron.js +0 -476
@@ -8,22 +8,23 @@
8
8
 
9
9
  import fs from 'fs-extra';
10
10
  import { transformClientJs, JSONweb } from './client-formatted.js';
11
- import { loggerFactory } from '../server/logger.js';
11
+ import { loggerFactory } from '../server/ops/logger.js';
12
12
  import {
13
13
  getCapVariableName,
14
14
  newInstance,
15
15
  orderArrayFromAttrInt,
16
16
  uniqueArray,
17
17
  } from '../client/components/core/CommonJs.js';
18
- import { readConfJson } from '../server/conf.js';
18
+ import { readConfJson } from '../server/runtime/conf.js';
19
19
  import { minify } from 'html-minifier-terser';
20
20
  import AdmZip from 'adm-zip';
21
21
  import * as dir from 'path';
22
- import { shellExec } from '../server/process.js';
22
+ import { shellExec } from '../server/runtime/process.js';
23
23
  import { SitemapStream, streamToPromise } from 'sitemap';
24
24
  import { Readable } from 'stream';
25
25
  import { buildIcons } from './client-icons.js';
26
- import { statusPageBuildSegment } from '../server/underpost-gateway.js';
26
+ import { statusPageBuildSegment } from '../server/network/underpost-gateway.js';
27
+ import { repositoryIdentityFactory } from '../server/storage/repository.js';
27
28
  import Underpost from '../index.js';
28
29
  import { buildDocs } from './client-build-docs.js';
29
30
  import { ssrFactory } from './ssr.js';
@@ -525,6 +526,7 @@ const defaultSitemapXsl = `<?xml version="1.0" encoding="UTF-8"?>
525
526
  * @param {string|number} options.split - Optional zip split size in MB.
526
527
  * @param {boolean} options.fullBuild - Whether to perform a full build.
527
528
  * @param {boolean} options.iconsBuild - Whether to build icons.
529
+ * @param {boolean} options.ssrOnly - Whether to rebuild only views defined in `conf.ssr.json`.
528
530
  * @returns {Promise<void>} - Promise that resolves when the build is complete.
529
531
  * @throws {Error} - If the build fails.
530
532
  * @memberof clientBuild
@@ -532,6 +534,7 @@ const defaultSitemapXsl = `<?xml version="1.0" encoding="UTF-8"?>
532
534
  const buildClient = async (
533
535
  options = {
534
536
  deployId: '',
537
+ subConf: '',
535
538
  liveClientBuildPaths: [],
536
539
  instances: [],
537
540
  buildZip: false,
@@ -542,12 +545,17 @@ const buildClient = async (
542
545
  ) => {
543
546
  const logger = loggerFactory(import.meta);
544
547
  const deployId = options.deployId || process.env.DEPLOY_ID;
545
- const confClient = readConfJson(deployId, 'client');
546
- const confServer = readConfJson(deployId, 'server', { loadReplicas: true });
547
- const confSSR = readConfJson(deployId, 'ssr');
548
+ // Carried as an argument rather than re-derived from DEPLOY_SUB_CONF: the caller already
549
+ // resolved which sub-configuration this build is for, and a build that reads a different
550
+ // conf.server than the one its caller loaded renders the wrong set of hosts.
551
+ const subConf = `${options.subConf ?? ''}`.trim();
552
+ const confClient = readConfJson(deployId, 'client', { subConf });
553
+ const confServer = readConfJson(deployId, 'server', { subConf, loadReplicas: true });
554
+ const confSSR = readConfJson(deployId, 'ssr', { subConf });
548
555
  const packageData = JSON.parse(fs.readFileSync(`./package.json`, 'utf8'));
549
556
  const acmeChallengePath = `/.well-known/acme-challenge`;
550
557
  const publicPath = `./public`;
558
+ const ssrOnly = options.ssrOnly === true;
551
559
 
552
560
  /**
553
561
  * @async
@@ -687,15 +695,27 @@ const buildClient = async (
687
695
  // ''; // process.env.NODE_ENV === 'production' ? `https://${host}` : ``;
688
696
  currentPort++;
689
697
 
698
+ // Every document this instance emits carries the same payload; the SSR
699
+ // views and the client index must never disagree about which repository
700
+ // published them.
701
+ const renderPayload = {
702
+ apiBaseProxyPath,
703
+ apiBaseHost,
704
+ apiBasePath: process.env.BASE_API,
705
+ version: Underpost.version,
706
+ repository: repositoryIdentityFactory(),
707
+ ...(isDevelopment ? { dev: true } : undefined),
708
+ };
709
+
690
710
  const acmeChallengeFullPath = directory
691
711
  ? `${directory}${acmeChallengePath}`
692
712
  : `${publicPath}/${host}${acmeChallengePath}`;
693
713
 
694
- if (!enableLiveRebuild) buildAcmeChallengePath(acmeChallengeFullPath);
714
+ if (!enableLiveRebuild && !ssrOnly) buildAcmeChallengePath(acmeChallengeFullPath);
695
715
 
696
716
  if (redirect || disabledRebuild) continue;
697
717
 
698
- if (fullBuildEnabled)
718
+ if (fullBuildEnabled && !ssrOnly)
699
719
  await fullBuild({
700
720
  path,
701
721
  logger,
@@ -710,7 +730,7 @@ const buildClient = async (
710
730
  publicCopyNonExistingFiles,
711
731
  });
712
732
 
713
- if (components)
733
+ if (!ssrOnly && components)
714
734
  for (const module of Object.keys(components)) {
715
735
  if (!fs.existsSync(`${rootClientPath}/components/${module}`))
716
736
  fs.mkdirSync(`${rootClientPath}/components/${module}`, { recursive: true });
@@ -733,7 +753,7 @@ const buildClient = async (
733
753
  }
734
754
  }
735
755
 
736
- if (services) {
756
+ if (!ssrOnly && services) {
737
757
  for (const module of services) {
738
758
  if (!fs.existsSync(`${rootClientPath}/services/${module}`))
739
759
  fs.mkdirSync(`${rootClientPath}/services/${module}`, { recursive: true });
@@ -796,7 +816,9 @@ const buildClient = async (
796
816
  const swSrcPath = `./src/client/sw/core.sw.js`;
797
817
  const swPublicPath = `${rootClientPath}/sw.js`;
798
818
  const swShouldRebuild =
799
- views && !(enableLiveRebuild && !options.liveClientBuildPaths.find((p) => p.srcBuildPath === swSrcPath));
819
+ !ssrOnly &&
820
+ views &&
821
+ !(enableLiveRebuild && !options.liveClientBuildPaths.find((p) => p.srcBuildPath === swSrcPath));
800
822
  // Transformed SW JS is held in memory; it gets prepended with renderPayload
801
823
  // and written once below, after PRE_CACHED_RESOURCES are known.
802
824
  let swTransformedJs = '';
@@ -810,13 +832,15 @@ const buildClient = async (
810
832
  });
811
833
  }
812
834
 
813
- if (views) {
814
- if (!(
815
- enableLiveRebuild &&
816
- !options.liveClientBuildPaths.find(
817
- (p) => p.srcBuildPath.startsWith(`./src/client/ssr`) || p.srcBuildPath.slice(-9) === '.index.js',
835
+ if (!ssrOnly && views) {
836
+ if (
837
+ !(
838
+ enableLiveRebuild &&
839
+ !options.liveClientBuildPaths.find(
840
+ (p) => p.srcBuildPath.startsWith(`./src/client/ssr`) || p.srcBuildPath.slice(-9) === '.index.js',
841
+ )
818
842
  )
819
- ))
843
+ )
820
844
  for (const view of views) {
821
845
  const buildPath = `${
822
846
  rootClientPath[rootClientPath.length - 1] === '/' ? rootClientPath.slice(0, -1) : rootClientPath
@@ -848,7 +872,7 @@ const buildClient = async (
848
872
  confSSR[view.ssr].head.unshift('Production');
849
873
 
850
874
  for (const ssrHeadComponent of confSSR[view.ssr].head) {
851
- const SrrComponent = await ssrFactory(`./src/client/ssr/head/${ssrHeadComponent}.js`);
875
+ const SSRComponent = await ssrFactory(`./src/client/ssr/head/${ssrHeadComponent}.js`);
852
876
 
853
877
  switch (ssrHeadComponent) {
854
878
  case 'Pwa':
@@ -903,11 +927,11 @@ const buildClient = async (
903
927
  // ],
904
928
  // "prefer_related_applications": true
905
929
  }
906
- if (validPwaBuild) ssrHeadComponents += SrrComponent({ title, ssrPath, canonicalURL, ...metadata });
930
+ if (validPwaBuild) ssrHeadComponents += SSRComponent({ title, ssrPath, canonicalURL, ...metadata });
907
931
  break;
908
932
  case 'Seo':
909
933
  if (metadata) {
910
- ssrHeadComponents += SrrComponent({ title, ssrPath, canonicalURL, ...metadata });
934
+ ssrHeadComponents += SSRComponent({ title, ssrPath, canonicalURL, ...metadata });
911
935
  }
912
936
  break;
913
937
  case 'Microdata':
@@ -919,18 +943,18 @@ const buildClient = async (
919
943
  const microdata = JSON.parse(
920
944
  fs.readFileSync(`./src/client/public/${publicClientId}/microdata.json`, 'utf8'),
921
945
  );
922
- ssrHeadComponents += SrrComponent({ microdata });
946
+ ssrHeadComponents += SSRComponent({ microdata });
923
947
  }
924
948
  break;
925
949
  default:
926
- ssrHeadComponents += SrrComponent({ ssrPath, host, path });
950
+ ssrHeadComponents += SSRComponent({ ssrPath, host, path });
927
951
  break;
928
952
  }
929
953
  }
930
954
 
931
955
  for (const ssrBodyComponent of confSSR[view.ssr].body) {
932
- const SrrComponent = await ssrFactory(`./src/client/ssr/body/${ssrBodyComponent}.js`);
933
- ssrBodyComponents += SrrComponent({
956
+ const SSRComponent = await ssrFactory(`./src/client/ssr/body/${ssrBodyComponent}.js`);
957
+ ssrBodyComponents += SSRComponent({
934
958
  ...metadata,
935
959
  ssrPath,
936
960
  host,
@@ -956,13 +980,7 @@ const buildClient = async (
956
980
  ssrPath,
957
981
  ssrHeadComponents,
958
982
  ssrBodyComponents,
959
- renderPayload: {
960
- apiBaseProxyPath,
961
- apiBaseHost,
962
- apiBasePath: process.env.BASE_API,
963
- version: Underpost.version,
964
- ...(isDevelopment ? { dev: true } : undefined),
965
- },
983
+ renderPayload,
966
984
  renderApi: {
967
985
  JSONweb,
968
986
  },
@@ -983,7 +1001,7 @@ const buildClient = async (
983
1001
  );
984
1002
  }
985
1003
  }
986
- if (!enableLiveRebuild && siteMapLinks.length > 0) {
1004
+ if (!ssrOnly && !enableLiveRebuild && siteMapLinks.length > 0) {
987
1005
  const hasSitemapTemplate = fs.existsSync(`${rootClientPath}/sitemap`);
988
1006
  const sitemapBaseUrl = `https://${host}${path === '/' ? '' : path}`;
989
1007
  // Create a stream to write to — omit xslUrl so we can inject a relative href below
@@ -1020,11 +1038,13 @@ Sitemap: ${sitemapBaseUrl}/sitemap.xml`,
1020
1038
  );
1021
1039
  }
1022
1040
 
1023
- if (fullBuildEnabled && docs) {
1041
+ if (!ssrOnly && fullBuildEnabled && docs) {
1024
1042
  await buildDocs({
1025
1043
  host,
1026
1044
  path,
1027
1045
  port,
1046
+ apiBaseHost,
1047
+ apiBaseProxyPath,
1028
1048
  metadata,
1029
1049
  apis,
1030
1050
  publicClientId,
@@ -1058,13 +1078,7 @@ Sitemap: ${sitemapBaseUrl}/sitemap.xml`,
1058
1078
  ssrPath,
1059
1079
  ssrHeadComponents: '<base target="_top">',
1060
1080
  ssrBodyComponents: SsrComponent(),
1061
- renderPayload: {
1062
- apiBaseProxyPath,
1063
- apiBaseHost,
1064
- apiBasePath: process.env.BASE_API,
1065
- version: Underpost.version,
1066
- ...(isDevelopment ? { dev: true } : undefined),
1067
- },
1081
+ renderPayload,
1068
1082
  renderApi: { JSONweb },
1069
1083
  });
1070
1084
 
@@ -1110,7 +1124,7 @@ Sitemap: ${sitemapBaseUrl}/sitemap.xml`,
1110
1124
 
1111
1125
  if (swShouldRebuild) {
1112
1126
  const cacheScope = path === '/' ? 'root' : path.replaceAll('/', '_');
1113
- const renderPayload = {
1127
+ const swRenderPayload = {
1114
1128
  PRE_CACHED_RESOURCES: uniqueArray(PRE_CACHED_RESOURCES),
1115
1129
  PROXY_PATH: path,
1116
1130
  CACHE_PREFIX: `engine-core-${cacheScope}`,
@@ -1121,14 +1135,14 @@ Sitemap: ${sitemapBaseUrl}/sitemap.xml`,
1121
1135
  // Single write: prepend the payload prelude to the transformed SW JS.
1122
1136
  fs.writeFileSync(
1123
1137
  swPublicPath,
1124
- `self.renderPayload = ${JSONweb(renderPayload)};
1138
+ `self.renderPayload = ${JSONweb(swRenderPayload)};
1125
1139
  self.__WB_DISABLE_DEV_LOGS = true;
1126
1140
  ${swTransformedJs}`,
1127
1141
  'utf8',
1128
1142
  );
1129
1143
  }
1130
1144
  }
1131
- if (!enableLiveRebuild && options.buildZip) {
1145
+ if (!ssrOnly && !enableLiveRebuild && options.buildZip) {
1132
1146
  logger.warn('build zip', rootClientPath);
1133
1147
 
1134
1148
  if (!fs.existsSync('./build')) fs.mkdirSync('./build');
@@ -6,8 +6,8 @@
6
6
  import fs from 'fs-extra';
7
7
  import nodemon from 'nodemon';
8
8
  import dotenv from 'dotenv';
9
- import { shellExec } from '../server/process.js';
10
- import { loggerFactory } from '../server/logger.js';
9
+ import { shellExec } from '../server/runtime/process.js';
10
+ import { loggerFactory } from '../server/ops/logger.js';
11
11
  import Underpost from '../index.js';
12
12
 
13
13
  const logger = loggerFactory(import.meta);
@@ -32,7 +32,7 @@ const createClientDevServer = async (
32
32
  const devClientEnvPath = `./engine-private/conf/${deployId}/.env.${process.env.NODE_ENV}.${subConf}-dev-client`;
33
33
  if (fs.existsSync(devClientEnvPath)) dotenv.config({ path: devClientEnvPath, override: true });
34
34
 
35
- await Underpost.repo.client(deployId, `${subConf}-dev-client`.trim(), host, path);
35
+ await Underpost.client.callback(deployId, `${subConf}-dev-client`.trim(), host, path);
36
36
 
37
37
  shellExec(`node src/server ${deployId} ${subConf}-dev-client`.trim(), {
38
38
  async: true,
@@ -4,7 +4,7 @@
4
4
  * @namespace clientIcons
5
5
  */
6
6
  import { favicons } from 'favicons';
7
- import { loggerFactory } from '../server/logger.js';
7
+ import { loggerFactory } from '../server/ops/logger.js';
8
8
  import fs from 'fs-extra';
9
9
  import { getCapVariableName } from '../client/components/core/CommonJs.js';
10
10
 
@@ -99,7 +99,7 @@ const buildIcons = async ({
99
99
 
100
100
  const ssrPath = `./src/client/ssr/head/Pwa${getCapVariableName(publicClientId)}.js`;
101
101
  if (!fs.existsSync(ssrPath))
102
- fs.writeFileSync(ssrPath, 'SrrComponent = () => html`' + response.html.join(`\n`) + '`;', 'utf8');
102
+ fs.writeFileSync(ssrPath, 'SSRComponent = () => html`' + response.html.join(`\n`) + '`;', 'utf8');
103
103
  } catch (error) {
104
104
  logger.error(error.message); // Error description e.g. "An unknown error has occurred"
105
105
  }
@@ -10,7 +10,7 @@ import vm from 'node:vm';
10
10
  import Underpost from '../index.js';
11
11
 
12
12
  import { srcFormatted } from './client-formatted.js';
13
- import { loggerFactory } from '../server/logger.js';
13
+ import { loggerFactory } from '../server/ops/logger.js';
14
14
 
15
15
  const logger = loggerFactory(import.meta);
16
16
 
@@ -22,10 +22,10 @@ const logger = loggerFactory(import.meta);
22
22
  * @memberof ServerSideRendering
23
23
  */
24
24
  const ssrFactory = async (componentPath = `./src/client/ssr/RootDocument.js`) => {
25
- const context = { SrrComponent: () => {}, npm_package_version: Underpost.version };
25
+ const context = { SSRComponent: () => {}, npm_package_version: Underpost.version };
26
26
  vm.createContext(context);
27
27
  vm.runInContext(await srcFormatted(fs.readFileSync(componentPath, 'utf8')), context);
28
- return context.SrrComponent;
28
+ return context.SSRComponent;
29
29
  };
30
30
 
31
31
  /**
@@ -1,11 +1,8 @@
1
1
  'use strict';
2
2
 
3
- // https://nodejs.org/api
4
- // https://expressjs.com/en/4x/api.html
5
-
6
- import { loggerFactory } from './server/logger.js';
7
- import { Config } from './server/conf.js';
8
- import { ProcessController } from './server/process.js';
3
+ import { loggerFactory } from './server/ops/logger.js';
4
+ import { Config } from './server/runtime/conf.js';
5
+ import { ProcessController } from './server/runtime/process.js';
9
6
  import { clientLiveBuild } from './client-builder/client-build-live.js';
10
7
 
11
8
  await Config.build();
package/src/client.dev.js CHANGED
@@ -1,11 +1,8 @@
1
1
  'use strict';
2
2
 
3
- // https://nodejs.org/api
4
- // https://expressjs.com/en/4x/api.html
5
-
6
- import { loggerFactory } from './server/logger.js';
7
- import { ProcessController } from './server/process.js';
8
- import { Config, buildClientStaticConf } from './server/conf.js';
3
+ import { loggerFactory } from './server/ops/logger.js';
4
+ import { ProcessController } from './server/runtime/process.js';
5
+ import { Config, buildClientStaticConf } from './server/runtime/conf.js';
9
6
  import { createClientDevServer } from './client-builder/client-dev-server.js';
10
7
 
11
8
  const logger = loggerFactory(import.meta);
@@ -1,8 +1,9 @@
1
1
  import { MongooseDB } from './mongo/MongooseDB.js';
2
- import { loggerFactory } from '../server/logger.js';
2
+ import { loggerFactory } from '../server/ops/logger.js';
3
3
  import { getCapVariableName } from '../client/components/core/CommonJs.js';
4
- import { resolveHostKeyContext } from '../server/conf.js';
4
+ import { resolveHostKeyContext } from '../server/runtime/conf.js';
5
5
  import Underpost from '../index.js';
6
+ import { latchRuntimeError } from '../server/runtime/runtime-status.js';
6
7
 
7
8
  /**
8
9
  * Module for managing and loading various database connections (e.g., Mongoose, MariaDB).
@@ -12,6 +13,60 @@ import Underpost from '../index.js';
12
13
 
13
14
  const logger = loggerFactory(import.meta);
14
15
 
16
+ // Health watch defaults. `DB_HEALTH_INTERVAL_MS=0` disables the watch entirely.
17
+ const HEALTH_INTERVAL_MS = Number(process.env.DB_HEALTH_INTERVAL_MS ?? 15000);
18
+ const HEALTH_PING_TIMEOUT_MS = 5000;
19
+
20
+ /**
21
+ * Bounds a liveness probe that would otherwise wait on the driver's own connection timeout.
22
+ * @param {Promise<boolean>} probe - Provider probe resolving to liveness.
23
+ * @returns {Promise<boolean>} `false` when the probe throws or outlives the bound.
24
+ */
25
+ const withPingTimeout = async (probe) => {
26
+ let timer;
27
+ try {
28
+ return await Promise.race([
29
+ probe,
30
+ new Promise((resolve) => {
31
+ timer = setTimeout(() => resolve(false), HEALTH_PING_TIMEOUT_MS);
32
+ }),
33
+ ]);
34
+ } catch {
35
+ return false;
36
+ } finally {
37
+ clearTimeout(timer);
38
+ }
39
+ };
40
+
41
+ /**
42
+ * Per-provider health strategies, keyed by `db.provider`.
43
+ *
44
+ * A provider with no entry here gets no health watch and no reconnect: liveness probes,
45
+ * rebuild semantics, and teardown differ per client, so there is nothing generic to fall
46
+ * back on. Adding one (MariaDB, PostgreSQL, …) means adding its case here — the rest of the
47
+ * service dispatches through this table and needs no change.
48
+ *
49
+ * @type {object.<string, {isAlive: Function, rebuild: Function, close: Function}>}
50
+ */
51
+ const PROVIDER_HEALTH = {
52
+ mongoose: {
53
+ isAlive: async (connection) => {
54
+ if (!connection || connection.readyState !== 1) return false;
55
+ return await withPingTimeout(
56
+ connection.db
57
+ .admin()
58
+ .command({ ping: 1 })
59
+ .then(() => true),
60
+ );
61
+ },
62
+ rebuild: async ({ apis, db }) => {
63
+ const connection = await MongooseDB.connect(db);
64
+ return { connection, models: await MongooseDB.loadModels({ conn: connection, apis }) };
65
+ },
66
+ close: async (connection) => await connection?.close(),
67
+ },
68
+ };
69
+
15
70
  /**
16
71
  * @class
17
72
  * @alias DataBaseProviderService
@@ -126,6 +181,128 @@ class DataBaseProviderService {
126
181
  });
127
182
  }
128
183
 
184
+ /**
185
+ * Timers for the per-provider health watch, keyed by `<contextKey>:<provider>`.
186
+ * @type {object.<string, {timer: object, attempts: number, rebuilding: boolean}>}
187
+ */
188
+ static #watchers = {};
189
+
190
+ /**
191
+ * Rebuilds a loaded provider in place: new connection, freshly bound models, old
192
+ * connection closed once the replacement is live.
193
+ *
194
+ * A reconnect is not the same as letting the driver heal itself. For mongoose: after a
195
+ * replica set is reconfigured the driver can hold a topology it will never accept a primary
196
+ * for again — it records the highest `setVersion`/`electionId` it has seen and rejects
197
+ * anything lower for the rest of the process — so only a new client recovers. Every consumer
198
+ * resolves models through this bucket on each call, so swapping the fields in place is
199
+ * enough; nothing has to be re-registered.
200
+ *
201
+ * Only providers present in {@link PROVIDER_HEALTH} can be rebuilt.
202
+ *
203
+ * @param {{host?: string, path?: string}|string} context - Context object or key.
204
+ * @param {string} [provider='mongoose'] - Provider name.
205
+ * @returns {Promise<boolean>} `true` when the provider was rebuilt.
206
+ */
207
+ static async reconnect(context, provider = 'mongoose') {
208
+ const key = resolveHostKeyContext(context);
209
+ const strategy = PROVIDER_HEALTH[provider];
210
+ if (!strategy) {
211
+ logger.warn('No reconnect strategy for provider', { key, provider });
212
+ return false;
213
+ }
214
+
215
+ const bucket = this.#instance[key]?.[provider];
216
+ if (!bucket?.rebuild) {
217
+ logger.warn('Cannot reconnect a provider that was never loaded', { key, provider });
218
+ return false;
219
+ }
220
+
221
+ const previousConnection = bucket.connection;
222
+
223
+ try {
224
+ const { connection, models } = await strategy.rebuild(bucket.rebuild);
225
+
226
+ bucket.connection = connection;
227
+ bucket.models = models;
228
+ logger.info('Database connection rebuilt', { key, provider });
229
+ } catch (error) {
230
+ logger.error('Database reconnect failed, keeping the existing connection', {
231
+ key,
232
+ provider,
233
+ error: error.message,
234
+ });
235
+ return false;
236
+ }
237
+
238
+ // Closed last and best-effort: the replacement is already serving, and a close that
239
+ // hangs on a dead server must not take the recovery with it.
240
+ try {
241
+ await strategy.close(previousConnection);
242
+ } catch (error) {
243
+ logger.warn('Could not close the replaced connection', { key, provider, error: error.message });
244
+ }
245
+ return true;
246
+ }
247
+
248
+ /**
249
+ * Starts the health watch for a provider: pings on an interval and rebuilds the connection
250
+ * on every tick that finds it down, until it answers again. A provider with no
251
+ * {@link PROVIDER_HEALTH} entry is left unwatched.
252
+ * @param {string} key - Resolved context key.
253
+ * @param {string} provider - Provider name.
254
+ */
255
+ static #startHealthWatch(key, provider) {
256
+ const strategy = PROVIDER_HEALTH[provider];
257
+ if (!strategy || !(HEALTH_INTERVAL_MS > 0)) return;
258
+ const watchKey = `${key}:${provider}`;
259
+ if (this.#watchers[watchKey]) return;
260
+
261
+ const state = { attempts: 0, rebuilding: false, timer: null };
262
+ state.timer = setInterval(async () => {
263
+ // A rebuild can outlast the interval (the driver's own connect timeout is longer), so
264
+ // ticks that land mid-attempt are skipped rather than stacking attempts.
265
+ if (state.rebuilding) return;
266
+ const bucket = this.#instance[key]?.[provider];
267
+ if (!bucket) return;
268
+
269
+ if (await strategy.isAlive(bucket.connection)) {
270
+ if (state.attempts > 0)
271
+ logger.info('Database connection restored', { key, provider, attempts: state.attempts });
272
+ state.attempts = 0;
273
+ return;
274
+ }
275
+
276
+ // Every tick retries until the connection answers again: there is no attempt budget to
277
+ // exhaust, because giving up leaves the runtime permanently unable to reach its database.
278
+ state.rebuilding = true;
279
+ state.attempts++;
280
+ logger.warn('Database connection is down, rebuilding', { key, provider, attempt: state.attempts });
281
+ try {
282
+ await this.reconnect(key, provider);
283
+ } finally {
284
+ state.rebuilding = false;
285
+ }
286
+ }, HEALTH_INTERVAL_MS);
287
+
288
+ // Never hold the event loop open on account of a health check.
289
+ state.timer.unref?.();
290
+ this.#watchers[watchKey] = state;
291
+ }
292
+
293
+ /**
294
+ * Stops the health watch for a provider.
295
+ * @param {string} key - Resolved context key.
296
+ * @param {string} provider - Provider name.
297
+ */
298
+ static #stopHealthWatch(key, provider) {
299
+ const watchKey = `${key}:${provider}`;
300
+ const state = this.#watchers[watchKey];
301
+ if (!state) return;
302
+ clearInterval(state.timer);
303
+ delete this.#watchers[watchKey];
304
+ }
305
+
129
306
  /**
130
307
  * Loads and initializes a database provider based on the configuration.
131
308
  * If the connection is already loaded for the given host/path, it returns the existing instance.
@@ -166,9 +343,12 @@ class DataBaseProviderService {
166
343
  const conn = await MongooseDB.connect(db);
167
344
  this.#instance[key][db.provider] = {
168
345
  dbSignature,
346
+ // Kept so the connection can be rebuilt later without the caller being present.
347
+ rebuild: { apis, host, path, db },
169
348
  models: await MongooseDB.loadModels({ conn, apis }),
170
349
  connection: conn,
171
350
  close: async () => {
351
+ this.#stopHealthWatch(key, db.provider);
172
352
  return await new Promise((resolve) => {
173
353
  this.#instance[key][db.provider].connection.close().then(() => {
174
354
  // logger.info('Mongoose connection is disconnected', db);
@@ -182,6 +362,9 @@ class DataBaseProviderService {
182
362
  default:
183
363
  break;
184
364
  }
365
+ // Self-guarding: only providers with a PROVIDER_HEALTH entry are actually watched, so a
366
+ // provider added above does not have to remember to opt in here.
367
+ this.#startHealthWatch(key, db.provider);
185
368
  return this.#instance[key][db.provider];
186
369
  } catch (error) {
187
370
  // Sanitize options to prevent credential exposure in logs
@@ -200,7 +383,7 @@ class DataBaseProviderService {
200
383
  : {},
201
384
  };
202
385
  logger.error(error.message, { safeOptions });
203
- if (Underpost.env.isInsideContainer()) Underpost.env.set('container-status', 'error');
386
+ latchRuntimeError();
204
387
  return undefined;
205
388
  }
206
389
  }
@@ -1,6 +1,7 @@
1
1
  import { createPool } from 'mariadb';
2
2
  import Underpost from '../../index.js';
3
- import { loggerFactory } from '../../server/logger.js';
3
+ import { loggerFactory } from '../../server/ops/logger.js';
4
+ import { latchRuntimeError } from '../../server/runtime/runtime-status.js';
4
5
 
5
6
  /**
6
7
  * Module for interacting with MariaDB/MySQL databases using the mariadb connector.
@@ -51,7 +52,7 @@ class MariaDBService {
51
52
  console.log(result);
52
53
  } catch (error) {
53
54
  logger.error('MariaDB query failed', { error: error.message });
54
- if (Underpost.env.isInsideContainer()) Underpost.env.set('container-status', 'error');
55
+ latchRuntimeError();
55
56
  } finally {
56
57
  if (conn) conn.release(); // release to pool
57
58
  await pool.end();