cyberia 3.1.3 → 3.2.9

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 (377) hide show
  1. package/.env.example +0 -2
  2. package/.github/workflows/engine-cyberia.cd.yml +10 -8
  3. package/.github/workflows/engine-cyberia.ci.yml +12 -29
  4. package/.github/workflows/ghpkg.ci.yml +4 -4
  5. package/.github/workflows/npmpkg.ci.yml +28 -11
  6. package/.github/workflows/publish.ci.yml +21 -2
  7. package/.github/workflows/pwa-microservices-template-page.cd.yml +4 -5
  8. package/.github/workflows/pwa-microservices-template-test.ci.yml +3 -3
  9. package/.github/workflows/release.cd.yml +14 -10
  10. package/CHANGELOG.md +783 -1
  11. package/CLI-HELP.md +95 -18
  12. package/Dockerfile +0 -2
  13. package/README.md +290 -220
  14. package/bin/build.js +24 -7
  15. package/bin/cyberia.js +2838 -252
  16. package/bin/deploy.js +747 -125
  17. package/bin/file.js +9 -0
  18. package/bin/index.js +2838 -252
  19. package/bin/vs.js +1 -1
  20. package/conf.js +99 -65
  21. package/deployment.yaml +18 -164
  22. package/hardhat/hardhat.config.js +13 -13
  23. package/hardhat/ignition/modules/ObjectLayerToken.js +1 -1
  24. package/hardhat/package-lock.json +2559 -5864
  25. package/hardhat/package.json +14 -23
  26. package/hardhat/scripts/deployObjectLayerToken.js +1 -1
  27. package/hardhat/test/ObjectLayerToken.js +4 -2
  28. package/hardhat/types/ethers-contracts/ObjectLayerToken.ts +690 -0
  29. package/hardhat/types/ethers-contracts/common.ts +92 -0
  30. package/hardhat/types/ethers-contracts/factories/ObjectLayerToken__factory.ts +1055 -0
  31. package/hardhat/types/ethers-contracts/factories/index.ts +4 -0
  32. package/hardhat/types/ethers-contracts/hardhat.d.ts +47 -0
  33. package/hardhat/types/ethers-contracts/index.ts +6 -0
  34. package/jsconfig.json +1 -1
  35. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +6 -5
  36. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +6 -5
  37. package/manifests/deployment/dd-cyberia-development/deployment.yaml +18 -164
  38. package/manifests/deployment/dd-cyberia-development/proxy.yaml +7 -79
  39. package/manifests/deployment/dd-default-development/deployment.yaml +2 -6
  40. package/manifests/deployment/dd-test-development/deployment.yaml +112 -28
  41. package/manifests/deployment/dd-test-development/proxy.yaml +46 -1
  42. package/manifests/deployment/playwright/deployment.yaml +1 -1
  43. package/nodemon.json +1 -1
  44. package/package.json +39 -24
  45. package/proxy.yaml +7 -79
  46. package/scripts/k3s-node-setup.sh +2 -2
  47. package/scripts/nat-iptables.sh +103 -18
  48. package/scripts/rhel-grpc-setup.sh +56 -0
  49. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +58 -14
  50. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +23 -14
  51. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +5 -0
  52. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +148 -20
  53. package/src/api/core/core.controller.js +10 -10
  54. package/src/api/core/core.service.js +10 -10
  55. package/src/api/crypto/crypto.controller.js +8 -8
  56. package/src/api/crypto/crypto.service.js +8 -8
  57. package/src/api/cyberia-action/cyberia-action.controller.js +74 -0
  58. package/src/api/cyberia-action/cyberia-action.model.js +87 -0
  59. package/src/api/cyberia-action/cyberia-action.router.js +27 -0
  60. package/src/api/cyberia-action/cyberia-action.service.js +42 -0
  61. package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +93 -0
  62. package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +36 -0
  63. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +29 -0
  64. package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +51 -0
  65. package/src/api/cyberia-entity/cyberia-entity.controller.js +74 -0
  66. package/src/api/cyberia-entity/cyberia-entity.model.js +24 -0
  67. package/src/api/cyberia-entity/cyberia-entity.router.js +27 -0
  68. package/src/api/cyberia-entity/cyberia-entity.service.js +42 -0
  69. package/src/api/cyberia-instance/cyberia-fallback-world.js +178 -0
  70. package/src/api/cyberia-instance/cyberia-instance.controller.js +92 -0
  71. package/src/api/cyberia-instance/cyberia-instance.model.js +87 -0
  72. package/src/api/cyberia-instance/cyberia-instance.router.js +63 -0
  73. package/src/api/cyberia-instance/cyberia-instance.service.js +156 -0
  74. package/src/api/cyberia-instance/cyberia-portal-connector.js +260 -0
  75. package/src/api/cyberia-instance/cyberia-world-generator.js +505 -0
  76. package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +74 -0
  77. package/src/api/cyberia-instance-conf/cyberia-instance-conf.defaults.js +574 -0
  78. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +231 -0
  79. package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +27 -0
  80. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +46 -0
  81. package/src/api/cyberia-map/cyberia-map.controller.js +79 -0
  82. package/src/api/cyberia-map/cyberia-map.model.js +30 -0
  83. package/src/api/cyberia-map/cyberia-map.router.js +40 -0
  84. package/src/api/cyberia-map/cyberia-map.service.js +74 -0
  85. package/src/api/cyberia-quest/cyberia-quest.controller.js +74 -0
  86. package/src/api/cyberia-quest/cyberia-quest.model.js +67 -0
  87. package/src/api/cyberia-quest/cyberia-quest.router.js +27 -0
  88. package/src/api/cyberia-quest/cyberia-quest.service.js +42 -0
  89. package/src/api/cyberia-quest-progress/cyberia-quest-progress.controller.js +74 -0
  90. package/src/api/cyberia-quest-progress/cyberia-quest-progress.model.js +49 -0
  91. package/src/api/cyberia-quest-progress/cyberia-quest-progress.router.js +27 -0
  92. package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +42 -0
  93. package/src/api/default/default.controller.js +10 -10
  94. package/src/api/default/default.service.js +10 -10
  95. package/src/api/document/document.controller.js +12 -12
  96. package/src/api/document/document.model.js +10 -16
  97. package/src/api/file/file.controller.js +8 -8
  98. package/src/api/file/file.model.js +10 -10
  99. package/src/api/file/file.ref.json +18 -0
  100. package/src/api/file/file.service.js +36 -36
  101. package/src/api/instance/instance.controller.js +10 -10
  102. package/src/api/instance/instance.model.js +4 -10
  103. package/src/api/instance/instance.service.js +10 -10
  104. package/src/api/ipfs/ipfs.controller.js +15 -36
  105. package/src/api/ipfs/ipfs.model.js +47 -47
  106. package/src/api/ipfs/ipfs.router.js +8 -13
  107. package/src/api/ipfs/ipfs.service.js +67 -129
  108. package/src/api/object-layer/object-layer.controller.js +12 -12
  109. package/src/api/object-layer/object-layer.model.js +4 -17
  110. package/src/api/object-layer/object-layer.router.js +30 -0
  111. package/src/api/object-layer/object-layer.service.js +126 -43
  112. package/src/api/object-layer-render-frames/object-layer-render-frames.controller.js +10 -10
  113. package/src/api/object-layer-render-frames/object-layer-render-frames.model.js +6 -16
  114. package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +18 -14
  115. package/src/api/test/test.controller.js +8 -8
  116. package/src/api/test/test.service.js +8 -8
  117. package/src/api/user/guest.service.js +99 -0
  118. package/src/api/user/user.controller.js +6 -6
  119. package/src/api/user/user.model.js +8 -13
  120. package/src/api/user/user.service.js +11 -27
  121. package/src/cli/cluster.js +68 -21
  122. package/src/cli/db.js +753 -825
  123. package/src/cli/deploy.js +215 -125
  124. package/src/cli/env.js +29 -0
  125. package/src/cli/fs.js +82 -8
  126. package/src/cli/image.js +43 -1
  127. package/src/cli/index.js +74 -3
  128. package/src/cli/kubectl.js +211 -0
  129. package/src/cli/release.js +340 -0
  130. package/src/cli/repository.js +475 -74
  131. package/src/cli/run.js +582 -43
  132. package/src/cli/secrets.js +73 -0
  133. package/src/cli/ssh.js +1 -1
  134. package/src/cli/static.js +43 -115
  135. package/src/cli/test.js +3 -3
  136. package/src/client/Cryptokoyn.index.js +18 -22
  137. package/src/client/CyberiaPortal.index.js +19 -24
  138. package/src/client/Default.index.js +21 -34
  139. package/src/client/Itemledger.index.js +20 -27
  140. package/src/client/Underpost.index.js +19 -24
  141. package/src/client/components/core/404.js +4 -4
  142. package/src/client/components/core/500.js +4 -4
  143. package/src/client/components/core/Account.js +73 -60
  144. package/src/client/components/core/AgGrid.js +23 -33
  145. package/src/client/components/core/Alert.js +12 -13
  146. package/src/client/components/core/AppStore.js +69 -0
  147. package/src/client/components/core/Auth.js +35 -37
  148. package/src/client/components/core/Badge.js +7 -13
  149. package/src/client/components/core/BtnIcon.js +15 -17
  150. package/src/client/components/core/CalendarCore.js +43 -64
  151. package/src/client/components/core/Chat.js +13 -15
  152. package/src/client/components/core/ClientEvents.js +87 -0
  153. package/src/client/components/core/ColorPaletteElement.js +309 -0
  154. package/src/client/components/core/Content.js +17 -14
  155. package/src/client/components/core/Css.js +15 -71
  156. package/src/client/components/core/CssCore.js +12 -16
  157. package/src/client/components/core/D3Chart.js +4 -4
  158. package/src/client/components/core/Docs.js +64 -91
  159. package/src/client/components/core/DropDown.js +194 -96
  160. package/src/client/components/core/EventBus.js +92 -0
  161. package/src/client/components/core/EventsUI.js +14 -17
  162. package/src/client/components/core/FileExplorer.js +96 -228
  163. package/src/client/components/core/FullScreen.js +47 -75
  164. package/src/client/components/core/Input.js +24 -69
  165. package/src/client/components/core/Keyboard.js +26 -19
  166. package/src/client/components/core/KeyboardAvoidance.js +145 -0
  167. package/src/client/components/core/LoadingAnimation.js +25 -31
  168. package/src/client/components/core/LogIn.js +43 -43
  169. package/src/client/components/core/LogOut.js +25 -16
  170. package/src/client/components/core/Modal.js +462 -179
  171. package/src/client/components/core/NotificationManager.js +14 -18
  172. package/src/client/components/core/Panel.js +54 -51
  173. package/src/client/components/core/PanelForm.js +44 -144
  174. package/src/client/components/core/Polyhedron.js +110 -214
  175. package/src/client/components/core/PublicProfile.js +39 -32
  176. package/src/client/components/core/Recover.js +48 -44
  177. package/src/client/components/core/Responsive.js +88 -32
  178. package/src/client/components/core/RichText.js +9 -18
  179. package/src/client/components/core/Router.js +24 -3
  180. package/src/client/components/core/SearchBox.js +37 -37
  181. package/src/client/components/core/SignUp.js +39 -30
  182. package/src/client/components/core/SocketIo.js +112 -30
  183. package/src/client/components/core/SocketIoHandler.js +75 -0
  184. package/src/client/components/core/Stream.js +143 -95
  185. package/src/client/components/core/ToggleSwitch.js +8 -20
  186. package/src/client/components/core/ToolTip.js +5 -17
  187. package/src/client/components/core/Translate.js +56 -59
  188. package/src/client/components/core/Validator.js +26 -16
  189. package/src/client/components/core/Wallet.js +15 -26
  190. package/src/client/components/core/Webhook.js +40 -7
  191. package/src/client/components/core/Worker.js +163 -27
  192. package/src/client/components/core/windowGetDimensions.js +7 -7
  193. package/src/client/components/cryptokoyn/{MenuCryptokoyn.js → AppShellCryptokoyn.js} +59 -59
  194. package/src/client/components/cryptokoyn/AppStoreCryptokoyn.js +5 -0
  195. package/src/client/components/cryptokoyn/CssCryptokoyn.js +15 -15
  196. package/src/client/components/cryptokoyn/LogInCryptokoyn.js +9 -7
  197. package/src/client/components/cryptokoyn/LogOutCryptokoyn.js +8 -6
  198. package/src/client/components/cryptokoyn/RouterCryptokoyn.js +37 -0
  199. package/src/client/components/cryptokoyn/SettingsCryptokoyn.js +4 -4
  200. package/src/client/components/cryptokoyn/SignUpCryptokoyn.js +6 -4
  201. package/src/client/components/cryptokoyn/SocketIoCryptokoyn.js +3 -51
  202. package/src/client/components/cyberia/InstanceEngineCyberia.js +781 -0
  203. package/src/client/components/cyberia/MapEngineCyberia.js +1836 -2
  204. package/src/client/components/cyberia/ObjectLayerEngine.js +19 -0
  205. package/src/client/components/cyberia/ObjectLayerEngineModal.js +1220 -99
  206. package/src/client/components/cyberia/ObjectLayerEngineViewer.js +252 -316
  207. package/src/client/components/cyberia-portal/{MenuCyberiaPortal.js → AppShellCyberiaPortal.js} +136 -103
  208. package/src/client/components/cyberia-portal/AppStoreCyberiaPortal.js +5 -0
  209. package/src/client/components/cyberia-portal/CommonCyberiaPortal.js +462 -32
  210. package/src/client/components/cyberia-portal/CssCyberiaPortal.js +15 -15
  211. package/src/client/components/cyberia-portal/LogInCyberiaPortal.js +9 -7
  212. package/src/client/components/cyberia-portal/LogOutCyberiaPortal.js +8 -6
  213. package/src/client/components/cyberia-portal/MainBodyCyberiaPortal.js +4 -4
  214. package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +60 -0
  215. package/src/client/components/cyberia-portal/SettingsCyberiaPortal.js +4 -4
  216. package/src/client/components/cyberia-portal/SignUpCyberiaPortal.js +6 -4
  217. package/src/client/components/cyberia-portal/SocketIoCyberiaPortal.js +3 -49
  218. package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +8 -4
  219. package/src/client/components/default/{MenuDefault.js → AppShellDefault.js} +91 -91
  220. package/src/client/components/default/AppStoreDefault.js +5 -0
  221. package/src/client/components/default/CssDefault.js +12 -12
  222. package/src/client/components/default/LogInDefault.js +9 -7
  223. package/src/client/components/default/LogOutDefault.js +8 -6
  224. package/src/client/components/default/RouterDefault.js +47 -0
  225. package/src/client/components/default/SettingsDefault.js +4 -4
  226. package/src/client/components/default/SignUpDefault.js +6 -4
  227. package/src/client/components/default/SocketIoDefault.js +3 -51
  228. package/src/client/components/default/TranslateDefault.js +3 -3
  229. package/src/client/components/itemledger/{MenuItemledger.js → AppShellItemledger.js} +59 -59
  230. package/src/client/components/itemledger/AppStoreItemledger.js +5 -0
  231. package/src/client/components/itemledger/CssItemledger.js +15 -15
  232. package/src/client/components/itemledger/LogInItemledger.js +9 -7
  233. package/src/client/components/itemledger/LogOutItemledger.js +8 -6
  234. package/src/client/components/itemledger/RouterItemledger.js +38 -0
  235. package/src/client/components/itemledger/SettingsItemledger.js +4 -4
  236. package/src/client/components/itemledger/SignUpItemledger.js +6 -4
  237. package/src/client/components/itemledger/SocketIoItemledger.js +3 -51
  238. package/src/client/components/itemledger/TranslateItemledger.js +3 -3
  239. package/src/client/components/underpost/{MenuUnderpost.js → AppShellUnderpost.js} +92 -92
  240. package/src/client/components/underpost/AppStoreUnderpost.js +5 -0
  241. package/src/client/components/underpost/CssUnderpost.js +14 -14
  242. package/src/client/components/underpost/CyberpunkBloggerUnderpost.js +4 -4
  243. package/src/client/components/underpost/DocumentSearchProvider.js +1 -1
  244. package/src/client/components/underpost/LabGalleryUnderpost.js +12 -15
  245. package/src/client/components/underpost/LogInUnderpost.js +9 -7
  246. package/src/client/components/underpost/LogOutUnderpost.js +8 -6
  247. package/src/client/components/underpost/RouterUnderpost.js +45 -0
  248. package/src/client/components/underpost/SettingsUnderpost.js +4 -4
  249. package/src/client/components/underpost/SignUpUnderpost.js +6 -4
  250. package/src/client/components/underpost/SocketIoUnderpost.js +3 -51
  251. package/src/client/components/underpost/TranslateUnderpost.js +4 -4
  252. package/src/client/public/cyberia-docs/ACTION-SYSTEM.md +235 -0
  253. package/src/client/public/cyberia-docs/ARCHITECTURE.md +443 -0
  254. package/src/client/public/cyberia-docs/CYBERIA-CLI.md +417 -0
  255. package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +313 -0
  256. package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +260 -0
  257. package/src/client/public/cyberia-docs/ENTITY-PROFILE.md +241 -0
  258. package/src/client/public/cyberia-docs/HARDHAT-MODULE.md +300 -0
  259. package/src/client/public/cyberia-docs/OFF-CHAIN-ECONOMY.md +279 -0
  260. package/src/client/public/cyberia-docs/QUEST-SYSTEM.md +206 -0
  261. package/src/client/public/cyberia-docs/ROADMAP.md +240 -0
  262. package/src/client/public/cyberia-docs/WHITE-PAPER.md +732 -0
  263. package/src/client/services/atlas-sprite-sheet/atlas-sprite-sheet.service.js +14 -20
  264. package/src/client/services/core/core.service.js +35 -55
  265. package/src/client/services/crypto/crypto.service.js +8 -13
  266. package/src/client/services/cyberia-action/cyberia-action.service.js +99 -0
  267. package/src/client/services/cyberia-dialogue/cyberia-dialogue.service.js +99 -0
  268. package/src/client/services/cyberia-entity/cyberia-entity.management.js +57 -0
  269. package/src/client/services/cyberia-entity/cyberia-entity.service.js +99 -0
  270. package/src/client/services/cyberia-instance/cyberia-instance.management.js +194 -0
  271. package/src/client/services/cyberia-instance/cyberia-instance.service.js +116 -0
  272. package/src/client/services/cyberia-instance-conf/cyberia-instance-conf.service.js +99 -0
  273. package/src/client/services/cyberia-map/cyberia-map.management.js +193 -0
  274. package/src/client/services/cyberia-map/cyberia-map.service.js +120 -0
  275. package/src/client/services/cyberia-quest/cyberia-quest.service.js +99 -0
  276. package/src/client/services/cyberia-quest-progress/cyberia-quest-progress.service.js +99 -0
  277. package/src/client/services/default/default.management.js +159 -267
  278. package/src/client/services/default/default.service.js +10 -16
  279. package/src/client/services/document/document.service.js +14 -19
  280. package/src/client/services/file/file.service.js +8 -13
  281. package/src/client/services/instance/instance.management.js +6 -6
  282. package/src/client/services/instance/instance.service.js +10 -15
  283. package/src/client/services/ipfs/ipfs.service.js +14 -40
  284. package/src/client/services/object-layer/object-layer.management.js +14 -14
  285. package/src/client/services/object-layer/object-layer.service.js +39 -24
  286. package/src/client/services/object-layer-render-frames/object-layer-render-frames.service.js +10 -16
  287. package/src/client/services/test/test.service.js +8 -13
  288. package/src/client/services/user/guest.service.js +86 -0
  289. package/src/client/services/user/user.management.js +6 -6
  290. package/src/client/services/user/user.service.js +14 -20
  291. package/src/client/ssr/body/404.js +3 -3
  292. package/src/client/ssr/body/500.js +3 -3
  293. package/src/client/ssr/body/CacheControl.js +5 -2
  294. package/src/client/ssr/body/DefaultSplashScreen.js +19 -12
  295. package/src/client/ssr/body/UnderpostDefaultSplashScreen.js +13 -6
  296. package/src/client/ssr/head/PwaItemledger.js +197 -60
  297. package/src/client/ssr/mailer/DefaultRecoverEmail.js +19 -20
  298. package/src/client/ssr/mailer/DefaultVerifyEmail.js +15 -16
  299. package/src/client/ssr/offline/Maintenance.js +12 -11
  300. package/src/client/ssr/offline/NoNetworkConnection.js +3 -3
  301. package/src/client/ssr/pages/CyberiaServerMetrics.js +1 -1
  302. package/src/client/ssr/pages/Test.js +2 -2
  303. package/src/client/sw/core.sw.js +212 -0
  304. package/src/grpc/cyberia/grpc-server.js +642 -0
  305. package/src/index.js +24 -1
  306. package/src/runtime/cyberia-client/Dockerfile +80 -0
  307. package/src/runtime/cyberia-server/Dockerfile +37 -0
  308. package/src/runtime/express/Dockerfile +5 -1
  309. package/src/runtime/express/Express.js +18 -1
  310. package/src/runtime/lampp/Dockerfile +17 -5
  311. package/src/runtime/lampp/Lampp.js +27 -4
  312. package/src/runtime/wp/Dockerfile +62 -0
  313. package/src/runtime/wp/Wp.js +639 -0
  314. package/src/server/atlas-sprite-sheet-generator.js +4 -2
  315. package/src/server/auth.js +24 -1
  316. package/src/server/backup.js +37 -9
  317. package/src/server/client-build-docs.js +52 -46
  318. package/src/server/client-build.js +356 -82
  319. package/src/server/client-formatted.js +140 -57
  320. package/src/server/conf.js +29 -13
  321. package/src/server/cron.js +25 -23
  322. package/src/server/data-query.js +32 -20
  323. package/src/server/dns.js +24 -1
  324. package/src/server/ipfs-client.js +253 -89
  325. package/src/server/object-layer.js +150 -114
  326. package/src/server/peer.js +8 -0
  327. package/src/server/process.js +13 -27
  328. package/src/server/runtime.js +25 -1
  329. package/src/server/semantic-layer-generator-floor.js +319 -0
  330. package/src/server/semantic-layer-generator-resource.js +259 -0
  331. package/src/server/semantic-layer-generator-skin.js +1164 -0
  332. package/src/server/semantic-layer-generator.js +211 -542
  333. package/src/server/shape-generator.js +108 -0
  334. package/src/server/start.js +19 -5
  335. package/src/server/valkey.js +141 -235
  336. package/src/ws/IoInterface.js +1 -10
  337. package/src/ws/IoServer.js +14 -33
  338. package/src/ws/core/channels/core.ws.chat.js +65 -20
  339. package/src/ws/core/channels/core.ws.mailer.js +113 -32
  340. package/src/ws/core/channels/core.ws.stream.js +90 -31
  341. package/src/ws/core/core.ws.connection.js +12 -33
  342. package/src/ws/core/core.ws.emit.js +10 -26
  343. package/src/ws/core/core.ws.server.js +25 -58
  344. package/src/ws/default/channels/default.ws.main.js +53 -12
  345. package/src/ws/default/default.ws.connection.js +26 -13
  346. package/src/ws/default/default.ws.server.js +30 -12
  347. package/tsconfig.docs.json +15 -0
  348. package/typedoc.dd-cyberia.json +29 -0
  349. package/typedoc.json +29 -0
  350. package/WHITE-PAPER.md +0 -1540
  351. package/hardhat/README.md +0 -531
  352. package/hardhat/WHITE-PAPER.md +0 -1540
  353. package/jsdoc.dd-cyberia.json +0 -59
  354. package/jsdoc.json +0 -59
  355. package/src/api/object-layer/README.md +0 -347
  356. package/src/client/components/core/ColorPalette.js +0 -5267
  357. package/src/client/components/core/JoyStick.js +0 -80
  358. package/src/client/components/cryptokoyn/CommonCryptokoyn.js +0 -29
  359. package/src/client/components/cryptokoyn/ElementsCryptokoyn.js +0 -38
  360. package/src/client/components/cryptokoyn/RoutesCryptokoyn.js +0 -39
  361. package/src/client/components/cyberia-portal/ElementsCyberiaPortal.js +0 -38
  362. package/src/client/components/cyberia-portal/RoutesCyberiaPortal.js +0 -58
  363. package/src/client/components/cyberia-portal/ServerCyberiaPortal.js +0 -136
  364. package/src/client/components/default/ElementsDefault.js +0 -38
  365. package/src/client/components/default/RoutesDefault.js +0 -49
  366. package/src/client/components/itemledger/CommonItemledger.js +0 -29
  367. package/src/client/components/itemledger/ElementsItemledger.js +0 -38
  368. package/src/client/components/itemledger/RoutesItemledger.js +0 -40
  369. package/src/client/components/underpost/CommonUnderpost.js +0 -29
  370. package/src/client/components/underpost/ElementsUnderpost.js +0 -38
  371. package/src/client/components/underpost/RoutesUnderpost.js +0 -47
  372. package/src/client/sw/default.sw.js +0 -127
  373. package/src/client/sw/template.sw.js +0 -84
  374. package/src/ws/core/management/core.ws.chat.js +0 -8
  375. package/src/ws/core/management/core.ws.mailer.js +0 -16
  376. package/src/ws/core/management/core.ws.stream.js +0 -8
  377. package/src/ws/default/management/default.ws.main.js +0 -8
package/bin/deploy.js CHANGED
@@ -3,7 +3,7 @@ import axios from 'axios';
3
3
 
4
4
  import dotenv from 'dotenv';
5
5
 
6
- import { pbcopy, shellCd, shellExec } from '../src/server/process.js';
6
+ import { pbcopy, shellExec } from '../src/server/process.js';
7
7
  import { loggerFactory } from '../src/server/logger.js';
8
8
  import {
9
9
  addApiConf,
@@ -20,7 +20,7 @@ import {
20
20
  } from '../src/server/conf.js';
21
21
  import colors from 'colors';
22
22
  import { program } from '../src/cli/index.js';
23
- import { timer } from '../src/client/components/core/CommonJs.js';
23
+ import { timer, getCapVariableName } from '../src/client/components/core/CommonJs.js';
24
24
  import Underpost from '../src/index.js';
25
25
 
26
26
  colors.enable();
@@ -218,115 +218,6 @@ try {
218
218
  break;
219
219
  }
220
220
 
221
- case 'version-build': {
222
- dotenv.config({ path: `./engine-private/conf/dd-cron/.env.production`, override: true });
223
- shellCd(`/home/dd/engine`);
224
- Underpost.repo.clean({ paths: ['/home/dd/engine', '/home/dd/engine/engine-private '] });
225
- shellExec(`node bin pull . ${process.env.GITHUB_USERNAME}/engine`);
226
- shellExec(`node bin run kill 4001`);
227
- shellExec(`node bin run kill 4002`);
228
- shellExec(`node bin run kill 4003`);
229
- shellExec(`npm run update:template`);
230
- shellExec(`cd ../pwa-microservices-template && npm install && npm run build`);
231
- console.log(fs.existsSync(`../pwa-microservices-template/engine-private/conf/dd-default`));
232
- shellExec(`cd ../pwa-microservices-template && ENABLE_FILE_LOGS=true timeout 5s npm run dev`, {
233
- async: true,
234
- });
235
- await timer(5500);
236
- const templateRunnerResult = fs.readFileSync(`../pwa-microservices-template/logs/start.js/all.log`, 'utf8');
237
- logger.info('Test template runner result');
238
- console.log(templateRunnerResult);
239
- if (!templateRunnerResult || templateRunnerResult.toLowerCase().match('error')) {
240
- logger.error('Test template runner result failed');
241
- break;
242
- }
243
- shellCd(`/home/dd/engine`);
244
- Underpost.repo.clean({ paths: ['/home/dd/engine', '/home/dd/engine/engine-private '] });
245
- const originPackageJson = JSON.parse(fs.readFileSync(`package.json`, 'utf8'));
246
- const newVersion = process.argv[3] ?? originPackageJson.version;
247
- const { version } = originPackageJson;
248
- originPackageJson.version = newVersion;
249
- fs.writeFileSync(`package.json`, JSON.stringify(originPackageJson, null, 4), 'utf8');
250
-
251
- const originPackageLockJson = JSON.parse(fs.readFileSync(`package-lock.json`, 'utf8'));
252
- originPackageLockJson.version = newVersion;
253
- originPackageLockJson.packages[''].version = newVersion;
254
- fs.writeFileSync(`package-lock.json`, JSON.stringify(originPackageLockJson, null, 4), 'utf8');
255
-
256
- if (fs.existsSync(`./engine-private/conf`)) {
257
- const files = await fs.readdir(`./engine-private/conf`, { recursive: true });
258
- for (const relativePath of files) {
259
- const filePah = `./engine-private/conf/${relativePath.replaceAll(`\\`, '/')}`;
260
- if (filePah.split('/').pop() === 'package.json') {
261
- const originPackage = JSON.parse(fs.readFileSync(filePah, 'utf8'));
262
- originPackage.version = newVersion;
263
- fs.writeFileSync(filePah, JSON.stringify(originPackage, null, 4), 'utf8');
264
- }
265
- if (filePah.split('/').pop() === 'deployment.yaml') {
266
- fs.writeFileSync(
267
- filePah,
268
- fs
269
- .readFileSync(filePah, 'utf8')
270
- .replaceAll(`v${version}`, `v${newVersion}`)
271
- .replaceAll(`engine.version: ${version}`, `engine.version: ${newVersion}`),
272
- 'utf8',
273
- );
274
- }
275
- }
276
- }
277
-
278
- fs.writeFileSync(
279
- `./manifests/deployment/dd-default-development/deployment.yaml`,
280
- fs
281
- .readFileSync(`./manifests/deployment/dd-default-development/deployment.yaml`, 'utf8')
282
- .replaceAll(`underpost:v${version}`, `underpost:v${newVersion}`),
283
- 'utf8',
284
- );
285
-
286
- if (fs.existsSync(`./.github/workflows/docker-image.ci.yml`))
287
- fs.writeFileSync(
288
- `./.github/workflows/docker-image.ci.yml`,
289
- fs
290
- .readFileSync(`./.github/workflows/docker-image.ci.yml`, 'utf8')
291
- .replaceAll(`underpost-engine:v${version}`, `underpost-engine:v${newVersion}`),
292
- 'utf8',
293
- );
294
-
295
- fs.writeFileSync(
296
- `./src/index.js`,
297
- fs.readFileSync(`./src/index.js`, 'utf8').replaceAll(`${version}`, `${newVersion}`),
298
- 'utf8',
299
- );
300
- shellExec(`node bin/deploy cli-docs ${version} ${newVersion}`);
301
- shellExec(`node bin/deploy update-dependencies`);
302
- shellExec(`node bin/build dd`);
303
- shellExec(`node bin deploy --build-manifest --sync --info-router --replicas 1 dd production`);
304
- shellExec(`node bin deploy --build-manifest --sync --info-router --replicas 1 dd development`);
305
- shellExec(`node bin/deploy build-default-confs`);
306
- shellExec(`sudo rm -rf ./engine-private/conf/dd-default`);
307
- shellExec(`node bin new --deploy-id dd-default`);
308
- console.log(fs.existsSync(`./engine-private/conf/dd-default`));
309
- shellExec(`sudo rm -rf ./engine-private/conf/dd-default`);
310
- shellExec(`node bin cron --dev --setup-start`);
311
- shellExec(`node bin cmt --changelog-build`);
312
- process.exit(0);
313
- break;
314
- }
315
-
316
- case 'version-deploy': {
317
- dotenv.config({ path: `./engine-private/conf/dd-cron/.env.production`, override: true });
318
- shellExec(
319
- `underpost secret underpost --create-from-file /home/dd/engine/engine-private/conf/dd-cron/.env.production`,
320
- );
321
- shellExec(`node bin/build dd conf`);
322
- shellExec(`git add . && cd ./engine-private && git add .`);
323
- shellExec(`node bin cmt . ci package-pwa-microservices-template 'New release v:${process.argv[3]}'`);
324
- shellExec(`node bin cmt ./engine-private ci package-pwa-microservices-template`);
325
- shellExec(`node bin push . ${process.env.GITHUB_USERNAME}/engine`);
326
- shellExec(`cd ./engine-private && node ../bin push . ${process.env.GITHUB_USERNAME}/engine-private`);
327
- break;
328
- }
329
-
330
221
  case 'build-default-confs': {
331
222
  for (const deployId of fs
332
223
  .readFileSync(`./engine-private/deploy/dd.router`, 'utf8')
@@ -475,7 +366,10 @@ ${shellExec(`git log | grep Author: | sort -u`, { stdout: true }).split(`\n`).jo
475
366
  `${path}/.env`,
476
367
  fs
477
368
  .readFileSync(`${path}/.env`, 'utf8')
478
- .replace(`FIRST_SUPERUSER=admin@example.com`, `FIRST_SUPERUSER=development@underpost.net`)
369
+ .replace(
370
+ `FIRST_SUPERUSER=admin@example.com`,
371
+ `FIRST_SUPERUSER=${process.env.GITHUB_EMAIL || 'development@underpost.net'}`,
372
+ )
479
373
  .replace(`FIRST_SUPERUSER_PASSWORD=changethis`, `FIRST_SUPERUSER_PASSWORD=${password}`)
480
374
  .replace(`SECRET_KEY=changethis`, `SECRET_KEY=${password}`)
481
375
  .replace(`POSTGRES_DB=app`, `POSTGRES_DB=postgresdb`)
@@ -561,7 +455,7 @@ ${shellExec(`git log | grep Author: | sort -u`, { stdout: true }).split(`\n`).jo
561
455
  shellExec(
562
456
  `sudo kubectl create secret generic ${secretSelector}` +
563
457
  ` --from-file=SECRET_KEY=/home/dd/engine/engine-private/postgresql-password` +
564
- ` --from-literal=FIRST_SUPERUSER=development@underpost.net` +
458
+ ` --from-literal=FIRST_SUPERUSER=${process.env.GITHUB_EMAIL || 'development@underpost.net'}` +
565
459
  ` --from-file=FIRST_SUPERUSER_PASSWORD=/home/dd/engine/engine-private/postgresql-password` +
566
460
  ` --dry-run=client -o yaml | kubectl apply -f - -n ${namespace}`,
567
461
  );
@@ -798,6 +692,7 @@ nvidia/gpu-operator \
798
692
  for (const file of fs.readdirSync(`./engine-private/conf/${deployId}/`)) {
799
693
  const deployPackage = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/package.json`, 'utf8'));
800
694
  deployPackage.overrides = originPackageJson.overrides;
695
+ deployPackage.dependencies = originPackageJson.dependencies;
801
696
  fs.writeFileSync(
802
697
  `./engine-private/conf/${deployId}/package.json`,
803
698
  JSON.stringify(deployPackage, null, 4),
@@ -810,7 +705,7 @@ nvidia/gpu-operator \
810
705
  for (const path of Object.keys(confObj[host])) {
811
706
  }
812
707
  }
813
- fs.writeFileSync(filePath, JSON.stringify(confObj, null, 4), 'utf8');
708
+ // fs.writeFileSync(filePath, JSON.stringify(confObj, null, 4), 'utf8');
814
709
  logger.info(`sync-conf`, { deployId, file });
815
710
  }
816
711
  }
@@ -839,7 +734,7 @@ nvidia/gpu-operator \
839
734
  if (fs.existsSync(toPath)) fs.removeSync(toPath);
840
735
  shellExec(`node bin/deploy pw-conf ${scriptPath}`);
841
736
  shellExec(`kubectl delete deployment playwright-server --ignore-not-found`);
842
- while (Underpost.deploy.get('playwright-server').length > 0) {
737
+ while (Underpost.kubectl.get('playwright-server').length > 0) {
843
738
  logger.info(`Waiting for playwright-server deployment to be deleted...`);
844
739
  await timer(1000);
845
740
  }
@@ -847,13 +742,13 @@ nvidia/gpu-operator \
847
742
  const id = 'playwright-server';
848
743
  await Underpost.test.statusMonitor(id);
849
744
  const nameSpace = 'default';
850
- const [pod] = Underpost.deploy.get(id);
745
+ const [pod] = Underpost.kubectl.get(id);
851
746
  const podName = pod.NAME;
852
747
  shellExec(`kubectl logs -f ${podName} -n ${nameSpace}`, {
853
748
  async: true,
854
749
  });
855
750
  (async () => {
856
- while (!Underpost.deploy.existsContainerFile({ podName, path: fromPath })) {
751
+ while (!Underpost.kubectl.existsFile({ podName, path: fromPath })) {
857
752
  await timer(1000);
858
753
  logger.info(`Waiting for file ${fromPath} in pod ${podName}...`);
859
754
  }
@@ -874,7 +769,8 @@ nvidia/gpu-operator \
874
769
  }
875
770
 
876
771
  case 'dependabot': {
877
- shellExec(`git fetch origin`);
772
+ dotenv.config({ path: `./engine-private/conf/dd-cron/.env.production`, override: true });
773
+ shellExec(`git fetch origin --prune`);
878
774
 
879
775
  const { stdout: branchOutput } = shellExec(`git branch -r`, { silent: true });
880
776
  const dependabotBranches = branchOutput
@@ -890,20 +786,746 @@ nvidia/gpu-operator \
890
786
 
891
787
  logger.info('Found dependabot branches:', dependabotBranches);
892
788
 
893
- for (const branch of dependabotBranches) {
894
- logger.info(`Checking out branch: ${branch}`);
895
- shellExec(`git checkout -B ${branch} origin/${branch}`);
789
+ // Stash local changes to prevent checkout/merge conflicts
790
+ const stashResult = shellExec(`git stash --include-untracked`, { silent: true });
791
+ const hasStash = !stashResult.stdout.includes('No local changes to save');
792
+
793
+ // Checkout master
794
+ const checkoutResult = shellExec(`git checkout master`);
795
+ if (checkoutResult.code !== 0) {
796
+ logger.error('Failed to checkout master');
797
+ if (hasStash) shellExec(`git stash pop`);
798
+ break;
896
799
  }
897
800
 
898
- logger.info('Checking out master');
899
- shellExec(`git checkout master`);
801
+ // Pull latest master
802
+ shellExec(`git pull origin master`);
803
+
804
+ // Get repo URI from remote
805
+ const remoteUrl = shellExec(`git config --get remote.origin.url`, { stdout: true, silent: true }).trim();
806
+ const gitUri = remoteUrl.replace(/.*github\.com[:/]/, '').replace(/\.git$/, '');
807
+
808
+ const mergedBranches = [];
809
+ const failedBranches = [];
900
810
 
901
811
  for (const branch of dependabotBranches) {
902
812
  logger.info(`Merging branch: ${branch}`);
903
- shellExec(`git merge ${branch}`);
813
+ const mergeResult = shellExec(`git merge origin/${branch}`);
814
+ if (mergeResult.code === 0) {
815
+ const isAlreadyMerged = mergeResult.stdout && mergeResult.stdout.includes('Already up to date');
816
+ mergedBranches.push({ branch, isAlreadyMerged });
817
+ } else {
818
+ logger.error(`Failed to merge branch: ${branch}`);
819
+ shellExec(`git merge --abort`, { silent: true });
820
+ failedBranches.push(branch);
821
+ }
822
+ }
823
+
824
+ // Delete merged local and remote branches
825
+ for (const { branch, isAlreadyMerged } of mergedBranches) {
826
+ shellExec(`git branch -D ${branch}`, { silent: true });
827
+ // logger.info(`Deleting remote branch: ${branch}${isAlreadyMerged ? ' (already merged)' : ''}`);
828
+ // shellExec(`git push https://${process.env.GITHUB_TOKEN}@github.com/${gitUri}.git --delete ${branch}`, {
829
+ // disableLog: true,
830
+ // });
831
+ }
832
+
833
+ // Restore stashed changes
834
+ if (hasStash) shellExec(`git stash pop`);
835
+
836
+ logger.info(
837
+ 'Merged branches:',
838
+ mergedBranches.map((m) => m.branch),
839
+ );
840
+ if (failedBranches.length > 0) logger.warn('Failed branches:', failedBranches);
841
+ logger.info('Dependabot merge completed');
842
+ break;
843
+ }
844
+
845
+ case 'add-server-client': {
846
+ // node bin/deploy add-server-client <clientId> <deployId> <host> <path>
847
+ // Example: node bin/deploy add-server-client cecinasmarcelina dd-core cecinasmarcelina.com /
848
+ // Example: node bin/deploy add-server-client cecinasmarcelina dd-core www.cecinasmarcelina.com /
849
+ const clientId = process.argv[3];
850
+ const deployId = process.argv[4];
851
+ const rawHost = process.argv[5];
852
+ const path = process.argv[6];
853
+
854
+ // Normalize: strip www. prefix to get the base host
855
+ const baseHost = rawHost.startsWith('www.') ? rawHost.slice(4) : rawHost;
856
+ const mainHost = `www.${baseHost}`;
857
+ const redirectHost = baseHost;
858
+
859
+ loadConf('clean');
860
+ loadConf();
861
+ shellExec(`node bin/deploy clone-client default ${clientId} dd-default ${deployId}`);
862
+ // default.net has the main client entry → maps to www.<host> (main)
863
+ shellExec(`node bin/deploy clone-server default.net / ${mainHost} ${path} dd-default ${deployId} ${clientId}`);
864
+ // www.default.net has the redirect/empty entry → maps to <host> (redirect)
865
+ shellExec(`node bin/deploy clone-server www.default.net / ${redirectHost} ${path} dd-default ${deployId}`);
866
+ fs.removeSync(`./engine-private/conf/dd-default`);
867
+ break;
868
+ }
869
+
870
+ case 'clone-client': {
871
+ // node bin/deploy clone-client <fromClientId> <toClientId> <fromDeployId> [toDeployId]
872
+ // Example: node bin/deploy clone-client dogmadual mynewclient dd-core
873
+ // Example: node bin/deploy clone-client dogmadual mynewclient dd-core dd-other
874
+ //
875
+ // Clones a client configuration and its src/ files with a new name.
876
+ // If toDeployId is omitted, it defaults to fromDeployId (same deploy).
877
+ // Steps:
878
+ // - conf.client.json: clones the fromClientId entry as toClientId (renames identifiers)
879
+ // - conf.ssr.json: clones the SSR entry under the new PascalCase name
880
+ // - conf.server.json + dev variants: duplicates paths referencing fromClientId for toClientId
881
+ // - src/client/components/<fromClientId>/ → src/client/components/<toClientId>/ (renames identifiers)
882
+ // - src/client/services/<fromClientId>/ → src/client/services/<toClientId>/ (renames identifiers)
883
+ // - src/client/ssr/head/<From>Scripts.js → <To>Scripts.js
884
+ // - src/client/ssr/body/<From>*.js → <To>*.js (SplashScreen, etc.)
885
+ // - src/client/ssr/mailer/<From>*.js → <To>*.js (VerifyEmail, RecoverEmail, etc.)
886
+ // - src/client/<From>.index.js → <To>.index.js
887
+ // - src/client/public/<fromClientId>/ → src/client/public/<toClientId>/
888
+
889
+ const fromClientId = process.argv[3];
890
+ const toClientId = process.argv[4];
891
+ const fromDeployId = process.argv[5];
892
+ const toDeployId = process.argv[6] || fromDeployId;
893
+
894
+ if (!fromClientId || !toClientId || !fromDeployId) {
895
+ logger.error('Usage: node bin/deploy clone-client <fromClientId> <toClientId> <fromDeployId> [toDeployId]');
896
+ logger.error('Example: node bin/deploy clone-client dogmadual mynewclient dd-core');
897
+ process.exit(1);
898
+ }
899
+
900
+ const fromCapName = getCapVariableName(fromClientId);
901
+ const toCapName = getCapVariableName(toClientId);
902
+
903
+ const confFromFolder = `./engine-private/conf/${fromDeployId}`;
904
+ const confToFolder = `./engine-private/conf/${toDeployId}`;
905
+
906
+ if (!fs.existsSync(confFromFolder)) {
907
+ logger.error(`Source config folder not found: ${confFromFolder}`);
908
+ process.exit(1);
909
+ }
910
+ if (!fs.existsSync(confToFolder)) {
911
+ logger.error(`Target config folder not found: ${confToFolder}`);
912
+ process.exit(1);
913
+ }
914
+
915
+ const formattedSrc = (src) => src.replaceAll(fromCapName, toCapName).replaceAll(fromClientId, toClientId);
916
+
917
+ // 1. Clone conf.client.json entry
918
+ {
919
+ const clientConf = JSON.parse(fs.readFileSync(`${confToFolder}/conf.client.json`, 'utf8'));
920
+ if (clientConf[toClientId]) {
921
+ logger.warn(`conf.client.json: "${toClientId}" already exists, skipping`);
922
+ } else {
923
+ const fromClientConf = JSON.parse(fs.readFileSync(`${confFromFolder}/conf.client.json`, 'utf8'));
924
+ if (!fromClientConf[fromClientId]) {
925
+ logger.error(`conf.client.json: "${fromClientId}" not found in ${confFromFolder}`);
926
+ process.exit(1);
927
+ }
928
+ clientConf[toClientId] = JSON.parse(formattedSrc(JSON.stringify(fromClientConf[fromClientId])));
929
+ fs.writeFileSync(`${confToFolder}/conf.client.json`, JSON.stringify(clientConf, null, 4), 'utf8');
930
+ logger.info(`conf.client.json: cloned "${fromClientId}" → "${toClientId}"`);
931
+ }
932
+ }
933
+
934
+ // 2. Clone conf.ssr.json entry
935
+ {
936
+ const ssrConf = JSON.parse(fs.readFileSync(`${confToFolder}/conf.ssr.json`, 'utf8'));
937
+ if (ssrConf[toCapName]) {
938
+ logger.warn(`conf.ssr.json: "${toCapName}" already exists, skipping`);
939
+ } else {
940
+ const fromSsrConf = JSON.parse(fs.readFileSync(`${confFromFolder}/conf.ssr.json`, 'utf8'));
941
+ if (fromSsrConf[fromCapName]) {
942
+ ssrConf[toCapName] = JSON.parse(formattedSrc(JSON.stringify(fromSsrConf[fromCapName])));
943
+ fs.writeFileSync(`${confToFolder}/conf.ssr.json`, JSON.stringify(ssrConf, null, 4), 'utf8');
944
+ logger.info(`conf.ssr.json: cloned "${fromCapName}" → "${toCapName}"`);
945
+ } else {
946
+ logger.warn(`conf.ssr.json: "${fromCapName}" not found in ${confFromFolder}, skipping`);
947
+ }
948
+ }
949
+ }
950
+
951
+ // 3. Clone server conf entries (conf.server.json + dev variants)
952
+ const cloneServerConfEntries = (filePath, label) => {
953
+ if (!fs.existsSync(filePath)) return;
954
+ const serverConf = JSON.parse(fs.readFileSync(filePath, 'utf8'));
955
+ let count = 0;
956
+ for (const host of Object.keys(serverConf)) {
957
+ for (const path of Object.keys(serverConf[host])) {
958
+ const entry = serverConf[host][path];
959
+ if (entry.client === fromClientId) {
960
+ const newPath = path === '/' ? `/${toClientId}` : path.replace(fromClientId, toClientId);
961
+ if (!serverConf[host][newPath]) {
962
+ serverConf[host][newPath] = JSON.parse(formattedSrc(JSON.stringify(entry)));
963
+ count++;
964
+ }
965
+ }
966
+ }
967
+ }
968
+ if (count > 0) {
969
+ fs.writeFileSync(filePath, JSON.stringify(serverConf, null, 4), 'utf8');
970
+ }
971
+ logger.info(`${label}: cloned ${count} server path(s) for "${toClientId}"`);
972
+ };
973
+
974
+ cloneServerConfEntries(`${confToFolder}/conf.server.json`, 'conf.server.json');
975
+ const devFiles = fs
976
+ .readdirSync(confToFolder)
977
+ .filter((f) => f.startsWith('conf.server.dev.') && f.endsWith('.json'));
978
+ for (const devFile of devFiles) {
979
+ cloneServerConfEntries(`${confToFolder}/${devFile}`, devFile);
980
+ }
981
+
982
+ // 4. Clone src/client/components/<fromClientId>/ → <toClientId>/
983
+ const srcFromFolder = `./src/client/components/${fromClientId}`;
984
+ const srcToFolder = `./src/client/components/${toClientId}`;
985
+
986
+ if (!fs.existsSync(srcFromFolder)) {
987
+ logger.warn(`Source component folder not found: ${srcFromFolder}, skipping src clone`);
988
+ } else if (fs.existsSync(srcToFolder)) {
989
+ logger.warn(`Target component folder already exists: ${srcToFolder}, skipping src clone`);
990
+ } else {
991
+ fs.mkdirSync(srcToFolder, { recursive: true });
992
+ const files = fs.readdirSync(srcFromFolder, { recursive: true });
993
+ for (const relativePath of files) {
994
+ const fromFilePath = `${srcFromFolder}/${relativePath}`;
995
+ if (fs.statSync(fromFilePath).isDirectory()) {
996
+ fs.mkdirSync(`${srcToFolder}/${formattedSrc(relativePath)}`, { recursive: true });
997
+ continue;
998
+ }
999
+ const toFileName = formattedSrc(relativePath);
1000
+ fs.writeFileSync(`${srcToFolder}/${toFileName}`, formattedSrc(fs.readFileSync(fromFilePath, 'utf8')), 'utf8');
1001
+ }
1002
+ logger.info(`src/client/components: cloned ${srcFromFolder} → ${srcToFolder} (${files.length} files)`);
1003
+ }
1004
+
1005
+ // 5. Clone src/client/services/<fromClientId>/ → <toClientId>/
1006
+ {
1007
+ const svcFromFolder = `./src/client/services/${fromClientId}`;
1008
+ const svcToFolder = `./src/client/services/${toClientId}`;
1009
+ if (!fs.existsSync(svcFromFolder)) {
1010
+ logger.warn(`Source services folder not found: ${svcFromFolder}, skipping`);
1011
+ } else if (fs.existsSync(svcToFolder)) {
1012
+ logger.warn(`Target services folder already exists: ${svcToFolder}, skipping`);
1013
+ } else {
1014
+ fs.mkdirSync(svcToFolder, { recursive: true });
1015
+ const svcFiles = fs.readdirSync(svcFromFolder, { recursive: true });
1016
+ for (const relativePath of svcFiles) {
1017
+ const fromFilePath = `${svcFromFolder}/${relativePath}`;
1018
+ if (fs.statSync(fromFilePath).isDirectory()) {
1019
+ fs.mkdirSync(`${svcToFolder}/${formattedSrc(relativePath)}`, { recursive: true });
1020
+ continue;
1021
+ }
1022
+ const toFileName = formattedSrc(relativePath);
1023
+ fs.writeFileSync(
1024
+ `${svcToFolder}/${toFileName}`,
1025
+ formattedSrc(fs.readFileSync(fromFilePath, 'utf8')),
1026
+ 'utf8',
1027
+ );
1028
+ }
1029
+ logger.info(`src/client/services: cloned ${svcFromFolder} → ${svcToFolder} (${svcFiles.length} files)`);
1030
+ }
1031
+ }
1032
+
1033
+ // 6. Clone SSR head scripts
1034
+ const fromScriptsPath = `./src/client/ssr/head/${fromCapName}Scripts.js`;
1035
+ const toScriptsPath = `./src/client/ssr/head/${toCapName}Scripts.js`;
1036
+ if (fs.existsSync(fromScriptsPath) && !fs.existsSync(toScriptsPath)) {
1037
+ fs.writeFileSync(toScriptsPath, formattedSrc(fs.readFileSync(fromScriptsPath, 'utf8')), 'utf8');
1038
+ logger.info(`ssr/head: cloned ${fromCapName}Scripts.js → ${toCapName}Scripts.js`);
1039
+ } else if (fs.existsSync(toScriptsPath)) {
1040
+ logger.warn(`ssr/head: ${toCapName}Scripts.js already exists, skipping`);
1041
+ }
1042
+
1043
+ // 7. Clone SSR body files
1044
+ {
1045
+ const ssrConf = JSON.parse(fs.readFileSync(`${confToFolder}/conf.ssr.json`, 'utf8'));
1046
+ const toSsr = ssrConf[toCapName];
1047
+ if (toSsr && Array.isArray(toSsr.body)) {
1048
+ for (const bodyName of toSsr.body) {
1049
+ if (!bodyName.startsWith(toCapName)) continue;
1050
+ const fromBodyName = bodyName.replace(toCapName, fromCapName);
1051
+ const fromBodyPath = `./src/client/ssr/body/${fromBodyName}.js`;
1052
+ const toBodyPath = `./src/client/ssr/body/${bodyName}.js`;
1053
+ if (fs.existsSync(fromBodyPath) && !fs.existsSync(toBodyPath)) {
1054
+ fs.writeFileSync(toBodyPath, formattedSrc(fs.readFileSync(fromBodyPath, 'utf8')), 'utf8');
1055
+ logger.info(`ssr/body: cloned ${fromBodyName}.js → ${bodyName}.js`);
1056
+ } else if (fs.existsSync(toBodyPath)) {
1057
+ logger.warn(`ssr/body: ${bodyName}.js already exists, skipping`);
1058
+ }
1059
+ }
1060
+ }
1061
+ }
1062
+
1063
+ // 8. Clone SSR mailer files
1064
+ {
1065
+ const ssrConf = JSON.parse(fs.readFileSync(`${confToFolder}/conf.ssr.json`, 'utf8'));
1066
+ const toSsr = ssrConf[toCapName];
1067
+ if (toSsr && toSsr.mailer && typeof toSsr.mailer === 'object') {
1068
+ for (const mailerName of Object.values(toSsr.mailer)) {
1069
+ if (!mailerName.startsWith(toCapName)) continue;
1070
+ const fromMailerName = mailerName.replace(toCapName, fromCapName);
1071
+ const fromMailerPath = `./src/client/ssr/mailer/${fromMailerName}.js`;
1072
+ const toMailerPath = `./src/client/ssr/mailer/${mailerName}.js`;
1073
+ if (fs.existsSync(fromMailerPath) && !fs.existsSync(toMailerPath)) {
1074
+ fs.writeFileSync(toMailerPath, formattedSrc(fs.readFileSync(fromMailerPath, 'utf8')), 'utf8');
1075
+ logger.info(`ssr/mailer: cloned ${fromMailerName}.js → ${mailerName}.js`);
1076
+ } else if (fs.existsSync(toMailerPath)) {
1077
+ logger.warn(`ssr/mailer: ${mailerName}.js already exists, skipping`);
1078
+ }
1079
+ }
1080
+ }
1081
+ }
1082
+
1083
+ // 9. Clone client index
1084
+ const fromIndexPath = `./src/client/${fromCapName}.index.js`;
1085
+ const toIndexPath = `./src/client/${toCapName}.index.js`;
1086
+ if (fs.existsSync(fromIndexPath) && !fs.existsSync(toIndexPath)) {
1087
+ fs.writeFileSync(toIndexPath, formattedSrc(fs.readFileSync(fromIndexPath, 'utf8')), 'utf8');
1088
+ logger.info(`client: cloned ${fromCapName}.index.js → ${toCapName}.index.js`);
1089
+ } else if (fs.existsSync(toIndexPath)) {
1090
+ logger.warn(`client: ${toCapName}.index.js already exists, skipping`);
1091
+ }
1092
+
1093
+ // 10. Clone public assets
1094
+ const fromPublicPath = `./src/client/public/${fromClientId}`;
1095
+ const toPublicPath = `./src/client/public/${toClientId}`;
1096
+ if (fs.existsSync(fromPublicPath) && !fs.existsSync(toPublicPath)) {
1097
+ fs.copySync(fromPublicPath, toPublicPath);
1098
+ logger.info(`public: cloned ${fromPublicPath} → ${toPublicPath}`);
1099
+ } else if (fs.existsSync(toPublicPath)) {
1100
+ logger.warn(`public: ${toPublicPath} already exists, skipping`);
1101
+ }
1102
+
1103
+ // 11. Rebuild default conf
1104
+ shellExec(`node bin new --default-conf --deploy-id ${toDeployId}`);
1105
+ logger.info(`Rebuilt default conf for ${toDeployId}`);
1106
+
1107
+ break;
1108
+ }
1109
+
1110
+ case 'clone-server': {
1111
+ // node bin/deploy clone-server <fromHost> <fromPath> <toHost> <toPath> <fromDeployId> <toDeployId>
1112
+ // Example: node bin/deploy clone-server www.dogmadual.com / www.newsite.com / dd-core dd-other
1113
+ // Example: node bin/deploy clone-server www.nexodev.org / www.newdomain.org /app dd-core dd-core
1114
+ //
1115
+ // Clones a specific server host/path entry from one deploy's conf to another,
1116
+ // optionally renaming the host and path in the target.
1117
+ // - conf.server.json: copies the fromHost/fromPath entry to toHost/toPath
1118
+ // - conf.server.dev.*.json: same treatment for all dev variants
1119
+
1120
+ const fromHost = process.argv[3];
1121
+ const fromPath = process.argv[4];
1122
+ const toHost = process.argv[5];
1123
+ const toPath = process.argv[6];
1124
+ const fromDeployId = process.argv[7];
1125
+ const toDeployId = process.argv[8];
1126
+ const overrideClientId = process.argv[9];
1127
+
1128
+ if (!fromHost || !fromPath || !toHost || !toPath || !fromDeployId || !toDeployId) {
1129
+ logger.error(
1130
+ 'Usage: node bin/deploy clone-server <fromHost> <fromPath> <toHost> <toPath> <fromDeployId> <toDeployId> [clientId]',
1131
+ );
1132
+ logger.error(
1133
+ 'Example: node bin/deploy clone-server www.dogmadual.com / www.newsite.com / dd-core dd-other newsite',
1134
+ );
1135
+ process.exit(1);
1136
+ }
1137
+
1138
+ const confFromFolder = `./engine-private/conf/${fromDeployId}`;
1139
+ const confToFolder = `./engine-private/conf/${toDeployId}`;
1140
+
1141
+ if (!fs.existsSync(confFromFolder)) {
1142
+ logger.error(`Source config folder not found: ${confFromFolder}`);
1143
+ process.exit(1);
1144
+ }
1145
+ if (!fs.existsSync(confToFolder)) {
1146
+ logger.error(`Target config folder not found: ${confToFolder}`);
1147
+ process.exit(1);
904
1148
  }
905
1149
 
906
- logger.info('All dependabot branches merged into master');
1150
+ const cloneServerEntry = (fromFilePath, toFilePath, label) => {
1151
+ if (!fs.existsSync(fromFilePath)) {
1152
+ logger.warn(`${label}: source file not found, skipping`);
1153
+ return;
1154
+ }
1155
+ const fromServerConf = JSON.parse(fs.readFileSync(fromFilePath, 'utf8'));
1156
+ if (!fromServerConf[fromHost] || !fromServerConf[fromHost][fromPath]) {
1157
+ logger.warn(`${label}: "${fromHost}" "${fromPath}" not found in source, skipping`);
1158
+ return;
1159
+ }
1160
+
1161
+ const toServerConf = fs.existsSync(toFilePath) ? JSON.parse(fs.readFileSync(toFilePath, 'utf8')) : {};
1162
+
1163
+ if (!toServerConf[toHost]) toServerConf[toHost] = {};
1164
+
1165
+ if (toServerConf[toHost][toPath]) {
1166
+ logger.warn(`${label}: "${toHost}" "${toPath}" already exists in target, skipping`);
1167
+ return;
1168
+ }
1169
+
1170
+ toServerConf[toHost][toPath] = JSON.parse(JSON.stringify(fromServerConf[fromHost][fromPath]));
1171
+ // Override client field if --client= is provided
1172
+ const entry = toServerConf[toHost][toPath];
1173
+ if (overrideClientId && entry.client) {
1174
+ entry.client = overrideClientId;
1175
+ }
1176
+ // Update db.name to use the client-specific env variable
1177
+ if (entry.client && entry.db && entry.db.name) {
1178
+ const upperClientId = entry.client.replaceAll('-', '_').toUpperCase();
1179
+ entry.db.name = `env:DB_NAME_${upperClientId}`;
1180
+ }
1181
+ fs.writeFileSync(toFilePath, JSON.stringify(toServerConf, null, 4), 'utf8');
1182
+ logger.info(
1183
+ `${label}: cloned "${fromHost}" "${fromPath}" → "${toHost}" "${toPath}" (${fromDeployId} → ${toDeployId})`,
1184
+ );
1185
+ };
1186
+
1187
+ // 1. Main conf.server.json
1188
+ cloneServerEntry(`${confFromFolder}/conf.server.json`, `${confToFolder}/conf.server.json`, 'conf.server.json');
1189
+
1190
+ // 2. Dev variants (clone from source dev files)
1191
+ const devFiles = fs
1192
+ .readdirSync(confFromFolder)
1193
+ .filter((f) => f.startsWith('conf.server.dev.') && f.endsWith('.json'));
1194
+ for (const devFile of devFiles) {
1195
+ cloneServerEntry(`${confFromFolder}/${devFile}`, `${confToFolder}/${devFile}`, devFile);
1196
+ }
1197
+
1198
+ // 3. Create individual dev file for the new entry (conf.server.dev.<clientId>.json)
1199
+ {
1200
+ const mainToPath = `${confToFolder}/conf.server.json`;
1201
+ if (fs.existsSync(mainToPath)) {
1202
+ const toServerConf = JSON.parse(fs.readFileSync(mainToPath, 'utf8'));
1203
+ const entry = toServerConf[toHost] && toServerConf[toHost][toPath];
1204
+ if (entry && entry.client) {
1205
+ const devFileName = `conf.server.dev.${entry.client}.json`;
1206
+ const devFilePath = `${confToFolder}/${devFileName}`;
1207
+ if (!fs.existsSync(devFilePath)) {
1208
+ const devConf = { [toHost]: { [toPath]: entry } };
1209
+ fs.writeFileSync(devFilePath, JSON.stringify(devConf, null, 4), 'utf8');
1210
+ logger.info(`${devFileName}: created dev file for "${toHost}" "${toPath}"`);
1211
+ } else {
1212
+ logger.info(`${devFileName}: already exists, skipping creation`);
1213
+ }
1214
+ }
1215
+ }
1216
+ }
1217
+
1218
+ // 4. Add DB_NAME_<UPPER> env variable to target deploy .env.* files
1219
+ {
1220
+ const mainFromPath = `${confFromFolder}/conf.server.json`;
1221
+ if (fs.existsSync(mainFromPath)) {
1222
+ const fromServerConf = JSON.parse(fs.readFileSync(mainFromPath, 'utf8'));
1223
+ const sourceEntry = fromServerConf[fromHost] && fromServerConf[fromHost][fromPath];
1224
+ if (sourceEntry && sourceEntry.client) {
1225
+ const clientId = overrideClientId || sourceEntry.client;
1226
+ const upperClientId = clientId.replaceAll('-', '_').toUpperCase();
1227
+ const envKey = `DB_NAME_${upperClientId}`;
1228
+ const envValue = `${envKey}=${clientId}`;
1229
+ for (const envFile of ['.env.production', '.env.development', '.env.test']) {
1230
+ const envPath = `${confToFolder}/${envFile}`;
1231
+ if (fs.existsSync(envPath)) {
1232
+ const envContent = fs.readFileSync(envPath, 'utf8');
1233
+ if (!envContent.includes(envKey)) {
1234
+ fs.writeFileSync(envPath, envContent.trimEnd() + '\n' + envValue + '\n', 'utf8');
1235
+ logger.info(`${envFile}: added ${envValue}`);
1236
+ } else {
1237
+ logger.info(`${envFile}: ${envKey} already exists, skipping`);
1238
+ }
1239
+ }
1240
+ }
1241
+ }
1242
+ }
1243
+ }
1244
+
1245
+ // 5. Rebuild default conf
1246
+ shellExec(`node bin new --default-conf --deploy-id ${toDeployId}`);
1247
+ logger.info(`Rebuilt default conf for ${toDeployId}`);
1248
+
1249
+ break;
1250
+ }
1251
+
1252
+ case 'add-api': {
1253
+ // node bin/deploy add-api <apiId> <deployId> [clientId] [host]
1254
+ // Example: node bin/deploy add-api cyberia-dialogue dd-cyberia cyberia-portal
1255
+ // Example: node bin/deploy add-api cyberia-dialogue dd-cyberia cyberia-portal underpost.net
1256
+ //
1257
+ // Adds an API to server conf files (main + dev variants) and client conf.
1258
+ // - conf.server.json: adds apiId to every host/path that has an `apis` array
1259
+ // (or only to the specified host if [host] is provided)
1260
+ // - conf.server.dev.*.json: same treatment for all dev variants
1261
+ // - conf.client.json: adds apiId to the specified clientId's `services` array
1262
+ // Idempotent: skips if the API is already present.
1263
+
1264
+ const apiId = process.argv[3];
1265
+ const deployId = process.argv[4];
1266
+ const clientId = process.argv[5];
1267
+ const targetHost = process.argv[6];
1268
+
1269
+ if (!apiId || !deployId) {
1270
+ logger.error('Usage: node bin/deploy add-api <apiId> <deployId> [clientId] [host]');
1271
+ logger.error('Example: node bin/deploy add-api cyberia-dialogue dd-cyberia cyberia-portal');
1272
+ logger.error('Example: node bin/deploy add-api cyberia-dialogue dd-cyberia cyberia-portal underpost.net');
1273
+ process.exit(1);
1274
+ }
1275
+
1276
+ const confFolder = `./engine-private/conf/${deployId}`;
1277
+ if (!fs.existsSync(confFolder)) {
1278
+ logger.error(`Config folder not found: ${confFolder}`);
1279
+ process.exit(1);
1280
+ }
1281
+
1282
+ // Helper: add apiId to apis[] arrays in a server conf file (idempotent)
1283
+ // When targetHost is set, only entries under that host are modified.
1284
+ const addApiToServerConf = (filePath) => {
1285
+ if (!fs.existsSync(filePath)) return 0;
1286
+ const conf = JSON.parse(fs.readFileSync(filePath, 'utf8'));
1287
+ let count = 0;
1288
+ const hosts = targetHost ? (conf[targetHost] ? [targetHost] : []) : Object.keys(conf);
1289
+ for (const host of hosts) {
1290
+ for (const path of Object.keys(conf[host])) {
1291
+ const entry = conf[host][path];
1292
+ if (Array.isArray(entry.apis) && entry.apis.length > 0 && !entry.apis.includes(apiId)) {
1293
+ entry.apis.push(apiId);
1294
+ count++;
1295
+ }
1296
+ }
1297
+ }
1298
+ if (count > 0) {
1299
+ fs.writeFileSync(filePath, JSON.stringify(conf, null, 4), 'utf8');
1300
+ }
1301
+ return count;
1302
+ };
1303
+
1304
+ // 1. Main conf.server.json
1305
+ const mainPath = `${confFolder}/conf.server.json`;
1306
+ const mainCount = addApiToServerConf(mainPath);
1307
+ logger.info(`conf.server.json: added "${apiId}" to ${mainCount} path(s)`);
1308
+
1309
+ // 2. All dev variants: conf.server.dev.*.json
1310
+ const devFiles = fs
1311
+ .readdirSync(confFolder)
1312
+ .filter((f) => f.startsWith('conf.server.dev.') && f.endsWith('.json'));
1313
+ for (const devFile of devFiles) {
1314
+ const devPath = `${confFolder}/${devFile}`;
1315
+ const devCount = addApiToServerConf(devPath);
1316
+ logger.info(`${devFile}: added "${apiId}" to ${devCount} path(s)`);
1317
+ }
1318
+
1319
+ // Helper: add apiId to a client conf file's clientId services (idempotent)
1320
+ const addApiToClientConf = (filePath, label) => {
1321
+ if (!clientId || !fs.existsSync(filePath)) return;
1322
+ const confClient = JSON.parse(fs.readFileSync(filePath, 'utf8'));
1323
+ if (confClient[clientId] && Array.isArray(confClient[clientId].services)) {
1324
+ if (!confClient[clientId].services.includes(apiId)) {
1325
+ confClient[clientId].services.push(apiId);
1326
+ fs.writeFileSync(filePath, JSON.stringify(confClient, null, 4), 'utf8');
1327
+ logger.info(`${label}: added "${apiId}" to "${clientId}" services`);
1328
+ } else {
1329
+ logger.info(`${label}: "${apiId}" already in "${clientId}" services`);
1330
+ }
1331
+ } else {
1332
+ logger.warn(`${label}: clientId "${clientId}" not found or has no services array`);
1333
+ }
1334
+ };
1335
+
1336
+ // 3. Client conf.client.json
1337
+ addApiToClientConf(`${confFolder}/conf.client.json`, 'conf.client.json');
1338
+
1339
+ // 4. Replicas: engine-private/replica/<deployId>-*
1340
+ const replicaBase = './engine-private/replica';
1341
+ if (fs.existsSync(replicaBase)) {
1342
+ const replicaDirs = fs
1343
+ .readdirSync(replicaBase)
1344
+ .filter((d) => d.startsWith(`${deployId}-`) && fs.statSync(`${replicaBase}/${d}`).isDirectory());
1345
+ for (const replicaDir of replicaDirs) {
1346
+ const replicaFolder = `${replicaBase}/${replicaDir}`;
1347
+ // Server conf
1348
+ const rMainCount = addApiToServerConf(`${replicaFolder}/conf.server.json`);
1349
+ logger.info(`replica/${replicaDir}/conf.server.json: added "${apiId}" to ${rMainCount} path(s)`);
1350
+ // Dev variants
1351
+ const rDevFiles = fs
1352
+ .readdirSync(replicaFolder)
1353
+ .filter((f) => f.startsWith('conf.server.dev.') && f.endsWith('.json'));
1354
+ for (const rDevFile of rDevFiles) {
1355
+ const rDevCount = addApiToServerConf(`${replicaFolder}/${rDevFile}`);
1356
+ logger.info(`replica/${replicaDir}/${rDevFile}: added "${apiId}" to ${rDevCount} path(s)`);
1357
+ }
1358
+ // Client conf
1359
+ addApiToClientConf(`${replicaFolder}/conf.client.json`, `replica/${replicaDir}/conf.client.json`);
1360
+ }
1361
+ }
1362
+
1363
+ // 5. Rebuild default conf
1364
+ shellExec(`node bin new --default-conf --deploy-id ${deployId}`);
1365
+ logger.info(`Rebuilt default conf for ${deployId}`);
1366
+
1367
+ break;
1368
+ }
1369
+
1370
+ case 'add-component': {
1371
+ // node bin/deploy add-component <componentId> [deployId] [clientId] [submoduleId]
1372
+ // Example: node bin/deploy add-component ColorPaletteElement dd-cyberia cyberia-portal core
1373
+ // Example: node bin/deploy add-component ColorPaletteElement dd-cyberia
1374
+ // Example: node bin/deploy add-component ColorPaletteElement
1375
+ // Adds a component to client conf.main and related replicas.
1376
+ // - if deployId is omitted: iterates deploys in engine-private/deploy/dd.router
1377
+ // - if clientId is omitted: iterates all clients in each conf.client.json
1378
+ // - if submoduleId is omitted: iterates all available components.<submoduleId> arrays per client
1379
+ // - validates src/client/components/<submoduleId>/<componentId>.js exists before adding
1380
+ // Idempotent: skips if the component is already present.
1381
+
1382
+ const componentId = process.argv[3];
1383
+ const deployIdArg = process.argv[4];
1384
+ const clientIdArg = process.argv[5];
1385
+ const submoduleIdArg = process.argv[6];
1386
+
1387
+ if (!componentId) {
1388
+ logger.error('Usage: node bin/deploy add-component <componentId> [deployId] [clientId] [submoduleId]');
1389
+ logger.error('Example: node bin/deploy add-component ColorPaletteElement dd-cyberia cyberia-portal core');
1390
+ logger.error('Example: node bin/deploy add-component ColorPaletteElement');
1391
+ process.exit(1);
1392
+ }
1393
+
1394
+ const deployIds = deployIdArg
1395
+ ? [deployIdArg]
1396
+ : fs
1397
+ .readFileSync(`./engine-private/deploy/dd.router`, 'utf8')
1398
+ .split(',')
1399
+ .map((d) => d.trim())
1400
+ .filter(Boolean);
1401
+
1402
+ const addComponentToClientConf = ({ filePath, label, targetClientId, targetSubmoduleId }) => {
1403
+ if (!fs.existsSync(filePath)) return { added: 0, checked: 0, hasComponentFile: false };
1404
+
1405
+ const confClient = JSON.parse(fs.readFileSync(filePath, 'utf8'));
1406
+ const clientIds = targetClientId ? [targetClientId] : Object.keys(confClient);
1407
+ let added = 0;
1408
+ let checked = 0;
1409
+ let hasComponentFile = false;
1410
+ let changed = false;
1411
+
1412
+ for (const currentClientId of clientIds) {
1413
+ const clientConf = confClient[currentClientId];
1414
+ if (!clientConf) {
1415
+ logger.warn(`${label}: clientId "${currentClientId}" not found`);
1416
+ continue;
1417
+ }
1418
+
1419
+ const components = clientConf.components || {};
1420
+ const submoduleIds = targetSubmoduleId
1421
+ ? [targetSubmoduleId]
1422
+ : Object.keys(components).filter((key) => Array.isArray(components[key]));
1423
+
1424
+ for (const currentSubmoduleId of submoduleIds) {
1425
+ checked++;
1426
+
1427
+ if (!Array.isArray(components[currentSubmoduleId])) {
1428
+ logger.warn(`${label}: clientId "${currentClientId}" has no components.${currentSubmoduleId} array`);
1429
+ continue;
1430
+ }
1431
+
1432
+ const componentPath = `./src/client/components/${currentSubmoduleId}/${componentId}.js`;
1433
+ if (!fs.existsSync(componentPath)) {
1434
+ continue;
1435
+ }
1436
+ hasComponentFile = true;
1437
+
1438
+ if (components[currentSubmoduleId].includes(componentId)) {
1439
+ logger.info(
1440
+ `${label}: "${componentId}" already in "${currentClientId}" components.${currentSubmoduleId}`,
1441
+ );
1442
+ continue;
1443
+ }
1444
+
1445
+ components[currentSubmoduleId].push(componentId);
1446
+ changed = true;
1447
+ added++;
1448
+ logger.info(`${label}: added "${componentId}" to "${currentClientId}" components.${currentSubmoduleId}`);
1449
+ }
1450
+ }
1451
+
1452
+ if (changed) {
1453
+ fs.writeFileSync(filePath, JSON.stringify(confClient, null, 4), 'utf8');
1454
+ }
1455
+
1456
+ return { added, checked, hasComponentFile };
1457
+ };
1458
+
1459
+ let totalAdded = 0;
1460
+ let totalChecked = 0;
1461
+ let hasAnyComponentFile = false;
1462
+ const processedDeployIds = [];
1463
+
1464
+ for (const deployId of deployIds) {
1465
+ const confFolder = `./engine-private/conf/${deployId}`;
1466
+ if (!fs.existsSync(confFolder)) {
1467
+ logger.warn(`Config folder not found: ${confFolder}`);
1468
+ continue;
1469
+ }
1470
+
1471
+ processedDeployIds.push(deployId);
1472
+
1473
+ const mainResult = addComponentToClientConf({
1474
+ filePath: `${confFolder}/conf.client.json`,
1475
+ label: `conf/${deployId}/conf.client.json`,
1476
+ targetClientId: clientIdArg,
1477
+ targetSubmoduleId: submoduleIdArg,
1478
+ });
1479
+ totalAdded += mainResult.added;
1480
+ totalChecked += mainResult.checked;
1481
+ hasAnyComponentFile = hasAnyComponentFile || mainResult.hasComponentFile;
1482
+
1483
+ const replicaBase = './engine-private/replica';
1484
+ if (fs.existsSync(replicaBase)) {
1485
+ const replicaDirs = fs
1486
+ .readdirSync(replicaBase)
1487
+ .filter((d) => d.startsWith(`${deployId}-`) && fs.statSync(`${replicaBase}/${d}`).isDirectory());
1488
+
1489
+ for (const replicaDir of replicaDirs) {
1490
+ const replicaResult = addComponentToClientConf({
1491
+ filePath: `${replicaBase}/${replicaDir}/conf.client.json`,
1492
+ label: `replica/${replicaDir}/conf.client.json`,
1493
+ targetClientId: clientIdArg,
1494
+ targetSubmoduleId: submoduleIdArg,
1495
+ });
1496
+ totalAdded += replicaResult.added;
1497
+ totalChecked += replicaResult.checked;
1498
+ hasAnyComponentFile = hasAnyComponentFile || replicaResult.hasComponentFile;
1499
+ }
1500
+ }
1501
+ }
1502
+
1503
+ if (totalChecked === 0) {
1504
+ logger.error('No eligible target entries found to add the component');
1505
+ process.exit(1);
1506
+ }
1507
+
1508
+ if (!hasAnyComponentFile) {
1509
+ if (submoduleIdArg) {
1510
+ logger.error(`Component file not found: ./src/client/components/${submoduleIdArg}/${componentId}.js`);
1511
+ } else {
1512
+ logger.error(`Component file not found in available submodules for "${componentId}"`);
1513
+ }
1514
+ process.exit(1);
1515
+ }
1516
+
1517
+ for (const deployId of processedDeployIds) {
1518
+ shellExec(`node bin new --default-conf --deploy-id ${deployId}`);
1519
+ logger.info(`Rebuilt default conf for ${deployId}`);
1520
+ }
1521
+
1522
+ logger.info(`add-component summary`, {
1523
+ componentId,
1524
+ deployIds: processedDeployIds,
1525
+ added: totalAdded,
1526
+ checked: totalChecked,
1527
+ });
1528
+
907
1529
  break;
908
1530
  }
909
1531
  }