@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,402 @@
1
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
2
+ import { Types } from 'mongoose';
3
+
4
+ const models = {};
5
+
6
+ vi.mock('../../../../src/db/DataBaseProvider.js', () => ({
7
+ DataBaseProviderService: { getModel: (name) => models[name] },
8
+ }));
9
+ vi.mock('../../../../src/api/ipfs/ipfs.service.js', () => ({ createPinRecord: async () => ({}) }));
10
+ vi.mock('../../../../src/projects/cyberia/ipfs-client.js', () => ({
11
+ IpfsClient: { addBufferToIpfs: async () => null, addJsonToIpfs: async () => null },
12
+ }));
13
+
14
+ const { AtlasSpriteSheetStore } = await import('../../../../src/projects/cyberia/atlas-sprite-sheet-store.js');
15
+
16
+ const colors = [
17
+ [255, 0, 0, 255],
18
+ [0, 255, 0, 255],
19
+ ];
20
+ const frame = [
21
+ [0, 1],
22
+ [null, 0],
23
+ ];
24
+ const renderFrames = (frames) => ({ colors, frame_duration: 250, frames });
25
+ const twoFrames = renderFrames({ down_idle: [frame], up_idle: [frame] });
26
+
27
+ /** Minimal File collection: the store only upserts, reads and bulk-deletes by _id. */
28
+ class FakeFiles {
29
+ constructor() {
30
+ this.docs = new Map();
31
+ }
32
+ async updateOne({ _id }, { $setOnInsert }) {
33
+ const key = String(_id);
34
+ if (!this.docs.has(key)) this.docs.set(key, { ...$setOnInsert });
35
+ return { acknowledged: true };
36
+ }
37
+ async deleteMany({ _id: { $in } }) {
38
+ for (const id of $in) this.docs.delete(String(id));
39
+ return { deletedCount: $in.length };
40
+ }
41
+ async findById(id) {
42
+ return this.docs.get(String(id)) ?? null;
43
+ }
44
+ get size() {
45
+ return this.docs.size;
46
+ }
47
+ has(id) {
48
+ return this.docs.has(String(id));
49
+ }
50
+ }
51
+
52
+ /** Minimal AtlasSpriteSheet collection keyed by metadata.itemKey. */
53
+ class FakeAtlases {
54
+ constructor() {
55
+ this.doc = null;
56
+ }
57
+ findOne() {
58
+ return Promise.resolve(this.doc);
59
+ }
60
+ async updateOne(_filter, { $set }) {
61
+ Object.assign(this.doc, $set);
62
+ return { acknowledged: true };
63
+ }
64
+ seed(fields) {
65
+ this.doc = {
66
+ _id: new Types.ObjectId(),
67
+ ...fields,
68
+ set(next) {
69
+ Object.assign(this, next);
70
+ },
71
+ save: async () => this.doc,
72
+ };
73
+ return this.doc;
74
+ }
75
+ }
76
+
77
+ const newAtlasModel = (files) => {
78
+ const atlases = new FakeAtlases();
79
+ function AtlasSpriteSheet(fields) {
80
+ Object.assign(this, fields);
81
+ this.set = (next) => Object.assign(this, next);
82
+ this.save = async () => {
83
+ atlases.doc = this;
84
+ return this;
85
+ };
86
+ }
87
+ AtlasSpriteSheet.findOne = () => atlases.findOne();
88
+ AtlasSpriteSheet.updateOne = (filter, update) => atlases.updateOne(filter, update);
89
+ AtlasSpriteSheet.store = atlases;
90
+ models.AtlasSpriteSheet = AtlasSpriteSheet;
91
+ models.File = files;
92
+ return AtlasSpriteSheet;
93
+ };
94
+
95
+ describe('atlas sprite sheet store', () => {
96
+ let files;
97
+
98
+ beforeEach(() => {
99
+ files = new FakeFiles();
100
+ newAtlasModel(files);
101
+ });
102
+
103
+ it('stores both renders, the idle still, and describes the minified one', async () => {
104
+ const { atlasDoc } = await AtlasSpriteSheetStore.persist({
105
+ itemKey: 'hatchet',
106
+ objectLayerRenderFrames: twoFrames,
107
+ upscaleFactor: 20,
108
+ });
109
+
110
+ expect(atlasDoc.metadata.cellPixelDim).toBe(1);
111
+ expect(atlasDoc.metadata.upscaleFactor).toBe(20);
112
+ const ids = [atlasDoc.fileId, atlasDoc.minifyFileId, atlasDoc.idlePreviewFileId].map(String);
113
+ expect(new Set(ids).size).toBe(3);
114
+ expect(files.size).toBe(3);
115
+ for (const id of ids) expect(files.has(id)).toBe(true);
116
+ });
117
+
118
+ it('leaves the same item untouched when persist reruns on unchanged frames', async () => {
119
+ const renders = (doc) => [doc.fileId, doc.minifyFileId, doc.idlePreviewFileId].map(String);
120
+ const first = await AtlasSpriteSheetStore.persist({ itemKey: 'hatchet', objectLayerRenderFrames: twoFrames });
121
+
122
+ const second = await AtlasSpriteSheetStore.persist({ itemKey: 'hatchet', objectLayerRenderFrames: twoFrames });
123
+
124
+ expect(renders(second.atlasDoc)).toEqual(renders(first.atlasDoc));
125
+ expect(files.size).toBe(3);
126
+ });
127
+
128
+ it('deletes the File it replaces when the upscale factor changes', async () => {
129
+ const first = await AtlasSpriteSheetStore.persist({
130
+ itemKey: 'hatchet',
131
+ objectLayerRenderFrames: twoFrames,
132
+ upscaleFactor: 4,
133
+ });
134
+ const firstFileId = String(first.atlasDoc.fileId);
135
+ const firstMinifyFileId = String(first.atlasDoc.minifyFileId);
136
+ const firstIdlePreviewFileId = String(first.atlasDoc.idlePreviewFileId);
137
+
138
+ const second = await AtlasSpriteSheetStore.persist({
139
+ itemKey: 'hatchet',
140
+ objectLayerRenderFrames: twoFrames,
141
+ upscaleFactor: 8,
142
+ });
143
+
144
+ expect(String(second.atlasDoc.fileId)).not.toBe(firstFileId);
145
+ expect(files.has(firstFileId)).toBe(false);
146
+ // The minified render does not move with the upscale factor; the still, cut from the
147
+ // human-resolution render, does.
148
+ expect(String(second.atlasDoc.minifyFileId)).toBe(firstMinifyFileId);
149
+ expect(String(second.atlasDoc.idlePreviewFileId)).not.toBe(firstIdlePreviewFileId);
150
+ expect(files.has(firstIdlePreviewFileId)).toBe(false);
151
+ expect(files.size).toBe(3);
152
+ });
153
+
154
+ it('cuts the still from the down-idle frame, and from any frame when there is none', async () => {
155
+ const { atlasDoc } = await AtlasSpriteSheetStore.persist({
156
+ itemKey: 'hatchet',
157
+ objectLayerRenderFrames: twoFrames,
158
+ });
159
+ expect(atlasDoc.idlePreviewFileId).toBeTruthy();
160
+
161
+ const onlyUp = renderFrames({ up_idle: [frame] });
162
+ const { atlasDoc: upOnly } = await AtlasSpriteSheetStore.persist({
163
+ itemKey: 'hatchet',
164
+ objectLayerRenderFrames: onlyUp,
165
+ });
166
+ expect(upOnly.idlePreviewFileId).toBeTruthy();
167
+ });
168
+
169
+ it('fills a missing still from the render the atlas already holds, then is a no-op', async () => {
170
+ const atlases = models.AtlasSpriteSheet.store;
171
+ await AtlasSpriteSheetStore.persist({ itemKey: 'hatchet', objectLayerRenderFrames: twoFrames });
172
+ const expected = String(atlases.doc.idlePreviewFileId);
173
+ // An atlas written before the still existed: the field is empty and the File is gone.
174
+ await models.AtlasSpriteSheet.updateOne({}, { $set: { idlePreviewFileId: null } });
175
+ files.docs.delete(expected);
176
+
177
+ const filled = await AtlasSpriteSheetStore.syncIdlePreview({ itemKey: 'hatchet' });
178
+ expect(filled.status).toBe('updated');
179
+ expect(String(atlases.doc.idlePreviewFileId)).toBe(expected);
180
+ expect(files.has(expected)).toBe(true);
181
+
182
+ const again = await AtlasSpriteSheetStore.syncIdlePreview({ itemKey: 'hatchet' });
183
+ expect(again.status).toBe('unchanged');
184
+ expect(files.size).toBe(3);
185
+ });
186
+
187
+ it('reports a missing atlas or render instead of inventing a still', async () => {
188
+ expect((await AtlasSpriteSheetStore.syncIdlePreview({ itemKey: 'nothing' })).status).toBe('missing');
189
+ });
190
+
191
+ it('reports a missing atlas instead of creating one', async () => {
192
+ const result = await AtlasSpriteSheetStore.syncMinifyRender({
193
+ itemKey: 'ghost',
194
+ objectLayerRenderFrames: twoFrames,
195
+ });
196
+ expect(result).toEqual({ status: 'missing' });
197
+ expect(files.size).toBe(0);
198
+ });
199
+
200
+ it('fills a missing minified render and touches no other attribute', async () => {
201
+ const atlases = models.AtlasSpriteSheet.store;
202
+ await AtlasSpriteSheetStore.persist({ itemKey: 'hatchet', objectLayerRenderFrames: twoFrames });
203
+ const fileIdBefore = atlases.doc.fileId;
204
+ const metadataBefore = atlases.doc.metadata;
205
+
206
+ await models.AtlasSpriteSheet.updateOne({}, { $set: { minifyFileId: null } });
207
+ files.docs.delete(String(atlases.doc.minifyFileId));
208
+
209
+ const result = await AtlasSpriteSheetStore.syncMinifyRender({
210
+ itemKey: 'hatchet',
211
+ objectLayerRenderFrames: twoFrames,
212
+ });
213
+
214
+ expect(result.status).toBe('updated');
215
+ expect(atlases.doc.minifyFileId).toBeTruthy();
216
+ expect(atlases.doc.fileId).toBe(fileIdBefore);
217
+ expect(atlases.doc.metadata).toBe(metadataBefore);
218
+ });
219
+
220
+ it('is a no-op when the minified render already matches', async () => {
221
+ const atlases = models.AtlasSpriteSheet.store;
222
+ await AtlasSpriteSheetStore.persist({ itemKey: 'hatchet', objectLayerRenderFrames: twoFrames });
223
+ const before = atlases.doc.minifyFileId;
224
+
225
+ const result = await AtlasSpriteSheetStore.syncMinifyRender({
226
+ itemKey: 'hatchet',
227
+ objectLayerRenderFrames: twoFrames,
228
+ });
229
+
230
+ expect(result.status).toBe('unchanged');
231
+ expect(String(atlases.doc.minifyFileId)).toBe(String(before));
232
+ expect(files.size).toBe(3);
233
+ });
234
+
235
+ it('refuses to write when the render frames moved the atlas layout', async () => {
236
+ const atlases = models.AtlasSpriteSheet.store;
237
+ await AtlasSpriteSheetStore.persist({ itemKey: 'hatchet', objectLayerRenderFrames: twoFrames });
238
+ const before = atlases.doc.minifyFileId;
239
+
240
+ const result = await AtlasSpriteSheetStore.syncMinifyRender({
241
+ itemKey: 'hatchet',
242
+ objectLayerRenderFrames: renderFrames({ down_idle: [frame, frame, frame], up_idle: [frame] }),
243
+ });
244
+
245
+ expect(result).toEqual({ status: 'stale' });
246
+ expect(String(atlases.doc.minifyFileId)).toBe(String(before));
247
+ });
248
+ });
249
+
250
+ /**
251
+ * Multi-document collections, because a drop is about more than one item. Supports the shapes the
252
+ * store queries with: `$or`, `$in`, `$regex` and the `metadata.itemKey` dot path.
253
+ */
254
+ const fakeCollection = (docs) => {
255
+ const read = (doc, path) => path.split('.').reduce((value, part) => value?.[part], doc);
256
+ const matches = (doc, filter) => {
257
+ if (filter.$or) return filter.$or.some((clause) => matches(doc, clause));
258
+ return Object.entries(filter).every(([field, condition]) => {
259
+ const value = read(doc, field);
260
+ if (condition && typeof condition === 'object' && '$in' in condition)
261
+ return value !== undefined && value !== null && condition.$in.map(String).includes(String(value));
262
+ if (condition && typeof condition === 'object' && '$regex' in condition)
263
+ return typeof value === 'string' && condition.$regex.test(value);
264
+ return String(value) === String(condition);
265
+ });
266
+ };
267
+ return {
268
+ get docs() {
269
+ return docs;
270
+ },
271
+ find(filter) {
272
+ return { lean: async () => docs.filter((doc) => matches(doc, filter)) };
273
+ },
274
+ async deleteMany(filter = {}) {
275
+ const removing = new Set(docs.filter((doc) => matches(doc, filter)).map((doc) => String(doc._id)));
276
+ const before = docs.length;
277
+ docs = docs.filter((doc) => !removing.has(String(doc._id)));
278
+ return { deletedCount: before - docs.length };
279
+ },
280
+ };
281
+ };
282
+
283
+ const atlasDoc = (itemKey, suffix) => ({
284
+ _id: new Types.ObjectId(),
285
+ cid: `cid-${itemKey}`,
286
+ fileId: `${itemKey}-atlas-${suffix}`,
287
+ minifyFileId: `${itemKey}-minify-${suffix}`,
288
+ idlePreviewFileId: `${itemKey}-idle-${suffix}`,
289
+ metadata: { itemKey },
290
+ });
291
+
292
+ const renderFile = (id, itemKey, role) => ({ _id: id, name: `${itemKey}-${role}.png` });
293
+
294
+ describe('purging an atlas', () => {
295
+ let atlases;
296
+ let files;
297
+
298
+ beforeEach(() => {
299
+ atlases = fakeCollection([atlasDoc('hatchet', 1), atlasDoc('sword', 1)]);
300
+ files = fakeCollection([
301
+ renderFile('hatchet-atlas-1', 'hatchet', 'atlas'),
302
+ renderFile('hatchet-minify-1', 'hatchet', 'minify'),
303
+ renderFile('hatchet-idle-1', 'hatchet', 'idle'),
304
+ renderFile('sword-atlas-1', 'sword', 'atlas'),
305
+ renderFile('sword-minify-1', 'sword', 'minify'),
306
+ renderFile('sword-idle-1', 'sword', 'idle'),
307
+ ]);
308
+ models.AtlasSpriteSheet = atlases;
309
+ models.File = files;
310
+ });
311
+
312
+ it('takes every render of the item, not only the human-resolution one', async () => {
313
+ // Regression: the CLI drop named `fileId` alone, so every minified render the client
314
+ // downloads stayed in the File collection with nothing left pointing at it.
315
+ const result = await AtlasSpriteSheetStore.purge({ itemKeys: ['hatchet'] });
316
+
317
+ expect(result).toEqual({ atlases: 1, files: 3, cids: ['cid-hatchet'] });
318
+ expect(files.docs.map((doc) => doc._id)).toEqual(['sword-atlas-1', 'sword-minify-1', 'sword-idle-1']);
319
+ expect(atlases.docs.map((doc) => doc.metadata.itemKey)).toEqual(['sword']);
320
+ });
321
+
322
+ it('drops an atlas selected by its document id as readily as by its item key', async () => {
323
+ const linked = atlases.docs.find((doc) => doc.metadata.itemKey === 'sword');
324
+
325
+ const result = await AtlasSpriteSheetStore.purge({ atlasIds: [linked._id] });
326
+
327
+ expect(result.atlases).toBe(1);
328
+ expect(result.files).toBe(3);
329
+ expect(atlases.docs).toHaveLength(1);
330
+ });
331
+
332
+ it('empties the collection when asked for all of it', async () => {
333
+ const result = await AtlasSpriteSheetStore.purge({ all: true });
334
+
335
+ expect(result.atlases).toBe(2);
336
+ expect(result.files).toBe(6);
337
+ expect(files.docs).toHaveLength(0);
338
+ });
339
+
340
+ it('deletes nothing when no selector is given', async () => {
341
+ expect(await AtlasSpriteSheetStore.purge({})).toEqual({ atlases: 0, files: 0, cids: [] });
342
+ expect(atlases.docs).toHaveLength(2);
343
+ expect(files.docs).toHaveLength(6);
344
+ });
345
+
346
+ it('reports zeros on a rerun instead of failing', async () => {
347
+ await AtlasSpriteSheetStore.purge({ itemKeys: ['hatchet'] });
348
+ expect(await AtlasSpriteSheetStore.purge({ itemKeys: ['hatchet'] })).toEqual({
349
+ atlases: 0,
350
+ files: 0,
351
+ cids: [],
352
+ });
353
+ });
354
+
355
+ it('keeps a render a second atlas of the same item still points at', async () => {
356
+ atlases.docs.push({ ...atlasDoc('hatchet', 1), _id: new Types.ObjectId() });
357
+ const first = atlases.docs.find((doc) => doc.metadata.itemKey === 'hatchet');
358
+
359
+ const result = await AtlasSpriteSheetStore.purge({ atlasIds: [first._id] });
360
+
361
+ expect(result.files).toBe(0);
362
+ expect(files.docs.map((doc) => doc._id)).toContain('hatchet-minify-1');
363
+ });
364
+ });
365
+
366
+ describe('pruning renders left behind by an earlier drop', () => {
367
+ it('removes the renders no atlas points at and keeps the ones in use', async () => {
368
+ const atlases = fakeCollection([atlasDoc('sword', 1)]);
369
+ const files = fakeCollection([
370
+ renderFile('hatchet-minify-1', 'hatchet', 'minify'),
371
+ renderFile('hatchet-idle-1', 'hatchet', 'idle'),
372
+ renderFile('sword-atlas-1', 'sword', 'atlas'),
373
+ renderFile('sword-minify-1', 'sword', 'minify'),
374
+ renderFile('sword-idle-1', 'sword', 'idle'),
375
+ { _id: 'thumbnail-1', name: 'map-thumbnail.png' },
376
+ ]);
377
+ models.AtlasSpriteSheet = atlases;
378
+ models.File = files;
379
+
380
+ const removed = await AtlasSpriteSheetStore.pruneOrphanRenders({});
381
+
382
+ expect(removed).toBe(2);
383
+ expect(files.docs.map((doc) => doc._id)).toEqual([
384
+ 'sword-atlas-1',
385
+ 'sword-minify-1',
386
+ 'sword-idle-1',
387
+ 'thumbnail-1',
388
+ ]);
389
+ });
390
+
391
+ it('is a no-op on a collection that leaked nothing', async () => {
392
+ models.AtlasSpriteSheet = fakeCollection([atlasDoc('sword', 1)]);
393
+ models.File = fakeCollection([
394
+ renderFile('sword-atlas-1', 'sword', 'atlas'),
395
+ renderFile('sword-minify-1', 'sword', 'minify'),
396
+ renderFile('sword-idle-1', 'sword', 'idle'),
397
+ ]);
398
+
399
+ expect(await AtlasSpriteSheetStore.pruneOrphanRenders({})).toBe(0);
400
+ expect(models.File.docs).toHaveLength(3);
401
+ });
402
+ });
@@ -0,0 +1,86 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { Jimp } from 'jimp';
3
+ import { AtlasSpriteSheetGenerator as Atlas } from '../../../../src/projects/cyberia/atlas-sprite-sheet-generator.js';
4
+
5
+ const colors = [
6
+ [255, 0, 0, 255],
7
+ [0, 255, 0, 255],
8
+ ];
9
+ const frame = [
10
+ [0, 1],
11
+ [null, 0],
12
+ ];
13
+
14
+ describe('Cyberia atlas generation', () => {
15
+ it('keeps source pixels and frame positions without square padding', async () => {
16
+ const source = { colors, frames: { down_idle: [frame], up_idle: [frame], down_walking: [frame] } };
17
+ const { minifyBuffer, metadata } = await Atlas.generateAtlas(source, 'test');
18
+ const image = await Jimp.read(minifyBuffer);
19
+ expect(metadata.cellPixelDim).toBe(1);
20
+ expect([image.bitmap.width, image.bitmap.height]).toEqual([4, 4]);
21
+ for (const frames of Object.values(metadata.frames)) {
22
+ const { x, y, width, height } = frames[0];
23
+ expect([width, height]).toEqual([2, 2]);
24
+ expect(image.getPixelColor(x, y)).toBe(0xff0000ff);
25
+ expect(image.getPixelColor(x + 1, y)).toBe(0x00ff00ff);
26
+ expect(image.getPixelColor(x, y + 1)).toBe(0);
27
+ }
28
+ });
29
+
30
+ it('fits 26 character frames without scaling each source pixel', async () => {
31
+ const pixels = Array.from({ length: 25 }, () => Array(25).fill(0));
32
+ const { minifyBuffer, metadata } = await Atlas.generateAtlas(
33
+ { colors, frames: { down_idle: Array(26).fill(pixels) } },
34
+ 'character',
35
+ 1,
36
+ );
37
+ expect([metadata.atlasWidth, metadata.atlasHeight]).toEqual([250, 75]);
38
+ expect((await Jimp.read(minifyBuffer)).bitmap.data.byteLength).toBe(75000);
39
+ expect(metadata.frames.down_idle).toHaveLength(26);
40
+ });
41
+
42
+ it('describes the minified render, whatever the upscale factor', async () => {
43
+ const source = { colors, frames: { down_idle: [frame], up_idle: [frame] } };
44
+ const { metadata } = await Atlas.generateAtlas(source, 'scaled', 20);
45
+ expect(metadata.cellPixelDim).toBe(1);
46
+ expect(metadata.upscaleFactor).toBe(20);
47
+ expect(metadata.frames.down_idle[0].width).toBe(2);
48
+ });
49
+
50
+ it('renders the upscaled atlas as an exact multiple of the minified one', async () => {
51
+ const source = { colors, frames: { down_idle: [frame], up_idle: [frame] } };
52
+ const { buffer, minifyBuffer, metadata } = await Atlas.generateAtlas(source, 'scaled', 3);
53
+ const minified = await Jimp.read(minifyBuffer);
54
+ const upscaled = await Jimp.read(buffer);
55
+
56
+ expect([upscaled.bitmap.width, upscaled.bitmap.height]).toEqual([
57
+ metadata.atlasWidth * 3,
58
+ metadata.atlasHeight * 3,
59
+ ]);
60
+ for (let y = 0; y < minified.bitmap.height; y++) {
61
+ for (let x = 0; x < minified.bitmap.width; x++) {
62
+ expect(upscaled.getPixelColor(x * 3, y * 3)).toBe(minified.getPixelColor(x, y));
63
+ expect(upscaled.getPixelColor(x * 3 + 2, y * 3 + 2)).toBe(minified.getPixelColor(x, y));
64
+ }
65
+ }
66
+ });
67
+
68
+ it('rejects clipped frames and invalid dimensions', async () => {
69
+ expect(() => Atlas.packFramesGrid([{ width: 5, height: 1 }], 4)).toThrow('exceed');
70
+ expect(() =>
71
+ Atlas.packFramesGrid(
72
+ [
73
+ { width: 4, height: 3 },
74
+ { width: 4, height: 3 },
75
+ ],
76
+ 4,
77
+ ),
78
+ ).toThrow('exceed');
79
+ await expect(Atlas.generateAtlas({ colors, frames: { down_idle: [frame] } }, 'invalid', 0)).rejects.toThrow(
80
+ 'pixel scale',
81
+ );
82
+ await expect(Atlas.generateAtlas({ colors, frames: { down_idle: [frame] } }, 'invalid', 1, 8192)).rejects.toThrow(
83
+ '4096',
84
+ );
85
+ });
86
+ });
@@ -0,0 +1,64 @@
1
+ 'use strict';
2
+
3
+ import { expect } from 'chai';
4
+ import { execFileSync } from 'node:child_process';
5
+ import fs from 'fs-extra';
6
+
7
+ // The reroute notice is diagnostic output, so it must not reach stdout: a caller reading a value
8
+ // out of `--plain` would otherwise parse the banner as the value. Spawning the real CLI is the
9
+ // only way to observe the two streams apart, and it belongs to this tier because the cyberia CLI
10
+ // boots on the native packages the product manifest pins — repositories that only run
11
+ // `unit,infra,app` never install them.
12
+ describe('rerouted plain reads stay machine-readable', () => {
13
+ const CYBERIA_CLI = 'bin/cyberia.js';
14
+ const repoRoot = new URL('../../../../', import.meta.url);
15
+
16
+ it.skipIf(!fs.existsSync(new URL(CYBERIA_CLI, repoRoot)))(
17
+ 'prints nothing for a key the store lacks',
18
+ () => {
19
+ const stdout = execFileSync(
20
+ process.execPath,
21
+ [CYBERIA_CLI, 'host', 'get', '--plain', 'UNDERPOST_TEST_MISSING_KEY'],
22
+ { cwd: repoRoot, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] },
23
+ );
24
+ expect(stdout.trim()).to.equal('');
25
+ // A cold CLI start imports the whole command surface; the budget is for the import, not the
26
+ // read, and is stated here rather than left to the runner default a loaded machine exceeds.
27
+ },
28
+ 60000,
29
+ );
30
+
31
+ it.skipIf(!fs.existsSync(new URL(CYBERIA_CLI, repoRoot)))(
32
+ 'keeps an argument whose value is the CLI name through the reroute',
33
+ () => {
34
+ // Regression: the reroute dropped every argv token equal to `underpost`, so an option value
35
+ // that happens to be the CLI name was removed and the parse failed on a missing argument.
36
+ // A redundant name can only precede the command; everything after it is an argument.
37
+ let output = '';
38
+ try {
39
+ output = execFileSync(
40
+ process.execPath,
41
+ [
42
+ CYBERIA_CLI,
43
+ 'fs',
44
+ 'src/client/public/underpost',
45
+ '--deploy-id',
46
+ 'dd-cyberia',
47
+ '--pull',
48
+ '--tracked',
49
+ '--storage-id',
50
+ 'underpost',
51
+ ],
52
+ { cwd: repoRoot, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] },
53
+ );
54
+ } catch (error) {
55
+ output = `${error.stdout ?? ''}${error.stderr ?? ''}`;
56
+ }
57
+
58
+ // Whatever the run goes on to do, it must not fail on the argument it was given.
59
+ expect(output).to.not.include('argument missing');
60
+ expect(output).to.include('Rerouting to underpost cli');
61
+ },
62
+ 60000,
63
+ );
64
+ });