@underpostnet/cyberia 3.2.80

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 (821) hide show
  1. package/.dockerignore +14 -0
  2. package/.env.example +144 -0
  3. package/.github/workflows/cyberia-client.cd.yml +40 -0
  4. package/.github/workflows/cyberia-server.cd.yml +40 -0
  5. package/.github/workflows/docker-image.cyberia-client.ci.yml +71 -0
  6. package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +70 -0
  7. package/.github/workflows/docker-image.cyberia-server.ci.yml +71 -0
  8. package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +71 -0
  9. package/.github/workflows/docker-image.engine-cyberia.ci.yml +53 -0
  10. package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +53 -0
  11. package/.github/workflows/engine-cyberia.cd.yml +157 -0
  12. package/.github/workflows/engine-cyberia.ci.yml +125 -0
  13. package/.github/workflows/ghpkg.ci.yml +160 -0
  14. package/.github/workflows/gitlab.ci.yml +20 -0
  15. package/.github/workflows/hardhat.ci.yml +82 -0
  16. package/.github/workflows/npmpkg.ci.yml +126 -0
  17. package/.github/workflows/publish.ci.yml +87 -0
  18. package/.github/workflows/publish.cyberia.ci.yml +79 -0
  19. package/.github/workflows/pwa-microservices-template-page.cd.yml +65 -0
  20. package/.github/workflows/pwa-microservices-template-test.ci.yml +33 -0
  21. package/.github/workflows/release.cd.yml +46 -0
  22. package/.nycrc +9 -0
  23. package/.prettierignore +12 -0
  24. package/.prettierrc +9 -0
  25. package/.vscode/extensions.json +10 -0
  26. package/.vscode/settings.json +26 -0
  27. package/CHANGELOG.md +2516 -0
  28. package/CLI-HELP.md +1099 -0
  29. package/Dockerfile +161 -0
  30. package/Dockerfile.dev +161 -0
  31. package/Dockerfile.test +165 -0
  32. package/LICENSE +21 -0
  33. package/README.md +259 -0
  34. package/baremetal/commission-workflows.json +211 -0
  35. package/baremetal/packer-workflows.json +24 -0
  36. package/bin/build.js +261 -0
  37. package/bin/build.template.js +33 -0
  38. package/bin/cyberia.js +5627 -0
  39. package/bin/deploy.js +1547 -0
  40. package/bin/index.js +5627 -0
  41. package/bump.config.js +27 -0
  42. package/compose.env +144 -0
  43. package/conf.js +952 -0
  44. package/deployment.yaml +418 -0
  45. package/docker-compose.yml +434 -0
  46. package/examples/static-page/README.md +150 -0
  47. package/examples/static-page/ssr-components/CustomPage.js +554 -0
  48. package/examples/static-page/static-config-example.json +57 -0
  49. package/hardhat/.env.example +31 -0
  50. package/hardhat/contracts/ObjectLayerToken.sol +391 -0
  51. package/hardhat/deployments/.gitkeep +0 -0
  52. package/hardhat/deployments/hardhat-ObjectLayerToken.json +11 -0
  53. package/hardhat/hardhat.config.js +136 -0
  54. package/hardhat/ignition/modules/.gitkeep +0 -0
  55. package/hardhat/ignition/modules/ObjectLayerToken.js +21 -0
  56. package/hardhat/networks/besu-object-layer.network.json +138 -0
  57. package/hardhat/package-lock.json +2871 -0
  58. package/hardhat/package.json +43 -0
  59. package/hardhat/scripts/deployObjectLayerToken.js +98 -0
  60. package/hardhat/test/ObjectLayerToken.js +696 -0
  61. package/hardhat/types/ethers-contracts/ObjectLayerToken.ts +690 -0
  62. package/hardhat/types/ethers-contracts/common.ts +92 -0
  63. package/hardhat/types/ethers-contracts/factories/ObjectLayerToken__factory.ts +1055 -0
  64. package/hardhat/types/ethers-contracts/factories/index.ts +4 -0
  65. package/hardhat/types/ethers-contracts/hardhat.d.ts +47 -0
  66. package/hardhat/types/ethers-contracts/index.ts +6 -0
  67. package/ipfs/configure-ipfs.sh +13 -0
  68. package/jsconfig.json +9 -0
  69. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +48 -0
  70. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +48 -0
  71. package/manifests/deployment/adminer/deployment.yaml +32 -0
  72. package/manifests/deployment/adminer/kustomization.yaml +7 -0
  73. package/manifests/deployment/adminer/service.yaml +13 -0
  74. package/manifests/deployment/dd-cyberia-development/deployment.yaml +418 -0
  75. package/manifests/deployment/dd-cyberia-development/grpc-service.yaml +17 -0
  76. package/manifests/deployment/dd-cyberia-development/proxy.yaml +189 -0
  77. package/manifests/deployment/dd-cyberia-development/pv-pvc.yaml +164 -0
  78. package/manifests/deployment/dd-default-development/deployment.yaml +163 -0
  79. package/manifests/deployment/dd-default-development/proxy.yaml +46 -0
  80. package/manifests/deployment/fastapi/backend-deployment.yml +120 -0
  81. package/manifests/deployment/fastapi/backend-service.yml +19 -0
  82. package/manifests/deployment/fastapi/frontend-deployment.yml +54 -0
  83. package/manifests/deployment/fastapi/frontend-service.yml +15 -0
  84. package/manifests/deployment/fastapi/initial_data.sh +8 -0
  85. package/manifests/deployment/kafka/deployment.yaml +67 -0
  86. package/manifests/deployment/mongo-express/deployment.yaml +62 -0
  87. package/manifests/deployment/phpmyadmin/deployment.yaml +54 -0
  88. package/manifests/deployment/playwright/deployment.yaml +52 -0
  89. package/manifests/deployment/spark/spark-pi-py.yaml +20 -0
  90. package/manifests/deployment/tensorflow/tf-gpu-test.yaml +63 -0
  91. package/manifests/envoy-service-nodeport.yaml +22 -0
  92. package/manifests/grafana/deployment.yaml +64 -0
  93. package/manifests/grafana/kustomization.yaml +7 -0
  94. package/manifests/grafana/pvc.yaml +12 -0
  95. package/manifests/grafana/service.yaml +14 -0
  96. package/manifests/grafana/storage-class.yaml +9 -0
  97. package/manifests/ipfs/configmap.yaml +64 -0
  98. package/manifests/ipfs/headless-service.yaml +35 -0
  99. package/manifests/ipfs/kustomization.yaml +8 -0
  100. package/manifests/ipfs/statefulset.yaml +149 -0
  101. package/manifests/ipfs/storage-class.yaml +9 -0
  102. package/manifests/kind-config-dev.yaml +20 -0
  103. package/manifests/kind-config.yaml +12 -0
  104. package/manifests/kubeadm-calico-config.yaml +14 -0
  105. package/manifests/letsencrypt-prod.yaml +14 -0
  106. package/manifests/lxd/lxd-admin-profile.yaml +26 -0
  107. package/manifests/lxd/lxd-preseed.yaml +30 -0
  108. package/manifests/mariadb/kustomization.yaml +9 -0
  109. package/manifests/mariadb/pv.yaml +12 -0
  110. package/manifests/mariadb/pvc.yaml +10 -0
  111. package/manifests/mariadb/service.yaml +10 -0
  112. package/manifests/mariadb/statefulset.yaml +56 -0
  113. package/manifests/mariadb/storage-class.yaml +10 -0
  114. package/manifests/mongodb/headless-service.yaml +10 -0
  115. package/manifests/mongodb/kustomization.yaml +11 -0
  116. package/manifests/mongodb/pv-pvc.yaml +59 -0
  117. package/manifests/mongodb/statefulset.yaml +113 -0
  118. package/manifests/mongodb/storage-class.yaml +9 -0
  119. package/manifests/mongodb-4.4/headless-service.yaml +10 -0
  120. package/manifests/mongodb-4.4/kustomization.yaml +9 -0
  121. package/manifests/mongodb-4.4/mongodb-nodeport.yaml +17 -0
  122. package/manifests/mongodb-4.4/pv-pvc.yaml +19 -0
  123. package/manifests/mongodb-4.4/service-deployment.yaml +61 -0
  124. package/manifests/mongodb-4.4/statefulset.yaml +79 -0
  125. package/manifests/mongodb-4.4/storage-class.yaml +9 -0
  126. package/manifests/mysql/kustomization.yaml +7 -0
  127. package/manifests/mysql/pv-pvc.yaml +27 -0
  128. package/manifests/mysql/statefulset.yaml +55 -0
  129. package/manifests/postgresql/configmap.yaml +9 -0
  130. package/manifests/postgresql/kustomization.yaml +10 -0
  131. package/manifests/postgresql/pv.yaml +15 -0
  132. package/manifests/postgresql/pvc.yaml +13 -0
  133. package/manifests/postgresql/service.yaml +10 -0
  134. package/manifests/postgresql/statefulset.yaml +37 -0
  135. package/manifests/prometheus/deployment.yaml +82 -0
  136. package/manifests/pv-pvc-dd.yaml +34 -0
  137. package/manifests/valkey/kustomization.yaml +7 -0
  138. package/manifests/valkey/service.yaml +10 -0
  139. package/manifests/valkey/statefulset.yaml +37 -0
  140. package/manifests/valkey/valkey-nodeport.yaml +17 -0
  141. package/mongodb/entrypoint.sh +76 -0
  142. package/nginx.conf +128 -0
  143. package/nodemon.json +6 -0
  144. package/package.json +101 -0
  145. package/packer/images/Rocky9Amd64/Makefile +62 -0
  146. package/packer/images/Rocky9Amd64/QUICKSTART.md +113 -0
  147. package/packer/images/Rocky9Amd64/README.md +122 -0
  148. package/packer/images/Rocky9Amd64/http/rocky9.ks.pkrtpl.hcl +114 -0
  149. package/packer/images/Rocky9Amd64/rocky9.pkr.hcl +164 -0
  150. package/packer/images/Rocky9Arm64/Makefile +69 -0
  151. package/packer/images/Rocky9Arm64/README.md +122 -0
  152. package/packer/images/Rocky9Arm64/http/rocky9.ks.pkrtpl.hcl +114 -0
  153. package/packer/images/Rocky9Arm64/rocky9.pkr.hcl +171 -0
  154. package/packer/scripts/fuse-nbd +64 -0
  155. package/packer/scripts/fuse-tar-root +63 -0
  156. package/proxy.yaml +189 -0
  157. package/pv-pvc.yaml +164 -0
  158. package/scripts/device-scan.sh +65 -0
  159. package/scripts/disk-clean.sh +182 -0
  160. package/scripts/disk-devices.sh +13 -0
  161. package/scripts/gen-fqdns.sh +14 -0
  162. package/scripts/gpu-diag.sh +19 -0
  163. package/scripts/ip-info.sh +118 -0
  164. package/scripts/ipxe-setup.sh +200 -0
  165. package/scripts/k3s-node-setup.sh +124 -0
  166. package/scripts/kubeadm-node-setup.sh +317 -0
  167. package/scripts/link-local-underpost-cli.sh +6 -0
  168. package/scripts/lxd-vm-setup.sh +208 -0
  169. package/scripts/maas-nat-firewalld.sh +145 -0
  170. package/scripts/maas-setup.sh +135 -0
  171. package/scripts/maas-upload-boot-resource.sh +183 -0
  172. package/scripts/nat-iptables.sh +111 -0
  173. package/scripts/nvim.sh +91 -0
  174. package/scripts/packer-init-vars-file.sh +40 -0
  175. package/scripts/packer-setup.sh +289 -0
  176. package/scripts/ports-ls.sh +33 -0
  177. package/scripts/quick-tftp.sh +19 -0
  178. package/scripts/rhel-grpc-setup.sh +56 -0
  179. package/scripts/rocky-kickstart.sh +986 -0
  180. package/scripts/rocky-pwa.sh +200 -0
  181. package/scripts/rocky-setup.sh +106 -0
  182. package/scripts/rpmfusion-ffmpeg-setup.sh +32 -0
  183. package/scripts/ssl.sh +170 -0
  184. package/scripts/test-monitor.sh +248 -0
  185. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +30 -0
  186. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +155 -0
  187. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +39 -0
  188. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +382 -0
  189. package/src/api/core/core.controller.js +8 -0
  190. package/src/api/core/core.model.js +11 -0
  191. package/src/api/core/core.router.js +23 -0
  192. package/src/api/core/core.service.js +30 -0
  193. package/src/api/crypto/crypto.controller.js +10 -0
  194. package/src/api/crypto/crypto.model.js +23 -0
  195. package/src/api/crypto/crypto.router.js +23 -0
  196. package/src/api/crypto/crypto.service.js +64 -0
  197. package/src/api/cyberia-action/cyberia-action.controller.js +8 -0
  198. package/src/api/cyberia-action/cyberia-action.model.js +79 -0
  199. package/src/api/cyberia-action/cyberia-action.router.js +21 -0
  200. package/src/api/cyberia-action/cyberia-action.service.js +58 -0
  201. package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +27 -0
  202. package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +100 -0
  203. package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +70 -0
  204. package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +152 -0
  205. package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +8 -0
  206. package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +36 -0
  207. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +20 -0
  208. package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +57 -0
  209. package/src/api/cyberia-entity/cyberia-entity.controller.js +6 -0
  210. package/src/api/cyberia-entity/cyberia-entity.model.js +25 -0
  211. package/src/api/cyberia-entity/cyberia-entity.router.js +17 -0
  212. package/src/api/cyberia-entity/cyberia-entity.service.js +42 -0
  213. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +6 -0
  214. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.model.js +67 -0
  215. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +17 -0
  216. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +46 -0
  217. package/src/api/cyberia-instance/cyberia-fallback-world.js +337 -0
  218. package/src/api/cyberia-instance/cyberia-instance-boot.service.js +57 -0
  219. package/src/api/cyberia-instance/cyberia-instance-map.service.js +583 -0
  220. package/src/api/cyberia-instance/cyberia-instance.controller.js +42 -0
  221. package/src/api/cyberia-instance/cyberia-instance.model.js +117 -0
  222. package/src/api/cyberia-instance/cyberia-instance.router.js +75 -0
  223. package/src/api/cyberia-instance/cyberia-instance.service.js +190 -0
  224. package/src/api/cyberia-instance/cyberia-portal-connector.js +260 -0
  225. package/src/api/cyberia-instance/cyberia-world-generator.js +618 -0
  226. package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +6 -0
  227. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +200 -0
  228. package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +17 -0
  229. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +46 -0
  230. package/src/api/cyberia-map/cyberia-map.controller.js +6 -0
  231. package/src/api/cyberia-map/cyberia-map.model.js +33 -0
  232. package/src/api/cyberia-map/cyberia-map.router.js +19 -0
  233. package/src/api/cyberia-map/cyberia-map.service.js +76 -0
  234. package/src/api/cyberia-quest/cyberia-quest.controller.js +9 -0
  235. package/src/api/cyberia-quest/cyberia-quest.model.js +67 -0
  236. package/src/api/cyberia-quest/cyberia-quest.router.js +26 -0
  237. package/src/api/cyberia-quest/cyberia-quest.service.js +97 -0
  238. package/src/api/cyberia-quest-progress/cyberia-quest-progress.controller.js +6 -0
  239. package/src/api/cyberia-quest-progress/cyberia-quest-progress.model.js +49 -0
  240. package/src/api/cyberia-quest-progress/cyberia-quest-progress.router.js +21 -0
  241. package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +42 -0
  242. package/src/api/cyberia-saga/cyberia-saga.controller.js +6 -0
  243. package/src/api/cyberia-saga/cyberia-saga.model.js +59 -0
  244. package/src/api/cyberia-saga/cyberia-saga.router.js +17 -0
  245. package/src/api/cyberia-saga/cyberia-saga.service.js +42 -0
  246. package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +1076 -0
  247. package/src/api/cyberia-skill/cyberia-skill.controller.js +6 -0
  248. package/src/api/cyberia-skill/cyberia-skill.model.js +50 -0
  249. package/src/api/cyberia-skill/cyberia-skill.router.js +17 -0
  250. package/src/api/cyberia-skill/cyberia-skill.service.js +42 -0
  251. package/src/api/default/default.controller.js +6 -0
  252. package/src/api/default/default.model.js +20 -0
  253. package/src/api/default/default.router.js +21 -0
  254. package/src/api/default/default.service.js +42 -0
  255. package/src/api/document/document.controller.js +9 -0
  256. package/src/api/document/document.model.js +95 -0
  257. package/src/api/document/document.router.js +32 -0
  258. package/src/api/document/document.service.js +633 -0
  259. package/src/api/file/file.controller.js +15 -0
  260. package/src/api/file/file.model.js +127 -0
  261. package/src/api/file/file.ref.json +39 -0
  262. package/src/api/file/file.router.js +36 -0
  263. package/src/api/file/file.service.js +499 -0
  264. package/src/api/instance/instance.controller.js +8 -0
  265. package/src/api/instance/instance.model.js +34 -0
  266. package/src/api/instance/instance.router.js +27 -0
  267. package/src/api/instance/instance.service.js +75 -0
  268. package/src/api/ipfs/ipfs.controller.js +8 -0
  269. package/src/api/ipfs/ipfs.model.js +77 -0
  270. package/src/api/ipfs/ipfs.router.js +26 -0
  271. package/src/api/ipfs/ipfs.service.js +145 -0
  272. package/src/api/object-layer/object-layer.controller.js +18 -0
  273. package/src/api/object-layer/object-layer.model.js +200 -0
  274. package/src/api/object-layer/object-layer.router.js +729 -0
  275. package/src/api/object-layer/object-layer.service.js +894 -0
  276. package/src/api/object-layer-render-frames/object-layer-render-frames.controller.js +6 -0
  277. package/src/api/object-layer-render-frames/object-layer-render-frames.model.js +93 -0
  278. package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +17 -0
  279. package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +46 -0
  280. package/src/api/test/test.controller.js +14 -0
  281. package/src/api/test/test.model.js +14 -0
  282. package/src/api/test/test.router.js +23 -0
  283. package/src/api/test/test.service.js +35 -0
  284. package/src/api/types.js +24 -0
  285. package/src/api/user/guest.service.js +100 -0
  286. package/src/api/user/postman_collection.json +216 -0
  287. package/src/api/user/user.build.js +16 -0
  288. package/src/api/user/user.controller.js +35 -0
  289. package/src/api/user/user.model.js +142 -0
  290. package/src/api/user/user.router.js +367 -0
  291. package/src/api/user/user.service.js +582 -0
  292. package/src/api.js +23 -0
  293. package/src/cli/baremetal.js +3947 -0
  294. package/src/cli/cloud-init.js +673 -0
  295. package/src/cli/cluster.js +1395 -0
  296. package/src/cli/db.js +1533 -0
  297. package/src/cli/deploy.js +1542 -0
  298. package/src/cli/docker-compose.js +802 -0
  299. package/src/cli/env.js +177 -0
  300. package/src/cli/fs.js +380 -0
  301. package/src/cli/image.js +389 -0
  302. package/src/cli/index.js +1059 -0
  303. package/src/cli/ipfs.js +182 -0
  304. package/src/cli/kickstart.js +271 -0
  305. package/src/cli/kubectl.js +214 -0
  306. package/src/cli/lxd.js +1232 -0
  307. package/src/cli/monitor.js +659 -0
  308. package/src/cli/release.js +595 -0
  309. package/src/cli/repository.js +1969 -0
  310. package/src/cli/run.js +3326 -0
  311. package/src/cli/secrets.js +151 -0
  312. package/src/cli/ssh.js +975 -0
  313. package/src/cli/static.js +653 -0
  314. package/src/cli/system.js +332 -0
  315. package/src/cli/test.js +176 -0
  316. package/src/client/Cryptokoyn.index.js +30 -0
  317. package/src/client/CyberiaPortal.index.js +31 -0
  318. package/src/client/Default.index.js +77 -0
  319. package/src/client/Itemledger.index.js +35 -0
  320. package/src/client/Underpost.index.js +31 -0
  321. package/src/client/components/core/404.js +20 -0
  322. package/src/client/components/core/500.js +20 -0
  323. package/src/client/components/core/Account.js +630 -0
  324. package/src/client/components/core/AgGrid.js +238 -0
  325. package/src/client/components/core/Alert.js +76 -0
  326. package/src/client/components/core/AppStore.js +69 -0
  327. package/src/client/components/core/Auth.js +353 -0
  328. package/src/client/components/core/Badge.js +26 -0
  329. package/src/client/components/core/Blockchain.js +41 -0
  330. package/src/client/components/core/Blog.js +9 -0
  331. package/src/client/components/core/BtnIcon.js +109 -0
  332. package/src/client/components/core/CalendarCore.js +437 -0
  333. package/src/client/components/core/Chat.js +62 -0
  334. package/src/client/components/core/ClientEvents.js +163 -0
  335. package/src/client/components/core/ColorPaletteElement.js +309 -0
  336. package/src/client/components/core/CommonJs.js +1014 -0
  337. package/src/client/components/core/Content.js +349 -0
  338. package/src/client/components/core/Css.js +1134 -0
  339. package/src/client/components/core/CssCore.js +881 -0
  340. package/src/client/components/core/D3Chart.js +44 -0
  341. package/src/client/components/core/Docs.js +478 -0
  342. package/src/client/components/core/DropDown.js +321 -0
  343. package/src/client/components/core/EventBus.js +96 -0
  344. package/src/client/components/core/EventsUI.js +130 -0
  345. package/src/client/components/core/FileExplorer.js +1406 -0
  346. package/src/client/components/core/FullScreen.js +201 -0
  347. package/src/client/components/core/Input.js +537 -0
  348. package/src/client/components/core/Keyboard.js +80 -0
  349. package/src/client/components/core/KeyboardAvoidance.js +145 -0
  350. package/src/client/components/core/LoadingAnimation.js +152 -0
  351. package/src/client/components/core/LogIn.js +212 -0
  352. package/src/client/components/core/LogOut.js +72 -0
  353. package/src/client/components/core/Logger.js +22 -0
  354. package/src/client/components/core/Modal.js +2956 -0
  355. package/src/client/components/core/NotificationManager.js +80 -0
  356. package/src/client/components/core/Pagination.js +252 -0
  357. package/src/client/components/core/Panel.js +1308 -0
  358. package/src/client/components/core/PanelForm.js +915 -0
  359. package/src/client/components/core/Polyhedron.js +947 -0
  360. package/src/client/components/core/PublicProfile.js +895 -0
  361. package/src/client/components/core/Recover.js +211 -0
  362. package/src/client/components/core/Responsive.js +146 -0
  363. package/src/client/components/core/RichText.js +33 -0
  364. package/src/client/components/core/Router.js +523 -0
  365. package/src/client/components/core/Scroll.js +76 -0
  366. package/src/client/components/core/SearchBox.js +1007 -0
  367. package/src/client/components/core/SignUp.js +153 -0
  368. package/src/client/components/core/SocketIo.js +163 -0
  369. package/src/client/components/core/SocketIoHandler.js +75 -0
  370. package/src/client/components/core/Stream.js +161 -0
  371. package/src/client/components/core/ToggleSwitch.js +89 -0
  372. package/src/client/components/core/ToolTip.js +78 -0
  373. package/src/client/components/core/Translate.js +694 -0
  374. package/src/client/components/core/Validator.js +140 -0
  375. package/src/client/components/core/VanillaJs.js +485 -0
  376. package/src/client/components/core/Wallet.js +95 -0
  377. package/src/client/components/core/WebComponent.js +44 -0
  378. package/src/client/components/core/Webhook.js +58 -0
  379. package/src/client/components/core/Worker.js +313 -0
  380. package/src/client/components/core/windowGetDimensions.js +269 -0
  381. package/src/client/components/cryptokoyn/AppShellCryptokoyn.js +352 -0
  382. package/src/client/components/cryptokoyn/AppStoreCryptokoyn.js +5 -0
  383. package/src/client/components/cryptokoyn/CssCryptokoyn.js +212 -0
  384. package/src/client/components/cryptokoyn/LogInCryptokoyn.js +16 -0
  385. package/src/client/components/cryptokoyn/LogOutCryptokoyn.js +12 -0
  386. package/src/client/components/cryptokoyn/RouterCryptokoyn.js +37 -0
  387. package/src/client/components/cryptokoyn/SettingsCryptokoyn.js +16 -0
  388. package/src/client/components/cryptokoyn/SignUpCryptokoyn.js +11 -0
  389. package/src/client/components/cryptokoyn/SocketIoCryptokoyn.js +6 -0
  390. package/src/client/components/cyberia/ActionEngineCyberia.js +1867 -0
  391. package/src/client/components/cyberia/EntityEngineCyberia.js +585 -0
  392. package/src/client/components/cyberia/InstanceEngineCyberia.js +1088 -0
  393. package/src/client/components/cyberia/InstanceSelectionView.js +900 -0
  394. package/src/client/components/cyberia/MapEngineCyberia.js +1976 -0
  395. package/src/client/components/cyberia/ObjectLayerEngine.js +1540 -0
  396. package/src/client/components/cyberia/ObjectLayerEngineModal.js +2420 -0
  397. package/src/client/components/cyberia/ObjectLayerEngineViewer.js +1522 -0
  398. package/src/client/components/cyberia/SharedDefaultsCyberia.js +528 -0
  399. package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +772 -0
  400. package/src/client/components/cyberia-portal/AppStoreCyberiaPortal.js +5 -0
  401. package/src/client/components/cyberia-portal/CssCyberiaPortal.js +254 -0
  402. package/src/client/components/cyberia-portal/LogInCyberiaPortal.js +21 -0
  403. package/src/client/components/cyberia-portal/LogOutCyberiaPortal.js +16 -0
  404. package/src/client/components/cyberia-portal/MainBodyCyberiaPortal.js +518 -0
  405. package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +72 -0
  406. package/src/client/components/cyberia-portal/SettingsCyberiaPortal.js +16 -0
  407. package/src/client/components/cyberia-portal/SignUpCyberiaPortal.js +13 -0
  408. package/src/client/components/cyberia-portal/SocketIoCyberiaPortal.js +6 -0
  409. package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +48 -0
  410. package/src/client/components/default/AppShellDefault.js +765 -0
  411. package/src/client/components/default/AppStoreDefault.js +5 -0
  412. package/src/client/components/default/CommonDefault.js +29 -0
  413. package/src/client/components/default/CssDefault.js +27 -0
  414. package/src/client/components/default/LogInDefault.js +16 -0
  415. package/src/client/components/default/LogOutDefault.js +12 -0
  416. package/src/client/components/default/RouterDefault.js +47 -0
  417. package/src/client/components/default/SettingsDefault.js +16 -0
  418. package/src/client/components/default/SignUpDefault.js +11 -0
  419. package/src/client/components/default/SocketIoDefault.js +6 -0
  420. package/src/client/components/default/TranslateDefault.js +7 -0
  421. package/src/client/components/itemledger/AppShellItemledger.js +353 -0
  422. package/src/client/components/itemledger/AppStoreItemledger.js +5 -0
  423. package/src/client/components/itemledger/CssItemledger.js +212 -0
  424. package/src/client/components/itemledger/LogInItemledger.js +17 -0
  425. package/src/client/components/itemledger/LogOutItemledger.js +12 -0
  426. package/src/client/components/itemledger/RouterItemledger.js +38 -0
  427. package/src/client/components/itemledger/SettingsItemledger.js +16 -0
  428. package/src/client/components/itemledger/SignUpItemledger.js +11 -0
  429. package/src/client/components/itemledger/SocketIoItemledger.js +6 -0
  430. package/src/client/components/itemledger/TranslateItemledger.js +7 -0
  431. package/src/client/components/underpost/AppShellUnderpost.js +691 -0
  432. package/src/client/components/underpost/AppStoreUnderpost.js +5 -0
  433. package/src/client/components/underpost/CssUnderpost.js +281 -0
  434. package/src/client/components/underpost/CyberpunkBloggerUnderpost.js +879 -0
  435. package/src/client/components/underpost/DocumentSearchProvider.js +448 -0
  436. package/src/client/components/underpost/LabGalleryUnderpost.js +79 -0
  437. package/src/client/components/underpost/LogInUnderpost.js +25 -0
  438. package/src/client/components/underpost/LogOutUnderpost.js +17 -0
  439. package/src/client/components/underpost/RouterUnderpost.js +45 -0
  440. package/src/client/components/underpost/SettingsUnderpost.js +16 -0
  441. package/src/client/components/underpost/SignUpUnderpost.js +11 -0
  442. package/src/client/components/underpost/SocketIoUnderpost.js +6 -0
  443. package/src/client/components/underpost/TranslateUnderpost.js +10 -0
  444. package/src/client/public/cryptokoyn/assets/banner/koyn-social.png +0 -0
  445. package/src/client/public/cryptokoyn/assets/logo/base-icon.png +0 -0
  446. package/src/client/public/cryptokoyn/assets/logo/cryptokoyn.png +0 -0
  447. package/src/client/public/cryptokoyn/assets/splash/android-chrome-144x144.png +0 -0
  448. package/src/client/public/cryptokoyn/assets/splash/android-chrome-192x192.png +0 -0
  449. package/src/client/public/cryptokoyn/assets/splash/android-chrome-256x256.png +0 -0
  450. package/src/client/public/cryptokoyn/assets/splash/android-chrome-36x36.png +0 -0
  451. package/src/client/public/cryptokoyn/assets/splash/android-chrome-48x48.png +0 -0
  452. package/src/client/public/cryptokoyn/assets/splash/android-chrome-72x72.png +0 -0
  453. package/src/client/public/cryptokoyn/assets/splash/android-chrome-96x96.png +0 -0
  454. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-114x114-precomposed.png +0 -0
  455. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-114x114.png +0 -0
  456. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-120x120-precomposed.png +0 -0
  457. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-120x120.png +0 -0
  458. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-144x144-precomposed.png +0 -0
  459. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-144x144.png +0 -0
  460. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-152x152-precomposed.png +0 -0
  461. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-152x152.png +0 -0
  462. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-180x180-precomposed.png +0 -0
  463. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-180x180.png +0 -0
  464. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-57x57-precomposed.png +0 -0
  465. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-57x57.png +0 -0
  466. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-60x60-precomposed.png +0 -0
  467. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-60x60.png +0 -0
  468. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-72x72-precomposed.png +0 -0
  469. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-72x72.png +0 -0
  470. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-76x76-precomposed.png +0 -0
  471. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-76x76.png +0 -0
  472. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-precomposed.png +0 -0
  473. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon.png +0 -0
  474. package/src/client/public/cryptokoyn/assets/splash/favicon-16x16.png +0 -0
  475. package/src/client/public/cryptokoyn/assets/splash/favicon-32x32.png +0 -0
  476. package/src/client/public/cryptokoyn/assets/splash/mstile-144x144.png +0 -0
  477. package/src/client/public/cryptokoyn/assets/splash/mstile-150x150.png +0 -0
  478. package/src/client/public/cryptokoyn/assets/splash/mstile-310x150.png +0 -0
  479. package/src/client/public/cryptokoyn/assets/splash/mstile-310x310.png +0 -0
  480. package/src/client/public/cryptokoyn/assets/splash/mstile-70x70.png +0 -0
  481. package/src/client/public/cryptokoyn/assets/splash/safari-pinned-tab.svg +42 -0
  482. package/src/client/public/cryptokoyn/browserconfig.xml +12 -0
  483. package/src/client/public/cryptokoyn/favicon.ico +0 -0
  484. package/src/client/public/cryptokoyn/microdata.json +85 -0
  485. package/src/client/public/cryptokoyn/site.webmanifest +57 -0
  486. package/src/client/public/cryptokoyn/sitemap +148 -0
  487. package/src/client/public/cyberia-docs/ACTION-SYSTEM.md +307 -0
  488. package/src/client/public/cyberia-docs/ARCHITECTURE.md +309 -0
  489. package/src/client/public/cyberia-docs/CYBERIA-CLI.md +204 -0
  490. package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +341 -0
  491. package/src/client/public/cyberia-docs/CYBERIA-LORE.md +84 -0
  492. package/src/client/public/cyberia-docs/CYBERIA-SAGA.md +394 -0
  493. package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +294 -0
  494. package/src/client/public/cyberia-docs/CYBERIA.md +259 -0
  495. package/src/client/public/cyberia-docs/ENTITY-PROFILE.md +242 -0
  496. package/src/client/public/cyberia-docs/HARDHAT-MODULE.md +300 -0
  497. package/src/client/public/cyberia-docs/OFF-CHAIN-ECONOMY.md +309 -0
  498. package/src/client/public/cyberia-docs/QUEST-SYSTEM.md +228 -0
  499. package/src/client/public/cyberia-docs/ROADMAP.md +240 -0
  500. package/src/client/public/cyberia-docs/UNDERPOST-PLATFORM.md +106 -0
  501. package/src/client/public/cyberia-docs/WHITE-PAPER.md +740 -0
  502. package/src/client/public/default/android-chrome-144x144.png +0 -0
  503. package/src/client/public/default/android-chrome-192x192.png +0 -0
  504. package/src/client/public/default/android-chrome-256x256.png +0 -0
  505. package/src/client/public/default/android-chrome-36x36.png +0 -0
  506. package/src/client/public/default/android-chrome-384x384.png +0 -0
  507. package/src/client/public/default/android-chrome-48x48.png +0 -0
  508. package/src/client/public/default/android-chrome-512x512.png +0 -0
  509. package/src/client/public/default/android-chrome-72x72.png +0 -0
  510. package/src/client/public/default/android-chrome-96x96.png +0 -0
  511. package/src/client/public/default/apple-touch-icon-1024x1024.png +0 -0
  512. package/src/client/public/default/apple-touch-icon-114x114-precomposed.png +0 -0
  513. package/src/client/public/default/apple-touch-icon-114x114.png +0 -0
  514. package/src/client/public/default/apple-touch-icon-120x120-precomposed.png +0 -0
  515. package/src/client/public/default/apple-touch-icon-120x120.png +0 -0
  516. package/src/client/public/default/apple-touch-icon-144x144-precomposed.png +0 -0
  517. package/src/client/public/default/apple-touch-icon-144x144.png +0 -0
  518. package/src/client/public/default/apple-touch-icon-152x152-precomposed.png +0 -0
  519. package/src/client/public/default/apple-touch-icon-152x152.png +0 -0
  520. package/src/client/public/default/apple-touch-icon-167x167.png +0 -0
  521. package/src/client/public/default/apple-touch-icon-180x180-precomposed.png +0 -0
  522. package/src/client/public/default/apple-touch-icon-180x180.png +0 -0
  523. package/src/client/public/default/apple-touch-icon-57x57-precomposed.png +0 -0
  524. package/src/client/public/default/apple-touch-icon-57x57.png +0 -0
  525. package/src/client/public/default/apple-touch-icon-60x60-precomposed.png +0 -0
  526. package/src/client/public/default/apple-touch-icon-60x60.png +0 -0
  527. package/src/client/public/default/apple-touch-icon-72x72-precomposed.png +0 -0
  528. package/src/client/public/default/apple-touch-icon-72x72.png +0 -0
  529. package/src/client/public/default/apple-touch-icon-76x76-precomposed.png +0 -0
  530. package/src/client/public/default/apple-touch-icon-76x76.png +0 -0
  531. package/src/client/public/default/apple-touch-icon-precomposed.png +0 -0
  532. package/src/client/public/default/apple-touch-icon.png +0 -0
  533. package/src/client/public/default/apple-touch-startup-image-1125x2436.png +0 -0
  534. package/src/client/public/default/apple-touch-startup-image-1136x640.png +0 -0
  535. package/src/client/public/default/apple-touch-startup-image-1170x2532.png +0 -0
  536. package/src/client/public/default/apple-touch-startup-image-1179x2556.png +0 -0
  537. package/src/client/public/default/apple-touch-startup-image-1242x2208.png +0 -0
  538. package/src/client/public/default/apple-touch-startup-image-1242x2688.png +0 -0
  539. package/src/client/public/default/apple-touch-startup-image-1284x2778.png +0 -0
  540. package/src/client/public/default/apple-touch-startup-image-1290x2796.png +0 -0
  541. package/src/client/public/default/apple-touch-startup-image-1334x750.png +0 -0
  542. package/src/client/public/default/apple-touch-startup-image-1488x2266.png +0 -0
  543. package/src/client/public/default/apple-touch-startup-image-1536x2048.png +0 -0
  544. package/src/client/public/default/apple-touch-startup-image-1620x2160.png +0 -0
  545. package/src/client/public/default/apple-touch-startup-image-1640x2160.png +0 -0
  546. package/src/client/public/default/apple-touch-startup-image-1668x2224.png +0 -0
  547. package/src/client/public/default/apple-touch-startup-image-1668x2388.png +0 -0
  548. package/src/client/public/default/apple-touch-startup-image-1792x828.png +0 -0
  549. package/src/client/public/default/apple-touch-startup-image-2048x1536.png +0 -0
  550. package/src/client/public/default/apple-touch-startup-image-2048x2732.png +0 -0
  551. package/src/client/public/default/apple-touch-startup-image-2160x1620.png +0 -0
  552. package/src/client/public/default/apple-touch-startup-image-2160x1640.png +0 -0
  553. package/src/client/public/default/apple-touch-startup-image-2208x1242.png +0 -0
  554. package/src/client/public/default/apple-touch-startup-image-2224x1668.png +0 -0
  555. package/src/client/public/default/apple-touch-startup-image-2266x1488.png +0 -0
  556. package/src/client/public/default/apple-touch-startup-image-2388x1668.png +0 -0
  557. package/src/client/public/default/apple-touch-startup-image-2436x1125.png +0 -0
  558. package/src/client/public/default/apple-touch-startup-image-2532x1170.png +0 -0
  559. package/src/client/public/default/apple-touch-startup-image-2556x1179.png +0 -0
  560. package/src/client/public/default/apple-touch-startup-image-2688x1242.png +0 -0
  561. package/src/client/public/default/apple-touch-startup-image-2732x2048.png +0 -0
  562. package/src/client/public/default/apple-touch-startup-image-2778x1284.png +0 -0
  563. package/src/client/public/default/apple-touch-startup-image-2796x1290.png +0 -0
  564. package/src/client/public/default/apple-touch-startup-image-640x1136.png +0 -0
  565. package/src/client/public/default/apple-touch-startup-image-750x1334.png +0 -0
  566. package/src/client/public/default/apple-touch-startup-image-828x1792.png +0 -0
  567. package/src/client/public/default/assets/background/dark.jpg +0 -0
  568. package/src/client/public/default/assets/background/dark.svg +557 -0
  569. package/src/client/public/default/assets/background/white.jpg +0 -0
  570. package/src/client/public/default/assets/background/white0-min.jpg +0 -0
  571. package/src/client/public/default/assets/background/white0.jpg +0 -0
  572. package/src/client/public/default/assets/logo/base-icon.png +0 -0
  573. package/src/client/public/default/assets/logo/underpost.gif +0 -0
  574. package/src/client/public/default/assets/mailer/api-user-check.png +0 -0
  575. package/src/client/public/default/assets/mailer/api-user-default-avatar.png +0 -0
  576. package/src/client/public/default/assets/mailer/api-user-invalid-token.png +0 -0
  577. package/src/client/public/default/assets/mailer/api-user-recover.png +0 -0
  578. package/src/client/public/default/browserconfig.xml +12 -0
  579. package/src/client/public/default/favicon-16x16.png +0 -0
  580. package/src/client/public/default/favicon-32x32.png +0 -0
  581. package/src/client/public/default/favicon-48x48.png +0 -0
  582. package/src/client/public/default/favicon.ico +0 -0
  583. package/src/client/public/default/manifest.webmanifest +69 -0
  584. package/src/client/public/default/mstile-144x144.png +0 -0
  585. package/src/client/public/default/mstile-150x150.png +0 -0
  586. package/src/client/public/default/mstile-310x150.png +0 -0
  587. package/src/client/public/default/mstile-310x310.png +0 -0
  588. package/src/client/public/default/mstile-70x70.png +0 -0
  589. package/src/client/public/default/safari-pinned-tab.svg +24 -0
  590. package/src/client/public/default/site.webmanifest +69 -0
  591. package/src/client/public/default/sitemap +148 -0
  592. package/src/client/public/default/yandex-browser-50x50.png +0 -0
  593. package/src/client/public/default/yandex-browser-manifest.json +9 -0
  594. package/src/client/public/doc/favicon.ico +0 -0
  595. package/src/client/public/doc/sitemap +148 -0
  596. package/src/client/public/itemledger/android-chrome-144x144.png +0 -0
  597. package/src/client/public/itemledger/android-chrome-192x192.png +0 -0
  598. package/src/client/public/itemledger/android-chrome-256x256.png +0 -0
  599. package/src/client/public/itemledger/android-chrome-36x36.png +0 -0
  600. package/src/client/public/itemledger/android-chrome-384x384.png +0 -0
  601. package/src/client/public/itemledger/android-chrome-48x48.png +0 -0
  602. package/src/client/public/itemledger/android-chrome-512x512.png +0 -0
  603. package/src/client/public/itemledger/android-chrome-72x72.png +0 -0
  604. package/src/client/public/itemledger/android-chrome-96x96.png +0 -0
  605. package/src/client/public/itemledger/apple-touch-icon-1024x1024.png +0 -0
  606. package/src/client/public/itemledger/apple-touch-icon-114x114.png +0 -0
  607. package/src/client/public/itemledger/apple-touch-icon-120x120.png +0 -0
  608. package/src/client/public/itemledger/apple-touch-icon-144x144.png +0 -0
  609. package/src/client/public/itemledger/apple-touch-icon-152x152.png +0 -0
  610. package/src/client/public/itemledger/apple-touch-icon-167x167.png +0 -0
  611. package/src/client/public/itemledger/apple-touch-icon-180x180.png +0 -0
  612. package/src/client/public/itemledger/apple-touch-icon-57x57.png +0 -0
  613. package/src/client/public/itemledger/apple-touch-icon-60x60.png +0 -0
  614. package/src/client/public/itemledger/apple-touch-icon-72x72.png +0 -0
  615. package/src/client/public/itemledger/apple-touch-icon-76x76.png +0 -0
  616. package/src/client/public/itemledger/apple-touch-icon-precomposed.png +0 -0
  617. package/src/client/public/itemledger/apple-touch-icon.png +0 -0
  618. package/src/client/public/itemledger/apple-touch-startup-image-1125x2436.png +0 -0
  619. package/src/client/public/itemledger/apple-touch-startup-image-1136x640.png +0 -0
  620. package/src/client/public/itemledger/apple-touch-startup-image-1170x2532.png +0 -0
  621. package/src/client/public/itemledger/apple-touch-startup-image-1179x2556.png +0 -0
  622. package/src/client/public/itemledger/apple-touch-startup-image-1242x2208.png +0 -0
  623. package/src/client/public/itemledger/apple-touch-startup-image-1242x2688.png +0 -0
  624. package/src/client/public/itemledger/apple-touch-startup-image-1284x2778.png +0 -0
  625. package/src/client/public/itemledger/apple-touch-startup-image-1290x2796.png +0 -0
  626. package/src/client/public/itemledger/apple-touch-startup-image-1334x750.png +0 -0
  627. package/src/client/public/itemledger/apple-touch-startup-image-1488x2266.png +0 -0
  628. package/src/client/public/itemledger/apple-touch-startup-image-1536x2048.png +0 -0
  629. package/src/client/public/itemledger/apple-touch-startup-image-1620x2160.png +0 -0
  630. package/src/client/public/itemledger/apple-touch-startup-image-1640x2160.png +0 -0
  631. package/src/client/public/itemledger/apple-touch-startup-image-1668x2224.png +0 -0
  632. package/src/client/public/itemledger/apple-touch-startup-image-1668x2388.png +0 -0
  633. package/src/client/public/itemledger/apple-touch-startup-image-1792x828.png +0 -0
  634. package/src/client/public/itemledger/apple-touch-startup-image-2048x1536.png +0 -0
  635. package/src/client/public/itemledger/apple-touch-startup-image-2048x2732.png +0 -0
  636. package/src/client/public/itemledger/apple-touch-startup-image-2160x1620.png +0 -0
  637. package/src/client/public/itemledger/apple-touch-startup-image-2160x1640.png +0 -0
  638. package/src/client/public/itemledger/apple-touch-startup-image-2208x1242.png +0 -0
  639. package/src/client/public/itemledger/apple-touch-startup-image-2224x1668.png +0 -0
  640. package/src/client/public/itemledger/apple-touch-startup-image-2266x1488.png +0 -0
  641. package/src/client/public/itemledger/apple-touch-startup-image-2388x1668.png +0 -0
  642. package/src/client/public/itemledger/apple-touch-startup-image-2436x1125.png +0 -0
  643. package/src/client/public/itemledger/apple-touch-startup-image-2532x1170.png +0 -0
  644. package/src/client/public/itemledger/apple-touch-startup-image-2556x1179.png +0 -0
  645. package/src/client/public/itemledger/apple-touch-startup-image-2688x1242.png +0 -0
  646. package/src/client/public/itemledger/apple-touch-startup-image-2732x2048.png +0 -0
  647. package/src/client/public/itemledger/apple-touch-startup-image-2778x1284.png +0 -0
  648. package/src/client/public/itemledger/apple-touch-startup-image-2796x1290.png +0 -0
  649. package/src/client/public/itemledger/apple-touch-startup-image-640x1136.png +0 -0
  650. package/src/client/public/itemledger/apple-touch-startup-image-750x1334.png +0 -0
  651. package/src/client/public/itemledger/apple-touch-startup-image-828x1792.png +0 -0
  652. package/src/client/public/itemledger/assets/logo/base-icon.png +0 -0
  653. package/src/client/public/itemledger/browserconfig.xml +12 -0
  654. package/src/client/public/itemledger/favicon-16x16.png +0 -0
  655. package/src/client/public/itemledger/favicon-32x32.png +0 -0
  656. package/src/client/public/itemledger/favicon-48x48.png +0 -0
  657. package/src/client/public/itemledger/favicon.ico +0 -0
  658. package/src/client/public/itemledger/manifest.webmanifest +69 -0
  659. package/src/client/public/itemledger/microdata.json +71 -0
  660. package/src/client/public/itemledger/mstile-144x144.png +0 -0
  661. package/src/client/public/itemledger/mstile-150x150.png +0 -0
  662. package/src/client/public/itemledger/mstile-310x150.png +0 -0
  663. package/src/client/public/itemledger/mstile-310x310.png +0 -0
  664. package/src/client/public/itemledger/mstile-70x70.png +0 -0
  665. package/src/client/public/itemledger/sitemap +148 -0
  666. package/src/client/public/itemledger/yandex-browser-50x50.png +0 -0
  667. package/src/client/public/itemledger/yandex-browser-manifest.json +9 -0
  668. package/src/client/public/test/favicon.ico +0 -0
  669. package/src/client/public/test/sitemap +148 -0
  670. package/src/client/services/atlas-sprite-sheet/atlas-sprite-sheet.service.js +137 -0
  671. package/src/client/services/core/core.service.js +309 -0
  672. package/src/client/services/crypto/crypto.service.js +68 -0
  673. package/src/client/services/cyberia-action/cyberia-action.service.js +99 -0
  674. package/src/client/services/cyberia-client-hints/cyberia-client-hints.service.js +99 -0
  675. package/src/client/services/cyberia-dialogue/cyberia-dialogue.service.js +99 -0
  676. package/src/client/services/cyberia-entity/cyberia-entity.management.js +57 -0
  677. package/src/client/services/cyberia-entity/cyberia-entity.service.js +99 -0
  678. package/src/client/services/cyberia-entity-type-default/cyberia-entity-type-default.service.js +99 -0
  679. package/src/client/services/cyberia-instance/cyberia-instance.management.js +194 -0
  680. package/src/client/services/cyberia-instance/cyberia-instance.service.js +137 -0
  681. package/src/client/services/cyberia-instance-conf/cyberia-instance-conf.service.js +99 -0
  682. package/src/client/services/cyberia-map/cyberia-map.management.js +193 -0
  683. package/src/client/services/cyberia-map/cyberia-map.service.js +120 -0
  684. package/src/client/services/cyberia-quest/cyberia-quest.service.js +99 -0
  685. package/src/client/services/cyberia-quest-progress/cyberia-quest-progress.service.js +99 -0
  686. package/src/client/services/cyberia-saga/cyberia-saga.service.js +99 -0
  687. package/src/client/services/cyberia-skill/cyberia-skill.service.js +99 -0
  688. package/src/client/services/default/default.management.js +1015 -0
  689. package/src/client/services/default/default.service.js +99 -0
  690. package/src/client/services/document/document.service.js +134 -0
  691. package/src/client/services/file/file.service.js +94 -0
  692. package/src/client/services/instance/instance.management.js +78 -0
  693. package/src/client/services/instance/instance.service.js +107 -0
  694. package/src/client/services/ipfs/ipfs.service.js +118 -0
  695. package/src/client/services/object-layer/object-layer.management.js +414 -0
  696. package/src/client/services/object-layer/object-layer.service.js +208 -0
  697. package/src/client/services/object-layer-render-frames/object-layer-render-frames.service.js +99 -0
  698. package/src/client/services/test/test.service.js +68 -0
  699. package/src/client/services/user/guest.service.js +86 -0
  700. package/src/client/services/user/user.management.js +51 -0
  701. package/src/client/services/user/user.service.js +106 -0
  702. package/src/client/ssr/RootDocument.js +237 -0
  703. package/src/client/ssr/body/404.js +77 -0
  704. package/src/client/ssr/body/500.js +76 -0
  705. package/src/client/ssr/body/CacheControl.js +117 -0
  706. package/src/client/ssr/body/CyberiaDefaultSplashScreen.js +97 -0
  707. package/src/client/ssr/body/DefaultSplashScreen.js +97 -0
  708. package/src/client/ssr/body/SwaggerDarkMode.js +285 -0
  709. package/src/client/ssr/body/UnderpostDefaultSplashScreen.js +90 -0
  710. package/src/client/ssr/head/CryptokoynScripts.js +4 -0
  711. package/src/client/ssr/head/Css.js +241 -0
  712. package/src/client/ssr/head/CyberiaPortalScripts.js +6 -0
  713. package/src/client/ssr/head/DefaultScripts.js +6 -0
  714. package/src/client/ssr/head/ItemledgerScripts.js +4 -0
  715. package/src/client/ssr/head/Microdata.js +11 -0
  716. package/src/client/ssr/head/Production.js +1 -0
  717. package/src/client/ssr/head/Pwa.js +146 -0
  718. package/src/client/ssr/head/PwaItemledger.js +197 -0
  719. package/src/client/ssr/head/Seo.js +15 -0
  720. package/src/client/ssr/head/UnderpostScripts.js +6 -0
  721. package/src/client/ssr/mailer/DefaultRecoverEmail.js +20 -0
  722. package/src/client/ssr/mailer/DefaultVerifyEmail.js +16 -0
  723. package/src/client/ssr/views/Cyberia404.js +328 -0
  724. package/src/client/ssr/views/CyberiaServerMetrics.js +1070 -0
  725. package/src/client/ssr/views/Maintenance.js +64 -0
  726. package/src/client/ssr/views/NoNetworkConnection.js +68 -0
  727. package/src/client/ssr/views/Test.js +199 -0
  728. package/src/client/sw/core.sw.js +274 -0
  729. package/src/client-builder/client-build-docs.js +493 -0
  730. package/src/client-builder/client-build-live.js +100 -0
  731. package/src/client-builder/client-build.js +1092 -0
  732. package/src/client-builder/client-dev-server.js +88 -0
  733. package/src/client-builder/client-formatted.js +170 -0
  734. package/src/client-builder/client-icons.js +108 -0
  735. package/src/client-builder/ssr.js +124 -0
  736. package/src/client.build.js +17 -0
  737. package/src/client.dev.js +21 -0
  738. package/src/db/DataBaseProvider.js +213 -0
  739. package/src/db/mariadb/MariaDB.js +72 -0
  740. package/src/db/mongo/MongoBootstrap.js +657 -0
  741. package/src/db/mongo/MongooseDB.js +199 -0
  742. package/src/grpc/cyberia/grpc-server.js +162 -0
  743. package/src/index.js +357 -0
  744. package/src/mailer/EmailRender.js +116 -0
  745. package/src/mailer/MailerProvider.js +213 -0
  746. package/src/projects/cyberia/atlas-sprite-sheet-generator.js +413 -0
  747. package/src/projects/cyberia/besu-genesis-generator.js +1630 -0
  748. package/src/projects/cyberia/catalog-cyberia.js +90 -0
  749. package/src/projects/cyberia/gemini-client.js +175 -0
  750. package/src/projects/cyberia/generate-saga.js +2137 -0
  751. package/src/projects/cyberia/hot-reload-trigger.js +163 -0
  752. package/src/projects/cyberia/instance-data.js +719 -0
  753. package/src/projects/cyberia/ipfs-client.js +599 -0
  754. package/src/projects/cyberia/map-preview-generator.js +304 -0
  755. package/src/projects/cyberia/object-layer.js +978 -0
  756. package/src/projects/cyberia/semantic-layer-generator-floor.js +319 -0
  757. package/src/projects/cyberia/semantic-layer-generator-resource.js +259 -0
  758. package/src/projects/cyberia/semantic-layer-generator-skin.js +1164 -0
  759. package/src/projects/cyberia/semantic-layer-generator.js +753 -0
  760. package/src/projects/cyberia/shape-generator.js +1060 -0
  761. package/src/projects/underpost/catalog-underpost.js +60 -0
  762. package/src/proxy.js +19 -0
  763. package/src/runtime/cyberia-client/Dockerfile +49 -0
  764. package/src/runtime/cyberia-client/Dockerfile.dev +75 -0
  765. package/src/runtime/cyberia-server/Dockerfile +43 -0
  766. package/src/runtime/cyberia-server/Dockerfile.dev +56 -0
  767. package/src/runtime/engine-cyberia/Dockerfile +161 -0
  768. package/src/runtime/engine-cyberia/Dockerfile.dev +161 -0
  769. package/src/runtime/engine-cyberia/Dockerfile.test +165 -0
  770. package/src/runtime/engine-cyberia/compose.env +144 -0
  771. package/src/runtime/engine-cyberia/docker-compose.yml +434 -0
  772. package/src/runtime/engine-cyberia/ipfs/configure-ipfs.sh +13 -0
  773. package/src/runtime/engine-cyberia/mongodb/entrypoint.sh +76 -0
  774. package/src/runtime/engine-cyberia/nginx.conf +128 -0
  775. package/src/runtime/express/Dockerfile +45 -0
  776. package/src/runtime/express/Express.js +285 -0
  777. package/src/runtime/lampp/Dockerfile +62 -0
  778. package/src/runtime/lampp/Lampp.js +359 -0
  779. package/src/runtime/nginx/Nginx.js +250 -0
  780. package/src/runtime/wp/Dockerfile +62 -0
  781. package/src/runtime/wp/Wp.js +642 -0
  782. package/src/server/auth.js +713 -0
  783. package/src/server/backup.js +102 -0
  784. package/src/server/catalog.js +72 -0
  785. package/src/server/conf.js +2316 -0
  786. package/src/server/cron.js +476 -0
  787. package/src/server/crypto.js +210 -0
  788. package/src/server/data-query.js +461 -0
  789. package/src/server/dns.js +553 -0
  790. package/src/server/downloader.js +73 -0
  791. package/src/server/logger.js +229 -0
  792. package/src/server/middlewares.js +152 -0
  793. package/src/server/peer.js +75 -0
  794. package/src/server/process.js +299 -0
  795. package/src/server/proxy.js +139 -0
  796. package/src/server/runtime-status.js +252 -0
  797. package/src/server/runtime.js +183 -0
  798. package/src/server/start.js +272 -0
  799. package/src/server/tls.js +254 -0
  800. package/src/server/valkey.js +201 -0
  801. package/src/server.js +25 -0
  802. package/src/ws/IoInterface.js +129 -0
  803. package/src/ws/IoServer.js +68 -0
  804. package/src/ws/core/channels/core.ws.chat.js +68 -0
  805. package/src/ws/core/channels/core.ws.mailer.js +116 -0
  806. package/src/ws/core/channels/core.ws.stream.js +90 -0
  807. package/src/ws/core/core.ws.connection.js +41 -0
  808. package/src/ws/core/core.ws.emit.js +37 -0
  809. package/src/ws/core/core.ws.server.js +44 -0
  810. package/src/ws/default/channels/default.ws.main.js +57 -0
  811. package/src/ws/default/default.ws.connection.js +35 -0
  812. package/src/ws/default/default.ws.emit.js +14 -0
  813. package/src/ws/default/default.ws.server.js +39 -0
  814. package/test/api.test.js +53 -0
  815. package/test/crypto.test.js +117 -0
  816. package/test/cyberia-instance-conf-defaults.test.js +140 -0
  817. package/test/deploy-monitor.test.js +267 -0
  818. package/test/shape-generator.test.js +463 -0
  819. package/tsconfig.docs.json +15 -0
  820. package/typedoc.dd-cyberia.json +31 -0
  821. package/typedoc.json +31 -0
@@ -0,0 +1,2316 @@
1
+ /**
2
+ * Provides utilities for building, loading, and managing server configurations,
3
+ * deployment contexts, and service configurations (API, Client, WS).
4
+ * @module src/server/conf.js
5
+ * @namespace ServerConfBuilder
6
+ */
7
+
8
+ import fs from 'fs-extra';
9
+ import dotenv from 'dotenv';
10
+ import {
11
+ capFirst,
12
+ getCapVariableName,
13
+ getDirname,
14
+ newInstance,
15
+ orderAbc,
16
+ orderArrayFromAttrInt,
17
+ range,
18
+ timer,
19
+ } from '../client/components/core/CommonJs.js';
20
+ import * as dir from 'path';
21
+ import colors from 'colors';
22
+ import { loggerFactory } from './logger.js';
23
+ import { shellExec } from './process.js';
24
+ import { DefaultConf } from '../../conf.js';
25
+ import splitFile from 'split-file';
26
+ import Underpost from '../index.js';
27
+
28
+ colors.enable();
29
+
30
+ const logger = loggerFactory(import.meta);
31
+
32
+ /**
33
+ * Prefix used in JSON configuration files to denote an environment variable reference.
34
+ * Any string value in a config object that starts with this prefix will be resolved
35
+ * to the corresponding `process.env` value at runtime.
36
+ *
37
+ * @constant {string}
38
+ * @memberof ServerConfBuilder
39
+ * @example
40
+ * // In conf.server.json:
41
+ * { "db": { "password": "env:MARIADB_PASSWORD" } }
42
+ */
43
+ const ENV_REF_PREFIX = 'env:';
44
+
45
+ /**
46
+ * Resolves a standardized context key from host/path descriptors.
47
+ * The key is used across DB, WS, mailer, and cache registries.
48
+ *
49
+ * @method resolveHostKeyContext
50
+ * @param {{host?: string, path?: string}|string} [context={ host: '', path: '' }] - Context object or prebuilt key.
51
+ * @returns {string} Host key context string.
52
+ * @memberof ServerConfBuilder
53
+ */
54
+ const resolveHostKeyContext = (context = { host: '', path: '' }) => {
55
+ if (typeof context === 'string') return context;
56
+ return `${context.host || ''}${context.path || ''}`;
57
+ };
58
+
59
+ /**
60
+ * Recursively walks a configuration object and replaces every string value that
61
+ * starts with {@link ENV_REF_PREFIX} (`"env:"`) with the corresponding
62
+ * `process.env[VAR_NAME]` value.
63
+ *
64
+ * Non-string values and strings that do not start with the prefix are left untouched.
65
+ *
66
+ * Supports three reference formats:
67
+ * - `"env:VAR_NAME"` — resolves to `process.env.VAR_NAME`, returns `''` if unset.
68
+ * - `"env:VAR_NAME:default_value"` — resolves to `process.env.VAR_NAME`, falls back to `default_value` if unset.
69
+ * - Type-coerced defaults:
70
+ * - `"env:VAR_NAME:int:465"` — resolved value is parsed as integer (`parseInt`), falls back to `465`.
71
+ * - `"env:VAR_NAME:bool:true"` — resolved value is coerced to boolean (`value !== 'false'`), falls back to `true`.
72
+ *
73
+ * @method resolveConfSecrets
74
+ * @param {any} obj - The configuration object (or value) to resolve.
75
+ * @returns {any} A **new** object with all `env:` references replaced by their runtime values.
76
+ * @memberof ServerConfBuilder
77
+ *
78
+ * @example
79
+ * // Given process.env.MARIADB_PASSWORD = 'supersecret'
80
+ * resolveConfSecrets({ db: { password: 'env:MARIADB_PASSWORD' } });
81
+ * // => { db: { password: 'supersecret' } }
82
+ *
83
+ * @example
84
+ * // With default value fallback (env var not set)
85
+ * resolveConfSecrets({ db: { provider: 'env:DB_PROVIDER:mongoose' } });
86
+ * // => { db: { provider: 'mongoose' } }
87
+ *
88
+ * @example
89
+ * // With int type coercion
90
+ * resolveConfSecrets({ port: 'env:SMTP_PORT:int:465' });
91
+ * // => { port: 465 }
92
+ *
93
+ * @example
94
+ * // With bool type coercion
95
+ * resolveConfSecrets({ secure: 'env:SMTP_SECURE:bool:true' });
96
+ * // => { secure: true }
97
+ */
98
+ const resolveConfSecrets = (obj) => {
99
+ if (obj === null || obj === undefined) return obj;
100
+ if (typeof obj === 'string') {
101
+ if (obj.startsWith(ENV_REF_PREFIX)) {
102
+ const ref = obj.slice(ENV_REF_PREFIX.length);
103
+ // Support env:VAR_NAME:default_value syntax (first colon separates key from default)
104
+ const colonIdx = ref.indexOf(':');
105
+ const envKey = colonIdx !== -1 ? ref.slice(0, colonIdx) : ref;
106
+ const defaultValue = colonIdx !== -1 ? ref.slice(colonIdx + 1) : undefined;
107
+ const envValue = process.env[envKey];
108
+
109
+ let resolved;
110
+ if (envValue !== undefined) {
111
+ resolved = envValue;
112
+ } else if (defaultValue !== undefined) {
113
+ resolved = defaultValue;
114
+ } else {
115
+ logger.warn(`resolveConfSecrets: environment variable "${envKey}" is not set (referenced as "${obj}")`);
116
+ return '';
117
+ }
118
+
119
+ // Type coercion via prefix in default value: int:N or bool:B
120
+ // Also apply coercion when an env value is present and a typed default is declared
121
+ if (defaultValue !== undefined) {
122
+ if (defaultValue.startsWith('int:')) {
123
+ return parseInt(resolved, 10) || parseInt(defaultValue.slice(4), 10) || 0;
124
+ }
125
+ if (defaultValue.startsWith('bool:')) {
126
+ const boolDefault = defaultValue.slice(5);
127
+ if (envValue !== undefined) return envValue !== 'false';
128
+ return boolDefault !== 'false';
129
+ }
130
+ }
131
+
132
+ return resolved;
133
+ }
134
+ return obj;
135
+ }
136
+ if (Array.isArray(obj)) return obj.map((item) => resolveConfSecrets(item));
137
+ if (typeof obj === 'object') {
138
+ const resolved = {};
139
+ for (const [key, value] of Object.entries(obj)) {
140
+ resolved[key] = resolveConfSecrets(value);
141
+ }
142
+ return resolved;
143
+ }
144
+ return obj;
145
+ };
146
+
147
+ /**
148
+ * Returns the private configuration folder for a given deploy ID.
149
+ * Checks for a replica folder first, then falls back to the standard conf folder.
150
+ *
151
+ * @method getConfFolder
152
+ * @param {string} deployId - The deploy ID.
153
+ * @returns {string} The path to the private configuration folder.
154
+ * @memberof ServerConfBuilder
155
+ *
156
+ * @example
157
+ * getConfFolder('dd-myapp');
158
+ * // => './engine-private/conf/dd-myapp' (or './engine-private/replica/dd-myapp' if it exists)
159
+ */
160
+ const getConfFolder = (deployId) => {
161
+ return fs.existsSync(`./engine-private/replica/${deployId}`)
162
+ ? `./engine-private/replica/${deployId}`
163
+ : `./engine-private/conf/${deployId}`;
164
+ };
165
+
166
+ /**
167
+ * Reads `engine-private/deploy/dd.cron` and returns the deploy-id string,
168
+ * or `null` if the file does not exist or is empty.
169
+ *
170
+ * @method cronDeployIdResolve
171
+ * @returns {string|null} The deploy-id from dd.cron, or null.
172
+ * @memberof ServerConfBuilder
173
+ */
174
+ const cronDeployIdResolve = () => {
175
+ const cronDeployFile = './engine-private/deploy/dd.cron';
176
+ if (fs.existsSync(cronDeployFile)) {
177
+ const id = fs.readFileSync(cronDeployFile, 'utf8').trim();
178
+ return id || null;
179
+ }
180
+ return null;
181
+ };
182
+
183
+ /**
184
+ * Loads the deployment-specific `.env` file referenced by `engine-private/deploy/dd.cron`
185
+ * into `process.env`. Uses `NODE_ENV` to select the environment variant
186
+ * (defaults to `production`).
187
+ *
188
+ * Safe to call multiple times; subsequent calls are no-ops once the env is loaded.
189
+ *
190
+ * @method loadCronDeployEnv
191
+ * @memberof ServerConfBuilder
192
+ */
193
+ function loadCronDeployEnv() {
194
+ const envName = process.env.NODE_ENV || 'production';
195
+
196
+ // 1) Load dd.cron env (takes full precedence)
197
+ const cronDeployId = cronDeployIdResolve();
198
+ if (cronDeployId) {
199
+ const cronEnvPath = `./engine-private/conf/${cronDeployId}/.env.${envName}`;
200
+ if (fs.existsSync(cronEnvPath)) {
201
+ const cronEnv = dotenv.parse(fs.readFileSync(cronEnvPath, 'utf8'));
202
+ process.env = { ...process.env, ...cronEnv };
203
+ }
204
+ }
205
+
206
+ // 2) Load dd.router envs — only keys not already present
207
+ const routerDeployFile = './engine-private/deploy/dd.router';
208
+ if (fs.existsSync(routerDeployFile)) {
209
+ const routerIds = fs.readFileSync(routerDeployFile, 'utf8').trim().split(',');
210
+ for (const deployId of routerIds) {
211
+ const id = deployId.trim();
212
+ if (!id) continue;
213
+ const envPath = `./engine-private/conf/${id}/.env.${envName}`;
214
+ if (!fs.existsSync(envPath)) continue;
215
+ const env = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
216
+ for (const key of Object.keys(env)) {
217
+ if (!(key in process.env)) process.env[key] = env[key];
218
+ }
219
+ }
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Resolves the full path to a specific configuration JSON file for a deploy ID.
225
+ * For `server` configs in development mode with a subConf, it will prefer the
226
+ * dev-specific variant if it exists.
227
+ *
228
+ * @method getConfFilePath
229
+ * @param {string} deployId - The deploy ID.
230
+ * @param {string} confType - The configuration type (e.g. 'server', 'client', 'cron', 'ssr').
231
+ * @param {string} [subConf=''] - Optional sub-configuration identifier (used for dev server variants).
232
+ * @returns {string} The resolved path to the configuration JSON file.
233
+ * @memberof ServerConfBuilder
234
+ *
235
+ * @example
236
+ * getConfFilePath('dd-myapp', 'server');
237
+ * // => './engine-private/conf/dd-myapp/conf.server.json'
238
+ *
239
+ * @example
240
+ * // In development with subConf 'local':
241
+ * getConfFilePath('dd-myapp', 'server', 'local');
242
+ * // => './engine-private/conf/dd-myapp/conf.server.dev.local.json' (if it exists)
243
+ */
244
+ const getConfFilePath = (deployId, confType, subConf = '') => {
245
+ const folder = getConfFolder(deployId);
246
+ // When no explicit subConf is given, fall back to the env var set by loadConf()
247
+ const effectiveSubConf = subConf || process.env.DEPLOY_SUB_CONF || '';
248
+ if (confType === 'server' && effectiveSubConf) {
249
+ const devConfPath = `${folder}/conf.${confType}.dev.${effectiveSubConf}.json`;
250
+ if (fs.existsSync(devConfPath)) return devConfPath;
251
+ }
252
+ return `${folder}/conf.${confType}.json`;
253
+ };
254
+
255
+ /**
256
+ * Reads and parses a configuration JSON file for a given deploy ID and config type.
257
+ * Optionally resolves `env:` secret references and/or applies replica expansion.
258
+ *
259
+ * @method readConfJson
260
+ * @param {string} deployId - The deploy ID.
261
+ * @param {string} confType - The configuration type (e.g. 'server', 'client', 'cron', 'ssr').
262
+ * @param {object} [options={}] - Options.
263
+ * @param {string} [options.subConf=''] - Sub-configuration identifier for dev variants.
264
+ * @param {boolean} [options.resolve=false] - Whether to resolve `env:` references.
265
+ * @param {boolean} [options.loadReplicas=false] - Whether to expand replica entries (server configs).
266
+ * @returns {object} The parsed (and optionally resolved) configuration object.
267
+ * @memberof ServerConfBuilder
268
+ */
269
+ const readConfJson = (deployId, confType, options = {}) => {
270
+ const filePath = getConfFilePath(deployId, confType, options.subConf || '');
271
+ if (!fs.existsSync(filePath)) {
272
+ throw new Error(`readConfJson: configuration file not found: ${filePath}`);
273
+ }
274
+ let parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
275
+ if (options.loadReplicas && confType === 'server') parsed = loadReplicas(deployId, parsed);
276
+ if (options.resolve) parsed = resolveConfSecrets(parsed);
277
+ return parsed;
278
+ };
279
+
280
+ /**
281
+ * Default deploy ID used when no deploy ID is specified.
282
+ * @constant {string}
283
+ * @memberof ServerConfBuilder
284
+ */
285
+ const DEFAULT_DEPLOY_ID = 'dd-default';
286
+
287
+ /**
288
+ * @class Config
289
+ * @description Manages the configuration of the server.
290
+ * This class provides a set of static methods to automate various
291
+ * infrastructure operations, including NFS management, control server setup,
292
+ * and system provisioning for different architectures.
293
+ * @memberof ServerConfBuilder
294
+ */
295
+ const Config = {
296
+ /**
297
+ * @method default
298
+ * @description The default configuration of the server.
299
+ * @memberof ServerConfBuilder
300
+ */
301
+ default: DefaultConf,
302
+ /**
303
+ * @method build
304
+ * @description Builds the configuration of the server.
305
+ * @param {string} [deployContext='dd-default'] - The deploy context.
306
+ * @param {string} [deployList=''] - The deploy list.
307
+ * @param {string} [subConf=''] - The sub configuration.
308
+ * @memberof ServerConfBuilder
309
+ */
310
+ build: async function (deployContext = DEFAULT_DEPLOY_ID, deployList, subConf) {
311
+ if (process.argv[2] && typeof process.argv[2] === 'string' && process.argv[2].startsWith('dd-'))
312
+ deployContext = process.argv[2];
313
+ else if (deployContext !== 'proxy' && process.env.DEPLOY_ID && process.env.DEPLOY_ID.startsWith('dd-'))
314
+ deployContext = process.env.DEPLOY_ID;
315
+ if (!subConf && process.argv[3] && typeof process.argv[3] === 'string') subConf = process.argv[3];
316
+
317
+ Underpost.env.set('await-deploy', new Date().toISOString());
318
+ if (deployContext.startsWith('dd-')) loadConf(deployContext, subConf);
319
+ if (deployContext === 'proxy') await Config.buildProxy(deployList, subConf);
320
+ },
321
+ /**
322
+ * @method deployIdFactory
323
+ * @description Creates a new deploy ID.
324
+ * @param {string} [deployId='dd-default']
325
+ * @param {object} [options={ subConf: '', cluster: false }] - The options.
326
+ * @memberof ServerConfBuilder
327
+ */
328
+ deployIdFactory: function (deployId = DEFAULT_DEPLOY_ID, options = { subConf: '', cluster: false }) {
329
+ if (!deployId.startsWith('dd-')) deployId = `dd-${deployId}`;
330
+
331
+ logger.info('Build deployId', deployId);
332
+
333
+ const folder = `./engine-private/conf/${deployId}`;
334
+ const repoName = `engine-${deployId.split('dd-')[1]}`;
335
+
336
+ if (!fs.existsSync(folder)) fs.mkdirSync(folder, { recursive: true });
337
+
338
+ const sharedEnvTemplate = fs.existsSync('./.env.example')
339
+ ? fs.readFileSync('./.env.example', 'utf8')
340
+ : fs.existsSync('./.env.production')
341
+ ? fs.readFileSync('./.env.production', 'utf8')
342
+ : '';
343
+
344
+ const envTemplates = {
345
+ production: fs.existsSync('./.env.production') ? fs.readFileSync('./.env.production', 'utf8') : sharedEnvTemplate,
346
+ development: fs.existsSync('./.env.development')
347
+ ? fs.readFileSync('./.env.development', 'utf8')
348
+ : sharedEnvTemplate
349
+ ? sharedEnvTemplate.replace('NODE_ENV=production', 'NODE_ENV=development').replace('PORT=3000', 'PORT=4000')
350
+ : '',
351
+ test: fs.existsSync('./.env.test')
352
+ ? fs.readFileSync('./.env.test', 'utf8')
353
+ : sharedEnvTemplate
354
+ ? sharedEnvTemplate.replace('NODE_ENV=production', 'NODE_ENV=test').replace('PORT=3000', 'PORT=5000')
355
+ : '',
356
+ };
357
+
358
+ for (const [envName, envTemplate] of Object.entries(envTemplates)) {
359
+ if (!envTemplate) continue;
360
+ fs.writeFileSync(`${folder}/.env.${envName}`, envTemplate.replaceAll('dd-default', deployId), 'utf8');
361
+ }
362
+
363
+ fs.writeFileSync(
364
+ `${folder}/package.json`,
365
+ fs.readFileSync('./package.json', 'utf8').replaceAll('dd-default', deployId),
366
+ 'utf8',
367
+ );
368
+
369
+ // Write default conf JSON files if they don't exist
370
+ for (const confType of Object.keys(this.default)) {
371
+ const confPath = `${folder}/conf.${confType}.json`;
372
+ if (!fs.existsSync(confPath)) fs.writeFileSync(confPath, JSON.stringify(this.default[confType], null, 4), 'utf8');
373
+ }
374
+
375
+ if (options.subConf) {
376
+ logger.info('Creating sub conf', {
377
+ deployId: deployId,
378
+ subConf: options.subConf,
379
+ });
380
+ fs.copySync(
381
+ `./engine-private/conf/${deployId}/conf.server.json`,
382
+ `./engine-private/conf/${deployId}/conf.server.dev.${options.subConf}.json`,
383
+ );
384
+ }
385
+
386
+ if (options.cluster === true) {
387
+ fs.writeFileSync(
388
+ `./.github/workflows/${repoName}.cd.yml`,
389
+ fs.readFileSync(`./.github/workflows/engine-test.cd.yml`, 'utf8').replaceAll('test', deployId.split('dd-')[1]),
390
+ 'utf8',
391
+ );
392
+ fs.writeFileSync(
393
+ `./.github/workflows/${repoName}.ci.yml`,
394
+ fs.readFileSync(`./.github/workflows/engine-test.ci.yml`, 'utf8').replaceAll('test', deployId.split('dd-')[1]),
395
+ 'utf8',
396
+ );
397
+ shellExec(`node bin new --default-conf --deploy-id ${deployId}`);
398
+
399
+ if (!fs.existsSync(`./engine-private/deploy/dd.router`))
400
+ fs.writeFileSync(`./engine-private/deploy/dd.router`, deployId, 'utf8');
401
+ else
402
+ fs.writeFileSync(
403
+ `./engine-private/deploy/dd.router`,
404
+ fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').trim() + `,${deployId}`,
405
+ 'utf8',
406
+ );
407
+ }
408
+
409
+ return { deployIdFolder: folder, deployId };
410
+ },
411
+ /**
412
+ * @method buildProxyByDeployId
413
+ * @description Builds the proxy by deploy ID.
414
+ * @param {string} [deployId='dd-default'] - The deploy ID.
415
+ * @param {string} [subConf=''] - The sub configuration.
416
+ * @memberof ServerConfBuilder
417
+ */
418
+ buildProxyByDeployId: function (deployId = 'dd-default', subConf = '') {
419
+ let confPath = fs.existsSync(`./engine-private/replica/${deployId}/conf.server.json`)
420
+ ? `./engine-private/replica/${deployId}/conf.server.json`
421
+ : `./engine-private/conf/${deployId}/conf.server.json`;
422
+
423
+ if (
424
+ process.env.NODE_ENV === 'development' &&
425
+ subConf &&
426
+ fs.existsSync(`./engine-private/conf/${deployId}/conf.server.dev.${subConf}.json`)
427
+ )
428
+ confPath = `./engine-private/conf/${deployId}/conf.server.dev.${subConf}.json`;
429
+
430
+ const serverConf = loadConfServerJson(confPath);
431
+
432
+ for (const host of Object.keys(loadReplicas(deployId, serverConf)))
433
+ this.default.server[host] = {
434
+ ...this.default.server[host],
435
+ ...serverConf[host],
436
+ };
437
+ },
438
+ /**
439
+ * @method buildProxy
440
+ * @description Builds the proxy.
441
+ * @param {string} [deployList='dd-default'] - The deploy list.
442
+ * @param {string} [subConf=''] - The sub configuration.
443
+ * @memberof ServerConfBuilder
444
+ */
445
+ buildProxy: async function (deployList = 'dd-default', subConf = '') {
446
+ if (!deployList) deployList = process.argv[3];
447
+ if (!subConf) subConf = process.argv[4];
448
+ this.default.server = {};
449
+ for (const deployId of deployList.split(',')) {
450
+ this.buildProxyByDeployId(deployId, subConf);
451
+ if (fs.existsSync(`./engine-private/replica`)) {
452
+ const singleReplicas = await fs.readdir(`./engine-private/replica`);
453
+ for (let replica of singleReplicas) {
454
+ if (replica.startsWith(deployId)) this.buildProxyByDeployId(replica, subConf);
455
+ }
456
+ }
457
+ }
458
+ },
459
+ };
460
+
461
+ /**
462
+ * @method loadConf
463
+ * @description Loads the configuration of the server.
464
+ * @param {string} [deployId='dd-default'] - The deploy ID.
465
+ * @param {string} [subConf=''] - The sub configuration.
466
+ * @memberof ServerConfBuilder
467
+ */
468
+ const loadConf = (deployId = DEFAULT_DEPLOY_ID, subConf) => {
469
+ if (deployId === 'current') {
470
+ console.log(process.env.DEPLOY_ID);
471
+ return;
472
+ }
473
+ if (deployId === 'clean') {
474
+ const path = '.';
475
+ fs.removeSync(`${path}/.env`);
476
+ fs.removeSync(`${path}/.env.production`);
477
+ fs.removeSync(`${path}/.env.development`);
478
+ fs.removeSync(`${path}/.env.test`);
479
+ return;
480
+ }
481
+ const folder = getConfFolder(deployId);
482
+
483
+ if (!fs.existsSync(folder)) Config.deployIdFactory(deployId);
484
+
485
+ if (subConf) process.env.DEPLOY_SUB_CONF = subConf;
486
+
487
+ for (const typeConf of Object.keys(Config.default)) {
488
+ let srcConf = fs.readFileSync(`${folder}/conf.${typeConf}.json`, 'utf8');
489
+ if (process.env.NODE_ENV === 'development' && typeConf === 'server' && subConf) {
490
+ const devConfPath = `${folder}/conf.${typeConf}.dev${subConf ? `.${subConf}` : ''}.json`;
491
+ if (fs.existsSync(devConfPath)) srcConf = fs.readFileSync(devConfPath, 'utf8');
492
+ }
493
+ let parsed = JSON.parse(srcConf);
494
+ if (typeConf === 'server') parsed = loadReplicas(deployId, parsed);
495
+ Config.default[typeConf] = parsed;
496
+ }
497
+ fs.writeFileSync(`./.env.production`, fs.readFileSync(`${folder}/.env.production`, 'utf8'), 'utf8');
498
+ fs.writeFileSync(`./.env.development`, fs.readFileSync(`${folder}/.env.development`, 'utf8'), 'utf8');
499
+ fs.writeFileSync(`./.env.test`, fs.readFileSync(`${folder}/.env.test`, 'utf8'), 'utf8');
500
+ const NODE_ENV = process.env.NODE_ENV || 'development';
501
+ if (NODE_ENV) {
502
+ const subPathEnv = fs.existsSync(`${folder}/.env.${NODE_ENV}.${subConf}`)
503
+ ? `${folder}/.env.${NODE_ENV}.${subConf}`
504
+ : `${folder}/.env.${NODE_ENV}`;
505
+ fs.writeFileSync(`./.env`, fs.readFileSync(subPathEnv, 'utf8'), 'utf8');
506
+ const env = dotenv.parse(fs.readFileSync(subPathEnv, 'utf8'));
507
+ process.env = {
508
+ ...process.env,
509
+ ...env,
510
+ };
511
+ }
512
+ const originPackageJson = JSON.parse(fs.readFileSync(`./package.json`, 'utf8'));
513
+ const packageJson = JSON.parse(fs.readFileSync(`${folder}/package.json`, 'utf8'));
514
+ originPackageJson.scripts.start = packageJson.scripts.start;
515
+ packageJson.scripts = originPackageJson.scripts;
516
+ fs.writeFileSync(`./package.json`, JSON.stringify(packageJson, null, 4), 'utf8');
517
+ return { folder, deployId };
518
+ };
519
+
520
+ /**
521
+ * @method loadReplicas
522
+ * @description Loads the replicas of the server.
523
+ * @param {object} confServer - The server configuration.
524
+ * @memberof ServerConfBuilder
525
+ */
526
+ const loadReplicas = (deployId, confServer) => {
527
+ const confServerOrigin = newInstance(confServer);
528
+ for (const host of Object.keys(confServer)) {
529
+ for (const path of Object.keys(confServer[host])) {
530
+ const { replicas, singleReplica } = confServer[host][path];
531
+ if (replicas) {
532
+ if (!singleReplica)
533
+ for (const replicaPath of replicas) {
534
+ {
535
+ confServer[host][replicaPath] = newInstance(confServer[host][path]);
536
+ delete confServer[host][replicaPath].replicas;
537
+ }
538
+ }
539
+ else {
540
+ const orderReplica = orderAbc(confServerOrigin[host][path].replicas);
541
+ confServerOrigin[host][path].replicas = orderReplica;
542
+ confServer[host][path].replicas = orderReplica;
543
+ }
544
+ }
545
+ }
546
+ }
547
+ const serverPath = `./engine-private/conf/${deployId}/conf.server${process.env.NODE_ENV === 'production' ? '' : '.dev'}.json`;
548
+ if (fs.existsSync(serverPath)) fs.writeFileSync(serverPath, JSON.stringify(confServerOrigin, null, 4), 'utf8');
549
+
550
+ return confServer;
551
+ };
552
+
553
+ /**
554
+ * @method cloneConf
555
+ * @description Clones the configuration of the server.
556
+ * @param {object} toOptions - The options for the target configuration.
557
+ * @param {object} fromOptions - The options for the source configuration.
558
+ * @param {object} [fromDefaultOptions={ deployId: 'dd-default', clientId: 'default' }] - The default options for the source configuration.
559
+ * @memberof ServerConfBuilder
560
+ */
561
+ const cloneConf = async (
562
+ { toOptions, fromOptions },
563
+ fromDefaultOptions = { deployId: 'dd-default', clientId: 'default' },
564
+ ) => {
565
+ if (!fromOptions.deployId) fromOptions.deployId = fromDefaultOptions.deployId;
566
+ if (!fromOptions.clientId) fromOptions.clientId = fromDefaultOptions.clientId;
567
+
568
+ const confFromFolder = `./engine-private/conf/${fromOptions.deployId}`;
569
+ const confToFolder = `./engine-private/conf/${toOptions.deployId}`;
570
+
571
+ const toClientVariableName = getCapVariableName(toOptions.clientId);
572
+ const fromClientVariableName = getCapVariableName(fromOptions.clientId);
573
+
574
+ const formattedSrc = (dataConf) =>
575
+ JSON.stringify(dataConf, null, 4)
576
+ .replaceAll(fromClientVariableName, toClientVariableName)
577
+ .replaceAll(fromOptions.clientId, toOptions.clientId);
578
+
579
+ const isMergeConf = fs.existsSync(confToFolder);
580
+ if (!isMergeConf) fs.mkdirSync(confToFolder, { recursive: true });
581
+
582
+ fs.writeFileSync(
583
+ `${confToFolder}/.env.production`,
584
+ fs.readFileSync(`${confFromFolder}/.env.production`, 'utf8'),
585
+ 'utf8',
586
+ );
587
+ fs.writeFileSync(
588
+ `${confToFolder}/.env.development`,
589
+ fs.readFileSync(`${confFromFolder}/.env.development`, 'utf8'),
590
+ 'utf8',
591
+ );
592
+ fs.writeFileSync(`${confToFolder}/.env.test`, fs.readFileSync(`${confFromFolder}/.env.test`, 'utf8'), 'utf8');
593
+
594
+ for (const confTypeId of ['server', 'client', 'cron', 'ssr']) {
595
+ const confFromData = JSON.parse(fs.readFileSync(`${confFromFolder}/conf.${confTypeId}.json`, 'utf8'));
596
+ fs.writeFileSync(`${confToFolder}/conf.${confTypeId}.json`, formattedSrc(confFromData), 'utf8');
597
+ }
598
+
599
+ const packageData = JSON.parse(fs.readFileSync(`${confFromFolder}/package.json`, 'utf8'));
600
+ packageData.scripts.start = packageData.scripts.start.replaceAll(fromOptions.deployId, toOptions.deployId);
601
+ fs.writeFileSync(`${confToFolder}/package.json`, JSON.stringify(packageData, null, 4), 'utf8');
602
+ };
603
+
604
+ /**
605
+ * @method addClientConf
606
+ * @description Adds the client configuration to the server.
607
+ * @param {object} toOptions - The options for the target configuration.
608
+ * @param {object} fromOptions - The options for the source configuration.
609
+ * @param {object} [fromDefaultOptions={ deployId: 'dd-default', clientId: 'default' }] - The default options for the source configuration.
610
+ * @memberof ServerConfBuilder
611
+ */
612
+ const addClientConf = async (
613
+ { toOptions, fromOptions },
614
+ fromDefaultOptions = { deployId: 'dd-default', clientId: 'default' },
615
+ ) => {
616
+ if (!fromOptions.deployId) fromOptions.deployId = fromDefaultOptions.deployId;
617
+ if (!fromOptions.clientId) fromOptions.clientId = fromDefaultOptions.clientId;
618
+
619
+ const confFromFolder = `./engine-private/conf/${fromOptions.deployId}`;
620
+ const confToFolder = `./engine-private/conf/${toOptions.deployId}`;
621
+
622
+ const toClientConf = JSON.parse(fs.readFileSync(`${confToFolder}/conf.client.json`, 'utf8'));
623
+ const fromClientConf = JSON.parse(fs.readFileSync(`${confFromFolder}/conf.client.json`, 'utf8'));
624
+
625
+ const toClientVariableName = getCapVariableName(toOptions.clientId);
626
+ const fromClientVariableName = getCapVariableName(fromOptions.clientId);
627
+
628
+ const { host, path } = toOptions;
629
+
630
+ toClientConf[fromOptions.clientId] = fromClientConf[fromOptions.clientId];
631
+
632
+ fs.writeFileSync(`${confToFolder}/conf.client.json`, JSON.stringify(toClientConf, null, 4), 'utf8');
633
+
634
+ const toServerConf = JSON.parse(fs.readFileSync(`${confToFolder}/conf.server.json`, 'utf8'));
635
+ const fromServerConf = JSON.parse(fs.readFileSync(`${confToFolder}/conf.server.json`, 'utf8'));
636
+
637
+ toServerConf[host][path].client = fromOptions.clientId;
638
+ toServerConf[host][path].runtime = 'nodejs';
639
+ toServerConf[host][path].apis = fromClientConf[fromOptions.clientId].services;
640
+
641
+ fs.writeFileSync(`${confToFolder}/conf.server.json`, JSON.stringify(toServerConf, null, 4), 'utf8');
642
+
643
+ const fromSsrConf = JSON.parse(fs.readFileSync(`${confFromFolder}/conf.ssr.json`, 'utf8'));
644
+ const toSsrConf = JSON.parse(fs.readFileSync(`${confToFolder}/conf.ssr.json`, 'utf8'));
645
+
646
+ toSsrConf[fromClientVariableName] = fromSsrConf[fromClientVariableName];
647
+
648
+ fs.writeFileSync(`${confToFolder}/conf.ssr.json`, JSON.stringify(toSsrConf, null, 4), 'utf8');
649
+ };
650
+
651
+ /**
652
+ * @method buildClientSrc
653
+ * @description Builds the client source code.
654
+ * @param {object} toOptions - The options for the target configuration.
655
+ * @param {object} fromOptions - The options for the source configuration.
656
+ * @param {object} [fromDefaultOptions={ deployId: 'dd-default', clientId: 'default' }] - The default options for the source configuration.
657
+ * @memberof ServerConfBuilder
658
+ */
659
+ const buildClientSrc = async (
660
+ { toOptions, fromOptions },
661
+ fromDefaultOptions = { deployId: 'dd-default', clientId: 'default' },
662
+ ) => {
663
+ if (!fromOptions.deployId) fromOptions.deployId = fromDefaultOptions.deployId;
664
+ if (!fromOptions.clientId) fromOptions.clientId = fromDefaultOptions.clientId;
665
+
666
+ const confFromFolder = `./src/client/components/${fromOptions.clientId}`;
667
+ const confToFolder = `./src/client/components/${toOptions.clientId}`;
668
+
669
+ const toClientVariableName = getCapVariableName(toOptions.clientId);
670
+ const fromClientVariableName = getCapVariableName(fromOptions.clientId);
671
+
672
+ const formattedSrc = (src) =>
673
+ src.replaceAll(fromClientVariableName, toClientVariableName).replaceAll(fromOptions.clientId, toOptions.clientId);
674
+
675
+ const isMergeConf = fs.existsSync(confToFolder);
676
+ if (!isMergeConf) fs.mkdirSync(confToFolder, { recursive: true });
677
+
678
+ const files = await fs.readdir(confFromFolder, { recursive: true });
679
+ for (const relativePath of files) {
680
+ const fromFilePath = dir.resolve(`${confFromFolder}/${relativePath}`);
681
+ const toFilePath = dir.resolve(`${confToFolder}/${relativePath}`);
682
+
683
+ fs.writeFileSync(formattedSrc(toFilePath), formattedSrc(fs.readFileSync(fromFilePath, 'utf8')), 'utf8');
684
+ }
685
+
686
+ fs.writeFileSync(
687
+ `./src/client/ssr/head/${toClientVariableName}Scripts.js`,
688
+ formattedSrc(fs.readFileSync(`./src/client/ssr/head/${fromClientVariableName}Scripts.js`, 'utf8')),
689
+ 'utf8',
690
+ );
691
+
692
+ fs.writeFileSync(
693
+ `./src/client/${toClientVariableName}.index.js`,
694
+ formattedSrc(fs.readFileSync(`./src/client/${fromClientVariableName}.index.js`, 'utf8')),
695
+ 'utf8',
696
+ );
697
+
698
+ fs.copySync(`./src/client/public/${fromOptions.clientId}`, `./src/client/public/${toOptions.clientId}`);
699
+ };
700
+
701
+ /**
702
+ * @method buildApiSrc
703
+ * @description Builds the API source code.
704
+ * @param {object} toOptions - The options for the target configuration.
705
+ * @param {object} fromOptions - The options for the source configuration.
706
+ * @param {object} [fromDefaultOptions={ apiId: 'default', deployId: 'dd-default', clientId: 'default' }] - The default options for the source configuration.
707
+ * @memberof ServerConfBuilder
708
+ */
709
+ const buildApiSrc = async (
710
+ { toOptions, fromOptions },
711
+ fromDefaultOptions = { apiId: 'default', deployId: 'dd-default', clientId: 'default' },
712
+ ) => {
713
+ if (!fromOptions.apiId) fromOptions.apiId = fromDefaultOptions.apiId;
714
+ if (!fromOptions.deployId) fromOptions.deployId = fromDefaultOptions.deployId;
715
+ if (!fromOptions.clientId) fromOptions.clientId = fromDefaultOptions.clientId;
716
+
717
+ const toClientVariableName = getCapVariableName(toOptions.apiId);
718
+ const fromClientVariableName = getCapVariableName(fromOptions.apiId);
719
+
720
+ const formattedSrc = (src) =>
721
+ src.replaceAll(fromClientVariableName, toClientVariableName).replaceAll(fromOptions.apiId, toOptions.apiId);
722
+
723
+ const apiToFolder = `./src/api/${toOptions.apiId}`;
724
+ const apiFromFolder = `./src/api/${fromOptions.apiId}`;
725
+
726
+ const isMergeConf = fs.existsSync(apiToFolder);
727
+ if (!isMergeConf) fs.mkdirSync(apiToFolder, { recursive: true });
728
+
729
+ for (const srcApiType of ['model', 'controller', 'service', 'router']) {
730
+ fs.writeFileSync(
731
+ `${apiToFolder}/${toOptions.apiId}.${srcApiType}.js`,
732
+ formattedSrc(fs.readFileSync(`${apiFromFolder}/${fromOptions.apiId}.${srcApiType}.js`, 'utf8')),
733
+ 'utf8',
734
+ );
735
+ }
736
+
737
+ fs.mkdirSync(`./src/client/services/${toOptions.apiId}`, { recursive: true });
738
+ if (fs.existsSync(`./src/client/services/${fromOptions.apiId}/${fromOptions.apiId}.service.js`))
739
+ fs.writeFileSync(
740
+ `./src/client/services/${toOptions.apiId}/${toOptions.apiId}.service.js`,
741
+ formattedSrc(
742
+ fs.readFileSync(`./src/client/services/${fromOptions.apiId}/${fromOptions.apiId}.service.js`, 'utf8'),
743
+ ),
744
+ 'utf8',
745
+ );
746
+ };
747
+
748
+ /**
749
+ * @method addApiConf
750
+ * @description Adds the API configuration to the server.
751
+ * @param {object} toOptions - The options for the target configuration.
752
+ * @param {object} fromOptions - The options for the source configuration.
753
+ * @param {object} [fromDefaultOptions={ apiId: 'default', deployId: 'dd-default', clientId: 'default' }] - The default options for the source configuration.
754
+ * @memberof ServerConfBuilder
755
+ */
756
+ const addApiConf = async (
757
+ { toOptions, fromOptions },
758
+ fromDefaultOptions = { apiId: 'default', deployId: 'dd-default', clientId: 'default' },
759
+ ) => {
760
+ if (!fromOptions.apiId) fromOptions.apiId = fromDefaultOptions.apiId;
761
+ if (!fromOptions.deployId) fromOptions.deployId = fromDefaultOptions.deployId;
762
+ if (!fromOptions.clientId) fromOptions.clientId = fromDefaultOptions.clientId;
763
+
764
+ const toClientVariableName = getCapVariableName(toOptions.apiId);
765
+ const fromClientVariableName = getCapVariableName(fromOptions.apiId);
766
+
767
+ const confFromFolder = `./engine-private/conf/${fromOptions.deployId}`;
768
+ const confToFolder = `./engine-private/conf/${toOptions.deployId}`;
769
+
770
+ const confServer = JSON.parse(fs.readFileSync(`${confToFolder}/conf.server.json`, 'utf8'));
771
+ for (const host of Object.keys(confServer))
772
+ for (const path of Object.keys(confServer[host]))
773
+ if (confServer[host][path].apis) confServer[host][path].apis.push(toOptions.apiId);
774
+ fs.writeFileSync(`${confToFolder}/conf.server.json`, JSON.stringify(confServer, null, 4), 'utf8');
775
+
776
+ const confClient = JSON.parse(fs.readFileSync(`${confToFolder}/conf.client.json`, 'utf8'));
777
+ confClient[toOptions.clientId].services.push(toOptions.apiId);
778
+ fs.writeFileSync(`${confToFolder}/conf.client.json`, JSON.stringify(confClient, null, 4), 'utf8');
779
+ };
780
+
781
+ /**
782
+ * @method addWsConf
783
+ * @description Adds the WebSocket configuration to the server.
784
+ * @param {object} toOptions - The options for the target configuration.
785
+ * @param {object} fromOptions - The options for the source configuration.
786
+ * @param {object} [fromDefaultOptions={ wsId: 'default', deployId: 'dd-default', host: 'default.net', paths: '/' }] - The default options for the source configuration.
787
+ * @memberof ServerConfBuilder
788
+ */
789
+ const addWsConf = async (
790
+ { toOptions, fromOptions },
791
+ fromDefaultOptions = { wsId: 'default', deployId: 'dd-default', host: 'default.net', paths: '/' },
792
+ ) => {
793
+ if (!fromOptions.wsId) fromOptions.wsId = fromDefaultOptions.wsId;
794
+ if (!fromOptions.deployId) fromOptions.deployId = fromDefaultOptions.deployId;
795
+ if (!fromOptions.host) fromOptions.host = fromDefaultOptions.host;
796
+ if (!fromOptions.paths) fromOptions.paths = fromDefaultOptions.paths;
797
+
798
+ const toClientVariableName = getCapVariableName(toOptions.apiId);
799
+ const fromClientVariableName = getCapVariableName(fromOptions.apiId);
800
+
801
+ const confFromFolder = `./engine-private/conf/${fromOptions.deployId}`;
802
+ const confToFolder = `./engine-private/conf/${toOptions.deployId}`;
803
+
804
+ const paths = toOptions.paths.split(',');
805
+
806
+ const confServer = JSON.parse(fs.readFileSync(`${confToFolder}/conf.server.json`, 'utf8'));
807
+ for (const host of Object.keys(confServer))
808
+ for (const path of Object.keys(confServer[host]))
809
+ if (host === toOptions.host && paths.includes(path) && confServer[host][path])
810
+ confServer[host][path].ws = toOptions.wsId;
811
+ fs.writeFileSync(`${confToFolder}/conf.server.json`, JSON.stringify(confServer, null, 4), 'utf8');
812
+ };
813
+
814
+ /**
815
+ * @method buildWsSrc
816
+ * @description Builds the WebSocket source code.
817
+ * @param {object} toOptions - The options for the target configuration.
818
+ * @param {object} fromOptions - The options for the source configuration.
819
+ * @param {object} [fromDefaultOptions={ wsId: 'default', deployId: 'dd-default', host: 'default.net', paths: '/' }] - The default options for the source configuration.
820
+ * @memberof ServerConfBuilder
821
+ */
822
+ const buildWsSrc = async (
823
+ { toOptions, fromOptions },
824
+ fromDefaultOptions = { wsId: 'default', deployId: 'dd-default', host: 'default.net', paths: '/' },
825
+ ) => {
826
+ if (!fromOptions.wsId) fromOptions.wsId = fromDefaultOptions.wsId;
827
+ if (!fromOptions.deployId) fromOptions.deployId = fromDefaultOptions.deployId;
828
+ if (!fromOptions.host) fromOptions.host = fromDefaultOptions.host;
829
+ if (!fromOptions.paths) fromOptions.paths = fromDefaultOptions.paths;
830
+
831
+ const toClientVariableName = getCapVariableName(toOptions.wsId);
832
+ const fromClientVariableName = getCapVariableName(fromOptions.wsId);
833
+
834
+ const confFromFolder = `./src/ws/${fromOptions.wsId}`;
835
+ const confToFolder = `./src/ws/${toOptions.wsId}`;
836
+
837
+ const paths = toOptions.paths.split(',');
838
+
839
+ const formattedSrc = (src) =>
840
+ src.replaceAll(fromClientVariableName, toClientVariableName).replaceAll(fromOptions.wsId, toOptions.wsId);
841
+
842
+ const files = await fs.readdir(confFromFolder, { recursive: true });
843
+ for (const relativePath of files) {
844
+ const fromFilePath = dir.resolve(`${confFromFolder}/${relativePath}`);
845
+ const toFilePath = dir.resolve(`${confToFolder}/${relativePath}`);
846
+
847
+ if (fs.lstatSync(fromFilePath).isDirectory() && !fs.existsSync(formattedSrc(toFilePath)))
848
+ fs.mkdirSync(formattedSrc(toFilePath), { recursive: true });
849
+
850
+ if (fs.lstatSync(fromFilePath).isFile() && !fs.existsSync(formattedSrc(toFilePath))) {
851
+ fs.writeFileSync(formattedSrc(toFilePath), formattedSrc(fs.readFileSync(fromFilePath, 'utf8')), 'utf8');
852
+ }
853
+ }
854
+ };
855
+
856
+ /**
857
+ * @method cloneSrcComponents
858
+ * @description Clones the source components.
859
+ * @param {object} toOptions - The options for the target configuration.
860
+ * @param {object} fromOptions - The options for the source configuration.
861
+ * @memberof ServerConfBuilder
862
+ */
863
+ const cloneSrcComponents = async ({ toOptions, fromOptions }) => {
864
+ const toClientVariableName = getCapVariableName(toOptions.componentsFolder);
865
+ const fromClientVariableName = getCapVariableName(fromOptions.componentsFolder);
866
+
867
+ const formattedSrc = (src) =>
868
+ src
869
+ .replaceAll(fromClientVariableName, toClientVariableName)
870
+ .replaceAll(fromOptions.componentsFolder, toOptions.componentsFolder);
871
+
872
+ const confFromFolder = `./src/client/components/${fromOptions.componentsFolder}`;
873
+ const confToFolder = `./src/client/components/${toOptions.componentsFolder}`;
874
+
875
+ fs.mkdirSync(confToFolder, { recursive: true });
876
+
877
+ const files = await fs.readdir(confFromFolder);
878
+ for (const relativePath of files) {
879
+ const fromFilePath = dir.resolve(`${confFromFolder}/${relativePath}`);
880
+ const toFilePath = dir.resolve(`${confToFolder}/${relativePath}`);
881
+
882
+ fs.writeFileSync(formattedSrc(toFilePath), formattedSrc(fs.readFileSync(fromFilePath, 'utf8')), 'utf8');
883
+ }
884
+ };
885
+
886
+ /**
887
+ * @method buildProxyRouter
888
+ * @description Builds the proxy router.
889
+ * @memberof ServerConfBuilder
890
+ */
891
+ const buildProxyRouter = () => {
892
+ const confServer = newInstance(Config.default.server);
893
+ let currentPort = parseInt(process.env.PORT) + 1;
894
+ const proxyRouter = {};
895
+ for (const host of Object.keys(confServer)) {
896
+ for (const path of Object.keys(confServer[host])) {
897
+ if (confServer[host][path].singleReplica) continue;
898
+
899
+ if (isDevProxyContext()) confServer[host][path].proxy = [isTlsDevProxy() ? 443 : 80];
900
+
901
+ confServer[host][path].port = newInstance(currentPort);
902
+ for (const port of confServer[host][path].proxy) {
903
+ if (!(port in proxyRouter)) proxyRouter[port] = {};
904
+ proxyRouter[port][`${host}${path}`] = {
905
+ // target: `http://${host}:${confServer[host][path].port}${path}`,
906
+ target: `http://localhost:${confServer[host][path].port}`,
907
+ // target: `http://127.0.0.1:${confServer[host][path].port}`,
908
+ proxy: confServer[host][path].proxy,
909
+ redirect: confServer[host][path].redirect,
910
+ host,
911
+ path,
912
+ };
913
+ }
914
+ currentPort++;
915
+ if (confServer[host][path].peer) {
916
+ const peerPath = path === '/' ? `/peer` : `${path}/peer`;
917
+ confServer[host][peerPath] = newInstance(confServer[host][path]);
918
+ confServer[host][peerPath].port = newInstance(currentPort);
919
+ for (const port of confServer[host][path].proxy) {
920
+ if (!(port in proxyRouter)) proxyRouter[port] = {};
921
+ proxyRouter[port][`${host}${peerPath}`] = {
922
+ // target: `http://${host}:${confServer[host][peerPath].port}${peerPath}`,
923
+ target: `http://localhost:${confServer[host][peerPath].port}`,
924
+ // target: `http://127.0.0.1:${confServer[host][peerPath].port}`,
925
+ proxy: confServer[host][peerPath].proxy,
926
+ host,
927
+ path: peerPath,
928
+ };
929
+ }
930
+ currentPort++;
931
+ }
932
+ }
933
+ }
934
+
935
+ return proxyRouter;
936
+ };
937
+
938
+ /**
939
+ * @method pathPortAssignmentFactory
940
+ * @description Creates the path port assignment.
941
+ * @param {string} deployId - The deploy ID.
942
+ * @param {object} router - The router.
943
+ * @param {object} confServer - The server configuration.
944
+ * @memberof ServerConfBuilder
945
+ */
946
+ const pathPortAssignmentFactory = async (deployId, router, confServer) => {
947
+ const pathPortAssignmentData = {};
948
+ for (const host of Object.keys(confServer)) {
949
+ const pathPortAssignment = [];
950
+ for (const path of Object.keys(confServer[host])) {
951
+ const { peer } = confServer[host][path];
952
+ if (!router[`${host}${path === '/' ? '' : path}`]) continue;
953
+ const port = parseInt(router[`${host}${path === '/' ? '' : path}`].split(':')[2]);
954
+ // logger.info('', { host, port, path });
955
+ pathPortAssignment.push({
956
+ port,
957
+ path,
958
+ });
959
+
960
+ if (peer) {
961
+ // logger.info('', { host, port: port + 1, path: '/peer' });
962
+ pathPortAssignment.push({
963
+ port: port + 1,
964
+ path: `${path === '/' ? '' : path}/peer`,
965
+ });
966
+ }
967
+ }
968
+ pathPortAssignmentData[host] = pathPortAssignment;
969
+ }
970
+ if (fs.existsSync(`./engine-private/replica`)) {
971
+ const singleReplicas = await fs.readdir(`./engine-private/replica`);
972
+ for (let replica of singleReplicas) {
973
+ if (replica.startsWith(deployId)) {
974
+ const replicaServerConf = loadConfServerJson(`./engine-private/replica/${replica}/conf.server.json`);
975
+ for (const host of Object.keys(replicaServerConf)) {
976
+ const pathPortAssignment = [];
977
+ for (const path of Object.keys(replicaServerConf[host])) {
978
+ const { peer } = replicaServerConf[host][path];
979
+ if (!router[`${host}${path === '/' ? '' : path}`]) continue;
980
+ const port = parseInt(router[`${host}${path === '/' ? '' : path}`].split(':')[2]);
981
+ // logger.info('', { host, port, path });
982
+ pathPortAssignment.push({
983
+ port,
984
+ path,
985
+ });
986
+
987
+ if (peer) {
988
+ // logger.info('', { host, port: port + 1, path: '/peer' });
989
+ pathPortAssignment.push({
990
+ port: port + 1,
991
+ path: `${path === '/' ? '' : path}/peer`,
992
+ });
993
+ }
994
+ }
995
+ pathPortAssignmentData[host] = pathPortAssignmentData[host].concat(pathPortAssignment);
996
+ }
997
+ }
998
+ }
999
+ }
1000
+ return pathPortAssignmentData;
1001
+ };
1002
+
1003
+ /**
1004
+ * @method deployRangePortFactory
1005
+ * @description Creates the deploy range port factory.
1006
+ * @param {object} router - The router.
1007
+ * @returns {object} - The deploy range port factory.
1008
+ * @memberof ServerConfBuilder
1009
+ */
1010
+ const deployRangePortFactory = (router) => {
1011
+ const ports = Object.values(router).map((p) => parseInt(p.split(':')[2]));
1012
+ const fromPort = Math.min(...ports);
1013
+ const toPort = Math.max(...ports);
1014
+ return { ports, fromPort, toPort };
1015
+ };
1016
+
1017
+ /**
1018
+ * @method buildKindPorts
1019
+ * @description Builds the kind ports.
1020
+ * @param {number} from - The from port.
1021
+ * @param {number} to - The to port.
1022
+ * @returns {string} - The kind ports.
1023
+ * @memberof ServerConfBuilder
1024
+ */
1025
+ const buildKindPorts = (from, to) =>
1026
+ range(parseInt(from), parseInt(to))
1027
+ .map(
1028
+ (port) => ` - name: 'tcp-${port}'
1029
+ protocol: TCP
1030
+ port: ${port}
1031
+ targetPort: ${port}
1032
+ - name: 'udp-${port}'
1033
+ protocol: UDP
1034
+ port: ${port}
1035
+ targetPort: ${port}
1036
+ `,
1037
+ )
1038
+ .join('\n');
1039
+
1040
+ /**
1041
+ * @method buildPortProxyRouter
1042
+ * @description Builds the port proxy router.
1043
+ * @param {object} options - The options.
1044
+ * @param {number} [options.port=4000] - The port.
1045
+ * @param {object} options.proxyRouter - The proxy router.
1046
+ * @param {object} [options.hosts] - The hosts.
1047
+ * @param {boolean} [options.orderByPathLength=false] - Whether to order by path length.
1048
+ * @param {boolean} [options.devProxyContext=false] - Whether to use dev proxy context.
1049
+ * @returns {object} - The port proxy router.
1050
+ * @memberof ServerConfBuilder
1051
+ */
1052
+ const buildPortProxyRouter = (
1053
+ options = { port: 4000, proxyRouter, hosts, orderByPathLength: false, devProxyContext: false },
1054
+ ) => {
1055
+ let { port, proxyRouter, hosts, orderByPathLength } = options;
1056
+ hosts = hosts || proxyRouter[port] || {};
1057
+
1058
+ const router = {};
1059
+ // build router
1060
+ Object.keys(hosts).map((hostKey) => {
1061
+ let { host, path, target, proxy, peer } = hosts[hostKey];
1062
+
1063
+ if (!proxy.includes(port)) {
1064
+ logger.warn('Proxy port not set on conf', { port, host, path, proxy, target });
1065
+ if (process.env.NODE_ENV === 'production') {
1066
+ logger.warn('Omitting host', { host, path, target });
1067
+ return;
1068
+ }
1069
+ }
1070
+ // ${process.env.NODE_ENV === 'development' && !isDevProxyContext() ? `:${port}` : ''}
1071
+ const absoluteHost = [80, 443].includes(port)
1072
+ ? `${host}${path === '/' ? '' : path}`
1073
+ : `${host}:${port}${path === '/' ? '' : path}`;
1074
+
1075
+ if (absoluteHost in router)
1076
+ logger.warn('Overwrite: Absolute host already exists on router', { absoluteHost, target });
1077
+
1078
+ if (options.devProxyContext === true) {
1079
+ const appDevPort = parseInt(target.split(':')[2]) - process.env.DEV_PROXY_PORT_OFFSET;
1080
+ router[absoluteHost] = `http://localhost:${appDevPort}`;
1081
+ } else router[absoluteHost] = target;
1082
+ }); // order router
1083
+
1084
+ if (Object.keys(router).length === 0) return router;
1085
+
1086
+ const devApiConfPath = `./engine-private/conf/${process.argv[3]}/conf.server.dev.${process.argv[4]}-dev-api.json`;
1087
+ if (options.devProxyContext === true && process.env.NODE_ENV === 'development' && fs.existsSync(devApiConfPath)) {
1088
+ const confDevApiServer = JSON.parse(fs.readFileSync(devApiConfPath, 'utf8'));
1089
+ let devApiHosts = [];
1090
+ let origins = [];
1091
+ for (const _host of Object.keys(confDevApiServer))
1092
+ for (const _path of Object.keys(confDevApiServer[_host])) {
1093
+ if (confDevApiServer[_host][_path].origins && confDevApiServer[_host][_path].origins.length) {
1094
+ origins.push(...confDevApiServer[_host][_path].origins);
1095
+ if (_path !== 'peer' && devApiHosts.length === 0)
1096
+ devApiHosts.push(
1097
+ `${_host}${[80, 443].includes(port) && isDevProxyContext() ? '' : `:${port}`}${_path == '/' ? '' : _path}`,
1098
+ );
1099
+ }
1100
+ }
1101
+ origins = Array.from(new Set(origins));
1102
+ console.log({
1103
+ origins,
1104
+ devApiHosts,
1105
+ });
1106
+ for (const devApiHost of devApiHosts) {
1107
+ if (devApiHost in router) {
1108
+ const target = router[devApiHost];
1109
+ delete router[devApiHost];
1110
+ router[`${devApiHost}/${process.env.BASE_API}`] = target;
1111
+ router[`${devApiHost}/socket.io`] = target;
1112
+ for (const origin of origins) router[devApiHost] = origin;
1113
+ }
1114
+ }
1115
+ }
1116
+
1117
+ if (orderByPathLength === true) {
1118
+ const reOrderRouter = {};
1119
+ for (const absoluteHostKey of orderArrayFromAttrInt(Object.keys(router), 'length'))
1120
+ reOrderRouter[absoluteHostKey] = router[absoluteHostKey];
1121
+ return reOrderRouter;
1122
+ }
1123
+
1124
+ return router;
1125
+ };
1126
+
1127
+ /**
1128
+ * @method buildReplicaId
1129
+ * @description Builds the replica ID.
1130
+ * @param {object} options - The options.
1131
+ * @param {string} options.deployId - The deploy ID.
1132
+ * @param {string} options.replica - The replica.
1133
+ * @returns {string} - The replica ID.
1134
+ * @memberof ServerConfBuilder
1135
+ */
1136
+ const buildReplicaId = ({ deployId, replica }) => `${deployId}-${replica.slice(1)}`;
1137
+
1138
+ /**
1139
+ * @method getDataDeploy
1140
+ * @description Gets the data deploy.
1141
+ * @param {object} options - The options.
1142
+ * @param {boolean} [options.buildSingleReplica=false] - The build single replica.
1143
+ * @param {string} [options.deployId] - The deploy ID.
1144
+ * @param {boolean} [options.disableSyncEnvPort=false] - The disable sync env port.
1145
+ * @returns {object} - The data deploy.
1146
+ * @memberof ServerConfBuilder
1147
+ */
1148
+ const getDataDeploy = async (
1149
+ options = {
1150
+ buildSingleReplica: false,
1151
+ disableSyncEnvPort: false,
1152
+ },
1153
+ ) => {
1154
+ let dataDeploy = fs
1155
+ .readFileSync(`./engine-private/deploy/dd.router`, 'utf8')
1156
+ .split(',')
1157
+ .map((deployId) => deployId.trim())
1158
+ .filter((deployId) => deployId);
1159
+
1160
+ dataDeploy = dataDeploy.map((deployId) => {
1161
+ return {
1162
+ deployId,
1163
+ };
1164
+ });
1165
+
1166
+ if (options && options.buildSingleReplica && fs.existsSync(`./engine-private/replica`))
1167
+ fs.removeSync(`./engine-private/replica`);
1168
+
1169
+ let buildDataDeploy = [];
1170
+ for (const deployObj of dataDeploy) {
1171
+ const isReplicaDeploy = fs.existsSync(`./engine-private/replica/${deployObj.deployId}`);
1172
+ const serverConf = loadReplicas(
1173
+ deployObj.deployId,
1174
+ loadConfServerJson(`./engine-private/conf/${deployObj.deployId}/conf.server.json`),
1175
+ );
1176
+ let replicaDataDeploy = [];
1177
+ for (const host of Object.keys(serverConf))
1178
+ for (const path of Object.keys(serverConf[host])) {
1179
+ if (!isReplicaDeploy && serverConf[host][path].replicas && serverConf[host][path].singleReplica) {
1180
+ if (options && options.buildSingleReplica)
1181
+ await Underpost.repo.client(deployObj.deployId, '', host, path, {
1182
+ singleReplica: true,
1183
+ });
1184
+ replicaDataDeploy = replicaDataDeploy.concat(
1185
+ serverConf[host][path].replicas.map((r) => {
1186
+ return {
1187
+ deployId: buildReplicaId({ deployId: deployObj.deployId, replica: r }),
1188
+ replicaHost: host,
1189
+ };
1190
+ }),
1191
+ );
1192
+ }
1193
+ }
1194
+ buildDataDeploy.push(deployObj);
1195
+ if (replicaDataDeploy.length > 0) buildDataDeploy = buildDataDeploy.concat(replicaDataDeploy);
1196
+ }
1197
+
1198
+ if (!options.disableSyncEnvPort && options.buildSingleReplica)
1199
+ await Underpost.repo.client(undefined, '', '', '', { syncEnvPort: true });
1200
+
1201
+ logger.info('Deployments configured', buildDataDeploy);
1202
+
1203
+ return buildDataDeploy;
1204
+ };
1205
+
1206
+ /**
1207
+ * @method validateTemplatePath
1208
+ * @description Validates the template path.
1209
+ * @param {string} absolutePath - The absolute path.
1210
+ * @returns {boolean} - The validation result.
1211
+ * @memberof ServerConfBuilder
1212
+ */
1213
+ const validateTemplatePath = (absolutePath = '') => {
1214
+ const host = 'default.net';
1215
+ const path = '/';
1216
+ const client = 'default';
1217
+ const ssr = 'Default';
1218
+ const confServer = DefaultConf.server[host][path];
1219
+ const confClient = DefaultConf.client[client];
1220
+ const confSsr = DefaultConf.ssr[ssr];
1221
+ const clients = DefaultConf.client.default.services;
1222
+
1223
+ if (
1224
+ absolutePath.match('src/api') &&
1225
+ !absolutePath.match('src/api/types.js') &&
1226
+ !confServer.apis.find((p) => absolutePath.match(`src/api/${p}/`))
1227
+ ) {
1228
+ return false;
1229
+ }
1230
+ if (absolutePath.match('conf.dd-') && absolutePath.match('.js')) return false;
1231
+ if (absolutePath.match('typedoc.dd-') && absolutePath.match('.json')) return false;
1232
+ if (
1233
+ absolutePath.match('src/client/services/') &&
1234
+ !clients.find((p) => absolutePath.match(`src/client/services/${p}/`))
1235
+ ) {
1236
+ return false;
1237
+ }
1238
+ if (absolutePath.match('src/client/public/') && !clients.find((p) => absolutePath.match(`src/client/public/${p}/`))) {
1239
+ return false;
1240
+ }
1241
+ if (
1242
+ absolutePath.match('src/client/components/') &&
1243
+ !clients.find((p) => absolutePath.match(`src/client/components/${p}/`))
1244
+ ) {
1245
+ return false;
1246
+ }
1247
+ if (absolutePath.match('src/client/sw/') && !absolutePath.match('src/client/sw/core.sw.js')) {
1248
+ return false;
1249
+ }
1250
+ if (
1251
+ absolutePath.match('src/client/ssr/body') &&
1252
+ !confSsr.body.find((p) => absolutePath.match(`src/client/ssr/body/${p}.js`))
1253
+ ) {
1254
+ return false;
1255
+ }
1256
+ if (
1257
+ absolutePath.match('src/client/ssr/head') &&
1258
+ !confSsr.head.find((p) => absolutePath.match(`src/client/ssr/head/${p}.js`))
1259
+ ) {
1260
+ return false;
1261
+ }
1262
+ if (
1263
+ absolutePath.match('src/client/ssr/mailer') &&
1264
+ !Object.keys(confSsr.mailer).find((p) => absolutePath.match(`src/client/ssr/mailer/${confSsr.mailer[p]}.js`))
1265
+ ) {
1266
+ return false;
1267
+ }
1268
+ if (
1269
+ absolutePath.match('src/client/ssr/views') &&
1270
+ !(confSsr.views || []).find((p) => absolutePath.match(`src/client/ssr/views/${p.client}.js`))
1271
+ ) {
1272
+ return false;
1273
+ }
1274
+ if (absolutePath.match('hardhat/')) return false;
1275
+ if (
1276
+ absolutePath.match('/client') &&
1277
+ absolutePath.match('.index.js') &&
1278
+ !absolutePath.match('/offline') &&
1279
+ !clients.find((p) => absolutePath.match(`src/client/${capFirst(p)}.index.js`))
1280
+ ) {
1281
+ return false;
1282
+ }
1283
+ if (absolutePath.match('src/ws/') && !clients.find((p) => absolutePath.match(`src/ws/${p}/`))) {
1284
+ return false;
1285
+ }
1286
+ return true;
1287
+ };
1288
+
1289
+ /**
1290
+ * @method awaitDeployMonitor
1291
+ * @description Waits for the deploy monitor.
1292
+ * @param {boolean} [isFinal=false] - If true, logs when the final (non-replica) deployment completes.
1293
+ * @param {number} [deltaMs=1000] - The delta ms.
1294
+ * @param {boolean} [callback=false] - The callback.
1295
+ * @returns {Promise<boolean>} - `false` if `container-status=error` was detected, `true` on clean completion.
1296
+ * @memberof ServerConfBuilder
1297
+ */
1298
+ const awaitDeployMonitor = async (isFinal = false, deltaMs = 1000, callback = false) => {
1299
+ if (!callback) Underpost.env.set('await-deploy', new Date().toISOString());
1300
+ if (isFinal) logger.info('Final deployment running (no replica)');
1301
+ await timer(deltaMs);
1302
+ if (Underpost.env.get('container-status') === 'error') return false;
1303
+ if (Underpost.env.get('await-deploy')) return await awaitDeployMonitor(false, deltaMs, true);
1304
+ return true;
1305
+ };
1306
+
1307
+ /**
1308
+ * @method mergeFile
1309
+ * @description Merges the file.
1310
+ * @param {Array} parts - The parts.
1311
+ * @param {string} outputFilePath - The output file path.
1312
+ * @returns {Promise<void>} - The merge file.
1313
+ * @memberof ServerConfBuilder
1314
+ */
1315
+ const mergeFile = async (parts = [], outputFilePath) => {
1316
+ await new Promise((resolve) => {
1317
+ splitFile
1318
+ .mergeFiles(parts, outputFilePath)
1319
+ .then(() => {
1320
+ resolve();
1321
+ })
1322
+ .catch((err) => {
1323
+ console.log('Error: ', err);
1324
+ resolve();
1325
+ });
1326
+ });
1327
+ };
1328
+
1329
+ /**
1330
+ * @method getPathsSSR
1331
+ * @description Gets the paths SSR.
1332
+ * @param {object} conf - The conf.
1333
+ * @returns {Array} - The paths SSR.
1334
+ * @memberof ServerConfBuilder
1335
+ */
1336
+ const getPathsSSR = (conf) => {
1337
+ const paths = ['src/client/ssr/RootDocument.js'];
1338
+ for (const o of conf.head) paths.push(`src/client/ssr/head/${o}.js`);
1339
+ for (const o of conf.body) paths.push(`src/client/ssr/body/${o}.js`);
1340
+ for (const o of Object.keys(conf.mailer)) paths.push(`src/client/ssr/mailer/${conf.mailer[o]}.js`);
1341
+ for (const o of conf.views || []) paths.push(`src/client/ssr/views/${o.client}.js`);
1342
+ return paths;
1343
+ };
1344
+
1345
+ /**
1346
+ * @method splitFileFactory
1347
+ * @description Splits the file factory.
1348
+ * @param {string} name - The name.
1349
+ * @param {string} _path - The path.
1350
+ * @returns {Promise<boolean>} - The split file factory.
1351
+ * @memberof ServerConfBuilder
1352
+ */
1353
+ const splitFileFactory = async (name, _path) => {
1354
+ const stats = fs.statSync(_path);
1355
+ const maxSizeInBytes = 1024 * 1024 * 50; // 50 mb
1356
+ const fileSizeInBytes = stats.size;
1357
+ if (fileSizeInBytes > maxSizeInBytes) {
1358
+ logger.info('splitFileFactory input', { name, from: _path });
1359
+ return await new Promise((resolve) => {
1360
+ splitFile
1361
+ .splitFileBySize(_path, maxSizeInBytes) // 50 mb
1362
+ .then((names) => {
1363
+ logger.info('splitFileFactory output', { parts: names });
1364
+ fs.writeFileSync(
1365
+ `${_path.split('/').slice(0, -1).join('/')}/${name}-parths.json`,
1366
+ JSON.stringify(names, null, 4),
1367
+ 'utf8',
1368
+ );
1369
+ fs.removeSync(_path);
1370
+ return resolve(true);
1371
+ })
1372
+ .catch((err) => {
1373
+ console.log('Error: ', err);
1374
+ return resolve(false);
1375
+ });
1376
+ });
1377
+ }
1378
+ return false;
1379
+ };
1380
+
1381
+ /**
1382
+ * @method getNpmRootPath
1383
+ * @description Gets the npm root path.
1384
+ * @returns {string} - The npm root path.
1385
+ * @memberof ServerConfBuilder
1386
+ */
1387
+ const getNpmRootPath = () =>
1388
+ shellExec(`npm root -g`, {
1389
+ stdout: true,
1390
+ disableLog: true,
1391
+ silent: true,
1392
+ }).trim();
1393
+
1394
+ /**
1395
+ * @method getUnderpostRootPath
1396
+ * @description Gets the underpost root path.
1397
+ * @returns {string} - The underpost root path.
1398
+ * @memberof ServerConfBuilder
1399
+ */
1400
+ const getUnderpostRootPath = () => `${getNpmRootPath()}/underpost`;
1401
+
1402
+ /**
1403
+ * @method writeEnv
1404
+ * @description Writes the environment variables.
1405
+ * @param {string} envPath - The environment path.
1406
+ * @param {object} envObj - The environment object.
1407
+ * @memberof ServerConfBuilder
1408
+ */
1409
+ const writeEnv = (envPath, envObj) =>
1410
+ fs.writeFileSync(
1411
+ envPath,
1412
+ Object.keys(envObj)
1413
+ .map((key) => `${key}=${envObj[key]}`)
1414
+ .join(`\n`),
1415
+ 'utf8',
1416
+ );
1417
+
1418
+ /**
1419
+ * @method buildCliDoc
1420
+ * @description Scrapes `node bin help` (and `node bin help <command>` for every
1421
+ * registered command) and renders a structured Markdown reference: a command
1422
+ * index with anchor links, plus a per-command section with its description,
1423
+ * usage, and Arguments/Options rendered as tables. Writes
1424
+ * `CLI-HELP.md` + the served reference doc, and refreshes the README CLI index.
1425
+ * @param {object} program - The commander program.
1426
+ * @param {string} oldVersion - The old version string to replace.
1427
+ * @param {string} newVersion - The new version string.
1428
+ * @memberof ServerConfBuilder
1429
+ */
1430
+ const buildCliDoc = (program, oldVersion, newVersion) => {
1431
+ const help = (args = '') => shellExec(`node bin help${args ? ` ${args}` : ''}`, { silent: true, stdout: true });
1432
+ // Escape table-breaking pipes and collapse wrapped whitespace for a Markdown cell.
1433
+ const cell = (s) => String(s).replace(/\s+/g, ' ').replaceAll('|', '\\|').trim();
1434
+ const anchor = (name) => `underpost-${name}`.toLowerCase().replace(/[^a-z0-9-]/g, '');
1435
+
1436
+ // Parse a commander help block into { usage, description, sections: { Options, Arguments, Commands } }.
1437
+ const parseHelp = (text) => {
1438
+ const lines = text.split('\n');
1439
+ const usageMatch = lines[0].match(/^Usage:\s*(.*)$/);
1440
+ const usage = usageMatch ? usageMatch[1].trim() : '';
1441
+ const sections = {};
1442
+ const descLines = [];
1443
+ let current = null;
1444
+ let buf = [];
1445
+ const flush = () => {
1446
+ if (current) sections[current] = buf.join('\n');
1447
+ buf = [];
1448
+ };
1449
+ for (let i = 1; i < lines.length; i++) {
1450
+ const line = lines[i];
1451
+ const head = line.match(/^([A-Za-z][\w ]*):\s*$/); // top-level "Options:", "Arguments:", "Commands:"
1452
+ if (head) {
1453
+ flush();
1454
+ current = head[1].trim();
1455
+ } else if (current !== null) {
1456
+ buf.push(line);
1457
+ } else {
1458
+ descLines.push(line);
1459
+ }
1460
+ }
1461
+ flush();
1462
+ return { usage, description: descLines.join('\n').trim(), sections };
1463
+ };
1464
+
1465
+ // Parse a columnar " <term> <description>" section (descriptions may wrap onto
1466
+ // indented continuation lines) into [{ term, desc }].
1467
+ const parseEntries = (text = '') => {
1468
+ const entries = [];
1469
+ for (const line of text.split('\n')) {
1470
+ if (!line.trim()) continue;
1471
+ const leading = line.length - line.trimStart().length;
1472
+ if (leading <= 2) {
1473
+ const rest = line.trim();
1474
+ const gap = rest.search(/\s{2,}/);
1475
+ entries.push(gap === -1 ? { term: rest, desc: '' } : { term: rest.slice(0, gap), desc: rest.slice(gap) });
1476
+ } else if (entries.length) {
1477
+ entries[entries.length - 1].desc += ` ${line.trim()}`;
1478
+ }
1479
+ }
1480
+ return entries;
1481
+ };
1482
+
1483
+ const table = (head, entries) =>
1484
+ !entries.length
1485
+ ? ''
1486
+ : `| ${head[0]} | ${head[1]} |\n| --- | --- |\n` +
1487
+ entries.map(({ term, desc }) => `| \`${cell(term)}\` | ${cell(desc)} |`).join('\n') +
1488
+ '\n';
1489
+
1490
+ const detailSection = (sections, name, head) => {
1491
+ const t = table(head, parseEntries(sections[name]));
1492
+ return t ? `\n#### ${name}\n\n${t}` : '';
1493
+ };
1494
+
1495
+ // ── Top-level index ──
1496
+ const root = parseHelp(help());
1497
+ const commandEntries = parseEntries(root.sections['Commands']).filter((e) => e.term.split(' ')[0] !== 'help');
1498
+
1499
+ const index =
1500
+ `## Underpost CLI\n\n` +
1501
+ (root.description ? `> ${root.description.replace(/\s+/g, ' ')}\n\n` : '') +
1502
+ `**Usage:** \`${root.usage}\`\n\n` +
1503
+ `### Global options\n\n${table(['Option', 'Description'], parseEntries(root.sections['Options']))}\n` +
1504
+ `### Commands\n\n| Command | Description |\n| --- | --- |\n` +
1505
+ commandEntries
1506
+ .map((e) => {
1507
+ const name = e.term.split(' ')[0];
1508
+ return `| [\`${name}\`](#${anchor(name)}) | ${cell(e.desc)} |`;
1509
+ })
1510
+ .join('\n') +
1511
+ '\n';
1512
+
1513
+ // ── Per-command detail ──
1514
+ let details = `\n## Command reference\n`;
1515
+ for (const cmd of program.commands) {
1516
+ const name = cmd._name;
1517
+ if (name === 'help') continue;
1518
+ const cmdHelp = parseHelp(help(name));
1519
+ details +=
1520
+ `\n### underpost ${name}\n\n` +
1521
+ (cmdHelp.description ? `${cmdHelp.description.replace(/\s+/g, ' ')}\n\n` : '') +
1522
+ `**Usage:** \`${cmdHelp.usage}\`\n` +
1523
+ detailSection(cmdHelp.sections, 'Arguments', ['Argument', 'Description']) +
1524
+ detailSection(cmdHelp.sections, 'Options', ['Option', 'Description']) +
1525
+ `\n---\n`;
1526
+ }
1527
+
1528
+ const md = `${index}${details}`.replaceAll(oldVersion, newVersion);
1529
+ fs.writeFileSync(`./src/client/public/nexodev/docs/references/Command Line Interface.md`, md, 'utf8');
1530
+ fs.writeFileSync(`./CLI-HELP.md`, md, 'utf8');
1531
+
1532
+ // Update README.md: bump version and refresh the CLI index between the comment tags.
1533
+ let readme = fs.readFileSync(`./README.md`, 'utf8').replaceAll(oldVersion, newVersion);
1534
+ const cliStartTag = '<!-- cli-index-start -->';
1535
+ const cliEndTag = '<!-- cli-index-end -->';
1536
+ const startIdx = readme.indexOf(cliStartTag);
1537
+ const endIdx = readme.indexOf(cliEndTag);
1538
+ if (startIdx !== -1 && endIdx !== -1) {
1539
+ const readmeIndex = index.replace(/\(#(underpost-[a-z0-9-]+)\)/g, '(CLI-HELP.md#$1)');
1540
+ readme =
1541
+ readme.substring(0, startIdx) +
1542
+ cliStartTag +
1543
+ '\n' +
1544
+ readmeIndex.replaceAll(oldVersion, newVersion) +
1545
+ '\n' +
1546
+ cliEndTag +
1547
+ readme.substring(endIdx + cliEndTag.length);
1548
+ }
1549
+ fs.writeFileSync('./README.md', readme, 'utf8');
1550
+ };
1551
+
1552
+ /**
1553
+ * @method getInstanceContext
1554
+ * @description Gets the instance context.
1555
+ * @param {object} options - The options.
1556
+ * @param {string} options.deployId - The deploy ID.
1557
+ * @param {boolean} options.singleReplica - The single replica.
1558
+ * @param {Array} options.replicas - The replicas.
1559
+ * @param {string} options.redirect - The redirect.
1560
+ * @param {boolean} [options.peer=false] - Whether peer is enabled on the parent singleReplica path (used for port offset estimation when replica conf is not yet built).
1561
+ * @returns {object} - The instance context.
1562
+ * @memberof ServerConfBuilder
1563
+ */
1564
+ const getInstanceContext = async (options = { deployId, singleReplica, replicas, redirect: '', peer: false }) => {
1565
+ const { deployId, singleReplica, replicas, redirect, peer } = options;
1566
+ let singleReplicaOffsetPortSum = 0;
1567
+
1568
+ if (singleReplica && replicas && replicas.length > 0) {
1569
+ for (const replica of replicas) {
1570
+ const replicaDeployId = buildReplicaId({ deployId, replica });
1571
+ const replicaConfPath = `./engine-private/replica/${replicaDeployId}/conf.server.json`;
1572
+ if (!fs.existsSync(replicaConfPath)) {
1573
+ // Replica folder not built yet (e.g. dev mode without prior build);
1574
+ // estimate port offset: 1 per replica path + 1 extra if peer is enabled on the parent singleReplica config
1575
+ singleReplicaOffsetPortSum++;
1576
+ if (peer) singleReplicaOffsetPortSum++;
1577
+ continue;
1578
+ }
1579
+ const confReplicaServer = loadConfServerJson(replicaConfPath);
1580
+ for (const host of Object.keys(confReplicaServer)) {
1581
+ for (const path of Object.keys(confReplicaServer[host])) {
1582
+ singleReplicaOffsetPortSum++;
1583
+ if (confReplicaServer[host][path].peer) singleReplicaOffsetPortSum++;
1584
+ }
1585
+ }
1586
+ }
1587
+ }
1588
+
1589
+ const redirectTarget = redirect
1590
+ ? redirect[redirect.length - 1] === '/'
1591
+ ? redirect.slice(0, -1)
1592
+ : redirect
1593
+ : undefined;
1594
+
1595
+ return { redirectTarget, singleReplicaOffsetPortSum };
1596
+ };
1597
+
1598
+ /**
1599
+ * @method buildApiConf
1600
+ * @description Builds the API configuration.
1601
+ * @param {object} options - The options.
1602
+ * @param {string} options.deployId - The deploy ID.
1603
+ * @param {string} options.subConf - The sub configuration.
1604
+ * @param {string} options.host - The host.
1605
+ * @param {string} options.path - The path.
1606
+ * @param {string} options.origin - The origin.
1607
+ * @returns {object} - The API configuration.
1608
+ * @memberof ServerConfBuilder
1609
+ */
1610
+ const buildApiConf = async (options = { deployId: '', subConf: '', host: '', path: '', origin: '' }) => {
1611
+ let { deployId, subConf, host, path, origin } = options;
1612
+ if (!deployId) deployId = process.argv[2].trim();
1613
+ if (!subConf) subConf = process.argv[3].trim();
1614
+ if (process.argv[4]) host = process.argv[4].trim();
1615
+ if (process.argv[5]) path = process.argv[5].trim();
1616
+ if (process.argv[6])
1617
+ origin = `${process.env.NODE_ENV === 'production' ? 'https' : 'http'}://${process.argv[6].trim()}`;
1618
+
1619
+ if (!origin) return;
1620
+ const confServer = JSON.parse(
1621
+ fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.dev.${subConf}.json`, 'utf8'),
1622
+ );
1623
+ const envObj = dotenv.parse(
1624
+ fs.readFileSync(`./engine-private/conf/${deployId}/.env.${process.env.NODE_ENV}`, 'utf8'),
1625
+ );
1626
+ if (host && path) {
1627
+ confServer[host][path].origins = [origin];
1628
+ logger.info('Build api conf', { host, path, origin });
1629
+ } else return;
1630
+ writeEnv(`./engine-private/conf/${deployId}/.env.${process.env.NODE_ENV}.${subConf}-dev-api`, envObj);
1631
+ fs.writeFileSync(
1632
+ `./engine-private/conf/${deployId}/conf.server.dev.${subConf}-dev-api.json`,
1633
+ JSON.stringify(confServer, null, 4),
1634
+ 'utf8',
1635
+ );
1636
+ };
1637
+
1638
+ /**
1639
+ * @method buildClientStaticConf
1640
+ * @description Builds the client static configuration.
1641
+ * @param {object} options - The options.
1642
+ * @param {string} options.deployId - The deploy ID.
1643
+ * @param {string} options.subConf - The sub configuration.
1644
+ * @param {string} options.apiBaseHost - The API base host.
1645
+ * @param {string} options.host - The host.
1646
+ * @param {string} options.path - The path.
1647
+ * @param {boolean} options.devProxy - The dev proxy flag.
1648
+ * @returns {void}
1649
+ * @memberof ServerConfBuilder
1650
+ */
1651
+ const buildClientStaticConf = async (
1652
+ options = { deployId: '', subConf: '', apiBaseHost: '', host: '', path: '', devProxy: false },
1653
+ ) => {
1654
+ let { deployId, subConf, host, path, devProxy } = options;
1655
+ if (!deployId) deployId = process.argv[2].trim();
1656
+ if (!subConf) subConf = process.argv[3].trim();
1657
+ if (!host) host = process.argv[4].trim();
1658
+ if (!path) path = process.argv[5].trim();
1659
+ const confServer = JSON.parse(
1660
+ fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.dev.${subConf}-dev-api.json`, 'utf8'),
1661
+ );
1662
+ const envObj = dotenv.parse(
1663
+ fs.readFileSync(`./engine-private/conf/${deployId}/.env.${process.env.NODE_ENV}.${subConf}-dev-api`, 'utf8'),
1664
+ );
1665
+ envObj.PORT = parseInt(envObj.PORT);
1666
+ const apiBaseHost = devProxy
1667
+ ? devProxyHostFactory({ host, tls: isTlsDevProxy() })
1668
+ : options?.apiBaseHost
1669
+ ? options.apiBaseHost
1670
+ : `localhost:${envObj.PORT + 1}`;
1671
+ confServer[host][path].apiBaseHost = apiBaseHost;
1672
+ confServer[host][path].apiBaseProxyPath = path;
1673
+ logger.warn('Build client static conf', { host, path, apiBaseHost });
1674
+ envObj.PORT = parseInt(confServer[host][path].origins[0].split(':')[2]) - 1;
1675
+ writeEnv(`./engine-private/conf/${deployId}/.env.${process.env.NODE_ENV}.${subConf}-dev-client`, envObj);
1676
+ fs.writeFileSync(
1677
+ `./engine-private/conf/${deployId}/conf.server.dev.${subConf}-dev-client.json`,
1678
+ JSON.stringify(confServer, null, 4),
1679
+ 'utf8',
1680
+ );
1681
+ };
1682
+
1683
+ /**
1684
+ * @method isDeployRunnerContext
1685
+ * @description Checks if the deploy runner context is valid.
1686
+ * @param {string} path - The path.
1687
+ * @param {object} options - The options.
1688
+ * @returns {boolean} - The deploy runner context.
1689
+ * @memberof ServerConfBuilder
1690
+ */
1691
+ const isDeployRunnerContext = (path, options) => !options.build && path && path !== 'template-deploy';
1692
+
1693
+ /**
1694
+ * @method isDevProxyContext
1695
+ * @description Checks if the dev proxy context is valid.
1696
+ * @returns {boolean} - The dev proxy context.
1697
+ * @memberof ServerConfBuilder
1698
+ */
1699
+ const isDevProxyContext = () => (process.argv.find((arg) => arg === 'proxy') ? true : false);
1700
+
1701
+ /**
1702
+ * @method devProxyHostFactory
1703
+ * @description Creates the dev proxy host.
1704
+ * @param {object} options - The options.
1705
+ * @param {string} [options.host='default.net'] - The host.
1706
+ * @param {boolean} [options.includeHttp=false] - Whether to include HTTP.
1707
+ * @param {number} [options.port=443] - The port.
1708
+ * @param {boolean} [options.tls=false] - Whether to use TLS.
1709
+ * @returns {string} - The dev proxy host.
1710
+ * @memberof ServerConfBuilder
1711
+ */
1712
+ const devProxyHostFactory = (options = { host: 'default.net', includeHttp: false, port: 80, tls: false }) => {
1713
+ const resolvedPort =
1714
+ (options.port ? options.port : options.tls ? 443 : 80) + parseInt(process.env.DEV_PROXY_PORT_OFFSET);
1715
+ const isDefaultPort = (options.tls && resolvedPort === 443) || (!options.tls && resolvedPort === 80);
1716
+ const protocol = options.includeHttp ? (options.tls ? 'https://' : 'http://') : '';
1717
+ const hostname = options.host ? options.host : 'localhost';
1718
+ return `${protocol}${hostname}${isDefaultPort ? '' : `:${resolvedPort}`}`;
1719
+ };
1720
+
1721
+ /**
1722
+ * @method isTlsDevProxy
1723
+ * @description Checks if TLS is used in the dev proxy.
1724
+ * @returns {boolean} - The TLS dev proxy status.
1725
+ * @memberof ServerConfBuilder
1726
+ */
1727
+ const isTlsDevProxy = () => process.env.NODE_ENV !== 'production' && !!process.argv.find((arg) => arg === 'tls');
1728
+
1729
+ /**
1730
+ * @method getTlsHosts
1731
+ * @description Gets the TLS hosts.
1732
+ * @param {object} confServer - The server configuration.
1733
+ * @returns {Array} - The TLS hosts.
1734
+ * @memberof ServerConfBuilder
1735
+ */
1736
+ const getTlsHosts = (confServer) =>
1737
+ Array.from(new Set(Object.keys(confServer).map((h) => new URL('https://' + h).hostname)));
1738
+
1739
+ /**
1740
+ * Reads a `conf.server.json` file from disk, parses it, and resolves all `env:` secret
1741
+ * references using {@link resolveConfSecrets}.
1742
+ *
1743
+ * Reads and parses a `conf.server.json` file from disk. The `env:` secret
1744
+ * references are **preserved** by default so that build/deploy tooling never
1745
+ * accidentally strips them. Callers that need the actual secret values
1746
+ * (e.g. database or mailer modules) should explicitly wrap the result with
1747
+ * {@link resolveConfSecrets}.
1748
+ *
1749
+ * @method loadConfServerJson
1750
+ * @param {string} jsonPath - Absolute or relative path to the `conf.server.json` file.
1751
+ * @param {object} [options] - Optional settings.
1752
+ * @param {boolean} [options.resolve=false] - When `true`, resolves `env:` references
1753
+ * via {@link resolveConfSecrets} before returning.
1754
+ * @returns {object} The parsed server configuration object (secrets unresolved unless
1755
+ * `options.resolve` is `true`).
1756
+ * @throws {Error} If the file does not exist or cannot be parsed.
1757
+ * @memberof ServerConfBuilder
1758
+ *
1759
+ * @example
1760
+ * // Structure-only read (env: strings preserved)
1761
+ * const confServer = loadConfServerJson(`./engine-private/conf/${deployId}/conf.server.json`);
1762
+ *
1763
+ * @example
1764
+ * // Resolved read (env: strings replaced with process.env values)
1765
+ * const confServer = loadConfServerJson(`./engine-private/conf/${deployId}/conf.server.json`, { resolve: true });
1766
+ */
1767
+ const loadConfServerJson = (jsonPath, options) => {
1768
+ if (!fs.existsSync(jsonPath)) {
1769
+ throw new Error(`loadConfServerJson: configuration file not found: ${jsonPath}`);
1770
+ }
1771
+ const raw = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
1772
+ return options && options.resolve === true ? resolveConfSecrets(raw) : raw;
1773
+ };
1774
+
1775
+ /**
1776
+ * @method deepReplaceToken
1777
+ * @description Recursively replaces every occurrence of `from` with `to` in all
1778
+ * string leaves of a JSON-shaped value, returning a new structure.
1779
+ * @param {*} value - Any JSON-serialisable value.
1780
+ * @param {string} from - Token to replace.
1781
+ * @param {string} to - Replacement token.
1782
+ * @returns {*} A structurally identical value with the token replaced.
1783
+ * @memberof ServerConfBuilder
1784
+ */
1785
+ const deepReplaceToken = (value, from, to) => {
1786
+ if (typeof value === 'string') return value.split(from).join(to);
1787
+ if (Array.isArray(value)) return value.map((entry) => deepReplaceToken(entry, from, to));
1788
+ if (value && typeof value === 'object')
1789
+ return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, deepReplaceToken(v, from, to)]));
1790
+ return value;
1791
+ };
1792
+
1793
+ /**
1794
+ * @method readConfInstances
1795
+ * @description Reads `conf.instances.json` and returns the bare array of instance
1796
+ * entries. The canonical shape is a plain array; the historical `{ instances }`
1797
+ * object wrapper is still unwrapped for backward compatibility. Multi-instance is
1798
+ * declared per entry under `entry.multiInstance` (not deploy-wide).
1799
+ * @param {string} deployId - Deployment identifier (e.g. `dd-cyberia`).
1800
+ * @returns {Array<object>} Instance entries.
1801
+ * @memberof ServerConfBuilder
1802
+ */
1803
+ const readConfInstances = (deployId) => {
1804
+ const raw = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.instances.json`, 'utf8'));
1805
+ return Array.isArray(raw) ? raw : raw.instances || [];
1806
+ };
1807
+
1808
+ /**
1809
+ * @method loadInstanceTopology
1810
+ * @description Returns `{ default, variants }` describing the deploy's instance
1811
+ * variants, or `null` when the deploy is single-instance. The topology is
1812
+ * declared per instance entry (`entry.multiInstance.default` / `.variants`); all
1813
+ * multi-instance entries share the same variant set, so this returns it from the
1814
+ * first entry that declares one.
1815
+ * @param {string} deployId - Deployment identifier (e.g. `dd-cyberia`).
1816
+ * @returns {?{default: string, variants: Array<object>}} The topology, or `null`.
1817
+ * @memberof ServerConfBuilder
1818
+ */
1819
+ const loadInstanceTopology = (deployId) => {
1820
+ for (const entry of readConfInstances(deployId)) {
1821
+ const mi = entry.multiInstance;
1822
+ if (mi && Array.isArray(mi.variants) && mi.variants.length > 0)
1823
+ return { default: mi.default || mi.variants[0].code, variants: mi.variants };
1824
+ }
1825
+ return null;
1826
+ };
1827
+
1828
+ /**
1829
+ * @method resolveInstanceEnvValue
1830
+ * @description Resolves one declared env value. A plain string is used verbatim;
1831
+ * an object is treated as env-scoped (`{ development, production }`), matching the
1832
+ * convention already used by `lifecycle` / `readinessProbe` / `livenessProbe`.
1833
+ * Placeholders are substituted from the variant tokens.
1834
+ * @param {string|object} value - Declared value.
1835
+ * @param {string} env - `development` | `production`.
1836
+ * @param {Object<string,string>} tokens - Placeholder name → replacement.
1837
+ * @returns {?string} Resolved value, or `null` when the env has no entry.
1838
+ * @memberof ServerConfBuilder
1839
+ */
1840
+ const resolveInstanceEnvValue = (value, env, tokens) => {
1841
+ const scoped = value && typeof value === 'object' && !Array.isArray(value) ? value[env] : value;
1842
+ if (scoped === undefined || scoped === null) return null;
1843
+ return `${scoped}`.replace(/\{\{(\w+)\}\}/g, (match, token) => (token in tokens ? tokens[token] : match));
1844
+ };
1845
+
1846
+ /**
1847
+ * @method loadConfInstances
1848
+ * @description Loads `conf.instances.json` and expands every entry carrying a
1849
+ * `multiInstance` block into one concrete instance per declared variant.
1850
+ *
1851
+ * A template entry is never deployed as-is once variants exist: each variant
1852
+ * produces its own entry whose id, env file path, volume mount and
1853
+ * container-status strings are derived by replacing the template id token
1854
+ * throughout. The variant whose `slug` is empty keeps the template id verbatim,
1855
+ * so pre-existing deployments, PVCs and env directories survive the move to
1856
+ * multi-instance untouched.
1857
+ *
1858
+ * Nothing here is application-specific: the variant set (`default` + `variants`),
1859
+ * which env keys an instance needs (`env`), and prefix-stripping (`stripPathPrefix`)
1860
+ * are all declared per entry under `entry.multiInstance`. Env values may use the
1861
+ * `{{code}}`, `{{slug}}`, `{{path}}`, `{{id}}` and `{{default}}` placeholders and
1862
+ * may be env-scoped objects.
1863
+ *
1864
+ * Every expanded entry carries three extra fields consumed by the deploy runners:
1865
+ * `instanceCode` (the variant this instance serves), `templateId` (so targeting
1866
+ * the template id acts on the whole family) and `instanceEnv` (the resolved env
1867
+ * keys to write, keyed by environment: `{ development, production }`, so a build
1868
+ * in either mode can emit a complete env directory).
1869
+ *
1870
+ * @param {string} deployId - Deployment identifier (e.g. `dd-cyberia`).
1871
+ * @returns {Array<object>} Expanded instance entries.
1872
+ * @memberof ServerConfBuilder
1873
+ */
1874
+ const INSTANCE_ENVS = ['development', 'production'];
1875
+
1876
+ const loadConfInstances = (deployId) => {
1877
+ const expanded = [];
1878
+ for (const entry of readConfInstances(deployId)) {
1879
+ const spec = entry.multiInstance;
1880
+ const variants = spec?.variants;
1881
+ if (!Array.isArray(variants) || 0 === variants.length) {
1882
+ expanded.push(entry);
1883
+ continue;
1884
+ }
1885
+ for (const variant of variants) {
1886
+ const id = variant.slug ? `${entry.id}-${variant.slug}` : entry.id;
1887
+ const instance = variant.slug ? deepReplaceToken(entry, entry.id, id) : JSON.parse(JSON.stringify(entry));
1888
+ delete instance.multiInstance;
1889
+ instance.id = id;
1890
+ instance.path = variant.path;
1891
+ instance.instanceCode = variant.code;
1892
+ instance.templateId = entry.id;
1893
+
1894
+ const tokens = {
1895
+ code: variant.code,
1896
+ slug: variant.slug || '',
1897
+ path: variant.path,
1898
+ id,
1899
+ default: spec.default || '',
1900
+ };
1901
+ // Resolve the declared env keys for both environments so a build in either
1902
+ // mode writes a complete env directory (development.env + production.env).
1903
+ instance.instanceEnv = Object.fromEntries(INSTANCE_ENVS.map((e) => [e, {}]));
1904
+ for (const [key, value] of Object.entries(spec.env || {}))
1905
+ for (const e of INSTANCE_ENVS) {
1906
+ const resolved = resolveInstanceEnvValue(value, e, tokens);
1907
+ if (resolved !== null) instance.instanceEnv[e][key] = resolved;
1908
+ }
1909
+
1910
+ // A backend that serves its routes at the root knows nothing about the
1911
+ // variant prefix — it is selected by env instead. Strip the prefix at the
1912
+ // proxy so the runtime stays instance-agnostic.
1913
+ if (spec.stripPathPrefix && variant.path !== '/')
1914
+ instance.pathRewritePolicy = [{ prefix: variant.path, replacement: '/' }];
1915
+ expanded.push(instance);
1916
+ }
1917
+ }
1918
+ return expanded;
1919
+ };
1920
+
1921
+ /**
1922
+ * @method selectConfInstances
1923
+ * @description Filters expanded instances for a deploy target. Targeting a
1924
+ * template id (`mmo-server`) selects the whole instance family; targeting a
1925
+ * concrete id (`mmo-server-forest`) selects just that one.
1926
+ * @param {Array<object>} instances - Expanded entries from {@link loadConfInstances}.
1927
+ * @param {string} id - Target instance id or template id.
1928
+ * @returns {Array<object>} Matching entries.
1929
+ * @memberof ServerConfBuilder
1930
+ */
1931
+ const selectConfInstances = (instances, id) =>
1932
+ instances.filter((instance) => instance.id === id || instance.templateId === id);
1933
+
1934
+ /**
1935
+ * Creates and writes the /etc/hosts file for a deployment.
1936
+ * @method etcHostFactory
1937
+ * @param {Array<string>} hosts - List of hosts to be added to the hosts file.
1938
+ * @param {object} options - Options for the hosts file creation.
1939
+ * @param {boolean} options.append - Whether to append to the existing hosts file.
1940
+ * @returns {object} - Object containing the rendered hosts file.
1941
+ * @memberof ServerConfBuilder
1942
+ */
1943
+ const etcHostFactory = (hosts = [], options = { append: false }) => {
1944
+ hosts = hosts.map((host) => {
1945
+ try {
1946
+ if (!host.startsWith('http')) host = `http://${host}`;
1947
+ const hostname = new URL(host).hostname;
1948
+ logger.info('Hostname extract valid', { host, hostname });
1949
+ return hostname;
1950
+ } catch (e) {
1951
+ logger.warn('No hostname extract valid', host);
1952
+ return host;
1953
+ }
1954
+ });
1955
+ const renderHosts = `127.0.0.1 ${hosts.join(
1956
+ ' ',
1957
+ )} localhost localhost.localdomain localhost4 localhost4.localdomain4
1958
+ ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6`;
1959
+
1960
+ if (options && options.append && fs.existsSync(`/etc/hosts`)) {
1961
+ fs.writeFileSync(
1962
+ `/etc/hosts`,
1963
+ fs.readFileSync(`/etc/hosts`, 'utf8') +
1964
+ `
1965
+ ${renderHosts}`,
1966
+ 'utf8',
1967
+ );
1968
+ } else fs.writeFileSync(`/etc/hosts`, renderHosts, 'utf8');
1969
+ return { renderHosts };
1970
+ };
1971
+
1972
+ /**
1973
+ * Resolves the concrete deploy ids a build or conf-sync run should iterate over.
1974
+ *
1975
+ * The meta deploy id `dd` fans out to the comma separated ids declared in
1976
+ * `engine-private/deploy/dd.router`; any other value is parsed as a comma separated list.
1977
+ * Entries are trimmed and empties dropped.
1978
+ *
1979
+ * @method resolveDeployList
1980
+ * @param {string} deployId - A deploy id, a comma separated list, or the `dd` meta id.
1981
+ * @returns {string[]} Ordered list of concrete deploy ids.
1982
+ * @memberof ServerConfBuilder
1983
+ */
1984
+ const resolveDeployList = (deployId) =>
1985
+ (deployId === 'dd' ? fs.readFileSync('./engine-private/deploy/dd.router', 'utf8') : deployId)
1986
+ .split(',')
1987
+ .map((id) => id.trim())
1988
+ .filter(Boolean);
1989
+
1990
+ /**
1991
+ * Syncs a single deploy id's private configuration into its dedicated
1992
+ * `engine-<suffix>-private` repository and pushes the result.
1993
+ *
1994
+ * Idempotent and safe to rerun: the private repo is cloned when missing or reset to a clean
1995
+ * checkout when present, then the deploy id's `conf` folder, matching `replica` and
1996
+ * `itc-scripts` entries, and any caller-supplied `extraPaths` payloads are mirrored. The
1997
+ * commit/push step is a no-op when nothing changed (`silentOnError`).
1998
+ *
1999
+ * @method syncPrivateConf
2000
+ * @param {string} deployId - A concrete deploy id (e.g. `dd-cyberia`), not the `dd` meta id.
2001
+ * @param {string[]} [extraPaths=[]] - Extra `./engine-private` payload paths to mirror (from the
2002
+ * deploy's product catalog), kept out of this module so it stays product-agnostic.
2003
+ * @returns {void}
2004
+ * @memberof ServerConfBuilder
2005
+ */
2006
+ const syncPrivateConf = (deployId, extraPaths = []) => {
2007
+ const suffix = deployId.split('dd-')[1];
2008
+ const privateRepoName = `engine-${suffix}-private`;
2009
+ const privateGitUri = `${process.env.GITHUB_USERNAME}/${privateRepoName}`;
2010
+ const privateRepoPath = `../${privateRepoName}`;
2011
+
2012
+ if (!fs.existsSync(privateRepoPath)) {
2013
+ shellExec(`cd .. && underpost clone ${privateGitUri}`, { silent: true });
2014
+ } else {
2015
+ shellExec(`git config --global --add safe.directory '${dir.resolve(privateRepoPath)}'`);
2016
+ shellExec(`cd ${privateRepoPath} && git checkout . && git clean -f -d && underpost pull . ${privateGitUri}`, {
2017
+ silent: true,
2018
+ });
2019
+ }
2020
+
2021
+ const confDest = `${privateRepoPath}/conf/${deployId}`;
2022
+ fs.removeSync(confDest);
2023
+ fs.mkdirSync(confDest, { recursive: true });
2024
+ fs.copySync(`./engine-private/conf/${deployId}`, confDest);
2025
+
2026
+ fs.removeSync(`${privateRepoPath}/replica`);
2027
+ for (const payloadDir of ['replica', 'itc-scripts']) {
2028
+ const srcDir = `./engine-private/${payloadDir}`;
2029
+ if (!fs.existsSync(srcDir)) continue;
2030
+ for (const entry of fs.readdirSync(srcDir))
2031
+ if (entry.match(deployId)) fs.copySync(`${srcDir}/${entry}`, `${privateRepoPath}/${payloadDir}/${entry}`);
2032
+ }
2033
+
2034
+ for (const extraPath of extraPaths) fs.copySync(`./engine-private/${extraPath}`, `${privateRepoPath}/${extraPath}`);
2035
+
2036
+ shellExec(
2037
+ `cd ${privateRepoPath}` +
2038
+ ` && git add .` +
2039
+ ` && underpost cmt . ci engine-core-conf 'Update ${deployId} conf'` +
2040
+ ` && underpost push . ${privateGitUri}`,
2041
+ { silent: true, silentOnError: true },
2042
+ );
2043
+ };
2044
+
2045
+ /**
2046
+ * Moves a deploy's public template sources into the engine working tree ahead of
2047
+ * the build copy step. Idempotent and safe to rerun: each move is guarded by
2048
+ * `existsSync`, so already-moved or absent sources are skipped rather than throwing.
2049
+ * The `[src, dest]` pairs come from the deploy's product catalog (passed in), so
2050
+ * this module stays product-agnostic.
2051
+ *
2052
+ * @method syncDeployIdSources
2053
+ * @param {Array<[string, string]>} [sourceMoves=[]] - Public `[src, dest]` move pairs.
2054
+ * @returns {boolean} `true` when any sources were declared, else `false`.
2055
+ * @memberof ServerConfBuilder
2056
+ */
2057
+ const syncDeployIdSources = (sourceMoves = []) => {
2058
+ if (!sourceMoves.length) return false;
2059
+ for (const dir of ['src/api', 'src/client/components', 'src/client/public', 'src/client/services'])
2060
+ fs.mkdirSync(dir, { recursive: true });
2061
+ for (const [src, dest] of sourceMoves) if (fs.existsSync(src)) fs.moveSync(src, dest, { overwrite: true });
2062
+ return true;
2063
+ };
2064
+
2065
+ /**
2066
+ * Rebuilds the standalone `pwa-microservices-template` from scratch out of the current
2067
+ * engine source tree.
2068
+ *
2069
+ * Clones the template repo next to the engine when missing, otherwise resets it to a clean
2070
+ * pristine checkout, then syncs every engine-tracked file the template is allowed to carry
2071
+ * ({@link validateTemplatePath}), strips engine-only + product modules, restores the template's
2072
+ * own CI workflows + guest services, and rewrites `package.json` / `package-lock.json` / `README`
2073
+ * so the result is a standalone, installable project. Throws on failure; callers own exit codes.
2074
+ *
2075
+ * Product catalogs are read dynamically ({@link module:src/server/catalog} `loadProductCatalogs`),
2076
+ * so this stays decoupled from — and survives removal of — any product module.
2077
+ *
2078
+ * @method buildTemplate
2079
+ * @param {object} [options]
2080
+ * @param {string} [options.srcPath='./'] - Engine source root to sync from.
2081
+ * @param {string} [options.toPath='../pwa-microservices-template'] - Template output path.
2082
+ * @returns {Promise<void>}
2083
+ * @memberof ServerConfBuilder
2084
+ */
2085
+ const buildTemplate = async ({ srcPath = './', toPath = '../pwa-microservices-template' } = {}) => {
2086
+ const walk = (await import('ignore-walk')).default;
2087
+ const { TEMPLATE_RESTORE_PATHS, TEMPLATE_KEYWORDS, TEMPLATE_DESCRIPTION } =
2088
+ await import('../projects/underpost/catalog-underpost.js');
2089
+ const { loadProductCatalogs } = await import('./catalog.js');
2090
+ const githubUsername = process.env.GITHUB_USERNAME;
2091
+
2092
+ logger.info('Build template', { srcPath, toPath });
2093
+
2094
+ const sourceFiles = (
2095
+ await new Promise((resolve) =>
2096
+ walk({ path: srcPath, ignoreFiles: [`.gitignore`], includeEmpty: false, follow: false }, (...args) =>
2097
+ resolve(args[1]),
2098
+ ),
2099
+ )
2100
+ ).filter((p) => !p.startsWith('.git'));
2101
+
2102
+ fs.removeSync(`${githubUsername}/pwa-microservices-template`);
2103
+ shellExec(`cd .. && node engine/bin clone ${githubUsername}/pwa-microservices-template`);
2104
+
2105
+ shellExec(`cd ${toPath} && git config core.filemode false`);
2106
+
2107
+ for (const copyPath of sourceFiles) {
2108
+ if (copyPath === 'NaN') continue;
2109
+ const absolutePath = `${srcPath}/${copyPath}`;
2110
+ if (!validateTemplatePath(absolutePath)) continue;
2111
+
2112
+ const folder = getDirname(`${toPath}/${copyPath}`);
2113
+ if (!fs.existsSync(folder)) fs.mkdirSync(folder, { recursive: true });
2114
+
2115
+ logger.info('build', `${toPath}/${copyPath}`);
2116
+ fs.copyFileSync(absolutePath, `${toPath}/${copyPath}`);
2117
+ }
2118
+
2119
+ fs.copySync(`./.vscode`, `${toPath}/.vscode`);
2120
+ fs.copySync(`./src/client/public/default`, `${toPath}/src/client/public/default`);
2121
+
2122
+ // Preserve the template's own README + package.json identity before merging engine metadata.
2123
+ for (const checkoutPath of ['README.md', 'package.json']) shellExec(`cd ${toPath} && git checkout ${checkoutPath}`);
2124
+
2125
+ // Strip each product catalog's `stripPaths` (aggregated dynamically) plus the engine-only
2126
+ // workflows, deploy manifests, and product catalog modules.
2127
+ const productStripPaths = (await loadProductCatalogs()).flatMap((c) => c.stripPaths);
2128
+ for (const deletePath of productStripPaths) {
2129
+ const target = `${toPath}/${deletePath}`;
2130
+ if (fs.existsSync(target)) fs.removeSync(target);
2131
+ }
2132
+ shellExec(`rm -rf ${toPath}/.github`);
2133
+ shellExec(`rm -rf ${toPath}/manifests/deployment/dd-*`);
2134
+
2135
+ fs.mkdirSync(`${toPath}/.github/workflows`, { recursive: true });
2136
+ for (const restorePath of TEMPLATE_RESTORE_PATHS) {
2137
+ const dest = `${toPath}/${restorePath}`;
2138
+ if (fs.statSync(restorePath).isDirectory()) fs.copySync(restorePath, dest, { overwrite: true });
2139
+ else fs.copyFileSync(restorePath, dest);
2140
+ }
2141
+
2142
+ // ── package.json: take engine deps/scripts/version, keep template identity. ──
2143
+ const originPackageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
2144
+ const templatePackageJson = JSON.parse(fs.readFileSync(`${toPath}/package.json`, 'utf8'));
2145
+ const templateName = templatePackageJson.name;
2146
+
2147
+ templatePackageJson.dependencies = originPackageJson.dependencies;
2148
+ templatePackageJson.devDependencies = originPackageJson.devDependencies;
2149
+ templatePackageJson.version = originPackageJson.version;
2150
+ templatePackageJson.scripts = originPackageJson.scripts;
2151
+ templatePackageJson.overrides = originPackageJson.overrides;
2152
+ templatePackageJson.name = templateName;
2153
+ templatePackageJson.description = TEMPLATE_DESCRIPTION;
2154
+ templatePackageJson.keywords = TEMPLATE_KEYWORDS;
2155
+ delete templatePackageJson.scripts['build:template'];
2156
+ fs.writeFileSync(`${toPath}/package.json`, JSON.stringify(templatePackageJson, null, 4), 'utf8');
2157
+
2158
+ // ── package-lock.json: mirror engine packages, keep template name/version on the root entry. ──
2159
+ const originPackageLockJson = JSON.parse(fs.readFileSync('./package-lock.json', 'utf8'));
2160
+ const templatePackageLockJson = JSON.parse(fs.readFileSync(`${toPath}/package-lock.json`, 'utf8'));
2161
+ const originBasePackageLock = newInstance(templatePackageLockJson.packages['']);
2162
+ templatePackageLockJson.name = templateName;
2163
+ templatePackageLockJson.version = originPackageLockJson.version;
2164
+ templatePackageLockJson.packages = originPackageLockJson.packages;
2165
+ templatePackageLockJson.packages[''].name = templateName;
2166
+ templatePackageLockJson.packages[''].version = originPackageLockJson.version;
2167
+ templatePackageLockJson.packages[''].hasInstallScript = originBasePackageLock.hasInstallScript;
2168
+ templatePackageLockJson.packages[''].license = originBasePackageLock.license;
2169
+ fs.writeFileSync(`${toPath}/package-lock.json`, JSON.stringify(templatePackageLockJson, null, 4), 'utf8');
2170
+
2171
+ fs.writeFileSync(
2172
+ `${toPath}/README.md`,
2173
+ fs
2174
+ .readFileSync('./README.md', 'utf8')
2175
+ .replace('<!-- template-title -->', '#### Base template for pwa/api-rest projects.'),
2176
+ 'utf8',
2177
+ );
2178
+ };
2179
+
2180
+ const updatePrivateTemplateRepo = async () => {
2181
+ const templatePath = '/home/dd/pwa-microservices-template';
2182
+ shellExec(`sudo rm -rf ${templatePath}
2183
+ cd /home/dd/engine && npm run build:template
2184
+ cd /home/dd
2185
+ underpost clone --bare underpostnet/pwa-microservices-template-private
2186
+ sudo rm -rf ${templatePath}/.git
2187
+ mv ./pwa-microservices-template-private.git ${templatePath}/.git
2188
+ cd ${templatePath}
2189
+ npm install --omit=dev --ignore-scripts
2190
+ git init
2191
+ git config user.name 'underpostnet'
2192
+ git config user.email 'development@underpost.net'
2193
+ git add .`);
2194
+ const hasChanges = shellExec(`node bin cmt ${templatePath} --has-changes`, {
2195
+ stdout: true,
2196
+ silent: true,
2197
+ disableLog: true,
2198
+ }).trim();
2199
+ if (hasChanges === '1') {
2200
+ shellExec(
2201
+ `cd ${templatePath} && git commit -m 'Update template' && underpost push . underpostnet/pwa-microservices-template-private`,
2202
+ );
2203
+ }
2204
+ };
2205
+
2206
+ /**
2207
+ * @method updatePrivateEngineTestRepo
2208
+ * @description Publishes a deploy id's freshly assembled template to its private
2209
+ * **test** source repo `engine-test-<idPart>` (separate from the production
2210
+ * `engine-<idPart>`). A pod started with `underpost start --build --private-test-repo`
2211
+ * clones this repo, so work-in-progress engine source can be tested end to end
2212
+ * without touching the production source. Mirrors {@link updatePrivateTemplateRepo}
2213
+ * but per-deploy-id and against the test repo.
2214
+ *
2215
+ * Assumes the deploy id template has already been assembled at the template path
2216
+ * (run `node bin/build <deployId>` first, or use `node bin/build <deployId> --update-private`).
2217
+ * @param {string} deployId - Concrete deploy id (e.g. `dd-core`).
2218
+ * @returns {Promise<void>}
2219
+ * @memberof ServerConfBuilder
2220
+ */
2221
+ const updatePrivateEngineTestRepo = async (deployId) => {
2222
+ const username = process.env.GITHUB_USERNAME || 'underpostnet';
2223
+ const repoName = `engine-test-${deployId.split('-')[1]}`;
2224
+ const templatePath = '/home/dd/pwa-microservices-template';
2225
+ if (!fs.existsSync(templatePath))
2226
+ throw new Error(`updatePrivateEngineTestRepo: assemble the template first (node bin/build ${deployId})`);
2227
+
2228
+ // Detach the assembled working tree from any engine-build git history.
2229
+ shellExec(`sudo rm -rf ${templatePath}/.git`);
2230
+
2231
+ // Adopt the test repo's existing history when present (so the push is a delta);
2232
+ // otherwise publish a fresh history on first push.
2233
+ shellExec(`cd /home/dd && sudo rm -rf ./${repoName}.git && underpost clone --bare ${username}/${repoName}`, {
2234
+ silent: true,
2235
+ disableLog: true,
2236
+ silentOnError: true,
2237
+ });
2238
+ if (fs.existsSync(`/home/dd/${repoName}.git`)) shellExec(`mv /home/dd/${repoName}.git ${templatePath}/.git`);
2239
+
2240
+ // `git init` converts the moved bare repo into a normal work-tree repo (bare
2241
+ // clones have no work tree, so `git add` would fail), and bootstraps a fresh
2242
+ // repo on first publish. Idempotent — mirrors updatePrivateTemplateRepo.
2243
+ shellExec(`cd ${templatePath}
2244
+ git init
2245
+ git config user.name '${username}'
2246
+ git config user.email 'development@underpost.net'
2247
+ git add .`);
2248
+
2249
+ const hasChanges = shellExec(`node bin cmt ${templatePath} --has-changes`, {
2250
+ stdout: true,
2251
+ silent: true,
2252
+ disableLog: true,
2253
+ }).trim();
2254
+ if (hasChanges === '1')
2255
+ shellExec(`cd ${templatePath} && git commit -m 'Update ${repoName}' && underpost push . ${username}/${repoName}`);
2256
+ else logger.info('No changes to publish', { repoName });
2257
+ };
2258
+
2259
+ export {
2260
+ Config,
2261
+ loadConf,
2262
+ loadConfInstances,
2263
+ loadInstanceTopology,
2264
+ readConfInstances,
2265
+ selectConfInstances,
2266
+ loadReplicas,
2267
+ cloneConf,
2268
+ getCapVariableName,
2269
+ addClientConf,
2270
+ buildClientSrc,
2271
+ buildApiSrc,
2272
+ addApiConf,
2273
+ addWsConf,
2274
+ buildWsSrc,
2275
+ cloneSrcComponents,
2276
+ buildProxyRouter,
2277
+ getDataDeploy,
2278
+ validateTemplatePath,
2279
+ buildReplicaId,
2280
+ mergeFile,
2281
+ getPathsSSR,
2282
+ buildKindPorts,
2283
+ buildPortProxyRouter,
2284
+ splitFileFactory,
2285
+ getNpmRootPath,
2286
+ getUnderpostRootPath,
2287
+ writeEnv,
2288
+ pathPortAssignmentFactory,
2289
+ deployRangePortFactory,
2290
+ awaitDeployMonitor,
2291
+ buildCliDoc,
2292
+ getInstanceContext,
2293
+ buildApiConf,
2294
+ buildClientStaticConf,
2295
+ isDeployRunnerContext,
2296
+ isDevProxyContext,
2297
+ devProxyHostFactory,
2298
+ isTlsDevProxy,
2299
+ getTlsHosts,
2300
+ resolveHostKeyContext,
2301
+ resolveConfSecrets,
2302
+ loadConfServerJson,
2303
+ getConfFolder,
2304
+ getConfFilePath,
2305
+ readConfJson,
2306
+ DEFAULT_DEPLOY_ID,
2307
+ loadCronDeployEnv,
2308
+ cronDeployIdResolve,
2309
+ etcHostFactory,
2310
+ resolveDeployList,
2311
+ syncPrivateConf,
2312
+ syncDeployIdSources,
2313
+ buildTemplate,
2314
+ updatePrivateTemplateRepo,
2315
+ updatePrivateEngineTestRepo,
2316
+ };