@underpostnet/cyberia 3.3.0 → 3.3.76

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 (568) hide show
  1. package/.dockerignore +1 -0
  2. package/.env.example +17 -5
  3. package/.github/workflows/coverall.cyberia.ci.yml +39 -14
  4. package/.github/workflows/cyberia-client.cd.yml +5 -0
  5. package/.github/workflows/cyberia-server.cd.yml +5 -0
  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 +3 -0
  13. package/.github/workflows/engine-cyberia.ci.yml +4 -5
  14. package/.github/workflows/ghpkg.ci.yml +35 -20
  15. package/.github/workflows/hardhat.ci.yml +12 -11
  16. package/.github/workflows/npmpkg.ci.yml +17 -15
  17. package/.github/workflows/publish.cyberia.ci.yml +44 -0
  18. package/.github/workflows/pwa-microservices-template-test.ci.yml +2 -2
  19. package/.github/workflows/release.cd.yml +1 -0
  20. package/.prettierignore +1 -0
  21. package/AGENTS.md +96 -0
  22. package/CHANGELOG.md +653 -2513
  23. package/CLI-HELP.md +323 -157
  24. package/Dockerfile +25 -9
  25. package/Dockerfile.dev +25 -9
  26. package/Dockerfile.test +5 -5
  27. package/README.md +48 -28
  28. package/bin/build.js +86 -41
  29. package/bin/build.template.js +8 -3
  30. package/bin/cyberia.js +1494 -1403
  31. package/bin/deploy.js +18 -104
  32. package/bin/index.js +1494 -1403
  33. package/compose.env +17 -5
  34. package/conf.js +98 -7
  35. package/deploy/cyberia-client/deploy.sh +46 -0
  36. package/deploy/cyberia-client/package.sh +17 -0
  37. package/deploy/cyberia-client/state.sh +15 -0
  38. package/deploy/cyberia-server/deploy.sh +46 -0
  39. package/deploy/cyberia-server/package.sh +17 -0
  40. package/deploy/cyberia-server/state.sh +15 -0
  41. package/deploy/dd-cyberia/deploy.sh +9 -29
  42. package/deploy/dd-cyberia/init.sh +38 -45
  43. package/deploy/dd-cyberia/package.sh +16 -0
  44. package/deploy/dd-cyberia/state.sh +14 -0
  45. package/deploy/dd-cyberia/sync-deploy.sh +133 -147
  46. package/deploy/lib/config.sh +29 -0
  47. package/deploy/lib/github-actions-logging.sh +610 -0
  48. package/deploy/lib/host.sh +194 -0
  49. package/deploy/lib/state.sh +65 -0
  50. package/deploy/pwa-microservices-template/deploy.sh +17 -42
  51. package/deploy/release/deploy.sh +9 -41
  52. package/deployment.yaml +7 -2
  53. package/docker-compose.yml +23 -17
  54. package/examples/static-page/README.md +19 -19
  55. package/examples/static-page/ssr-components/CustomPage.js +2 -2
  56. package/hardhat/hardhat.config.js +1 -6
  57. package/hardhat/package-lock.json +101 -40
  58. package/hardhat/package.json +2 -2
  59. package/manifests/alertmanager/deployment.yaml +81 -0
  60. package/manifests/blackbox-exporter/deployment.yaml +75 -0
  61. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +32 -7
  62. package/manifests/cronjobs/dd-cron/dd-cron-vultr.yaml +32 -7
  63. package/manifests/deployment/dd-cyberia-development/deployment.yaml +7 -2
  64. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  65. package/manifests/deployment/mongo-express/deployment.yaml +46 -27
  66. package/manifests/deployment/mongo-express/kustomization.yaml +9 -0
  67. package/manifests/deployment/mongo-express/mongo-express-nodeport.yaml +18 -0
  68. package/manifests/deployment/mongo-express/service.yaml +16 -0
  69. package/manifests/deployment/mongo-express-no-auth/disable-mongodb-auth.yaml +16 -0
  70. package/manifests/deployment/mongo-express-no-auth/kustomization.yaml +16 -0
  71. package/manifests/grafana/deployment.yaml +26 -7
  72. package/manifests/grafana/kustomization.yaml +1 -0
  73. package/manifests/grafana/service.yaml +6 -1
  74. package/manifests/mariadb/pv.yaml +1 -0
  75. package/manifests/mongodb/statefulset.yaml +9 -2
  76. package/manifests/mysql/pv-pvc.yaml +14 -1
  77. package/manifests/postgresql/pv.yaml +12 -1
  78. package/manifests/postgresql/pvc.yaml +1 -0
  79. package/manifests/prometheus/deployment.yaml +18 -22
  80. package/package.json +87 -12
  81. package/scripts/event-monitor.sh +56 -0
  82. package/scripts/k3s-node-setup.sh +27 -14
  83. package/scripts/kubeadm-node-setup.sh +31 -11
  84. package/scripts/maas-setup.sh +8 -8
  85. package/scripts/selinux-normalize.sh +907 -0
  86. package/scripts/shutdown-machine.sh +843 -0
  87. package/scripts/test-monitor.sh +5 -5
  88. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +15 -10
  89. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +36 -5
  90. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +7 -2
  91. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +90 -186
  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 +3 -3
  99. package/src/api/cyberia-action/cyberia-action.service.js +5 -6
  100. package/src/api/cyberia-audio/cyberia-audio.controller.js +6 -0
  101. package/src/api/cyberia-audio/cyberia-audio.model.js +61 -0
  102. package/src/api/cyberia-audio/cyberia-audio.router.js +21 -0
  103. package/src/api/cyberia-audio/cyberia-audio.service.js +220 -0
  104. package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +2 -4
  105. package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +10 -19
  106. package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +13 -35
  107. package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +17 -34
  108. package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +1 -1
  109. package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +1 -1
  110. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +2 -2
  111. package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +2 -2
  112. package/src/api/cyberia-entity/cyberia-entity.controller.js +1 -1
  113. package/src/api/cyberia-entity/cyberia-entity.model.js +2 -0
  114. package/src/api/cyberia-entity/cyberia-entity.router.js +1 -1
  115. package/src/api/cyberia-entity/cyberia-entity.service.js +3 -3
  116. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +8 -2
  117. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.model.js +31 -20
  118. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +17 -2
  119. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +468 -5
  120. package/src/api/cyberia-instance/cyberia-fallback-capture.js +502 -0
  121. package/src/api/cyberia-instance/cyberia-fallback-world.js +29 -40
  122. package/src/api/cyberia-instance/cyberia-instance-boot.service.js +5 -5
  123. package/src/api/cyberia-instance/cyberia-instance-items.js +98 -0
  124. package/src/api/cyberia-instance/cyberia-instance-map.service.js +35 -142
  125. package/src/api/cyberia-instance/cyberia-instance.controller.js +2 -5
  126. package/src/api/cyberia-instance/cyberia-instance.model.js +0 -14
  127. package/src/api/cyberia-instance/cyberia-instance.router.js +5 -18
  128. package/src/api/cyberia-instance/cyberia-instance.service.js +5 -45
  129. package/src/api/cyberia-instance/cyberia-world-generator.js +8 -13
  130. package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +1 -1
  131. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +49 -75
  132. package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +1 -1
  133. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +76 -3
  134. package/src/api/cyberia-map/cyberia-map.controller.js +1 -1
  135. package/src/api/cyberia-map/cyberia-map.router.js +1 -1
  136. package/src/api/cyberia-map/cyberia-map.service.js +8 -4
  137. package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.controller.js +31 -0
  138. package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.model.js +56 -0
  139. package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.router.js +33 -0
  140. package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.service.js +189 -0
  141. package/src/api/cyberia-quest/cyberia-quest.controller.js +1 -1
  142. package/src/api/cyberia-quest/cyberia-quest.router.js +4 -4
  143. package/src/api/cyberia-quest/cyberia-quest.service.js +5 -5
  144. package/src/api/cyberia-quest-progress/cyberia-quest-progress.controller.js +1 -1
  145. package/src/api/cyberia-quest-progress/cyberia-quest-progress.model.js +1 -1
  146. package/src/api/cyberia-quest-progress/cyberia-quest-progress.router.js +1 -1
  147. package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +2 -2
  148. package/src/api/cyberia-saga/cyberia-saga.controller.js +1 -1
  149. package/src/api/cyberia-saga/cyberia-saga.router.js +1 -1
  150. package/src/api/cyberia-saga/cyberia-saga.service.js +2 -2
  151. package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +403 -214
  152. package/src/api/cyberia-skill/cyberia-skill.controller.js +1 -1
  153. package/src/api/cyberia-skill/cyberia-skill.model.js +6 -6
  154. package/src/api/cyberia-skill/cyberia-skill.router.js +1 -1
  155. package/src/api/cyberia-skill/cyberia-skill.service.js +2 -2
  156. package/src/api/default/default.controller.js +1 -1
  157. package/src/api/default/default.router.js +1 -1
  158. package/src/api/default/default.service.js +2 -2
  159. package/src/api/document/document.controller.js +1 -1
  160. package/src/api/document/document.router.js +1 -1
  161. package/src/api/document/document.service.js +2 -2
  162. package/src/api/file/file.controller.js +1 -1
  163. package/src/api/file/file.ref.js +153 -0
  164. package/src/api/file/file.ref.json +11 -2
  165. package/src/api/file/file.router.js +1 -1
  166. package/src/api/file/file.service.js +2 -2
  167. package/src/api/instance/instance.controller.js +1 -1
  168. package/src/api/instance/instance.router.js +1 -1
  169. package/src/api/instance/instance.service.js +2 -2
  170. package/src/api/ipfs/ipfs.controller.js +1 -1
  171. package/src/api/ipfs/ipfs.router.js +2 -2
  172. package/src/api/ipfs/ipfs.service.js +2 -2
  173. package/src/api/object-layer/object-layer.controller.js +1 -1
  174. package/src/api/object-layer/object-layer.model.js +47 -12
  175. package/src/api/object-layer/object-layer.router.js +12 -5
  176. package/src/api/object-layer/object-layer.service.js +116 -123
  177. package/src/api/object-layer-render-frames/object-layer-render-frames.controller.js +1 -1
  178. package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +1 -1
  179. package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +2 -2
  180. package/src/api/test/test.controller.js +1 -1
  181. package/src/api/test/test.service.js +1 -1
  182. package/src/api/user/guest.service.js +3 -3
  183. package/src/api/user/user.controller.js +1 -1
  184. package/src/api/user/user.router.js +2 -2
  185. package/src/api/user/user.service.js +4 -4
  186. package/src/api.js +5 -9
  187. package/src/cli/app.js +402 -0
  188. package/src/cli/baremetal.js +30 -35
  189. package/src/cli/client.js +306 -0
  190. package/src/cli/cloud-init.js +3 -3
  191. package/src/cli/cluster.js +191 -82
  192. package/src/cli/db.js +120 -68
  193. package/src/cli/deploy.js +157 -75
  194. package/src/cli/docker-compose.js +5 -5
  195. package/src/cli/domains.js +184 -0
  196. package/src/cli/dotenv-store.js +143 -0
  197. package/src/cli/event.js +2281 -0
  198. package/src/cli/fs-selection.js +115 -0
  199. package/src/cli/fs.js +159 -343
  200. package/src/cli/host.js +672 -0
  201. package/src/cli/image.js +4 -6
  202. package/src/cli/index.js +343 -151
  203. package/src/cli/ipfs.js +34 -30
  204. package/src/cli/kickstart.js +1 -1
  205. package/src/cli/kubectl.js +218 -41
  206. package/src/cli/lxd.js +3 -3
  207. package/src/cli/monitor.js +1359 -26
  208. package/src/cli/package.js +90 -0
  209. package/src/cli/release.js +16 -12
  210. package/src/cli/repository.js +586 -363
  211. package/src/cli/run.js +398 -843
  212. package/src/cli/secrets.js +2158 -964
  213. package/src/cli/ssh.js +478 -279
  214. package/src/cli/state.js +394 -0
  215. package/src/cli/static.js +2 -2
  216. package/src/cli/system.js +1 -1
  217. package/src/cli/test.js +203 -125
  218. package/src/cli/vultr.js +61 -21
  219. package/src/cli/wireguard.js +1112 -306
  220. package/src/client/components/core/CalendarCore.js +53 -96
  221. package/src/client/components/core/Docs.js +327 -303
  222. package/src/client/components/core/FileExplorer.js +0 -21
  223. package/src/client/components/core/Modal.js +56 -1
  224. package/src/client/components/core/PanelForm.js +4 -4
  225. package/src/client/components/core/Repository.js +56 -0
  226. package/src/client/components/core/Translate.js +7 -0
  227. package/src/client/components/core/{FullScreen.js → ViewModeController.js} +42 -39
  228. package/src/client/components/core/Wallet.js +0 -11
  229. package/src/client/components/core/Worker.js +7 -1
  230. package/src/client/components/cryptokoyn/SettingsCryptokoyn.js +2 -2
  231. package/src/client/components/cyberia/ActionEngineCyberia.js +12 -36
  232. package/src/client/components/cyberia/EntityEngineCyberia.js +492 -121
  233. package/src/client/components/cyberia/InstanceEngineCyberia.js +129 -160
  234. package/src/client/components/cyberia/InstanceSelectionView.js +7 -1
  235. package/src/client/components/cyberia/MapEngineCyberia.js +175 -69
  236. package/src/client/components/cyberia/ObjectLayerEngine.js +1011 -97
  237. package/src/client/components/cyberia/ObjectLayerEngineModal.js +81 -155
  238. package/src/client/components/cyberia/ObjectLayerEngineViewer.js +113 -98
  239. package/src/client/components/cyberia/SharedDefaultsCyberia.js +215 -56
  240. package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +2 -35
  241. package/src/client/components/cyberia-portal/CssCyberiaPortal.js +1 -1
  242. package/src/client/components/cyberia-portal/MainBodyCyberiaPortal.js +712 -76
  243. package/src/client/components/cyberia-portal/SettingsCyberiaPortal.js +2 -2
  244. package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +0 -4
  245. package/src/client/components/default/AppShellDefault.js +1 -1
  246. package/src/client/components/default/SettingsDefault.js +2 -2
  247. package/src/client/components/itemledger/SettingsItemledger.js +2 -2
  248. package/src/client/components/underpost/AppShellUnderpost.js +3 -2
  249. package/src/client/components/underpost/SettingsUnderpost.js +2 -2
  250. package/src/client/public/cyberia-docs/ARCHITECTURE.md +15 -15
  251. package/src/client/public/cyberia-docs/CYBERIA-CLI.md +313 -30
  252. package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +51 -29
  253. package/src/client/public/cyberia-docs/CYBERIA-SAGA.md +52 -53
  254. package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +16 -11
  255. package/src/client/public/cyberia-docs/CYBERIA.md +48 -28
  256. package/src/client/public/cyberia-docs/ENTITY-PROFILE.md +11 -5
  257. package/src/client/public/cyberia-docs/HARDHAT-MODULE.md +23 -6
  258. package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
  259. package/src/client/public/cyberia-docs/STATS-PROGRESSION.md +217 -0
  260. package/src/client/public/cyberia-docs/WHITE-PAPER.md +20 -12
  261. package/src/client/public/default/android-chrome-144x144.png +0 -0
  262. package/src/client/public/default/android-chrome-192x192.png +0 -0
  263. package/src/client/public/default/android-chrome-256x256.png +0 -0
  264. package/src/client/public/default/android-chrome-36x36.png +0 -0
  265. package/src/client/public/default/android-chrome-384x384.png +0 -0
  266. package/src/client/public/default/android-chrome-48x48.png +0 -0
  267. package/src/client/public/default/android-chrome-512x512.png +0 -0
  268. package/src/client/public/default/android-chrome-72x72.png +0 -0
  269. package/src/client/public/default/android-chrome-96x96.png +0 -0
  270. package/src/client/public/default/apple-touch-icon-1024x1024.png +0 -0
  271. package/src/client/public/default/apple-touch-icon-114x114.png +0 -0
  272. package/src/client/public/default/apple-touch-icon-120x120.png +0 -0
  273. package/src/client/public/default/apple-touch-icon-144x144.png +0 -0
  274. package/src/client/public/default/apple-touch-icon-152x152.png +0 -0
  275. package/src/client/public/default/apple-touch-icon-167x167.png +0 -0
  276. package/src/client/public/default/apple-touch-icon-180x180.png +0 -0
  277. package/src/client/public/default/apple-touch-icon-57x57.png +0 -0
  278. package/src/client/public/default/apple-touch-icon-60x60.png +0 -0
  279. package/src/client/public/default/apple-touch-icon-72x72.png +0 -0
  280. package/src/client/public/default/apple-touch-icon-76x76.png +0 -0
  281. package/src/client/public/default/apple-touch-icon-precomposed.png +0 -0
  282. package/src/client/public/default/apple-touch-icon.png +0 -0
  283. package/src/client/public/default/apple-touch-startup-image-1125x2436.png +0 -0
  284. package/src/client/public/default/apple-touch-startup-image-1136x640.png +0 -0
  285. package/src/client/public/default/apple-touch-startup-image-1170x2532.png +0 -0
  286. package/src/client/public/default/apple-touch-startup-image-1179x2556.png +0 -0
  287. package/src/client/public/default/apple-touch-startup-image-1242x2208.png +0 -0
  288. package/src/client/public/default/apple-touch-startup-image-1242x2688.png +0 -0
  289. package/src/client/public/default/apple-touch-startup-image-1284x2778.png +0 -0
  290. package/src/client/public/default/apple-touch-startup-image-1290x2796.png +0 -0
  291. package/src/client/public/default/apple-touch-startup-image-1334x750.png +0 -0
  292. package/src/client/public/default/apple-touch-startup-image-1488x2266.png +0 -0
  293. package/src/client/public/default/apple-touch-startup-image-1536x2048.png +0 -0
  294. package/src/client/public/default/apple-touch-startup-image-1620x2160.png +0 -0
  295. package/src/client/public/default/apple-touch-startup-image-1640x2160.png +0 -0
  296. package/src/client/public/default/apple-touch-startup-image-1668x2224.png +0 -0
  297. package/src/client/public/default/apple-touch-startup-image-1668x2388.png +0 -0
  298. package/src/client/public/default/apple-touch-startup-image-1792x828.png +0 -0
  299. package/src/client/public/default/apple-touch-startup-image-2048x1536.png +0 -0
  300. package/src/client/public/default/apple-touch-startup-image-2048x2732.png +0 -0
  301. package/src/client/public/default/apple-touch-startup-image-2160x1620.png +0 -0
  302. package/src/client/public/default/apple-touch-startup-image-2160x1640.png +0 -0
  303. package/src/client/public/default/apple-touch-startup-image-2208x1242.png +0 -0
  304. package/src/client/public/default/apple-touch-startup-image-2224x1668.png +0 -0
  305. package/src/client/public/default/apple-touch-startup-image-2266x1488.png +0 -0
  306. package/src/client/public/default/apple-touch-startup-image-2388x1668.png +0 -0
  307. package/src/client/public/default/apple-touch-startup-image-2436x1125.png +0 -0
  308. package/src/client/public/default/apple-touch-startup-image-2532x1170.png +0 -0
  309. package/src/client/public/default/apple-touch-startup-image-2556x1179.png +0 -0
  310. package/src/client/public/default/apple-touch-startup-image-2688x1242.png +0 -0
  311. package/src/client/public/default/apple-touch-startup-image-2732x2048.png +0 -0
  312. package/src/client/public/default/apple-touch-startup-image-2778x1284.png +0 -0
  313. package/src/client/public/default/apple-touch-startup-image-2796x1290.png +0 -0
  314. package/src/client/public/default/apple-touch-startup-image-640x1136.png +0 -0
  315. package/src/client/public/default/apple-touch-startup-image-750x1334.png +0 -0
  316. package/src/client/public/default/apple-touch-startup-image-828x1792.png +0 -0
  317. package/src/client/public/default/assets/background/white.jpg +0 -0
  318. package/src/client/public/default/browserconfig.xml +1 -1
  319. package/src/client/public/default/favicon-16x16.png +0 -0
  320. package/src/client/public/default/favicon-32x32.png +0 -0
  321. package/src/client/public/default/favicon-48x48.png +0 -0
  322. package/src/client/public/default/favicon.ico +0 -0
  323. package/src/client/public/default/manifest.webmanifest +5 -5
  324. package/src/client/public/default/mstile-144x144.png +0 -0
  325. package/src/client/public/default/mstile-150x150.png +0 -0
  326. package/src/client/public/default/mstile-310x150.png +0 -0
  327. package/src/client/public/default/mstile-310x310.png +0 -0
  328. package/src/client/public/default/mstile-70x70.png +0 -0
  329. package/src/client/public/default/yandex-browser-50x50.png +0 -0
  330. package/src/client/public/default/yandex-browser-manifest.json +1 -1
  331. package/src/client/services/cyberia-audio/cyberia-audio.service.js +99 -0
  332. package/src/client/services/cyberia-entity-type-default/cyberia-entity-type-default.service.js +38 -0
  333. package/src/client/services/cyberia-instance/cyberia-instance.management.js +5 -4
  334. package/src/client/services/cyberia-instance/cyberia-instance.service.js +0 -40
  335. package/src/client/services/cyberia-map/cyberia-map.management.js +5 -4
  336. package/src/client/services/cyberia-map-audio-conf/cyberia-map-audio-conf.service.js +138 -0
  337. package/src/client/services/object-layer/object-layer.management.js +6 -10
  338. package/src/client/services/object-layer/object-layer.service.js +3 -1
  339. package/src/client/ssr/RootDocument.js +1 -1
  340. package/src/client/ssr/body/404.js +1 -1
  341. package/src/client/ssr/body/500.js +1 -1
  342. package/src/client/ssr/body/CacheControl.js +1 -1
  343. package/src/client/ssr/body/CyberiaDefaultSplashScreen.js +1 -1
  344. package/src/client/ssr/body/DefaultSplashScreen.js +1 -1
  345. package/src/client/ssr/body/SwaggerDarkMode.js +1 -1
  346. package/src/client/ssr/body/UnderpostDefaultSplashScreen.js +1 -1
  347. package/src/client/ssr/head/CryptokoynScripts.js +1 -1
  348. package/src/client/ssr/head/Css.js +1 -1
  349. package/src/client/ssr/head/CyberiaPortalScripts.js +1 -1
  350. package/src/client/ssr/head/DefaultScripts.js +1 -1
  351. package/src/client/ssr/head/ItemledgerScripts.js +1 -1
  352. package/src/client/ssr/head/Microdata.js +1 -1
  353. package/src/client/ssr/head/Production.js +1 -1
  354. package/src/client/ssr/head/Pwa.js +1 -1
  355. package/src/client/ssr/head/PwaItemledger.js +1 -1
  356. package/src/client/ssr/head/Seo.js +1 -1
  357. package/src/client/ssr/head/UnderpostScripts.js +1 -1
  358. package/src/client/ssr/mailer/DefaultRecoverEmail.js +1 -1
  359. package/src/client/ssr/mailer/DefaultVerifyEmail.js +1 -1
  360. package/src/client/ssr/views/Cyberia404.js +7 -5
  361. package/src/client/ssr/views/CyberiaServerMetrics.js +1 -1
  362. package/src/client/ssr/views/Maintenance.js +1 -1
  363. package/src/client/ssr/views/NoNetworkConnection.js +1 -1
  364. package/src/client/ssr/views/Test.js +1 -1
  365. package/src/client/sw/core.sw.js +20 -14
  366. package/src/client-builder/client-build-docs.js +92 -77
  367. package/src/client-builder/client-build-live.js +2 -2
  368. package/src/client-builder/client-build.js +105 -74
  369. package/src/client-builder/client-bundle.js +347 -0
  370. package/src/client-builder/client-dev-server.js +3 -3
  371. package/src/client-builder/client-icons.js +2 -2
  372. package/src/client-builder/ssr.js +3 -3
  373. package/src/client.build.js +3 -6
  374. package/src/client.dev.js +3 -6
  375. package/src/db/DataBaseProvider.js +186 -3
  376. package/src/db/mariadb/MariaDB.js +3 -2
  377. package/src/db/mongo/MongoBootstrap.js +273 -101
  378. package/src/db/mongo/MongoExpress.js +187 -0
  379. package/src/db/mongo/MongooseDB.js +7 -8
  380. package/src/{server/valkey.js → db/valkey/Valkey.js} +5 -4
  381. package/src/grpc/cyberia/grpc-server.js +6 -8
  382. package/src/index.js +79 -23
  383. package/src/mailer/EmailRender.js +2 -2
  384. package/src/mailer/MailerInterceptor.js +87 -0
  385. package/src/mailer/MailerProvider.js +50 -6
  386. package/src/projects/cyberia/atlas-sprite-sheet-generator.js +144 -147
  387. package/src/projects/cyberia/atlas-sprite-sheet-store.js +391 -0
  388. package/src/projects/cyberia/besu-genesis-generator.js +8 -17
  389. package/src/projects/cyberia/boot-contract-fixtures.js +32 -0
  390. package/src/projects/cyberia/catalog-cyberia.js +54 -10
  391. package/src/projects/cyberia/gemini-client.js +4 -4
  392. package/src/projects/cyberia/generate-saga.js +5 -12
  393. package/src/projects/cyberia/hot-reload-trigger.js +18 -29
  394. package/src/projects/cyberia/instance-backup.js +210 -0
  395. package/src/projects/cyberia/instance-data.js +178 -258
  396. package/src/projects/cyberia/ipfs-client.js +4 -4
  397. package/src/projects/cyberia/map-preview-generator.js +33 -69
  398. package/src/projects/cyberia/object-layer.js +153 -320
  399. package/src/projects/cyberia/seed-audio.js +139 -0
  400. package/src/projects/cyberia/semantic-layer-generator-skin.js +2 -8
  401. package/src/projects/cyberia/semantic-layer-generator.js +3 -19
  402. package/src/projects/cyberia/stat-balance.js +81 -0
  403. package/src/projects/cyberia/stat-commands.js +23 -0
  404. package/src/projects/cyberia/stat-contract-generator.js +101 -0
  405. package/src/projects/underpost/catalog-underpost.js +9 -1
  406. package/src/proxy.js +4 -7
  407. package/src/runtime/cyberia-client/Dockerfile +17 -8
  408. package/src/runtime/cyberia-client/Dockerfile.dev +13 -4
  409. package/src/runtime/cyberia-server/Dockerfile +18 -6
  410. package/src/runtime/cyberia-server/Dockerfile.dev +16 -3
  411. package/src/runtime/engine-cyberia/Dockerfile +25 -9
  412. package/src/runtime/engine-cyberia/Dockerfile.dev +25 -9
  413. package/src/runtime/engine-cyberia/Dockerfile.test +5 -5
  414. package/src/runtime/engine-cyberia/compose.env +17 -5
  415. package/src/runtime/engine-cyberia/docker-compose.yml +23 -17
  416. package/src/runtime/express/Express.js +32 -27
  417. package/src/runtime/lampp/Lampp.js +22 -70
  418. package/src/runtime/nginx/Nginx.js +1 -1
  419. package/src/runtime/wp/Wp.js +16 -48
  420. package/src/server/{catalog.js → build/catalog.js} +15 -5
  421. package/src/server/build/coverage.js +188 -0
  422. package/src/server/build/execution.js +402 -0
  423. package/src/server/build/package.js +508 -0
  424. package/src/server/build/testing.js +578 -0
  425. package/src/server/{dns.js → network/dns.js} +144 -11
  426. package/src/server/{forward-proxy.js → network/forward-proxy.js} +10 -18
  427. package/src/server/{middlewares.js → network/middlewares.js} +11 -5
  428. package/src/server/network/node-capability.js +98 -0
  429. package/src/server/{peer.js → network/peer.js} +3 -3
  430. package/src/server/{proxy.js → network/proxy.js} +7 -13
  431. package/src/server/network/router.js +356 -0
  432. package/src/server/{tls.js → network/tls.js} +2 -2
  433. package/src/server/{underpost-compression.js → network/underpost-compression.js} +1 -1
  434. package/src/server/{underpost-gateway.js → network/underpost-gateway.js} +7 -7
  435. package/src/server/{underpost-ingress.js → network/underpost-ingress.js} +2 -2
  436. package/src/server/{cri.js → ops/cri.js} +2 -2
  437. package/src/server/{cron.js → ops/cron.js} +349 -74
  438. package/src/server/ops/event-notification.js +284 -0
  439. package/src/server/{logger.js → ops/logger.js} +143 -23
  440. package/src/server/ops/monitoring.js +1724 -0
  441. package/src/server/{systemd.js → ops/systemd.js} +55 -1
  442. package/src/server/{conf.js → runtime/conf.js} +516 -423
  443. package/src/server/runtime/config-scope.js +215 -0
  444. package/src/server/runtime/environment.js +149 -0
  445. package/src/server/{process.js → runtime/process.js} +153 -12
  446. package/src/server/{runtime-status.js → runtime/runtime-status.js} +155 -16
  447. package/src/server/{runtime.js → runtime/runtime.js} +12 -8
  448. package/src/server/{start.js → runtime/start.js} +128 -39
  449. package/src/server/{auth.js → security/auth.js} +7 -8
  450. package/src/server/security/container-storage.js +382 -0
  451. package/src/server/{crypto.js → security/crypto.js} +1 -1
  452. package/src/server/{selinux.js → security/selinux.js} +1 -1
  453. package/src/server/{backup.js → storage/backup.js} +17 -8
  454. package/src/server/{data-query.js → storage/data-query.js} +17 -1
  455. package/src/server/storage/downloader.js +121 -0
  456. package/src/server/storage/repository.js +68 -0
  457. package/src/server/storage/zip.js +153 -0
  458. package/src/server.js +4 -7
  459. package/src/ws/IoInterface.js +1 -1
  460. package/src/ws/IoServer.js +1 -1
  461. package/src/ws/core/core.ws.connection.js +1 -1
  462. package/src/ws/core/core.ws.emit.js +1 -1
  463. package/src/ws/core/core.ws.server.js +1 -1
  464. package/src/ws/default/default.ws.connection.js +1 -1
  465. package/src/ws/default/default.ws.emit.js +1 -1
  466. package/src/ws/default/default.ws.server.js +1 -1
  467. package/test/e2e/event-e2e-public-ingress-down.js +45 -0
  468. package/test/e2e/event-e2e-wireguard-server-down.js +38 -0
  469. package/test/e2e/event-e2e-wireguard-spoke-down.js +40 -0
  470. package/test/{api.test.js → integration/app/api.test.js} +33 -14
  471. package/test/integration/app/cyberia/atlas-sprite-sheet-store.test.js +402 -0
  472. package/test/integration/app/cyberia/atlas-sprite-sheet.test.js +86 -0
  473. package/test/integration/app/cyberia/cyberia-cli-plain-reads.test.js +64 -0
  474. package/test/integration/app/cyberia/cyberia-entity-type-default.test.js +842 -0
  475. package/test/integration/app/cyberia/cyberia-instance-conf-coerce.test.js +53 -0
  476. package/test/{cyberia-instance-conf-defaults.test.js → integration/app/cyberia/cyberia-instance-conf-defaults.test.js} +17 -23
  477. package/test/integration/app/cyberia/cyberia-instance-items.test.js +90 -0
  478. package/test/integration/app/cyberia/cyberia-load.test.js +466 -0
  479. package/test/integration/app/cyberia/cyberia-map-audio-conf.test.js +256 -0
  480. package/test/integration/app/cyberia/cyberia-stats.test.js +150 -0
  481. package/test/integration/app/cyberia/fallback-world-capture.test.js +338 -0
  482. package/test/integration/app/cyberia/object-layer-item-id.test.js +327 -0
  483. package/test/integration/app/cyberia/object-layer-item-selection.test.js +56 -0
  484. package/test/integration/app/cyberia/object-layer-natural-key.test.js +32 -0
  485. package/test/integration/app/cyberia/seed-audio.test.js +275 -0
  486. package/test/{shape-generator.test.js → integration/app/cyberia/shape-generator.test.js} +1 -1
  487. package/test/integration/infra/1-security/config-scope.test.js +305 -0
  488. package/test/integration/infra/1-security/container-storage.test.js +356 -0
  489. package/test/integration/infra/1-security/in-pod-cli-surface.test.js +86 -0
  490. package/test/integration/infra/1-security/secret-onboarding.test.js +930 -0
  491. package/test/{selinux.test.js → integration/infra/1-security/selinux.test.js} +2 -4
  492. package/test/{sops-secret-store.test.js → integration/infra/1-security/sops-secret-store.test.js} +415 -61
  493. package/test/integration/infra/1-security/systemd-service.test.js +70 -0
  494. package/test/integration/infra/1-security/underpost-config-secret.test.js +367 -0
  495. package/test/integration/infra/2-network/dns-firewall.test.js +566 -0
  496. package/test/integration/infra/2-network/node-capability.test.js +182 -0
  497. package/test/integration/infra/2-network/wireguard-cli.test.js +1187 -0
  498. package/test/{wireguard-edge.test.js → integration/infra/2-network/wireguard-edge.test.js} +649 -55
  499. package/test/{cluster-instances.test.js → integration/infra/3-cluster/cluster-instances.test.js} +11 -15
  500. package/test/{deploy-node-placement.test.js → integration/infra/3-cluster/deploy-node-placement.test.js} +3 -3
  501. package/test/integration/infra/3-cluster/docker-compose-stack.test.js +313 -0
  502. package/test/integration/infra/3-cluster/mongo-express-deploy.test.js +156 -0
  503. package/test/integration/infra/4-ingress/api-cross-origin.test.js +79 -0
  504. package/test/integration/infra/4-ingress/deploy-routes.test.js +379 -0
  505. package/test/integration/infra/4-ingress/gateway-static-assets.test.js +453 -0
  506. package/test/{instance-traffic-plan.test.js → integration/infra/4-ingress/instance-traffic-plan.test.js} +10 -6
  507. package/test/{underpost-gateway.test.js → integration/infra/4-ingress/underpost-gateway.test.js} +6 -6
  508. package/test/{underpost-ingress.test.js → integration/infra/4-ingress/underpost-ingress.test.js} +4 -4
  509. package/test/integration/infra/5-observability/cron-jobs.test.js +652 -0
  510. package/test/{deploy-monitor.test.js → integration/infra/5-observability/deploy-monitor.test.js} +29 -31
  511. package/test/integration/infra/5-observability/event-notification.test.js +197 -0
  512. package/test/integration/infra/5-observability/event-remediation.test.js +502 -0
  513. package/test/integration/infra/5-observability/event-targets.test.js +1174 -0
  514. package/test/integration/infra/5-observability/monitoring-stack.test.js +902 -0
  515. package/test/support/shell-harness.js +73 -0
  516. package/test/unit/build-template.test.js +97 -0
  517. package/test/unit/build-workflow-offline.test.js +132 -0
  518. package/test/unit/catalog.test.js +71 -0
  519. package/test/unit/client-build-docs.test.js +361 -0
  520. package/test/unit/client-bundle.test.js +313 -0
  521. package/test/unit/conf-loading.test.js +303 -0
  522. package/test/unit/conf-resolution.test.js +996 -0
  523. package/test/unit/coverage-artifact.test.js +221 -0
  524. package/test/{crypto.test.js → unit/crypto.test.js} +1 -1
  525. package/test/unit/cyberia/instance-backup.test.js +183 -0
  526. package/test/unit/cyberia/instance-object-layer-items.test.js +46 -0
  527. package/test/unit/cyberia/publish-workflow.test.js +22 -0
  528. package/test/unit/cyberia/stat-balance.test.js +69 -0
  529. package/test/unit/deploy-env-resolution.test.js +38 -0
  530. package/test/unit/deploy-log-table.test.js +518 -0
  531. package/test/unit/downloader-integrity.test.js +98 -0
  532. package/test/unit/execution-profiles.test.js +219 -0
  533. package/test/unit/file-reference-registry.test.js +141 -0
  534. package/test/unit/fleet-sync-source.test.js +149 -0
  535. package/test/unit/fs-storage-paths.test.js +596 -0
  536. package/test/unit/logger-redaction.test.js +141 -0
  537. package/test/unit/package.test.js +457 -0
  538. package/test/unit/prepare-host.test.js +382 -0
  539. package/test/unit/process-environment.test.js +18 -0
  540. package/test/unit/propagation-message.test.js +78 -0
  541. package/test/unit/redeploy-plan.test.js +24 -0
  542. package/test/unit/release-bump.test.js +474 -0
  543. package/test/unit/ssh-output-redaction.test.js +28 -0
  544. package/test/unit/start-options.test.js +46 -0
  545. package/test/unit/test-tiers.test.js +220 -0
  546. package/test/unit/zip-archive.test.js +142 -0
  547. package/vitest.config.js +66 -0
  548. package/.nycrc +0 -9
  549. package/deploy/lib/logging.sh +0 -96
  550. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +0 -48
  551. package/manifests/mongodb/pv-pvc.yaml +0 -59
  552. package/scripts/coverall-test-cyberia.sh +0 -24
  553. package/scripts/coverall-test.sh +0 -24
  554. package/scripts/link-local-underpost-cli.sh +0 -6
  555. package/src/api/cyberia-instance/cyberia-fallback-default-items.js +0 -63
  556. package/src/api/user/user.build.js +0 -16
  557. package/src/cli/env.js +0 -177
  558. package/src/client/components/core/Blockchain.js +0 -41
  559. package/src/client/components/core/Blog.js +0 -9
  560. package/src/client/components/core/KeyboardAvoidance.js +0 -145
  561. package/src/client/components/cyberia/FallbackWorldEngineCyberia.js +0 -368
  562. package/src/client/public/default/assets/background/dark.jpg +0 -0
  563. package/src/client/public/default/assets/background/white0-min.jpg +0 -0
  564. package/src/client/public/default/assets/background/white0.jpg +0 -0
  565. package/src/client/public/doc/favicon.ico +0 -0
  566. package/src/client/public/doc/sitemap +0 -148
  567. package/src/server/downloader.js +0 -73
  568. package/src/server/environment.js +0 -98
@@ -0,0 +1,347 @@
1
+ /**
2
+ * Client bundle transport: enumerates the zip artifacts a deployment's routes build, uploads
3
+ * them to file storage, and restores them into the served public directory.
4
+ *
5
+ * Push and pull are two directions of one contract, so both resolve their routes, bundle ids,
6
+ * storage keys and part files through the helpers here rather than re-deriving them. Route
7
+ * selection mirrors {@link buildClient}: a route that the build skips produces no artifact for
8
+ * either direction to carry.
9
+ * @module src/client-builder/client-bundle.js
10
+ * @namespace clientBundle
11
+ */
12
+
13
+ import fs from 'fs-extra';
14
+ import UnderpostFileStorage from '../cli/fs.js';
15
+ import * as dir from 'path';
16
+ import { cli } from '../server/build/execution.js';
17
+ import { loadConfServerJson, loadReplicas } from '../server/runtime/conf.js';
18
+ import { loggerFactory } from '../server/ops/logger.js';
19
+ import { shellArgumentFactory, shellExec } from '../server/runtime/process.js';
20
+ import { clientBundleIdFactory, getZipPartPaths, mergeClientBuildZip, unzipClientBuild } from './client-build.js';
21
+
22
+ const logger = loggerFactory(import.meta);
23
+
24
+ /** @type {string} Directory `buildClient --build-zip` writes bundles to, and the only place push and pull look. */
25
+ const CLIENT_BUNDLE_DIRECTORY = './build';
26
+
27
+ /** @type {number} Part size in MB applied when the caller names none. */
28
+ const DEFAULT_SPLIT_MB = 8;
29
+
30
+ /** @type {string} `--split` value that turns splitting off. */
31
+ const SPLIT_DISABLED = 'none';
32
+
33
+ const CLIENT_BUNDLE_STORAGE_ID = 'bundle';
34
+
35
+ /**
36
+ * Resolves the conf.server path a deployment's routes are read from.
37
+ * @function clientBundleConfServerPath
38
+ * @param {string} deployId - The deployment identifier.
39
+ * @returns {string} Path to the deployment's `conf.server.json`.
40
+ * @memberof clientBundle
41
+ */
42
+ const clientBundleConfServerPath = (deployId) => `./engine-private/conf/${deployId}/conf.server.json`;
43
+
44
+ /**
45
+ * Resolves the storage manifest that tracks a deployment's uploaded bundle parts. Push writes
46
+ * it and pull reads it, so both must name the same file.
47
+ * @function clientBundleStorageFilePath
48
+ * @param {string} deployId - The deployment identifier.
49
+ * @returns {string} Path to the deployment's bundle storage manifest.
50
+ * @memberof clientBundle
51
+ */
52
+ const clientBundleStorageFilePath = (deployId) =>
53
+ UnderpostFileStorage.API.resolveManifest({ deployId, storageId: CLIENT_BUNDLE_STORAGE_ID });
54
+
55
+ /**
56
+ * Parses the comma-separated host filter both runners accept as their positional argument.
57
+ * @function clientBundleHostFilter
58
+ * @param {string} [hosts] - Comma-separated host names, or empty for every host.
59
+ * @returns {string[]} The requested hosts, empty when no filter was given.
60
+ * @memberof clientBundle
61
+ */
62
+ const clientBundleHostFilter = (hosts = '') =>
63
+ `${hosts ?? ''}`
64
+ .split(',')
65
+ .map((host) => host.trim())
66
+ .filter(Boolean);
67
+
68
+ /**
69
+ * Resolves the `--split` option into the flag `underpost client` expects.
70
+ * @function clientBundleSplitFlag
71
+ * @param {string|number} [split] - Part size in MB, or `none` to disable splitting.
72
+ * @returns {string} The flag to append, empty when splitting is disabled.
73
+ * @memberof clientBundle
74
+ */
75
+ const clientBundleSplitFlag = (split) => {
76
+ const requested = `${split ?? ''}`.trim();
77
+ if (!requested) return `--split ${DEFAULT_SPLIT_MB}`;
78
+ if (requested === SPLIT_DISABLED) return '';
79
+
80
+ const splitMb = Number(requested);
81
+ if (Number.isFinite(splitMb) && splitMb > 0) return `--split ${splitMb}`;
82
+
83
+ logger.warn('invalid split option, falling back to the default', { split: requested, default: DEFAULT_SPLIT_MB });
84
+ return `--split ${DEFAULT_SPLIT_MB}`;
85
+ };
86
+
87
+ /**
88
+ * Enumerates the bundles a deployment's routes build to.
89
+ *
90
+ * A route is skipped exactly where {@link buildClient} skips it — redirects and
91
+ * rebuild-disabled routes emit nothing, and a `singleReplica` route's replicas are built under
92
+ * their own deploy ids, so they carry their own manifests rather than the parent's.
93
+ * @function clientBundleEntriesFactory
94
+ * @param {object} options
95
+ * @param {string} options.deployId - The deployment identifier.
96
+ * @param {string[]} [options.hosts] - Optional host filter; empty selects every configured host.
97
+ * @returns {Array<{host: string, routePath: string, buildId: string, zipPath: string, publicPath: string}>}
98
+ * One entry per bundle, in configuration order.
99
+ * @memberof clientBundle
100
+ */
101
+ const clientBundleEntriesFactory = ({ deployId, hosts = [] }) => {
102
+ const confServerPath = clientBundleConfServerPath(deployId);
103
+ if (!fs.existsSync(confServerPath)) {
104
+ logger.warn('conf.server not found', { deployId, confServerPath });
105
+ return [];
106
+ }
107
+
108
+ const confServer = loadReplicas(deployId, loadConfServerJson(confServerPath));
109
+ const selectedHosts = hosts.length > 0 ? hosts : Object.keys(confServer);
110
+
111
+ return selectedHosts.flatMap((host) => {
112
+ if (!confServer[host]) {
113
+ logger.warn('host not present in conf.server', { deployId, host });
114
+ return [];
115
+ }
116
+ return Object.keys(confServer[host])
117
+ .filter((routePath) => {
118
+ const { redirect, disabledRebuild, singleReplica } = confServer[host][routePath] ?? {};
119
+ return !redirect && !disabledRebuild && !singleReplica;
120
+ })
121
+ .map((routePath) => {
122
+ const buildId = clientBundleIdFactory(host, routePath);
123
+ return {
124
+ host,
125
+ routePath,
126
+ buildId,
127
+ zipPath: `${CLIENT_BUNDLE_DIRECTORY}/${buildId}.zip`,
128
+ publicPath: routePath === '/' ? `public/${host}` : `public/${host}${routePath}`,
129
+ };
130
+ });
131
+ });
132
+ };
133
+
134
+ /**
135
+ * Reads the storage manifest as a plain lookup, for the recorded size of each uploaded artifact.
136
+ * Read-only and tolerant: a manifest that is absent or unreadable disables verification rather
137
+ * than failing the pull, since the artifacts themselves are still what the bundle is made of.
138
+ * @function clientBundleManifestFactory
139
+ * @param {string} deployId - The deployment identifier.
140
+ * @returns {Object<string, {bytes?: number}>} The tracked entries, empty when unavailable.
141
+ * @memberof clientBundle
142
+ */
143
+ const clientBundleManifestFactory = (deployId) => {
144
+ const manifestPath = clientBundleStorageFilePath(deployId);
145
+ try {
146
+ return UnderpostFileStorage.API.readManifest({ deployId, storageId: CLIENT_BUNDLE_STORAGE_ID }).storage;
147
+ } catch (error) {
148
+ logger.warn('bundle manifest unreadable, size verification skipped', { manifestPath, error: error?.message });
149
+ return {};
150
+ }
151
+ };
152
+
153
+ /**
154
+ * The tracked keys that belong to one bundle.
155
+ *
156
+ * A bundle owns its zip and every split part of it, and nothing else: the id already carries the
157
+ * route, so `underpost.net-` and `underpost.net-peer` cannot claim each other's parts.
158
+ * @function clientBundleTrackedKeys
159
+ * @param {object} manifest - The bundle storage manifest.
160
+ * @param {string} buildId - The bundle's build id.
161
+ * @returns {string[]} The manifest keys for that bundle.
162
+ * @memberof clientBundle
163
+ */
164
+ const clientBundleTrackedKeys = (manifest, buildId) =>
165
+ Object.keys(manifest).filter((key) =>
166
+ key.replace(/^\.\//, '').startsWith(`${CLIENT_BUNDLE_DIRECTORY.replace(/^\.\//, '')}/${buildId}.zip`),
167
+ );
168
+
169
+ /**
170
+ * Lists the local artifacts that make up one bundle: its split parts when the build split it,
171
+ * otherwise the single zip.
172
+ * @function clientBundleArtifactPaths
173
+ * @param {string} zipPath - The bundle's zip path.
174
+ * @returns {{zipPath: string, partPaths: string[], artifactPaths: string[]}} The resolved artifacts.
175
+ * @memberof clientBundle
176
+ */
177
+ const clientBundleArtifactPaths = (zipPath) => {
178
+ const partPaths = fs.existsSync(dir.dirname(zipPath)) ? getZipPartPaths(zipPath) : [];
179
+ const artifactPaths = partPaths.length > 0 ? partPaths : fs.existsSync(zipPath) ? [zipPath] : [];
180
+ return { zipPath, partPaths, artifactPaths };
181
+ };
182
+
183
+ /**
184
+ * Builds a deployment's client bundles and uploads them to file storage.
185
+ *
186
+ * The build and the uploads run as child processes because `underpost app load` writes the
187
+ * `.env` this deployment's credentials live in, and only a freshly started process reads it.
188
+ * @function pushClientBundle
189
+ * @param {object} options
190
+ * @param {string} options.deployId - The deployment identifier.
191
+ * @param {string[]} [options.hosts] - Optional host filter; empty selects every configured host.
192
+ * @param {string|number} [options.split] - Part size in MB, or `none` to upload one zip.
193
+ * @param {boolean} [options.dev] - Build for development; defaults to production.
194
+ * @returns {{pushed: number, skipped: number}} How many bundles were uploaded and skipped.
195
+ * @memberof clientBundle
196
+ */
197
+ const pushClientBundle = ({ deployId, hosts = [], split, dev = false }) => {
198
+ const baseCommand = cli('underpost', { local: true });
199
+ const env = dev ? 'development' : 'production';
200
+ const splitFlag = clientBundleSplitFlag(split);
201
+
202
+ shellExec(`${baseCommand} app load --env ${env} --args deploy-id=${deployId}`);
203
+ shellExec(`${baseCommand} client ${deployId} --env ${env} --build-zip${splitFlag ? ` ${splitFlag}` : ''}`);
204
+
205
+ const entries = clientBundleEntriesFactory({ deployId, hosts });
206
+ if (entries.length === 0) logger.warn('no bundle routes resolved', { deployId, hosts });
207
+
208
+ let pushed = 0;
209
+ let skipped = 0;
210
+ for (const { host, routePath, buildId, zipPath } of entries) {
211
+ const { artifactPaths } = clientBundleArtifactPaths(zipPath);
212
+ if (artifactPaths.length === 0) {
213
+ logger.warn('no bundle artifacts found', { host, routePath, buildId });
214
+ skipped++;
215
+ continue;
216
+ }
217
+ for (const artifactPath of artifactPaths) {
218
+ shellExec(
219
+ `${baseCommand} fs ${artifactPath} --deploy-id ${deployId} --storage-id ${CLIENT_BUNDLE_STORAGE_ID} --force`,
220
+ );
221
+ }
222
+
223
+ // Remove obsolete manifest entries only after all current artifacts upload.
224
+ const current = new Set(artifactPaths.map((artifactPath) => artifactPath.replace(/^\.\//, '')));
225
+ const stale = clientBundleTrackedKeys(clientBundleManifestFactory(deployId), buildId).filter(
226
+ (key) => !current.has(key.replace(/^\.\//, '')),
227
+ );
228
+ for (const key of stale) {
229
+ shellExec(
230
+ `${baseCommand} fs --tracked --key ${shellArgumentFactory(key)} ` +
231
+ `--deploy-id ${deployId} --storage-id ${CLIENT_BUNDLE_STORAGE_ID} --rm`,
232
+ );
233
+ }
234
+ if (stale.length > 0) logger.warn('removed bundle parts a smaller build no longer produces', { buildId, stale });
235
+
236
+ logger.info('pushed bundle', { host, routePath, artifacts: artifactPaths.length });
237
+ pushed++;
238
+ }
239
+ return { pushed, skipped };
240
+ };
241
+
242
+ /**
243
+ * Downloads a deployment's client bundles from file storage and installs them into the served
244
+ * public directory.
245
+ *
246
+ * Merging and extraction run in-process through the build module; only the download needs a
247
+ * child process, for the same `.env` reason {@link pushClientBundle} documents.
248
+ * @function pullClientBundle
249
+ * @param {object} options
250
+ * @param {string} options.deployId - The deployment identifier.
251
+ * @param {string[]} [options.hosts] - Optional host filter; empty selects every configured host.
252
+ * @param {boolean} [options.dev] - Pull the development environment; defaults to production.
253
+ * @returns {Promise<{pulled: number, skipped: number}>} How many bundles were installed and skipped.
254
+ * @memberof clientBundle
255
+ */
256
+ const expectedBundleBytes = ({ manifest, partPaths }) => {
257
+ const sizes = partPaths.map((partPath) => manifest[clientBundleManifestKey(partPath)]?.bytes);
258
+ return sizes.every((bytes) => Number.isFinite(bytes)) ? sizes.reduce((total, bytes) => total + bytes, 0) : undefined;
259
+ };
260
+
261
+ /**
262
+ * The manifest key a locally pulled part corresponds to. A pull writes the downloaded archive as
263
+ * `<key>.zip`, so the wrapper suffix is what has to come off to address the entry that produced it.
264
+ * @function clientBundleManifestKey
265
+ * @param {string} partPath - Local path of a pulled part.
266
+ * @returns {string} The tracked storage key.
267
+ * @memberof clientBundle
268
+ */
269
+ const clientBundleManifestKey = (partPath) => partPath.replace(/^\.\//, '').replace(/\.zip$/i, '');
270
+
271
+ const pullClientBundle = async ({ deployId, hosts = [], dev = false }) => {
272
+ const baseCommand = cli('underpost', { local: true });
273
+ const env = dev ? 'development' : 'production';
274
+
275
+ const entries = clientBundleEntriesFactory({ deployId, hosts });
276
+ if (entries.length === 0) {
277
+ logger.error('no bundle routes resolved', { deployId, hosts });
278
+ return { pulled: 0, skipped: 0 };
279
+ }
280
+
281
+ shellExec(`${baseCommand} app load --env ${env} --args deploy-id=${deployId}`);
282
+ fs.mkdirSync(CLIENT_BUNDLE_DIRECTORY, { recursive: true });
283
+ shellExec(
284
+ `${baseCommand} fs ${CLIENT_BUNDLE_DIRECTORY.replace(/^\.\//, '')} --tracked --deploy-id ${deployId} ` +
285
+ `--storage-id ${CLIENT_BUNDLE_STORAGE_ID} --pull --omit-unzip`,
286
+ );
287
+
288
+ const manifest = clientBundleManifestFactory(deployId);
289
+
290
+ let pulled = 0;
291
+ let skipped = 0;
292
+ for (const { host, routePath, buildId, zipPath, publicPath } of entries) {
293
+ const { partPaths, artifactPaths } = clientBundleArtifactPaths(zipPath);
294
+ if (artifactPaths.length === 0) {
295
+ logger.warn('bundle not found, skipping', { host, routePath, buildId, zipPath });
296
+ skipped++;
297
+ continue;
298
+ }
299
+
300
+ if (partPaths.length > 0) {
301
+ const { mergedBytes } = await mergeClientBuildZip({ buildPrefix: zipPath, logger });
302
+ // The parts are reassembled blind, so a single stale or short one is only visible as a
303
+ // corrupt archive far downstream. The pushed sizes are what identify it here instead.
304
+ const expectedBytes = expectedBundleBytes({ manifest, partPaths });
305
+ if (expectedBytes !== undefined && expectedBytes !== mergedBytes)
306
+ throw new Error(
307
+ `Bundle ${buildId} reassembled to ${mergedBytes} bytes, expected ${expectedBytes} from ${partPaths.length} pushed parts`,
308
+ );
309
+ }
310
+ const { outputPath } = await unzipClientBuild({ buildPrefix: zipPath, logger });
311
+
312
+ fs.removeSync(zipPath);
313
+ for (const partPath of partPaths) fs.removeSync(partPath);
314
+
315
+ if (!fs.existsSync(outputPath)) {
316
+ logger.warn('extracted bundle directory not found, skipping', { host, routePath, outputPath });
317
+ skipped++;
318
+ continue;
319
+ }
320
+
321
+ // The previous deployment's document root can be owned by the web server user, so the
322
+ // replace goes through sudo; the extracted tree this process just wrote does not.
323
+ shellExec(`sudo rm -rf ${publicPath}`);
324
+ if (routePath !== '/') shellExec(`sudo mkdir -p public/${host}`);
325
+ fs.copySync(outputPath, publicPath);
326
+
327
+ logger.info('pulled bundle', { host, routePath, publicPath });
328
+ pulled++;
329
+ }
330
+ return { pulled, skipped };
331
+ };
332
+
333
+ export {
334
+ CLIENT_BUNDLE_DIRECTORY,
335
+ clientBundleTrackedKeys,
336
+ clientBundleManifestFactory,
337
+ clientBundleManifestKey,
338
+ DEFAULT_SPLIT_MB,
339
+ clientBundleArtifactPaths,
340
+ clientBundleConfServerPath,
341
+ clientBundleEntriesFactory,
342
+ clientBundleHostFilter,
343
+ clientBundleSplitFlag,
344
+ clientBundleStorageFilePath,
345
+ pullClientBundle,
346
+ pushClientBundle,
347
+ };
@@ -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
  }