@underpostnet/cyberia 3.3.73 → 3.3.77

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 (269) hide show
  1. package/.env.example +17 -5
  2. package/.github/workflows/coverall.cyberia.ci.yml +10 -0
  3. package/.github/workflows/docker-image.cyberia-client.ci.yml +1 -1
  4. package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +1 -1
  5. package/.github/workflows/docker-image.cyberia-server.ci.yml +1 -1
  6. package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +1 -1
  7. package/.github/workflows/docker-image.engine-cyberia.ci.yml +1 -1
  8. package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +1 -1
  9. package/.github/workflows/engine-cyberia.ci.yml +1 -1
  10. package/.github/workflows/publish.cyberia.ci.yml +44 -0
  11. package/AGENTS.md +61 -12
  12. package/CHANGELOG.md +159 -161
  13. package/CLI-HELP.md +20 -13
  14. package/Dockerfile +1 -1
  15. package/Dockerfile.dev +1 -1
  16. package/Dockerfile.test +2 -2
  17. package/README.md +28 -28
  18. package/bin/build.js +96 -21
  19. package/bin/cyberia.js +1247 -1373
  20. package/bin/index.js +1247 -1373
  21. package/compose.env +17 -5
  22. package/conf.js +7 -4
  23. package/deploy/cyberia-client/deploy.sh +10 -11
  24. package/deploy/cyberia-client/package.sh +0 -1
  25. package/deploy/cyberia-client/state.sh +3 -2
  26. package/deploy/cyberia-server/deploy.sh +10 -11
  27. package/deploy/cyberia-server/package.sh +0 -1
  28. package/deploy/cyberia-server/state.sh +3 -2
  29. package/deploy/dd-cyberia/deploy.sh +5 -5
  30. package/deploy/dd-cyberia/init.sh +15 -14
  31. package/deploy/dd-cyberia/package.sh +0 -1
  32. package/deploy/dd-cyberia/state.sh +2 -2
  33. package/deploy/dd-cyberia/sync-deploy.sh +139 -75
  34. package/deploy/lib/config.sh +29 -0
  35. package/deploy/lib/github-actions-logging.sh +92 -76
  36. package/deploy/lib/host.sh +28 -19
  37. package/deploy/lib/state.sh +4 -1
  38. package/deploy/pwa-microservices-template/deploy.sh +10 -8
  39. package/deploy/release/deploy.sh +0 -2
  40. package/deployment.yaml +1 -1
  41. package/docker-compose.yml +23 -17
  42. package/docs/coverage/cyberia/api/cyberia-instance/cyberia-fallback-capture.js.html +1591 -0
  43. package/docs/coverage/cyberia/api/cyberia-instance/index.html +116 -0
  44. package/docs/coverage/cyberia/api/cyberia-server-defaults/cyberia-server-defaults.js.html +4138 -0
  45. package/docs/coverage/cyberia/api/cyberia-server-defaults/index.html +116 -0
  46. package/docs/coverage/cyberia/api/object-layer/index.html +116 -0
  47. package/docs/coverage/cyberia/api/object-layer/object-layer.model.js.html +1393 -0
  48. package/docs/coverage/cyberia/base.css +362 -0
  49. package/docs/coverage/cyberia/block-navigation.js +82 -0
  50. package/docs/coverage/cyberia/favicon.png +0 -0
  51. package/docs/coverage/cyberia/index.html +161 -0
  52. package/docs/coverage/cyberia/prettify.css +101 -0
  53. package/docs/coverage/cyberia/prettify.js +937 -0
  54. package/docs/coverage/cyberia/projects/cyberia/atlas-sprite-sheet-generator.js.html +1315 -0
  55. package/docs/coverage/cyberia/projects/cyberia/index.html +176 -0
  56. package/docs/coverage/cyberia/projects/cyberia/instance-backup.js.html +715 -0
  57. package/docs/coverage/cyberia/projects/cyberia/instance-data.js.html +2182 -0
  58. package/docs/coverage/cyberia/projects/cyberia/shape-generator.js.html +3265 -0
  59. package/docs/coverage/cyberia/projects/cyberia/stat-balance.js.html +328 -0
  60. package/docs/coverage/cyberia/sort-arrow-sprite.png +0 -0
  61. package/docs/coverage/cyberia/sorter.js +205 -0
  62. package/hardhat/package-lock.json +2 -2
  63. package/hardhat/package.json +1 -1
  64. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
  65. package/manifests/cronjobs/dd-cron/dd-cron-vultr.yaml +1 -1
  66. package/manifests/deployment/dd-cyberia-development/deployment.yaml +1 -1
  67. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  68. package/manifests/deployment/mongo-express/deployment.yaml +46 -27
  69. package/manifests/deployment/mongo-express/kustomization.yaml +9 -0
  70. package/manifests/deployment/mongo-express/mongo-express-nodeport.yaml +18 -0
  71. package/manifests/deployment/mongo-express/service.yaml +16 -0
  72. package/manifests/deployment/mongo-express-no-auth/disable-mongodb-auth.yaml +16 -0
  73. package/manifests/deployment/mongo-express-no-auth/kustomization.yaml +16 -0
  74. package/package.json +19 -17
  75. package/scripts/test-monitor.sh +3 -3
  76. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +14 -9
  77. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +36 -5
  78. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +5 -0
  79. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +88 -184
  80. package/src/api/cyberia-action/cyberia-action.router.js +2 -2
  81. package/src/api/cyberia-action/cyberia-action.service.js +3 -4
  82. package/src/api/cyberia-audio/cyberia-audio.controller.js +6 -0
  83. package/src/api/cyberia-audio/cyberia-audio.model.js +61 -0
  84. package/src/api/cyberia-audio/cyberia-audio.router.js +21 -0
  85. package/src/api/cyberia-audio/cyberia-audio.service.js +220 -0
  86. package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +1 -3
  87. package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +10 -19
  88. package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +12 -34
  89. package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +15 -32
  90. package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +1 -1
  91. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +1 -1
  92. package/src/api/cyberia-entity/cyberia-entity.model.js +2 -0
  93. package/src/api/cyberia-entity/cyberia-entity.service.js +1 -1
  94. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +8 -2
  95. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.model.js +31 -20
  96. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +16 -1
  97. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +466 -3
  98. package/src/api/cyberia-instance/cyberia-fallback-capture.js +87 -51
  99. package/src/api/cyberia-instance/cyberia-fallback-world.js +21 -40
  100. package/src/api/cyberia-instance/cyberia-instance-boot.service.js +5 -5
  101. package/src/api/cyberia-instance/cyberia-instance-items.js +98 -0
  102. package/src/api/cyberia-instance/cyberia-instance-map.service.js +34 -141
  103. package/src/api/cyberia-instance/cyberia-instance.controller.js +1 -4
  104. package/src/api/cyberia-instance/cyberia-instance.model.js +0 -14
  105. package/src/api/cyberia-instance/cyberia-instance.router.js +3 -16
  106. package/src/api/cyberia-instance/cyberia-instance.service.js +3 -43
  107. package/src/api/cyberia-instance/cyberia-world-generator.js +8 -13
  108. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +49 -75
  109. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +74 -1
  110. package/src/api/cyberia-map/cyberia-map.service.js +6 -2
  111. package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.controller.js +31 -0
  112. package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.model.js +56 -0
  113. package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.router.js +33 -0
  114. package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.service.js +189 -0
  115. package/src/api/cyberia-quest/cyberia-quest.router.js +3 -3
  116. package/src/api/cyberia-quest/cyberia-quest.service.js +3 -3
  117. package/src/api/cyberia-quest-progress/cyberia-quest-progress.model.js +1 -1
  118. package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +403 -188
  119. package/src/api/cyberia-skill/cyberia-skill.model.js +6 -6
  120. package/src/api/file/file.ref.js +153 -0
  121. package/src/api/file/file.ref.json +11 -2
  122. package/src/api/object-layer/object-layer.model.js +47 -12
  123. package/src/api/object-layer/object-layer.router.js +10 -3
  124. package/src/api/object-layer/object-layer.service.js +113 -120
  125. package/src/cli/app.js +12 -1
  126. package/src/cli/client.js +2 -2
  127. package/src/cli/cluster.js +80 -18
  128. package/src/cli/deploy.js +215 -34
  129. package/src/cli/docker-compose.js +1 -1
  130. package/src/cli/fs-selection.js +115 -0
  131. package/src/cli/fs.js +158 -342
  132. package/src/cli/index.js +22 -10
  133. package/src/cli/release.js +3 -1
  134. package/src/cli/repository.js +35 -9
  135. package/src/cli/run.js +80 -199
  136. package/src/cli/test.js +4 -0
  137. package/src/cli/wireguard.js +32 -31
  138. package/src/client/components/core/Badge.js +0 -1
  139. package/src/client/components/core/CssCore.js +8 -1
  140. package/src/client/components/core/Docs.js +77 -100
  141. package/src/client/components/core/Modal.js +77 -23
  142. package/src/client/components/core/ToolTip.js +15 -0
  143. package/src/client/components/cryptokoyn/AppShellCryptokoyn.js +0 -2
  144. package/src/client/components/cyberia/ActionEngineCyberia.js +12 -36
  145. package/src/client/components/cyberia/EntityEngineCyberia.js +492 -121
  146. package/src/client/components/cyberia/InstanceEngineCyberia.js +129 -160
  147. package/src/client/components/cyberia/InstanceSelectionView.js +7 -1
  148. package/src/client/components/cyberia/MapEngineCyberia.js +175 -69
  149. package/src/client/components/cyberia/ObjectLayerEngine.js +1011 -97
  150. package/src/client/components/cyberia/ObjectLayerEngineModal.js +81 -155
  151. package/src/client/components/cyberia/ObjectLayerEngineViewer.js +113 -98
  152. package/src/client/components/cyberia/SharedDefaultsCyberia.js +215 -56
  153. package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +0 -34
  154. package/src/client/components/cyberia-portal/CssCyberiaPortal.js +1 -1
  155. package/src/client/components/cyberia-portal/MainBodyCyberiaPortal.js +712 -76
  156. package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +0 -4
  157. package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +0 -4
  158. package/src/client/components/default/AppShellDefault.js +1 -4
  159. package/src/client/components/itemledger/AppShellItemledger.js +0 -2
  160. package/src/client/components/underpost/AppShellUnderpost.js +1 -1
  161. package/src/client/public/cyberia-docs/ARCHITECTURE.md +15 -15
  162. package/src/client/public/cyberia-docs/CYBERIA-CLI.md +280 -24
  163. package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +51 -29
  164. package/src/client/public/cyberia-docs/CYBERIA-SAGA.md +52 -53
  165. package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +16 -11
  166. package/src/client/public/cyberia-docs/CYBERIA.md +28 -28
  167. package/src/client/public/cyberia-docs/ENTITY-PROFILE.md +11 -5
  168. package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
  169. package/src/client/public/cyberia-docs/STATS-PROGRESSION.md +217 -0
  170. package/src/client/public/cyberia-docs/WHITE-PAPER.md +20 -12
  171. package/src/client/services/cyberia-audio/cyberia-audio.service.js +99 -0
  172. package/src/client/services/cyberia-entity-type-default/cyberia-entity-type-default.service.js +38 -0
  173. package/src/client/services/cyberia-instance/cyberia-instance.management.js +5 -4
  174. package/src/client/services/cyberia-instance/cyberia-instance.service.js +0 -40
  175. package/src/client/services/cyberia-map/cyberia-map.management.js +5 -4
  176. package/src/client/services/cyberia-map-audio-conf/cyberia-map-audio-conf.service.js +138 -0
  177. package/src/client/services/object-layer/object-layer.management.js +6 -10
  178. package/src/client/services/object-layer/object-layer.service.js +3 -1
  179. package/src/client/ssr/views/Cyberia404.js +3 -3
  180. package/src/client-builder/client-build-docs.js +52 -52
  181. package/src/client-builder/client-build.js +67 -44
  182. package/src/client-builder/client-bundle.js +347 -0
  183. package/src/db/mongo/MongoExpress.js +187 -0
  184. package/src/grpc/cyberia/grpc-server.js +5 -7
  185. package/src/index.js +1 -1
  186. package/src/projects/cyberia/atlas-sprite-sheet-generator.js +143 -146
  187. package/src/projects/cyberia/atlas-sprite-sheet-store.js +391 -0
  188. package/src/projects/cyberia/besu-genesis-generator.js +5 -14
  189. package/src/projects/cyberia/boot-contract-fixtures.js +32 -0
  190. package/src/projects/cyberia/catalog-cyberia.js +3 -2
  191. package/src/projects/cyberia/gemini-client.js +3 -3
  192. package/src/projects/cyberia/generate-saga.js +4 -11
  193. package/src/projects/cyberia/hot-reload-trigger.js +17 -28
  194. package/src/projects/cyberia/instance-backup.js +210 -0
  195. package/src/projects/cyberia/instance-data.js +177 -257
  196. package/src/projects/cyberia/map-preview-generator.js +32 -68
  197. package/src/projects/cyberia/object-layer.js +152 -319
  198. package/src/projects/cyberia/seed-audio.js +139 -0
  199. package/src/projects/cyberia/semantic-layer-generator-skin.js +2 -8
  200. package/src/projects/cyberia/semantic-layer-generator.js +2 -18
  201. package/src/projects/cyberia/stat-balance.js +81 -0
  202. package/src/projects/cyberia/stat-commands.js +23 -0
  203. package/src/projects/cyberia/stat-contract-generator.js +101 -0
  204. package/src/runtime/cyberia-client/Dockerfile +8 -4
  205. package/src/runtime/cyberia-client/Dockerfile.dev +5 -2
  206. package/src/runtime/cyberia-server/Dockerfile +9 -2
  207. package/src/runtime/cyberia-server/Dockerfile.dev +8 -1
  208. package/src/runtime/engine-cyberia/Dockerfile +1 -1
  209. package/src/runtime/engine-cyberia/Dockerfile.dev +1 -1
  210. package/src/runtime/engine-cyberia/Dockerfile.test +2 -2
  211. package/src/runtime/engine-cyberia/compose.env +17 -5
  212. package/src/runtime/engine-cyberia/docker-compose.yml +23 -17
  213. package/src/server/build/coverage.js +113 -42
  214. package/src/server/build/package.js +55 -12
  215. package/src/server/build/testing.js +59 -9
  216. package/src/server/network/middlewares.js +8 -2
  217. package/src/server/network/underpost-gateway.js +113 -14
  218. package/src/server/ops/logger.js +70 -35
  219. package/src/server/runtime/conf.js +40 -9
  220. package/src/server/runtime/start.js +11 -2
  221. package/src/server/storage/data-query.js +16 -0
  222. package/src/server/storage/downloader.js +62 -14
  223. package/src/server/storage/zip.js +153 -0
  224. package/test/integration/app/cyberia/atlas-sprite-sheet-store.test.js +402 -0
  225. package/test/integration/app/cyberia/atlas-sprite-sheet.test.js +86 -0
  226. package/test/integration/app/cyberia/cyberia-cli-plain-reads.test.js +34 -0
  227. package/test/integration/app/cyberia/cyberia-entity-type-default.test.js +842 -0
  228. package/test/integration/app/cyberia/cyberia-instance-conf-coerce.test.js +53 -0
  229. package/test/integration/app/cyberia/cyberia-instance-conf-defaults.test.js +16 -22
  230. package/test/integration/app/cyberia/cyberia-instance-items.test.js +90 -0
  231. package/test/integration/app/cyberia/cyberia-load.test.js +6 -7
  232. package/test/integration/app/cyberia/cyberia-map-audio-conf.test.js +256 -0
  233. package/test/integration/app/cyberia/cyberia-stats.test.js +150 -0
  234. package/test/integration/app/cyberia/fallback-world-capture.test.js +87 -2
  235. package/test/integration/app/cyberia/object-layer-item-selection.test.js +56 -0
  236. package/test/integration/app/cyberia/object-layer-natural-key.test.js +32 -0
  237. package/test/integration/app/cyberia/seed-audio.test.js +275 -0
  238. package/test/integration/infra/2-network/wireguard-cli.test.js +33 -9
  239. package/test/integration/infra/2-network/wireguard-edge.test.js +58 -18
  240. package/test/integration/infra/3-cluster/mongo-express-deploy.test.js +156 -0
  241. package/test/integration/infra/4-ingress/api-cross-origin.test.js +79 -0
  242. package/test/integration/infra/4-ingress/underpost-ingress.test.js +1 -1
  243. package/test/unit/catalog.test.js +13 -2
  244. package/test/unit/client-build-docs.test.js +97 -70
  245. package/test/unit/client-bundle.test.js +313 -0
  246. package/test/unit/conf-resolution.test.js +58 -7
  247. package/test/unit/coverage-artifact.test.js +120 -29
  248. package/test/unit/cyberia/instance-backup.test.js +183 -0
  249. package/test/unit/cyberia/instance-data.test.js +314 -0
  250. package/test/unit/cyberia/instance-object-layer-items.test.js +46 -0
  251. package/test/unit/cyberia/publish-workflow.test.js +22 -0
  252. package/test/unit/cyberia/stat-balance.test.js +69 -0
  253. package/test/unit/deploy-legacy-gateway-sweep.test.js +63 -0
  254. package/test/unit/deploy-log-table.test.js +76 -5
  255. package/test/unit/downloader-integrity.test.js +98 -0
  256. package/test/unit/file-reference-registry.test.js +141 -0
  257. package/test/unit/fs-storage-paths.test.js +596 -0
  258. package/test/unit/gateway-pod-fetch.test.js +95 -0
  259. package/test/unit/letsencrypt-issuer.test.js +101 -0
  260. package/test/unit/logger-redaction.test.js +29 -0
  261. package/test/unit/package.test.js +70 -16
  262. package/test/unit/prepare-host.test.js +84 -1
  263. package/test/unit/release-bump.test.js +3 -1
  264. package/test/unit/start-options.test.js +12 -0
  265. package/test/unit/test-tiers.test.js +27 -0
  266. package/test/unit/zip-archive.test.js +142 -0
  267. package/vitest.config.js +11 -3
  268. package/src/api/cyberia-instance/cyberia-fallback-default-items.js +0 -63
  269. package/src/client/components/cyberia/FallbackWorldEngineCyberia.js +0 -368
@@ -135,7 +135,7 @@ services:
135
135
  # server starts but rejects connections until seeded). Serves REST/WS on 4005
136
136
  # and the engine gRPC data service on 50051.
137
137
  engine-cyberia-runtime:
138
- image: ${ENGINE_CYBERIA_IMAGE:-underpost/engine-cyberia-dev}:${ENGINE_CYBERIA_TAG:-v3.3.73}
138
+ image: ${ENGINE_CYBERIA_IMAGE:-underpost/engine-cyberia-dev}:${ENGINE_CYBERIA_TAG:-v3.3.77}
139
139
  container_name: dd-cyberia-engine
140
140
  command:
141
141
  - /bin/sh
@@ -190,14 +190,16 @@ services:
190
190
  # gateway on host port 8081, so one port can route every instance path.
191
191
 
192
192
  cyberia-server-runtime:
193
- image: ${CYBERIA_SERVER_IMAGE:-underpost/cyberia-server-dev}:${CYBERIA_SERVER_TAG:-v3.3.73}
193
+ image: ${CYBERIA_SERVER_IMAGE:-underpost/cyberia-server-dev}:${CYBERIA_SERVER_TAG:-v3.3.77}
194
194
  container_name: dd-cyberia-server
195
195
  environment:
196
196
  INSTANCE_CODE: amethyst-strata-expansion
197
197
  CYBERIA_BASE_PATH: /
198
- ENGINE_API_BASE_URL: ${ENGINE_API_BASE_URL:-http://engine-cyberia}
198
+ CYBERIA_PUBLIC_URL: ${CYBERIA_SERVER_PUBLIC_ORIGIN:-http://localhost:8081}
199
+ CYBERIA_SERVER_API_KEY: ${CYBERIA_SERVER_API_KEY:-}
200
+ CYBERIA_DATA_SERVER_URL: ${CYBERIA_DATA_SERVER_URL:-http://engine-cyberia}
199
201
  ENGINE_PUBLIC_URL: ${ENGINE_PUBLIC_URL:-http://localhost:4005}
200
- ENGINE_GRPC_ADDRESS: ${ENGINE_GRPC_ADDRESS:-engine-cyberia-runtime:50051}
202
+ CYBERIA_DATA_SERVER_GRPC: ${CYBERIA_DATA_SERVER_GRPC:-engine-cyberia-runtime:50051}
201
203
  ENGINE_GRPC_RELOAD_INTERVAL_SEC: ${ENGINE_GRPC_RELOAD_INTERVAL_SEC:-300}
202
204
  STATIC_DIR: ${CYBERIA_SERVER_STATIC_DIR:-/home/dd/engine/cyberia-server/public}
203
205
  APP_ENV: ${CYBERIA_SERVER_APP_ENV:-development}
@@ -226,14 +228,16 @@ services:
226
228
  restart: unless-stopped
227
229
 
228
230
  cyberia-server-forest:
229
- image: ${CYBERIA_SERVER_IMAGE:-underpost/cyberia-server-dev}:${CYBERIA_SERVER_TAG:-v3.3.73}
231
+ image: ${CYBERIA_SERVER_IMAGE:-underpost/cyberia-server-dev}:${CYBERIA_SERVER_TAG:-v3.3.77}
230
232
  container_name: dd-cyberia-server-forest
231
233
  environment:
232
234
  INSTANCE_CODE: FOREST
233
235
  CYBERIA_BASE_PATH: /FOREST
234
- ENGINE_API_BASE_URL: ${ENGINE_API_BASE_URL:-http://engine-cyberia}
236
+ CYBERIA_PUBLIC_URL: ${CYBERIA_SERVER_PUBLIC_ORIGIN:-http://localhost:8081}/FOREST
237
+ CYBERIA_SERVER_API_KEY: ${CYBERIA_SERVER_API_KEY:-}
238
+ CYBERIA_DATA_SERVER_URL: ${CYBERIA_DATA_SERVER_URL:-http://engine-cyberia}
235
239
  ENGINE_PUBLIC_URL: ${ENGINE_PUBLIC_URL:-http://localhost:4005}
236
- ENGINE_GRPC_ADDRESS: ${ENGINE_GRPC_ADDRESS:-engine-cyberia-runtime:50051}
240
+ CYBERIA_DATA_SERVER_GRPC: ${CYBERIA_DATA_SERVER_GRPC:-engine-cyberia-runtime:50051}
237
241
  ENGINE_GRPC_RELOAD_INTERVAL_SEC: ${ENGINE_GRPC_RELOAD_INTERVAL_SEC:-300}
238
242
  STATIC_DIR: ${CYBERIA_SERVER_STATIC_DIR:-/home/dd/engine/cyberia-server/public}
239
243
  APP_ENV: ${CYBERIA_SERVER_APP_ENV:-development}
@@ -262,14 +266,16 @@ services:
262
266
  restart: unless-stopped
263
267
 
264
268
  cyberia-server-test:
265
- image: ${CYBERIA_SERVER_IMAGE:-underpost/cyberia-server-dev}:${CYBERIA_SERVER_TAG:-v3.3.73}
269
+ image: ${CYBERIA_SERVER_IMAGE:-underpost/cyberia-server-dev}:${CYBERIA_SERVER_TAG:-v3.3.77}
266
270
  container_name: dd-cyberia-server-test
267
271
  environment:
268
272
  INSTANCE_CODE: TEST
269
273
  CYBERIA_BASE_PATH: /TEST
270
- ENGINE_API_BASE_URL: ${ENGINE_API_BASE_URL:-http://engine-cyberia}
274
+ CYBERIA_PUBLIC_URL: ${CYBERIA_SERVER_PUBLIC_ORIGIN:-http://localhost:8081}/TEST
275
+ CYBERIA_SERVER_API_KEY: ${CYBERIA_SERVER_API_KEY:-}
276
+ CYBERIA_DATA_SERVER_URL: ${CYBERIA_DATA_SERVER_URL:-http://engine-cyberia}
271
277
  ENGINE_PUBLIC_URL: ${ENGINE_PUBLIC_URL:-http://localhost:4005}
272
- ENGINE_GRPC_ADDRESS: ${ENGINE_GRPC_ADDRESS:-engine-cyberia-runtime:50051}
278
+ CYBERIA_DATA_SERVER_GRPC: ${CYBERIA_DATA_SERVER_GRPC:-engine-cyberia-runtime:50051}
273
279
  ENGINE_GRPC_RELOAD_INTERVAL_SEC: ${ENGINE_GRPC_RELOAD_INTERVAL_SEC:-300}
274
280
  STATIC_DIR: ${CYBERIA_SERVER_STATIC_DIR:-/home/dd/engine/cyberia-server/public}
275
281
  APP_ENV: ${CYBERIA_SERVER_APP_ENV:-development}
@@ -299,7 +305,7 @@ services:
299
305
 
300
306
  # === Tier 4: presentation, one per variant ===============================
301
307
  # One WASM bundle serves every variant; the instance is resolved from the URL
302
- # sub-path (docker-driver injects CYBERIA_BASE_PATH / _WS_ORIGIN /
308
+ # sub-path (wasm-driver injects CYBERIA_BASE_PATH / _WS_ORIGIN /
303
309
  # _ENGINE_API_ORIGIN / _DEFAULT_INSTANCE). Each client waits ONLY for its own
304
310
  # server, so the three
305
311
  # server->client pipelines advance independently (in parallel) once the shared
@@ -309,7 +315,7 @@ services:
309
315
  # cyberia-server-test -> cyberia-client-test
310
316
 
311
317
  cyberia-client-runtime:
312
- image: ${CYBERIA_CLIENT_IMAGE:-underpost/cyberia-client-dev}:${CYBERIA_CLIENT_TAG:-v3.3.73}
318
+ image: ${CYBERIA_CLIENT_IMAGE:-underpost/cyberia-client-dev}:${CYBERIA_CLIENT_TAG:-v3.3.77}
313
319
  container_name: dd-cyberia-client
314
320
  environment:
315
321
  CYBERIA_PORT: '8081'
@@ -317,7 +323,7 @@ services:
317
323
  CYBERIA_BASE_PATH: /
318
324
  CYBERIA_DEFAULT_INSTANCE: ${CYBERIA_DEFAULT_INSTANCE:-amethyst-strata-expansion}
319
325
  CYBERIA_WS_ORIGIN: ${CYBERIA_WS_ORIGIN:-ws://localhost:8081}
320
- CYBERIA_ENGINE_API_ORIGIN: ${CYBERIA_ENGINE_API_ORIGIN:-http://localhost}
326
+ CYBERIA_DATA_SERVER_URL: ${CYBERIA_CLIENT_DATA_SERVER_URL:-http://localhost}
321
327
  networks:
322
328
  - dd-cyberia-internal
323
329
  expose:
@@ -334,7 +340,7 @@ services:
334
340
  restart: unless-stopped
335
341
 
336
342
  cyberia-client-forest:
337
- image: ${CYBERIA_CLIENT_IMAGE:-underpost/cyberia-client-dev}:${CYBERIA_CLIENT_TAG:-v3.3.73}
343
+ image: ${CYBERIA_CLIENT_IMAGE:-underpost/cyberia-client-dev}:${CYBERIA_CLIENT_TAG:-v3.3.77}
338
344
  container_name: dd-cyberia-client-forest
339
345
  environment:
340
346
  CYBERIA_PORT: '8081'
@@ -342,7 +348,7 @@ services:
342
348
  CYBERIA_BASE_PATH: /FOREST
343
349
  CYBERIA_DEFAULT_INSTANCE: ${CYBERIA_DEFAULT_INSTANCE:-amethyst-strata-expansion}
344
350
  CYBERIA_WS_ORIGIN: ${CYBERIA_WS_ORIGIN:-ws://localhost:8081}
345
- CYBERIA_ENGINE_API_ORIGIN: ${CYBERIA_ENGINE_API_ORIGIN:-http://localhost}
351
+ CYBERIA_DATA_SERVER_URL: ${CYBERIA_CLIENT_DATA_SERVER_URL:-http://localhost}
346
352
  networks:
347
353
  - dd-cyberia-internal
348
354
  expose:
@@ -359,7 +365,7 @@ services:
359
365
  restart: unless-stopped
360
366
 
361
367
  cyberia-client-test:
362
- image: ${CYBERIA_CLIENT_IMAGE:-underpost/cyberia-client-dev}:${CYBERIA_CLIENT_TAG:-v3.3.73}
368
+ image: ${CYBERIA_CLIENT_IMAGE:-underpost/cyberia-client-dev}:${CYBERIA_CLIENT_TAG:-v3.3.77}
363
369
  container_name: dd-cyberia-client-test
364
370
  environment:
365
371
  CYBERIA_PORT: '8081'
@@ -367,7 +373,7 @@ services:
367
373
  CYBERIA_BASE_PATH: /TEST
368
374
  CYBERIA_DEFAULT_INSTANCE: ${CYBERIA_DEFAULT_INSTANCE:-amethyst-strata-expansion}
369
375
  CYBERIA_WS_ORIGIN: ${CYBERIA_WS_ORIGIN:-ws://localhost:8081}
370
- CYBERIA_ENGINE_API_ORIGIN: ${CYBERIA_ENGINE_API_ORIGIN:-http://localhost}
376
+ CYBERIA_DATA_SERVER_URL: ${CYBERIA_CLIENT_DATA_SERVER_URL:-http://localhost}
371
377
  networks:
372
378
  - dd-cyberia-internal
373
379
  expose:
@@ -1,85 +1,152 @@
1
1
  /**
2
- * The coverage HTML report as a build artifact: where a run leaves it, where an assembled
3
- * deploy artifact carries it, and what is published when nothing was carried.
2
+ * The coverage HTML reports as build artifacts: which reports a deploy declares, where a run
3
+ * leaves each, where an assembled deploy artifact carries them, and what is published when
4
+ * one was not carried.
4
5
  *
5
- * The report is produced by the test stage (`npm run test:coverage`) and bundled into the
6
- * deploy artifact at {@link COVERAGE_BUNDLE_DIRECTORY}. A workload container only ever
7
- * copies it: running a test runner inside a pod costs minutes of the build phase, needs the
8
- * dev dependency tree, andbecause a suite's expected non-zero exits latch the runtime
9
- * status contract publishes a false `error` to the deployment monitor.
6
+ * A deploy declares its reports in `conf.server.json` under `docs.coverage`, one entry per
7
+ * report it publishes at `/docs/coverage/<id>`:
8
+ *
9
+ * - `{ id, label, suite }` — a test tier selection run by `node bin test <suite>`, whose HTML
10
+ * report the runner writes under `coverage/<key>` (see `coverageReportKey`). A deploy shows
11
+ * the run it names, never the last run a host happened to make.
12
+ * - `{ id, label, path }` — a report another tree produces on its own (Hardhat's Solidity
13
+ * coverage), read from that tree's `coverage/` output.
14
+ *
15
+ * Reports are produced by the test stage and bundled into the deploy artifact under
16
+ * {@link COVERAGE_BUNDLE_DIRECTORY}. A workload container only ever copies them: running a
17
+ * test runner inside a pod costs minutes of the build phase, needs the dev dependency tree,
18
+ * and — because a suite's expected non-zero exits latch the runtime status contract —
19
+ * publishes a false `error` to the deployment monitor.
10
20
  *
11
21
  * @module src/server/build/coverage.js
12
22
  * @namespace UnderpostCoverage
13
23
  */
14
24
 
15
25
  import fs from 'fs-extra';
16
- import { UNDERPOST_TESTING } from './testing.js';
26
+ import { UNDERPOST_TESTING, coverageReportKey } from './testing.js';
17
27
 
18
28
  /**
19
29
  * @constant COVERAGE_BUNDLE_DIRECTORY
20
- * @description Path, relative to a source tree root, where an assembled deploy artifact
21
- * carries the report. Deliberately not under `coverage/`, which every engine `.gitignore`
22
- * excludes, so the bundled artifact is publishable source rather than a local run's output.
30
+ * @description Path, relative to a source tree root, under which an assembled deploy
31
+ * artifact carries each report as `<id>/`. Deliberately not under `coverage/`, which every
32
+ * engine `.gitignore` excludes, so the bundled artifact is publishable source rather than a
33
+ * local run's output.
23
34
  * @memberof UnderpostCoverage
24
35
  */
25
36
  const COVERAGE_BUNDLE_DIRECTORY = 'docs/coverage';
26
37
 
27
38
  const normalizeRoot = (root) => `${root ?? ''}`.replace(/\/+$/, '');
28
39
 
40
+ /**
41
+ * @method coverageReportsFactory
42
+ * @description The reports a deploy's `docs` conf declares, validated: every entry names an
43
+ * id and exactly one source, and no two entries publish under the same id.
44
+ * @param {object} [docs] - The `docs` block of one `conf.server.json` route.
45
+ * @returns {Array<{id: string, label: string, suite?: string, path?: string}>} Declared reports.
46
+ * @throws {Error} When an entry is malformed or an id repeats.
47
+ * @memberof UnderpostCoverage
48
+ */
49
+ const coverageReportsFactory = (docs = {}) => {
50
+ const reports = docs.coverage ?? [];
51
+ const ids = new Set();
52
+ for (const report of reports) {
53
+ const { id, suite, path } = report;
54
+ if (!id || !/^[a-z0-9-]+$/.test(id)) throw new Error(`[coverage] report id must be a slug, got '${id}'`);
55
+ if (ids.has(id)) throw new Error(`[coverage] report id '${id}' is declared twice`);
56
+ if (!suite === !path) throw new Error(`[coverage] report '${id}' needs exactly one of 'suite' or 'path'`);
57
+ ids.add(id);
58
+ }
59
+ return reports.map(({ id, label = id, suite, path }) => ({ id, label, suite, path }));
60
+ };
61
+
62
+ /**
63
+ * @method deployCoverageReports
64
+ * @description Every report a deploy declares across its routes, once each: two routes may
65
+ * publish the same report, never the same id with different sources.
66
+ * @param {object} confServer - A parsed `conf.server.json`.
67
+ * @returns {Array<{id: string, label: string, suite?: string, path?: string}>} Declared reports.
68
+ * @throws {Error} When an id is declared with two different sources.
69
+ * @memberof UnderpostCoverage
70
+ */
71
+ const deployCoverageReports = (confServer) => {
72
+ const reports = new Map();
73
+ for (const routes of Object.values(confServer)) {
74
+ for (const { docs } of Object.values(routes)) {
75
+ for (const report of coverageReportsFactory(docs)) {
76
+ const known = reports.get(report.id);
77
+ if (known && (known.suite !== report.suite || known.path !== report.path))
78
+ throw new Error(`[coverage] report id '${report.id}' is declared with two different sources`);
79
+ reports.set(report.id, report);
80
+ }
81
+ }
82
+ }
83
+ return [...reports.values()];
84
+ };
85
+
29
86
  /**
30
87
  * @method coverageReportCandidates
31
- * @description Every place a coverage HTML report can live in a source tree, most local
32
- * first: the reporters a run writes, then the bundled artifact a deploy artifact carries.
33
- * @param {string} root - Source tree root.
88
+ * @description Every place one report can live, most local first: the output of the run
89
+ * that produces it, then the bundled artifact a deploy artifact carries.
90
+ * @param {{id: string, suite?: string, path?: string}} report - A declared report.
34
91
  * @returns {string[]} Candidate report directories.
35
92
  * @memberof UnderpostCoverage
36
93
  */
37
- const coverageReportCandidates = (root) => {
38
- const base = normalizeRoot(root);
39
- if (!base) return [];
40
- const coverage = `${base}/${UNDERPOST_TESTING.coverageDirectory}`;
41
- return [`${coverage}/html`, `${coverage}/lcov-report`, coverage, `${base}/${COVERAGE_BUNDLE_DIRECTORY}`];
94
+ const coverageReportCandidates = ({ id, suite, path }) => {
95
+ const bundled = `${COVERAGE_BUNDLE_DIRECTORY}/${id}`;
96
+ if (suite) return [`${UNDERPOST_TESTING.coverageDirectory}/${coverageReportKey(suite)}`, bundled];
97
+ const coverage = `${normalizeRoot(path)}/${UNDERPOST_TESTING.coverageDirectory}`;
98
+ return [`${coverage}/html`, `${coverage}/lcov-report`, coverage, bundled];
42
99
  };
43
100
 
44
101
  /**
45
102
  * @method resolveCoverageReportPath
46
103
  * @description First candidate that actually carries an HTML index. A directory holding
47
104
  * only `lcov.info` is not a report and is not published.
48
- * @param {string} root - Source tree root.
105
+ * @param {{id: string, suite?: string, path?: string}} report - A declared report.
49
106
  * @returns {string|undefined} Report directory, or undefined when none is available.
50
107
  * @memberof UnderpostCoverage
51
108
  */
52
- const resolveCoverageReportPath = (root) =>
53
- coverageReportCandidates(root).find((candidate) => fs.existsSync(`${candidate}/index.html`));
109
+ const resolveCoverageReportPath = (report) =>
110
+ coverageReportCandidates(report).find((candidate) => fs.existsSync(`${candidate}/index.html`));
111
+
112
+ /**
113
+ * @method coverageReportCommand
114
+ * @description The command that produces a report, for the operator who finds it missing.
115
+ * @param {{suite?: string, path?: string}} report - A declared report.
116
+ * @returns {string} Shell command.
117
+ * @memberof UnderpostCoverage
118
+ */
119
+ const coverageReportCommand = ({ suite, path }) =>
120
+ suite ? `node bin test ${suite}` : `npm run coverage --prefix ${normalizeRoot(path)}`;
54
121
 
55
122
  /**
56
- * @method bundleCoverageReport
57
- * @description Copies the report of `fromRoot` into `toRoot`'s bundle directory, so the
123
+ * @method bundleCoverageReports
124
+ * @description Copies each declared report into `toRoot`'s bundle directory, so the
58
125
  * assembled artifact carries it into every container started from that source.
59
- * @param {string} fromRoot - Tree the report was generated in.
126
+ * @param {Array<{id: string, suite?: string, path?: string}>} reports - Declared reports.
60
127
  * @param {string} toRoot - Assembled artifact root.
61
- * @returns {{bundled: boolean, from?: string, to: string}} What was bundled, and where.
128
+ * @returns {Array<{id: string, bundled: boolean, from?: string, to: string}>} What was bundled, and where.
62
129
  * @memberof UnderpostCoverage
63
130
  */
64
- const bundleCoverageReport = (fromRoot, toRoot) => {
65
- const to = `${normalizeRoot(toRoot)}/${COVERAGE_BUNDLE_DIRECTORY}`;
66
- const from = resolveCoverageReportPath(fromRoot);
67
- if (!from) return { bundled: false, to };
68
- fs.emptyDirSync(to);
69
- fs.copySync(from, to);
70
- return { bundled: true, from, to };
71
- };
131
+ const bundleCoverageReports = (reports, toRoot) =>
132
+ reports.map((report) => {
133
+ const to = `${normalizeRoot(toRoot)}/${COVERAGE_BUNDLE_DIRECTORY}/${report.id}`;
134
+ const from = resolveCoverageReportPath(report);
135
+ if (!from) return { id: report.id, bundled: false, to };
136
+ fs.emptyDirSync(to);
137
+ fs.copySync(from, to);
138
+ return { id: report.id, bundled: true, from, to };
139
+ });
72
140
 
73
141
  /**
74
142
  * @method coverageUnavailablePage
75
143
  * @description The page served where a report was expected but none was bundled. A static
76
144
  * document, never an invitation to generate one at runtime.
77
- * @param {object} [options]
78
- * @param {string} [options.command] - Command that produces the report.
145
+ * @param {{id: string, suite?: string, path?: string}} report - The missing report.
79
146
  * @returns {string} HTML document.
80
147
  * @memberof UnderpostCoverage
81
148
  */
82
- const coverageUnavailablePage = ({ command = 'npm run test:coverage' } = {}) => `<!doctype html>
149
+ const coverageUnavailablePage = (report) => `<!doctype html>
83
150
  <html lang="en">
84
151
  <head>
85
152
  <meta charset="utf-8" />
@@ -99,10 +166,11 @@ const coverageUnavailablePage = ({ command = 'npm run test:coverage' } = {}) =>
99
166
  <body>
100
167
  <main>
101
168
  <h1>Coverage report unavailable</h1>
102
- <p>This build carried no coverage report. The report is generated by the test stage and
103
- bundled into the deploy artifact under <code>${COVERAGE_BUNDLE_DIRECTORY}</code>.</p>
104
- <p>Run <code>${command}</code> in the build environment, then assemble and publish the
105
- artifact again.</p>
169
+ <p>This build carried no <code>${report.id}</code> coverage report. The report is generated
170
+ by the test stage and bundled into the deploy artifact under
171
+ <code>${COVERAGE_BUNDLE_DIRECTORY}/${report.id}</code>.</p>
172
+ <p>Run <code>${coverageReportCommand(report)}</code> in the build environment, then assemble
173
+ and publish the artifact again.</p>
106
174
  </main>
107
175
  </body>
108
176
  </html>
@@ -110,8 +178,11 @@ const coverageUnavailablePage = ({ command = 'npm run test:coverage' } = {}) =>
110
178
 
111
179
  export {
112
180
  COVERAGE_BUNDLE_DIRECTORY,
113
- bundleCoverageReport,
181
+ bundleCoverageReports,
114
182
  coverageReportCandidates,
183
+ coverageReportCommand,
184
+ coverageReportsFactory,
115
185
  coverageUnavailablePage,
186
+ deployCoverageReports,
116
187
  resolveCoverageReportPath,
117
188
  };
@@ -93,12 +93,10 @@ const replaceTemplateReferences = (value, templateRepositoryName, repositoryName
93
93
  /**
94
94
  * Builds the development dependency set for a generated product repository.
95
95
  *
96
- * Product packages replace the engine's broad runtime dependency set with a
97
- * deliberately small product-specific one. The generated repository still
98
- * contains the engine source, build scripts, Vitest configuration and tests,
99
- * though, so those engine dependencies remain necessary when developing or
100
- * testing the checkout. Keeping them as dev dependencies makes CI complete
101
- * without adding them to installations of the published product package.
96
+ * A product's runtime set is the engine's plus whatever its catalog pins, so what stays here is
97
+ * the toolchain the generated repository needs to build and test itself: engine dev
98
+ * dependencies, the template's own, and anything the product already declares at runtime is
99
+ * dropped rather than pinned twice.
102
100
  *
103
101
  * @param {object} [params]
104
102
  * @param {Record<string, string>} [params.engineDependencies]
@@ -181,6 +179,46 @@ const buildProductPackageJson = ({
181
179
  };
182
180
  };
183
181
 
182
+ /**
183
+ * The manifest a product publishes to npm, from the one its repository runs on.
184
+ *
185
+ * A product repository declares the engine's runtime dependencies because it runs the engine
186
+ * source it ships. Somebody installing the product from the registry does not run that
187
+ * checkout: they get the engine through the published `underpost` package, so the tarball
188
+ * declares that package and the catalog's pins, and everything else stays the toolchain the
189
+ * repository builds and tests itself with. Publishing the repository's manifest unchanged
190
+ * would install a second copy of the engine's dependency set next to `underpost`'s own.
191
+ *
192
+ * The rewrite belongs to the publish step alone — never to the checkout, whose install is what
193
+ * a node and a pod run from.
194
+ * @param {object} params
195
+ * @param {object} params.packageJson - The repository's manifest.
196
+ * @param {object} [params.catalog] - That product's catalog.
197
+ * @param {string} [params.underpostVersion] - Engine CLI version to pin; the manifest's own otherwise.
198
+ * @returns {object} The manifest to publish.
199
+ * @memberof PackageBuilder
200
+ */
201
+ const publishedProductPackageJson = ({ packageJson, catalog = {}, underpostVersion = '' } = {}) => {
202
+ if (!packageJson || typeof packageJson !== 'object')
203
+ throw new TypeError('publishedProductPackageJson requires packageJson');
204
+
205
+ const version = `${underpostVersion || packageJson.version || ''}`.replace(/^v/, '');
206
+ if (!version) throw new TypeError('publishedProductPackageJson requires a version to pin the engine CLI at');
207
+
208
+ const dependencies = { underpost: `^${version}`, ...(catalog.packageDependencies ?? {}) };
209
+ const devDependencies = productDevDependenciesFactory({
210
+ engineDependencies: packageJson.dependencies,
211
+ engineDevDependencies: packageJson.devDependencies,
212
+ productDependencies: dependencies,
213
+ });
214
+
215
+ return {
216
+ ...packageJson,
217
+ dependencies,
218
+ ...(Object.keys(devDependencies).length ? { devDependencies } : {}),
219
+ };
220
+ };
221
+
184
222
  /**
185
223
  * Where a deploy id's package manifest lives.
186
224
  *
@@ -276,22 +314,26 @@ const buildDeployPackageJson = ({
276
314
  /**
277
315
  * The product-CLI manifest overrides a catalog declares, for {@link buildProductPackageJson}.
278
316
  *
279
- * A product package replaces the engine's runtime dependency set with its own, so it always
280
- * carries the published `underpost` alongside whatever the catalog pins. A catalog that
281
- * declares no package contract returns nothing, leaving the base template's manifest as it is.
317
+ * A product package carries the engine's own runtime dependencies, not the published
318
+ * `underpost` package. A product repository ships the engine source and runs it directly, so
319
+ * depending on the package installed a second copy of the engine whose nested tree `src/`
320
+ * cannot resolve from — and a dependency pinned at a different version there was reachable
321
+ * only as a dev entry, which a production install omits, leaving the deployment unable to
322
+ * import its own modules. A catalog that declares no package contract returns nothing, leaving
323
+ * the base template's manifest as it is.
282
324
  * @param {object} params
283
325
  * @param {object} params.catalog - The deploy's product catalog.
284
- * @param {string} params.underpostVersion - Version of the engine CLI the product depends on.
326
+ * @param {Record<string, string>} [params.engineDependencies] - The engine's runtime dependencies.
285
327
  * @returns {{customDependencies?: object, customScripts?: object, customBin?: object}} Overrides.
286
328
  * @memberof PackageBuilder
287
329
  */
288
- const productPackageOptionsFactory = ({ catalog = {}, underpostVersion = '' } = {}) => {
330
+ const productPackageOptionsFactory = ({ catalog = {}, engineDependencies = {} } = {}) => {
289
331
  const dependencies = catalog.packageDependencies ?? {};
290
332
  const bin = catalog.packageBin ?? {};
291
333
  if (Object.keys(dependencies).length === 0 && Object.keys(bin).length === 0) return {};
292
334
 
293
335
  return {
294
- customDependencies: { underpost: `^${`${underpostVersion}`.replace(/^v/, '')}`, ...dependencies },
336
+ customDependencies: { ...engineDependencies, ...dependencies },
295
337
  customScripts: { ...catalog.packageScripts },
296
338
  ...(Object.keys(bin).length ? { customBin: { ...bin } } : {}),
297
339
  };
@@ -457,6 +499,7 @@ export {
457
499
  packageRepositoryFactory,
458
500
  productDevDependenciesFactory,
459
501
  productPackageOptionsFactory,
502
+ publishedProductPackageJson,
460
503
  renamePackage,
461
504
  setPackageRepository,
462
505
  stageCliPackage,
@@ -103,8 +103,22 @@ const TEST_TIERS = [
103
103
  'src/server/runtime/conf.js',
104
104
  'src/server/security/crypto.js',
105
105
  ],
106
+ // Non-recursive: the cyberia extension underneath is its own tier, so a
107
+ // tree that strips the product keeps a unit tier that imports none of it.
108
+ recursive: false,
106
109
  description: 'Pure functions with no host, cluster or network dependency.',
107
110
  },
111
+ {
112
+ name: 'cyberia:unit',
113
+ directory: 'test/unit/cyberia',
114
+ groupOrder: 1,
115
+ sources: [
116
+ 'src/projects/cyberia/instance-backup.js',
117
+ 'src/projects/cyberia/instance-data.js',
118
+ 'src/projects/cyberia/stat-balance.js',
119
+ ],
120
+ description: 'Cyberia MMO extension: pure functions over instance data, backups and stat policies.',
121
+ },
108
122
  {
109
123
  name: 'infra:1-security',
110
124
  directory: 'test/integration/infra/1-security',
@@ -128,7 +142,7 @@ const TEST_TIERS = [
128
142
  name: 'infra:3-cluster',
129
143
  directory: 'test/integration/infra/3-cluster',
130
144
  groupOrder: 4,
131
- sources: ['src/cli/docker-compose.js', 'src/server/runtime/conf.js'],
145
+ sources: ['src/cli/docker-compose.js', 'src/db/mongo/MongoExpress.js', 'src/server/runtime/conf.js'],
132
146
  description: 'Instance clustering, node assignment and compute scheduling.',
133
147
  },
134
148
  {
@@ -136,6 +150,7 @@ const TEST_TIERS = [
136
150
  directory: 'test/integration/infra/4-ingress',
137
151
  groupOrder: 5,
138
152
  sources: [
153
+ 'src/server/network/middlewares.js',
139
154
  'src/server/network/router.js',
140
155
  'src/server/network/underpost-compression.js',
141
156
  'src/server/network/underpost-gateway.js',
@@ -169,16 +184,17 @@ const TEST_TIERS = [
169
184
  description: 'Platform application layer served over the provisioned stack.',
170
185
  },
171
186
  {
172
- name: 'cyberia',
187
+ name: 'cyberia:app',
173
188
  directory: 'test/integration/app/cyberia',
174
189
  groupOrder: 7,
175
190
  sources: [
176
191
  'src/api/cyberia-server-defaults/*.js',
177
192
  'src/api/cyberia-instance/cyberia-fallback-capture.js',
178
193
  'src/api/object-layer/object-layer.model.js',
194
+ 'src/projects/cyberia/atlas-sprite-sheet-generator.js',
179
195
  'src/projects/cyberia/shape-generator.js',
180
196
  ],
181
- description: 'Cyberia MMO extension: content, persistence and shape generation.',
197
+ description: 'Cyberia MMO extension: content, persistence, sprite atlases and shape generation.',
182
198
  },
183
199
  {
184
200
  name: 'contracts',
@@ -346,12 +362,44 @@ const coverageThresholdFactory = ({ COVERAGE_ENFORCE, COVERAGE_MIN } = {}) => {
346
362
  * @throws {Error} When a selected project matches no declared tier.
347
363
  * @memberof UnderpostTesting
348
364
  */
349
- const coverageIncludeFactory = (argv = []) => {
350
- const projects = argv.flatMap((arg, index) =>
351
- arg === '--project' ? [argv[index + 1]] : arg.startsWith('--project=') ? [arg.slice('--project='.length)] : [],
352
- );
353
- return [...new Set(resolveTestTiers(projects.filter(Boolean).join(',')).flatMap(({ sources = [] }) => sources))];
354
- };
365
+ const coverageIncludeFactory = (argv = []) => [
366
+ ...new Set(resolveTestTiers(vitestProjectSelector(argv)).flatMap(({ sources = [] }) => sources)),
367
+ ];
368
+
369
+ /**
370
+ * @method vitestProjectSelector
371
+ * @description The tier selector a Vitest argument vector carries, as `--project` flags.
372
+ * @param {string[]} [argv] - Argument vector.
373
+ * @returns {string} Comma separated selector, empty when no project was named.
374
+ * @memberof UnderpostTesting
375
+ */
376
+ const vitestProjectSelector = (argv = []) =>
377
+ argv
378
+ .flatMap((arg, index) =>
379
+ arg === '--project' ? [argv[index + 1]] : arg.startsWith('--project=') ? [arg.slice('--project='.length)] : [],
380
+ )
381
+ .filter(Boolean)
382
+ .join(',');
383
+
384
+ /**
385
+ * @method coverageReportKey
386
+ * @description The directory under `coverage/` a selection's HTML report is written to,
387
+ * derived from the suites the selection spans so a deploy that names `unit,infra,app` and
388
+ * a run that was handed the same tiers as `--project` flags address one report — and so
389
+ * two selections never overwrite each other's on a host that serves both.
390
+ * @param {string} [selector] - Suite names, tier names, or empty for every tier.
391
+ * @returns {string} Suite names in declaration order, joined with `-`.
392
+ * @throws {Error} When a selector matches no declared tier.
393
+ * @memberof UnderpostTesting
394
+ */
395
+ const coverageReportKey = (selector = '') =>
396
+ [
397
+ ...new Set(
398
+ resolveTestTiers(selector)
399
+ .filter(({ delegate }) => !delegate)
400
+ .map(({ name }) => name.split(':')[0]),
401
+ ),
402
+ ].join('-');
355
403
 
356
404
  /**
357
405
  * @method allureManifestsFactory
@@ -518,6 +566,7 @@ export {
518
566
  TEST_TIERS,
519
567
  allureManifestsFactory,
520
568
  coverageIncludeFactory,
569
+ coverageReportKey,
521
570
  coverageThresholdFactory,
522
571
  resolveTestSelection,
523
572
  resolveTestTiers,
@@ -525,4 +574,5 @@ export {
525
574
  testProjectsFactory,
526
575
  testSuiteNames,
527
576
  vitestArgsFactory,
577
+ vitestProjectSelector,
528
578
  };
@@ -152,8 +152,14 @@ const buildCrudController = (service, extend = {}) => {
152
152
  class CrudController {
153
153
  /** @static @memberof Middlewares */
154
154
  static post = serviceHandler(service.post);
155
- /** @static @memberof Middlewares */
156
- static get = serviceHandler(service.get, { pagination: true });
155
+ /**
156
+ * Reads are public and cross-origin by default: the browser client is served from its own
157
+ * origin and every list/read it issues is a plain GET against this API. Without the header
158
+ * the response arrives with status 200 and is then discarded by the same-origin policy,
159
+ * which reads as a silent feature failure rather than a transport error.
160
+ * @static @memberof Middlewares
161
+ */
162
+ static get = serviceHandler(service.get, { pagination: true, crossOrigin: true });
157
163
  /** @static @memberof Middlewares */
158
164
  static put = serviceHandler(service.put);
159
165
  /** @static @memberof Middlewares */