cyberia 3.0.3 → 3.2.5

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 (296) hide show
  1. package/{.env.production → .env.example} +20 -4
  2. package/.github/workflows/engine-cyberia.cd.yml +43 -10
  3. package/.github/workflows/engine-cyberia.ci.yml +48 -26
  4. package/.github/workflows/ghpkg.ci.yml +5 -5
  5. package/.github/workflows/gitlab.ci.yml +1 -1
  6. package/.github/workflows/hardhat.ci.yml +82 -0
  7. package/.github/workflows/npmpkg.ci.yml +60 -14
  8. package/.github/workflows/publish.ci.yml +26 -7
  9. package/.github/workflows/publish.cyberia.ci.yml +5 -5
  10. package/.github/workflows/pwa-microservices-template-page.cd.yml +6 -7
  11. package/.github/workflows/pwa-microservices-template-test.ci.yml +4 -4
  12. package/.github/workflows/release.cd.yml +14 -8
  13. package/.vscode/extensions.json +9 -8
  14. package/.vscode/settings.json +3 -2
  15. package/CHANGELOG.md +643 -1
  16. package/CLI-HELP.md +132 -57
  17. package/Dockerfile +4 -2
  18. package/README.md +347 -22
  19. package/WHITE-PAPER.md +1540 -0
  20. package/bin/build.js +21 -12
  21. package/bin/cyberia.js +2640 -106
  22. package/bin/deploy.js +258 -372
  23. package/bin/file.js +5 -1
  24. package/bin/index.js +2640 -106
  25. package/bin/vs.js +3 -3
  26. package/conf.js +169 -105
  27. package/deployment.yaml +236 -20
  28. package/hardhat/.env.example +31 -0
  29. package/hardhat/README.md +531 -0
  30. package/hardhat/WHITE-PAPER.md +1540 -0
  31. package/hardhat/contracts/ObjectLayerToken.sol +391 -0
  32. package/hardhat/deployments/.gitkeep +0 -0
  33. package/hardhat/deployments/hardhat-ObjectLayerToken.json +11 -0
  34. package/hardhat/hardhat.config.js +136 -0
  35. package/hardhat/ignition/modules/ObjectLayerToken.js +21 -0
  36. package/hardhat/networks/besu-object-layer.network.json +138 -0
  37. package/hardhat/package-lock.json +4323 -0
  38. package/hardhat/package.json +36 -0
  39. package/hardhat/scripts/deployObjectLayerToken.js +98 -0
  40. package/hardhat/test/ObjectLayerToken.js +592 -0
  41. package/hardhat/types/ethers-contracts/ObjectLayerToken.ts +690 -0
  42. package/hardhat/types/ethers-contracts/common.ts +92 -0
  43. package/hardhat/types/ethers-contracts/factories/ObjectLayerToken__factory.ts +1055 -0
  44. package/hardhat/types/ethers-contracts/factories/index.ts +4 -0
  45. package/hardhat/types/ethers-contracts/hardhat.d.ts +47 -0
  46. package/hardhat/types/ethers-contracts/index.ts +6 -0
  47. package/jsdoc.dd-cyberia.json +68 -0
  48. package/jsdoc.json +65 -49
  49. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +5 -4
  50. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +5 -4
  51. package/manifests/deployment/dd-cyberia-development/deployment.yaml +562 -0
  52. package/manifests/deployment/dd-cyberia-development/proxy.yaml +297 -0
  53. package/manifests/deployment/dd-cyberia-development/pv-pvc.yaml +132 -0
  54. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  55. package/manifests/deployment/dd-test-development/deployment.yaml +88 -74
  56. package/manifests/deployment/dd-test-development/proxy.yaml +13 -4
  57. package/manifests/deployment/playwright/deployment.yaml +1 -1
  58. package/manifests/pv-pvc-dd.yaml +1 -1
  59. package/nodemon.json +1 -1
  60. package/package.json +60 -48
  61. package/proxy.yaml +118 -10
  62. package/pv-pvc.yaml +132 -0
  63. package/scripts/k3s-node-setup.sh +1 -1
  64. package/scripts/ports-ls.sh +2 -0
  65. package/scripts/rhel-grpc-setup.sh +56 -0
  66. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +47 -1
  67. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +17 -2
  68. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +5 -0
  69. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +80 -7
  70. package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +93 -0
  71. package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +36 -0
  72. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +29 -0
  73. package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +51 -0
  74. package/src/api/cyberia-entity/cyberia-entity.controller.js +74 -0
  75. package/src/api/cyberia-entity/cyberia-entity.model.js +24 -0
  76. package/src/api/cyberia-entity/cyberia-entity.router.js +27 -0
  77. package/src/api/cyberia-entity/cyberia-entity.service.js +42 -0
  78. package/src/api/cyberia-instance/cyberia-fallback-world.js +368 -0
  79. package/src/api/cyberia-instance/cyberia-instance.controller.js +92 -0
  80. package/src/api/cyberia-instance/cyberia-instance.model.js +84 -0
  81. package/src/api/cyberia-instance/cyberia-instance.router.js +63 -0
  82. package/src/api/cyberia-instance/cyberia-instance.service.js +191 -0
  83. package/src/api/cyberia-instance/cyberia-portal-connector.js +486 -0
  84. package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +74 -0
  85. package/src/api/cyberia-instance-conf/cyberia-instance-conf.defaults.js +413 -0
  86. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +228 -0
  87. package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +27 -0
  88. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +42 -0
  89. package/src/api/cyberia-map/cyberia-map.controller.js +79 -0
  90. package/src/api/cyberia-map/cyberia-map.model.js +30 -0
  91. package/src/api/cyberia-map/cyberia-map.router.js +40 -0
  92. package/src/api/cyberia-map/cyberia-map.service.js +74 -0
  93. package/src/api/document/document.service.js +1 -1
  94. package/src/api/file/file.controller.js +3 -1
  95. package/src/api/file/file.ref.json +18 -0
  96. package/src/api/file/file.service.js +28 -5
  97. package/src/api/ipfs/ipfs.controller.js +4 -25
  98. package/src/api/ipfs/ipfs.model.js +43 -34
  99. package/src/api/ipfs/ipfs.router.js +8 -13
  100. package/src/api/ipfs/ipfs.service.js +56 -104
  101. package/src/api/object-layer/README.md +347 -22
  102. package/src/api/object-layer/object-layer.controller.js +6 -2
  103. package/src/api/object-layer/object-layer.model.js +12 -8
  104. package/src/api/object-layer/object-layer.router.js +698 -42
  105. package/src/api/object-layer/object-layer.service.js +119 -37
  106. package/src/api/object-layer-render-frames/object-layer-render-frames.model.js +1 -2
  107. package/src/api/user/user.router.js +10 -5
  108. package/src/api/user/user.service.js +15 -14
  109. package/src/cli/baremetal.js +6 -10
  110. package/src/cli/cloud-init.js +0 -3
  111. package/src/cli/cluster.js +7 -7
  112. package/src/cli/db.js +723 -857
  113. package/src/cli/deploy.js +215 -105
  114. package/src/cli/env.js +34 -5
  115. package/src/cli/fs.js +5 -4
  116. package/src/cli/image.js +0 -3
  117. package/src/cli/index.js +83 -15
  118. package/src/cli/kubectl.js +211 -0
  119. package/src/cli/monitor.js +5 -6
  120. package/src/cli/release.js +284 -0
  121. package/src/cli/repository.js +708 -62
  122. package/src/cli/run.js +371 -151
  123. package/src/cli/secrets.js +73 -2
  124. package/src/cli/ssh.js +1 -1
  125. package/src/cli/test.js +3 -3
  126. package/src/client/Cryptokoyn.index.js +3 -4
  127. package/src/client/CyberiaPortal.index.js +3 -4
  128. package/src/client/Default.index.js +3 -4
  129. package/src/client/Itemledger.index.js +4 -963
  130. package/src/client/Underpost.index.js +3 -4
  131. package/src/client/components/core/AgGrid.js +20 -5
  132. package/src/client/components/core/Alert.js +2 -2
  133. package/src/client/components/core/AppStore.js +69 -0
  134. package/src/client/components/core/CalendarCore.js +2 -2
  135. package/src/client/components/core/Content.js +22 -3
  136. package/src/client/components/core/Docs.js +30 -6
  137. package/src/client/components/core/DropDown.js +137 -17
  138. package/src/client/components/core/FileExplorer.js +71 -4
  139. package/src/client/components/core/Input.js +1 -1
  140. package/src/client/components/core/Keyboard.js +2 -2
  141. package/src/client/components/core/LogIn.js +2 -2
  142. package/src/client/components/core/LogOut.js +2 -2
  143. package/src/client/components/core/Modal.js +20 -7
  144. package/src/client/components/core/Panel.js +0 -1
  145. package/src/client/components/core/PanelForm.js +19 -19
  146. package/src/client/components/core/RichText.js +1 -2
  147. package/src/client/components/core/SocketIo.js +82 -29
  148. package/src/client/components/core/SocketIoHandler.js +75 -0
  149. package/src/client/components/core/Stream.js +143 -95
  150. package/src/client/components/core/Webhook.js +40 -7
  151. package/src/client/components/cryptokoyn/AppStoreCryptokoyn.js +5 -0
  152. package/src/client/components/cryptokoyn/LogInCryptokoyn.js +3 -3
  153. package/src/client/components/cryptokoyn/LogOutCryptokoyn.js +2 -2
  154. package/src/client/components/cryptokoyn/MenuCryptokoyn.js +3 -3
  155. package/src/client/components/cryptokoyn/SocketIoCryptokoyn.js +3 -51
  156. package/src/client/components/cyberia/InstanceEngineCyberia.js +700 -0
  157. package/src/client/components/cyberia/MapEngineCyberia.js +1359 -2
  158. package/src/client/components/cyberia/ObjectLayerEngineModal.js +17 -6
  159. package/src/client/components/cyberia/ObjectLayerEngineViewer.js +92 -54
  160. package/src/client/components/cyberia-portal/AppStoreCyberiaPortal.js +5 -0
  161. package/src/client/components/cyberia-portal/CommonCyberiaPortal.js +217 -30
  162. package/src/client/components/cyberia-portal/CssCyberiaPortal.js +44 -2
  163. package/src/client/components/cyberia-portal/LogInCyberiaPortal.js +3 -4
  164. package/src/client/components/cyberia-portal/LogOutCyberiaPortal.js +2 -2
  165. package/src/client/components/cyberia-portal/MenuCyberiaPortal.js +104 -9
  166. package/src/client/components/cyberia-portal/RoutesCyberiaPortal.js +5 -0
  167. package/src/client/components/cyberia-portal/SocketIoCyberiaPortal.js +3 -49
  168. package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +4 -0
  169. package/src/client/components/default/AppStoreDefault.js +5 -0
  170. package/src/client/components/default/LogInDefault.js +3 -3
  171. package/src/client/components/default/LogOutDefault.js +2 -2
  172. package/src/client/components/default/MenuDefault.js +5 -5
  173. package/src/client/components/default/SocketIoDefault.js +3 -51
  174. package/src/client/components/itemledger/AppStoreItemledger.js +5 -0
  175. package/src/client/components/itemledger/LogInItemledger.js +3 -3
  176. package/src/client/components/itemledger/LogOutItemledger.js +2 -2
  177. package/src/client/components/itemledger/MenuItemledger.js +3 -3
  178. package/src/client/components/itemledger/SocketIoItemledger.js +3 -51
  179. package/src/client/components/underpost/AppStoreUnderpost.js +5 -0
  180. package/src/client/components/underpost/CssUnderpost.js +59 -0
  181. package/src/client/components/underpost/LogInUnderpost.js +6 -3
  182. package/src/client/components/underpost/LogOutUnderpost.js +4 -2
  183. package/src/client/components/underpost/MenuUnderpost.js +104 -18
  184. package/src/client/components/underpost/RoutesUnderpost.js +2 -0
  185. package/src/client/components/underpost/SocketIoUnderpost.js +3 -51
  186. package/src/client/public/cryptokoyn/assets/logo/base-icon.png +0 -0
  187. package/src/client/public/cryptokoyn/browserconfig.xml +12 -0
  188. package/src/client/public/cryptokoyn/microdata.json +85 -0
  189. package/src/client/public/cryptokoyn/site.webmanifest +57 -0
  190. package/src/client/public/cryptokoyn/sitemap +3 -3
  191. package/src/client/public/default/sitemap +3 -3
  192. package/src/client/public/itemledger/browserconfig.xml +2 -2
  193. package/src/client/public/itemledger/manifest.webmanifest +4 -4
  194. package/src/client/public/itemledger/microdata.json +71 -0
  195. package/src/client/public/itemledger/sitemap +3 -3
  196. package/src/client/public/itemledger/yandex-browser-manifest.json +2 -2
  197. package/src/client/public/test/sitemap +3 -3
  198. package/src/client/services/core/core.service.js +20 -8
  199. package/src/client/services/cyberia-dialogue/cyberia-dialogue.service.js +105 -0
  200. package/src/client/services/cyberia-entity/cyberia-entity.management.js +57 -0
  201. package/src/client/services/cyberia-entity/cyberia-entity.service.js +105 -0
  202. package/src/client/services/cyberia-instance/cyberia-instance.management.js +194 -0
  203. package/src/client/services/cyberia-instance/cyberia-instance.service.js +122 -0
  204. package/src/client/services/cyberia-instance-conf/cyberia-instance-conf.service.js +105 -0
  205. package/src/client/services/cyberia-map/cyberia-map.management.js +193 -0
  206. package/src/client/services/cyberia-map/cyberia-map.service.js +126 -0
  207. package/src/client/services/instance/instance.management.js +2 -2
  208. package/src/client/services/ipfs/ipfs.service.js +3 -23
  209. package/src/client/services/object-layer/object-layer.management.js +3 -3
  210. package/src/client/services/object-layer/object-layer.service.js +21 -0
  211. package/src/client/services/user/user.management.js +2 -2
  212. package/src/client/ssr/body/404.js +15 -11
  213. package/src/client/ssr/body/500.js +15 -11
  214. package/src/client/ssr/body/SwaggerDarkMode.js +285 -0
  215. package/src/client/ssr/head/PwaItemledger.js +60 -0
  216. package/src/client/ssr/offline/NoNetworkConnection.js +11 -10
  217. package/src/client/ssr/pages/CyberiaServerMetrics.js +1 -1
  218. package/src/client/ssr/pages/Test.js +11 -10
  219. package/src/client.build.js +0 -3
  220. package/src/client.dev.js +0 -3
  221. package/src/db/DataBaseProvider.js +17 -2
  222. package/src/db/mariadb/MariaDB.js +14 -9
  223. package/src/db/mongo/MongooseDB.js +17 -1
  224. package/src/grpc/cyberia/OFF_CHAIN_ECONOMY.md +305 -0
  225. package/src/grpc/cyberia/README.md +326 -0
  226. package/src/grpc/cyberia/grpc-server.js +530 -0
  227. package/src/index.js +24 -1
  228. package/src/proxy.js +0 -3
  229. package/src/runtime/express/Dockerfile +4 -0
  230. package/src/runtime/express/Express.js +33 -10
  231. package/src/runtime/lampp/Dockerfile +13 -2
  232. package/src/runtime/lampp/Lampp.js +33 -17
  233. package/src/runtime/wp/Dockerfile +68 -0
  234. package/src/runtime/wp/Wp.js +639 -0
  235. package/src/server/auth.js +36 -15
  236. package/src/server/backup.js +39 -12
  237. package/src/server/besu-genesis-generator.js +1630 -0
  238. package/src/server/client-build-docs.js +133 -17
  239. package/src/server/client-build-live.js +9 -18
  240. package/src/server/client-build.js +229 -101
  241. package/src/server/client-dev-server.js +14 -13
  242. package/src/server/client-formatted.js +109 -57
  243. package/src/server/conf.js +391 -164
  244. package/src/server/cron.js +27 -24
  245. package/src/server/dns.js +29 -12
  246. package/src/server/downloader.js +0 -2
  247. package/src/server/ipfs-client.js +24 -1
  248. package/src/server/logger.js +27 -9
  249. package/src/server/object-layer.js +217 -103
  250. package/src/server/peer.js +8 -2
  251. package/src/server/process.js +1 -50
  252. package/src/server/proxy.js +4 -8
  253. package/src/server/runtime.js +30 -9
  254. package/src/server/semantic-layer-generator-floor.js +359 -0
  255. package/src/server/semantic-layer-generator-skin.js +1294 -0
  256. package/src/server/semantic-layer-generator.js +116 -555
  257. package/src/server/ssr.js +0 -3
  258. package/src/server/start.js +19 -12
  259. package/src/server/tls.js +0 -2
  260. package/src/server.js +0 -4
  261. package/src/ws/IoInterface.js +1 -10
  262. package/src/ws/IoServer.js +14 -33
  263. package/src/ws/core/channels/core.ws.chat.js +65 -20
  264. package/src/ws/core/channels/core.ws.mailer.js +113 -32
  265. package/src/ws/core/channels/core.ws.stream.js +90 -31
  266. package/src/ws/core/core.ws.connection.js +12 -33
  267. package/src/ws/core/core.ws.emit.js +10 -26
  268. package/src/ws/core/core.ws.server.js +25 -58
  269. package/src/ws/default/channels/default.ws.main.js +53 -12
  270. package/src/ws/default/default.ws.connection.js +26 -13
  271. package/src/ws/default/default.ws.server.js +30 -12
  272. package/.env.development +0 -43
  273. package/.env.test +0 -43
  274. package/hardhat/contracts/CryptoKoyn.sol +0 -59
  275. package/hardhat/contracts/ItemLedger.sol +0 -73
  276. package/hardhat/contracts/Lock.sol +0 -34
  277. package/hardhat/hardhat.config.cjs +0 -45
  278. package/hardhat/ignition/modules/Lock.js +0 -18
  279. package/hardhat/networks/cryptokoyn-itemledger.network.json +0 -29
  280. package/hardhat/scripts/deployCryptokoyn.cjs +0 -25
  281. package/hardhat/scripts/deployItemledger.cjs +0 -25
  282. package/hardhat/test/Lock.js +0 -126
  283. package/hardhat/white-paper.md +0 -581
  284. package/src/client/components/cryptokoyn/CommonCryptokoyn.js +0 -29
  285. package/src/client/components/cryptokoyn/ElementsCryptokoyn.js +0 -38
  286. package/src/client/components/cyberia-portal/ElementsCyberiaPortal.js +0 -38
  287. package/src/client/components/default/ElementsDefault.js +0 -38
  288. package/src/client/components/itemledger/CommonItemledger.js +0 -29
  289. package/src/client/components/itemledger/ElementsItemledger.js +0 -38
  290. package/src/client/components/underpost/CommonUnderpost.js +0 -29
  291. package/src/client/components/underpost/ElementsUnderpost.js +0 -38
  292. package/src/ws/core/management/core.ws.chat.js +0 -8
  293. package/src/ws/core/management/core.ws.mailer.js +0 -16
  294. package/src/ws/core/management/core.ws.stream.js +0 -8
  295. package/src/ws/default/management/default.ws.main.js +0 -8
  296. package/white-paper.md +0 -581
@@ -26,10 +26,249 @@ import Underpost from '../index.js';
26
26
 
27
27
  colors.enable();
28
28
 
29
- dotenv.config();
30
-
31
29
  const logger = loggerFactory(import.meta);
32
30
 
31
+ /**
32
+ * Prefix used in JSON configuration files to denote an environment variable reference.
33
+ * Any string value in a config object that starts with this prefix will be resolved
34
+ * to the corresponding `process.env` value at runtime.
35
+ *
36
+ * @constant {string}
37
+ * @memberof ServerConfBuilder
38
+ * @example
39
+ * // In conf.server.json:
40
+ * { "db": { "password": "env:MARIADB_PASSWORD" } }
41
+ */
42
+ const ENV_REF_PREFIX = 'env:';
43
+
44
+ /**
45
+ * Recursively walks a configuration object and replaces every string value that
46
+ * starts with {@link ENV_REF_PREFIX} (`"env:"`) with the corresponding
47
+ * `process.env[VAR_NAME]` value.
48
+ *
49
+ * Non-string values and strings that do not start with the prefix are left untouched.
50
+ *
51
+ * Supports three reference formats:
52
+ * - `"env:VAR_NAME"` — resolves to `process.env.VAR_NAME`, returns `''` if unset.
53
+ * - `"env:VAR_NAME:default_value"` — resolves to `process.env.VAR_NAME`, falls back to `default_value` if unset.
54
+ * - Type-coerced defaults:
55
+ * - `"env:VAR_NAME:int:465"` — resolved value is parsed as integer (`parseInt`), falls back to `465`.
56
+ * - `"env:VAR_NAME:bool:true"` — resolved value is coerced to boolean (`value !== 'false'`), falls back to `true`.
57
+ *
58
+ * @method resolveConfSecrets
59
+ * @param {any} obj - The configuration object (or value) to resolve.
60
+ * @returns {any} A **new** object with all `env:` references replaced by their runtime values.
61
+ * @memberof ServerConfBuilder
62
+ *
63
+ * @example
64
+ * // Given process.env.MARIADB_PASSWORD = 'supersecret'
65
+ * resolveConfSecrets({ db: { password: 'env:MARIADB_PASSWORD' } });
66
+ * // => { db: { password: 'supersecret' } }
67
+ *
68
+ * @example
69
+ * // With default value fallback (env var not set)
70
+ * resolveConfSecrets({ db: { provider: 'env:DB_PROVIDER:mongoose' } });
71
+ * // => { db: { provider: 'mongoose' } }
72
+ *
73
+ * @example
74
+ * // With int type coercion
75
+ * resolveConfSecrets({ port: 'env:SMTP_PORT:int:465' });
76
+ * // => { port: 465 }
77
+ *
78
+ * @example
79
+ * // With bool type coercion
80
+ * resolveConfSecrets({ secure: 'env:SMTP_SECURE:bool:true' });
81
+ * // => { secure: true }
82
+ */
83
+ const resolveConfSecrets = (obj) => {
84
+ if (obj === null || obj === undefined) return obj;
85
+ if (typeof obj === 'string') {
86
+ if (obj.startsWith(ENV_REF_PREFIX)) {
87
+ const ref = obj.slice(ENV_REF_PREFIX.length);
88
+ // Support env:VAR_NAME:default_value syntax (first colon separates key from default)
89
+ const colonIdx = ref.indexOf(':');
90
+ const envKey = colonIdx !== -1 ? ref.slice(0, colonIdx) : ref;
91
+ const defaultValue = colonIdx !== -1 ? ref.slice(colonIdx + 1) : undefined;
92
+ const envValue = process.env[envKey];
93
+
94
+ let resolved;
95
+ if (envValue !== undefined) {
96
+ resolved = envValue;
97
+ } else if (defaultValue !== undefined) {
98
+ resolved = defaultValue;
99
+ } else {
100
+ logger.warn(`resolveConfSecrets: environment variable "${envKey}" is not set (referenced as "${obj}")`);
101
+ return '';
102
+ }
103
+
104
+ // Type coercion via prefix in default value: int:N or bool:B
105
+ // Also apply coercion when an env value is present and a typed default is declared
106
+ if (defaultValue !== undefined) {
107
+ if (defaultValue.startsWith('int:')) {
108
+ return parseInt(resolved, 10) || parseInt(defaultValue.slice(4), 10) || 0;
109
+ }
110
+ if (defaultValue.startsWith('bool:')) {
111
+ const boolDefault = defaultValue.slice(5);
112
+ if (envValue !== undefined) return envValue !== 'false';
113
+ return boolDefault !== 'false';
114
+ }
115
+ }
116
+
117
+ return resolved;
118
+ }
119
+ return obj;
120
+ }
121
+ if (Array.isArray(obj)) return obj.map((item) => resolveConfSecrets(item));
122
+ if (typeof obj === 'object') {
123
+ const resolved = {};
124
+ for (const [key, value] of Object.entries(obj)) {
125
+ resolved[key] = resolveConfSecrets(value);
126
+ }
127
+ return resolved;
128
+ }
129
+ return obj;
130
+ };
131
+
132
+ /**
133
+ * Returns the private configuration folder for a given deploy ID.
134
+ * Checks for a replica folder first, then falls back to the standard conf folder.
135
+ *
136
+ * @method getConfFolder
137
+ * @param {string} deployId - The deploy ID.
138
+ * @returns {string} The path to the private configuration folder.
139
+ * @memberof ServerConfBuilder
140
+ *
141
+ * @example
142
+ * getConfFolder('dd-myapp');
143
+ * // => './engine-private/conf/dd-myapp' (or './engine-private/replica/dd-myapp' if it exists)
144
+ */
145
+ const getConfFolder = (deployId) => {
146
+ return fs.existsSync(`./engine-private/replica/${deployId}`)
147
+ ? `./engine-private/replica/${deployId}`
148
+ : `./engine-private/conf/${deployId}`;
149
+ };
150
+
151
+ /**
152
+ * Reads `engine-private/deploy/dd.cron` and returns the deploy-id string,
153
+ * or `null` if the file does not exist or is empty.
154
+ *
155
+ * @method cronDeployIdResolve
156
+ * @returns {string|null} The deploy-id from dd.cron, or null.
157
+ * @memberof ServerConfBuilder
158
+ */
159
+ const cronDeployIdResolve = () => {
160
+ const cronDeployFile = './engine-private/deploy/dd.cron';
161
+ if (fs.existsSync(cronDeployFile)) {
162
+ const id = fs.readFileSync(cronDeployFile, 'utf8').trim();
163
+ return id || null;
164
+ }
165
+ return null;
166
+ };
167
+
168
+ /**
169
+ * Loads the deployment-specific `.env` file referenced by `engine-private/deploy/dd.cron`
170
+ * into `process.env`. Uses `NODE_ENV` to select the environment variant
171
+ * (defaults to `production`).
172
+ *
173
+ * Safe to call multiple times; subsequent calls are no-ops once the env is loaded.
174
+ *
175
+ * @method loadCronDeployEnv
176
+ * @memberof ServerConfBuilder
177
+ */
178
+ function loadCronDeployEnv() {
179
+ const envName = process.env.NODE_ENV || 'production';
180
+
181
+ // 1) Load dd.cron env (takes full precedence)
182
+ const cronDeployId = cronDeployIdResolve();
183
+ if (cronDeployId) {
184
+ const cronEnvPath = `./engine-private/conf/${cronDeployId}/.env.${envName}`;
185
+ if (fs.existsSync(cronEnvPath)) {
186
+ const cronEnv = dotenv.parse(fs.readFileSync(cronEnvPath, 'utf8'));
187
+ process.env = { ...process.env, ...cronEnv };
188
+ }
189
+ }
190
+
191
+ // 2) Load dd.router envs — only keys not already present
192
+ const routerDeployFile = './engine-private/deploy/dd.router';
193
+ if (fs.existsSync(routerDeployFile)) {
194
+ const routerIds = fs.readFileSync(routerDeployFile, 'utf8').trim().split(',');
195
+ for (const deployId of routerIds) {
196
+ const id = deployId.trim();
197
+ if (!id) continue;
198
+ const envPath = `./engine-private/conf/${id}/.env.${envName}`;
199
+ if (!fs.existsSync(envPath)) continue;
200
+ const env = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
201
+ for (const key of Object.keys(env)) {
202
+ if (!(key in process.env)) process.env[key] = env[key];
203
+ }
204
+ }
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Resolves the full path to a specific configuration JSON file for a deploy ID.
210
+ * For `server` configs in development mode with a subConf, it will prefer the
211
+ * dev-specific variant if it exists.
212
+ *
213
+ * @method getConfFilePath
214
+ * @param {string} deployId - The deploy ID.
215
+ * @param {string} confType - The configuration type (e.g. 'server', 'client', 'cron', 'ssr').
216
+ * @param {string} [subConf=''] - Optional sub-configuration identifier (used for dev server variants).
217
+ * @returns {string} The resolved path to the configuration JSON file.
218
+ * @memberof ServerConfBuilder
219
+ *
220
+ * @example
221
+ * getConfFilePath('dd-myapp', 'server');
222
+ * // => './engine-private/conf/dd-myapp/conf.server.json'
223
+ *
224
+ * @example
225
+ * // In development with subConf 'local':
226
+ * getConfFilePath('dd-myapp', 'server', 'local');
227
+ * // => './engine-private/conf/dd-myapp/conf.server.dev.local.json' (if it exists)
228
+ */
229
+ const getConfFilePath = (deployId, confType, subConf = '') => {
230
+ const folder = getConfFolder(deployId);
231
+ // When no explicit subConf is given, fall back to the env var set by loadConf()
232
+ const effectiveSubConf = subConf || process.env.DEPLOY_SUB_CONF || '';
233
+ if (confType === 'server' && effectiveSubConf) {
234
+ const devConfPath = `${folder}/conf.${confType}.dev.${effectiveSubConf}.json`;
235
+ if (fs.existsSync(devConfPath)) return devConfPath;
236
+ }
237
+ return `${folder}/conf.${confType}.json`;
238
+ };
239
+
240
+ /**
241
+ * Reads and parses a configuration JSON file for a given deploy ID and config type.
242
+ * Optionally resolves `env:` secret references and/or applies replica expansion.
243
+ *
244
+ * @method readConfJson
245
+ * @param {string} deployId - The deploy ID.
246
+ * @param {string} confType - The configuration type (e.g. 'server', 'client', 'cron', 'ssr').
247
+ * @param {object} [options={}] - Options.
248
+ * @param {string} [options.subConf=''] - Sub-configuration identifier for dev variants.
249
+ * @param {boolean} [options.resolve=false] - Whether to resolve `env:` references.
250
+ * @param {boolean} [options.loadReplicas=false] - Whether to expand replica entries (server configs).
251
+ * @returns {object} The parsed (and optionally resolved) configuration object.
252
+ * @memberof ServerConfBuilder
253
+ */
254
+ const readConfJson = (deployId, confType, options = {}) => {
255
+ const filePath = getConfFilePath(deployId, confType, options.subConf || '');
256
+ if (!fs.existsSync(filePath)) {
257
+ throw new Error(`readConfJson: configuration file not found: ${filePath}`);
258
+ }
259
+ let parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
260
+ if (options.loadReplicas && confType === 'server') parsed = loadReplicas(deployId, parsed);
261
+ if (options.resolve) parsed = resolveConfSecrets(parsed);
262
+ return parsed;
263
+ };
264
+
265
+ /**
266
+ * Default deploy ID used when no deploy ID is specified.
267
+ * @constant {string}
268
+ * @memberof ServerConfBuilder
269
+ */
270
+ const DEFAULT_DEPLOY_ID = 'dd-default';
271
+
33
272
  /**
34
273
  * @class Config
35
274
  * @description Manages the configuration of the server.
@@ -53,12 +292,13 @@ const Config = {
53
292
  * @param {string} [subConf=''] - The sub configuration.
54
293
  * @memberof ServerConfBuilder
55
294
  */
56
- build: async function (deployContext = 'dd-default', deployList, subConf) {
295
+ build: async function (deployContext = DEFAULT_DEPLOY_ID, deployList, subConf) {
57
296
  if (process.argv[2] && typeof process.argv[2] === 'string' && process.argv[2].startsWith('dd-'))
58
297
  deployContext = process.argv[2];
298
+ else if (deployContext !== 'proxy' && process.env.DEPLOY_ID && process.env.DEPLOY_ID.startsWith('dd-'))
299
+ deployContext = process.env.DEPLOY_ID;
59
300
  if (!subConf && process.argv[3] && typeof process.argv[3] === 'string') subConf = process.argv[3];
60
- if (!fs.existsSync(`./tmp`)) fs.mkdirSync(`./tmp`);
61
- if (!fs.existsSync(`./conf`)) fs.mkdirSync(`./conf`);
301
+
62
302
  Underpost.env.set('await-deploy', new Date().toISOString());
63
303
  if (deployContext.startsWith('dd-')) loadConf(deployContext, subConf);
64
304
  if (deployContext === 'proxy') await Config.buildProxy(deployList, subConf);
@@ -70,7 +310,7 @@ const Config = {
70
310
  * @param {object} [options={ subConf: '', cluster: false }] - The options.
71
311
  * @memberof ServerConfBuilder
72
312
  */
73
- deployIdFactory: function (deployId = 'dd-default', options = { subConf: '', cluster: false }) {
313
+ deployIdFactory: function (deployId = DEFAULT_DEPLOY_ID, options = { subConf: '', cluster: false }) {
74
314
  if (!deployId.startsWith('dd-')) deployId = `dd-${deployId}`;
75
315
 
76
316
  logger.info('Build deployId', deployId);
@@ -79,28 +319,39 @@ const Config = {
79
319
  const repoName = `engine-${deployId.split('dd-')[1]}`;
80
320
 
81
321
  if (!fs.existsSync(folder)) fs.mkdirSync(folder, { recursive: true });
82
- fs.writeFileSync(
83
- `${folder}/.env.production`,
84
- fs.readFileSync('./.env.production', 'utf8').replaceAll('dd-default', deployId),
85
- 'utf8',
86
- );
87
- fs.writeFileSync(
88
- `${folder}/.env.development`,
89
- fs.readFileSync('./.env.development', 'utf8').replaceAll('dd-default', deployId),
90
- 'utf8',
91
- );
92
- fs.writeFileSync(
93
- `${folder}/.env.test`,
94
- fs.readFileSync('./.env.test', 'utf8').replaceAll('dd-default', deployId),
95
- 'utf8',
96
- );
322
+
323
+ const envTemplate = fs.existsSync('./.env.example')
324
+ ? fs.readFileSync('./.env.example', 'utf8')
325
+ : fs.existsSync('./.env.production')
326
+ ? fs.readFileSync('./.env.production', 'utf8')
327
+ : '';
328
+
329
+ if (envTemplate) {
330
+ const prodEnv = envTemplate.replaceAll('dd-default', deployId);
331
+ fs.writeFileSync(`${folder}/.env.production`, prodEnv, 'utf8');
332
+ fs.writeFileSync(
333
+ `${folder}/.env.development`,
334
+ prodEnv.replace('NODE_ENV=production', 'NODE_ENV=development').replace('PORT=3000', 'PORT=4000'),
335
+ 'utf8',
336
+ );
337
+ fs.writeFileSync(
338
+ `${folder}/.env.test`,
339
+ prodEnv.replace('NODE_ENV=production', 'NODE_ENV=test').replace('PORT=3000', 'PORT=5000'),
340
+ 'utf8',
341
+ );
342
+ }
343
+
97
344
  fs.writeFileSync(
98
345
  `${folder}/package.json`,
99
346
  fs.readFileSync('./package.json', 'utf8').replaceAll('dd-default', deployId),
100
347
  'utf8',
101
348
  );
102
349
 
103
- this.buildTmpConf(folder);
350
+ // Write default conf JSON files if they don't exist
351
+ for (const confType of Object.keys(this.default)) {
352
+ const confPath = `${folder}/conf.${confType}.json`;
353
+ if (!fs.existsSync(confPath)) fs.writeFileSync(confPath, JSON.stringify(this.default[confType], null, 4), 'utf8');
354
+ }
104
355
 
105
356
  if (options.subConf) {
106
357
  logger.info('Creating sub conf', {
@@ -127,38 +378,17 @@ const Config = {
127
378
  shellExec(`node bin new --default-conf --deploy-id ${deployId}`);
128
379
 
129
380
  if (!fs.existsSync(`./engine-private/deploy/dd.router`))
130
- fs.writeFileSync(`./engine-private/deploy/dd.router`, '', 'utf8');
131
-
132
- fs.writeFileSync(
133
- `./engine-private/deploy/dd.router`,
134
- fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').trim() + `,${deployId}`,
135
- 'utf8',
136
- );
137
- const updateRepo = (stage = 1) => {
138
- shellExec(`git add . && git commit -m "Add base deployId ${deployId} cluster files stage:${stage}"`);
139
- shellExec(
140
- `cd engine-private && git add . && git commit -m "Add base deployId ${deployId} cluster files stage:${stage}"`,
381
+ fs.writeFileSync(`./engine-private/deploy/dd.router`, deployId, 'utf8');
382
+ else
383
+ fs.writeFileSync(
384
+ `./engine-private/deploy/dd.router`,
385
+ fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').trim() + `,${deployId}`,
386
+ 'utf8',
141
387
  );
142
- };
143
- updateRepo(1);
144
- shellExec(`node bin run --build --dev sync`);
145
- updateRepo(2);
146
- shellExec(`node bin run --build sync`);
147
- updateRepo(3);
148
388
  }
149
389
 
150
390
  return { deployIdFolder: folder, deployId };
151
391
  },
152
- /**
153
- * @method buildTmpConf
154
- * @description Builds the temporary configuration of the server.
155
- * @param {string} [folder='./conf'] - The folder.
156
- * @memberof ServerConfBuilder
157
- */
158
- buildTmpConf: function (folder = './conf') {
159
- for (const confType of Object.keys(this.default))
160
- fs.writeFileSync(`${folder}/conf.${confType}.json`, JSON.stringify(this.default[confType], null, 4), 'utf8');
161
- },
162
392
  /**
163
393
  * @method buildProxyByDeployId
164
394
  * @description Builds the proxy by deploy ID.
@@ -178,7 +408,7 @@ const Config = {
178
408
  )
179
409
  confPath = `./engine-private/conf/${deployId}/conf.server.dev.${subConf}.json`;
180
410
 
181
- const serverConf = JSON.parse(fs.readFileSync(confPath, 'utf8'));
411
+ const serverConf = loadConfServerJson(confPath);
182
412
 
183
413
  for (const host of Object.keys(loadReplicas(deployId, serverConf)))
184
414
  this.default.server[host] = {
@@ -206,7 +436,6 @@ const Config = {
206
436
  }
207
437
  }
208
438
  }
209
- this.buildTmpConf();
210
439
  },
211
440
  };
212
441
 
@@ -217,7 +446,7 @@ const Config = {
217
446
  * @param {string} [subConf=''] - The sub configuration.
218
447
  * @memberof ServerConfBuilder
219
448
  */
220
- const loadConf = (deployId = 'dd-default', subConf) => {
449
+ const loadConf = (deployId = DEFAULT_DEPLOY_ID, subConf) => {
221
450
  if (deployId === 'current') {
222
451
  console.log(process.env.DEPLOY_ID);
223
452
  return;
@@ -225,20 +454,19 @@ const loadConf = (deployId = 'dd-default', subConf) => {
225
454
  if (deployId === 'clean') {
226
455
  const path = '.';
227
456
  fs.removeSync(`${path}/.env`);
228
- shellExec(`git checkout ${path}/.env.production`);
229
- shellExec(`git checkout ${path}/.env.development`);
230
- shellExec(`git checkout ${path}/.env.test`);
457
+ fs.removeSync(`${path}/.env.production`);
458
+ fs.removeSync(`${path}/.env.development`);
459
+ fs.removeSync(`${path}/.env.test`);
231
460
  if (fs.existsSync(`${path}/jsdoc.json`)) shellExec(`git checkout ${path}/jsdoc.json`);
232
461
  shellExec(`git checkout ${path}/package.json`);
233
462
  shellExec(`git checkout ${path}/package-lock.json`);
234
463
  return;
235
464
  }
236
- const folder = fs.existsSync(`./engine-private/replica/${deployId}`)
237
- ? `./engine-private/replica/${deployId}`
238
- : `./engine-private/conf/${deployId}`;
465
+ const folder = getConfFolder(deployId);
466
+
239
467
  if (!fs.existsSync(folder)) Config.deployIdFactory(deployId);
240
- if (!fs.existsSync(`./conf`)) fs.mkdirSync(`./conf`);
241
- if (!fs.existsSync(`./tmp`)) fs.mkdirSync(`./tmp`);
468
+
469
+ if (subConf) process.env.DEPLOY_SUB_CONF = subConf;
242
470
 
243
471
  for (const typeConf of Object.keys(Config.default)) {
244
472
  let srcConf = fs.readFileSync(`${folder}/conf.${typeConf}.json`, 'utf8');
@@ -246,13 +474,14 @@ const loadConf = (deployId = 'dd-default', subConf) => {
246
474
  const devConfPath = `${folder}/conf.${typeConf}.dev${subConf ? `.${subConf}` : ''}.json`;
247
475
  if (fs.existsSync(devConfPath)) srcConf = fs.readFileSync(devConfPath, 'utf8');
248
476
  }
249
- if (typeConf === 'server') srcConf = JSON.stringify(loadReplicas(deployId, JSON.parse(srcConf)), null, 4);
250
- fs.writeFileSync(`./conf/conf.${typeConf}.json`, srcConf, 'utf8');
477
+ let parsed = JSON.parse(srcConf);
478
+ if (typeConf === 'server') parsed = loadReplicas(deployId, parsed);
479
+ Config.default[typeConf] = parsed;
251
480
  }
252
481
  fs.writeFileSync(`./.env.production`, fs.readFileSync(`${folder}/.env.production`, 'utf8'), 'utf8');
253
482
  fs.writeFileSync(`./.env.development`, fs.readFileSync(`${folder}/.env.development`, 'utf8'), 'utf8');
254
483
  fs.writeFileSync(`./.env.test`, fs.readFileSync(`${folder}/.env.test`, 'utf8'), 'utf8');
255
- const NODE_ENV = process.env.NODE_ENV;
484
+ const NODE_ENV = process.env.NODE_ENV || 'development';
256
485
  if (NODE_ENV) {
257
486
  const subPathEnv = fs.existsSync(`${folder}/.env.${NODE_ENV}.${subConf}`)
258
487
  ? `${folder}/.env.${NODE_ENV}.${subConf}`
@@ -644,7 +873,7 @@ const cloneSrcComponents = async ({ toOptions, fromOptions }) => {
644
873
  * @memberof ServerConfBuilder
645
874
  */
646
875
  const buildProxyRouter = () => {
647
- const confServer = JSON.parse(fs.readFileSync(`./conf/conf.server.json`, 'utf8'));
876
+ const confServer = newInstance(Config.default.server);
648
877
  let currentPort = parseInt(process.env.PORT) + 1;
649
878
  const proxyRouter = {};
650
879
  for (const host of Object.keys(confServer)) {
@@ -726,9 +955,7 @@ const pathPortAssignmentFactory = async (deployId, router, confServer) => {
726
955
  const singleReplicas = await fs.readdir(`./engine-private/replica`);
727
956
  for (let replica of singleReplicas) {
728
957
  if (replica.startsWith(deployId)) {
729
- const replicaServerConf = JSON.parse(
730
- fs.readFileSync(`./engine-private/replica/${replica}/conf.server.json`, 'utf8'),
731
- );
958
+ const replicaServerConf = loadConfServerJson(`./engine-private/replica/${replica}/conf.server.json`);
732
959
  for (const host of Object.keys(replicaServerConf)) {
733
960
  const pathPortAssignment = [];
734
961
  for (const path of Object.keys(replicaServerConf[host])) {
@@ -906,7 +1133,7 @@ const buildReplicaId = ({ deployId, replica }) => `${deployId}-${replica.slice(1
906
1133
  * @returns {object} - The data deploy.
907
1134
  * @memberof ServerConfBuilder
908
1135
  */
909
- const getDataDeploy = (
1136
+ const getDataDeploy = async (
910
1137
  options = {
911
1138
  buildSingleReplica: false,
912
1139
  disableSyncEnvPort: false,
@@ -931,13 +1158,16 @@ const getDataDeploy = (
931
1158
  for (const deployObj of dataDeploy) {
932
1159
  const serverConf = loadReplicas(
933
1160
  deployObj.deployId,
934
- JSON.parse(fs.readFileSync(`./engine-private/conf/${deployObj.deployId}/conf.server.json`, 'utf8')),
1161
+ loadConfServerJson(`./engine-private/conf/${deployObj.deployId}/conf.server.json`),
935
1162
  );
936
1163
  let replicaDataDeploy = [];
937
1164
  for (const host of Object.keys(serverConf))
938
1165
  for (const path of Object.keys(serverConf[host])) {
939
1166
  if (serverConf[host][path].replicas && serverConf[host][path].singleReplica) {
940
- if (options && options.buildSingleReplica) shellExec(Cmd.replica(deployObj.deployId, host, path));
1167
+ if (options && options.buildSingleReplica)
1168
+ await Underpost.repo.client(deployObj.deployId, '', host, path, {
1169
+ singleReplica: true,
1170
+ });
941
1171
  replicaDataDeploy = replicaDataDeploy.concat(
942
1172
  serverConf[host][path].replicas.map((r) => {
943
1173
  return {
@@ -952,7 +1182,8 @@ const getDataDeploy = (
952
1182
  if (replicaDataDeploy.length > 0) buildDataDeploy = buildDataDeploy.concat(replicaDataDeploy);
953
1183
  }
954
1184
 
955
- if (!options.disableSyncEnvPort && options.buildSingleReplica) shellExec(Cmd.syncPorts());
1185
+ if (!options.disableSyncEnvPort && options.buildSingleReplica)
1186
+ await Underpost.repo.client(undefined, '', '', '', { syncEnvPort: true });
956
1187
 
957
1188
  logger.info('Deployments configured', buildDataDeploy);
958
1189
 
@@ -980,6 +1211,7 @@ const validateTemplatePath = (absolutePath = '') => {
980
1211
  return false;
981
1212
  }
982
1213
  if (absolutePath.match('conf.dd-') && absolutePath.match('.js')) return false;
1214
+ if (absolutePath.match('jsdoc.dd-') && absolutePath.match('.json')) return false;
983
1215
  if (
984
1216
  absolutePath.match('src/client/services/') &&
985
1217
  !clients.find((p) => absolutePath.match(`src/client/services/${p}/`))
@@ -1057,18 +1289,6 @@ const awaitDeployMonitor = async (init = false, deltaMs = 1000) => {
1057
1289
  if (Underpost.env.get('await-deploy')) return await awaitDeployMonitor();
1058
1290
  };
1059
1291
 
1060
- /**
1061
- * @method getCronBackUpFolder
1062
- * @description Gets the cron back up folder.
1063
- * @param {string} host - The host.
1064
- * @param {string} path - The path.
1065
- * @returns {string} - The cron back up folder.
1066
- * @memberof ServerConfBuilder
1067
- */
1068
- const getCronBackUpFolder = (host = '', path = '') => {
1069
- return `${host}${path.replace(/\\/g, '/').replace(`/`, '-')}`;
1070
- };
1071
-
1072
1292
  /**
1073
1293
  * @method mergeFile
1074
1294
  * @description Merges the file.
@@ -1102,10 +1322,7 @@ const mergeFile = async (parts = [], outputFilePath) => {
1102
1322
  * @memberof ServerConfBuilder
1103
1323
  */
1104
1324
  const rebuildConfFactory = ({ deployId, valkey, mongo }) => {
1105
- const confServer = loadReplicas(
1106
- deployId,
1107
- JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8')),
1108
- );
1325
+ const confServer = loadReplicas(deployId, loadConfServerJson(`./engine-private/conf/${deployId}/conf.server.json`));
1109
1326
  const hosts = {};
1110
1327
  for (const host of Object.keys(confServer)) {
1111
1328
  hosts[host] = {};
@@ -1116,9 +1333,7 @@ const rebuildConfFactory = ({ deployId, valkey, mongo }) => {
1116
1333
  if (singleReplica) {
1117
1334
  for (const replica of replicas) {
1118
1335
  const deployIdReplica = buildReplicaId({ replica, deployId });
1119
- const confServerReplica = JSON.parse(
1120
- fs.readFileSync(`./engine-private/replica/${deployIdReplica}/conf.server.json`, 'utf8'),
1121
- );
1336
+ const confServerReplica = loadConfServerJson(`./engine-private/replica/${deployIdReplica}/conf.server.json`);
1122
1337
  for (const _host of Object.keys(confServerReplica)) {
1123
1338
  for (const _path of Object.keys(confServerReplica[_host])) {
1124
1339
  hosts[host][_path] = { replica: { host, path } };
@@ -1166,56 +1381,6 @@ const getPathsSSR = (conf) => {
1166
1381
  return paths;
1167
1382
  };
1168
1383
 
1169
- /**
1170
- * @method CmdUnderpost
1171
- * @description The command factory.
1172
- * @memberof ServerConfBuilder
1173
- * @namespace CmdUnderpost
1174
- */
1175
- const Cmd = {
1176
- /**
1177
- * @method run
1178
- * @description Runs the deploy.
1179
- * @returns {string} - The run command.
1180
- * @memberof Cmd
1181
- */
1182
- run: () => `npm start`,
1183
- /**
1184
- * @method build
1185
- * @description Builds the deploy.
1186
- * @param {string} deployId - The deploy ID.
1187
- * @returns {string} - The build command.
1188
- * @memberof CmdUnderpost
1189
- */
1190
- build: (deployId) => `node bin/deploy build-full-client ${deployId}`,
1191
- /**
1192
- * @method conf
1193
- * @description Configures the deploy.
1194
- * @param {string} deployId - The deploy ID.
1195
- * @param {string} env - The environment.
1196
- * @returns {string} - The conf command.
1197
- * @memberof CmdUnderpost
1198
- */
1199
- conf: (deployId, env) => `node bin/deploy conf ${deployId} ${env ? env : 'production'}`,
1200
- /**
1201
- * @method replica
1202
- * @description Builds the replica.
1203
- * @param {string} deployId - The deploy ID.
1204
- * @param {string} host - The host.
1205
- * @param {string} path - The path.
1206
- * @returns {string} - The replica command.
1207
- * @memberof CmdUnderpost
1208
- */
1209
- replica: (deployId, host, path) => `node bin/deploy build-single-replica ${deployId} ${host} ${path}`,
1210
- /**
1211
- * @method syncPorts
1212
- * @description Syncs the ports.
1213
- * @returns {string} - The sync ports command.
1214
- * @memberof CmdUnderpost
1215
- */
1216
- syncPorts: () => `node bin/deploy sync-env-port`,
1217
- };
1218
-
1219
1384
  /**
1220
1385
  * @method splitFileFactory
1221
1386
  * @description Splits the file factory.
@@ -1336,8 +1501,25 @@ const buildCliDoc = (program, oldVersion, newVersion) => {
1336
1501
  md = md.replaceAll(oldVersion, newVersion);
1337
1502
  fs.writeFileSync(`./src/client/public/nexodev/docs/references/Command Line Interface.md`, md, 'utf8');
1338
1503
  fs.writeFileSync(`./CLI-HELP.md`, md, 'utf8');
1339
- const readme = fs.readFileSync(`./README.md`, 'utf8');
1340
- fs.writeFileSync('./README.md', readme.replaceAll(oldVersion, newVersion), 'utf8');
1504
+
1505
+ // Update README.md: replace version and CLI index section between comment tags
1506
+ let readme = fs.readFileSync(`./README.md`, 'utf8');
1507
+ readme = readme.replaceAll(oldVersion, newVersion);
1508
+ const cliStartTag = '<!-- cli-index-start -->';
1509
+ const cliEndTag = '<!-- cli-index-end -->';
1510
+ const startIdx = readme.indexOf(cliStartTag);
1511
+ const endIdx = readme.indexOf(cliEndTag);
1512
+ if (startIdx !== -1 && endIdx !== -1) {
1513
+ readme =
1514
+ readme.substring(0, startIdx) +
1515
+ cliStartTag +
1516
+ '\n' +
1517
+ baseOptions +
1518
+ '\n' +
1519
+ cliEndTag +
1520
+ readme.substring(endIdx + cliEndTag.length);
1521
+ }
1522
+ fs.writeFileSync('./README.md', readme, 'utf8');
1341
1523
  };
1342
1524
 
1343
1525
  /**
@@ -1348,19 +1530,26 @@ const buildCliDoc = (program, oldVersion, newVersion) => {
1348
1530
  * @param {boolean} options.singleReplica - The single replica.
1349
1531
  * @param {Array} options.replicas - The replicas.
1350
1532
  * @param {string} options.redirect - The redirect.
1533
+ * @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).
1351
1534
  * @returns {object} - The instance context.
1352
1535
  * @memberof ServerConfBuilder
1353
1536
  */
1354
- const getInstanceContext = async (options = { deployId, singleReplica, replicas, redirect: '' }) => {
1355
- const { deployId, singleReplica, replicas, redirect } = options;
1537
+ const getInstanceContext = async (options = { deployId, singleReplica, replicas, redirect: '', peer: false }) => {
1538
+ const { deployId, singleReplica, replicas, redirect, peer } = options;
1356
1539
  let singleReplicaOffsetPortSum = 0;
1357
1540
 
1358
1541
  if (singleReplica && replicas && replicas.length > 0) {
1359
1542
  for (const replica of replicas) {
1360
1543
  const replicaDeployId = buildReplicaId({ deployId, replica });
1361
- const confReplicaServer = JSON.parse(
1362
- fs.readFileSync(`./engine-private/replica/${replicaDeployId}/conf.server.json`, 'utf8'),
1363
- );
1544
+ const replicaConfPath = `./engine-private/replica/${replicaDeployId}/conf.server.json`;
1545
+ if (!fs.existsSync(replicaConfPath)) {
1546
+ // Replica folder not built yet (e.g. dev mode without prior build);
1547
+ // estimate port offset: 1 per replica path + 1 extra if peer is enabled on the parent singleReplica config
1548
+ singleReplicaOffsetPortSum++;
1549
+ if (peer) singleReplicaOffsetPortSum++;
1550
+ continue;
1551
+ }
1552
+ const confReplicaServer = loadConfServerJson(replicaConfPath);
1364
1553
  for (const host of Object.keys(confReplicaServer)) {
1365
1554
  for (const path of Object.keys(confReplicaServer[host])) {
1366
1555
  singleReplicaOffsetPortSum++;
@@ -1480,15 +1669,7 @@ const isDeployRunnerContext = (path, options) => !options.build && path && path
1480
1669
  * @returns {boolean} - The dev proxy context.
1481
1670
  * @memberof ServerConfBuilder
1482
1671
  */
1483
- const isDevProxyContext = () => {
1484
- try {
1485
- if (process.argv[2] === 'proxy') return true;
1486
- if (!process.argv[6].startsWith('localhost')) return false;
1487
- return new URL('http://' + process.argv[6]).hostname ? true : false;
1488
- } catch {
1489
- return false;
1490
- }
1491
- };
1672
+ const isDevProxyContext = () => (process.argv.find((arg) => arg === 'proxy') ? true : false);
1492
1673
 
1493
1674
  /**
1494
1675
  * @method devProxyHostFactory
@@ -1501,10 +1682,14 @@ const isDevProxyContext = () => {
1501
1682
  * @returns {string} - The dev proxy host.
1502
1683
  * @memberof ServerConfBuilder
1503
1684
  */
1504
- const devProxyHostFactory = (options = { host: 'default.net', includeHttp: false, port: 80, tls: false }) =>
1505
- `${options.includeHttp ? (options.tls ? 'https://' : 'http://') : ''}${options.host ? options.host : 'localhost'}:${
1506
- (options.port ? options.port : options.tls ? 443 : 80) + parseInt(process.env.DEV_PROXY_PORT_OFFSET)
1507
- }`;
1685
+ const devProxyHostFactory = (options = { host: 'default.net', includeHttp: false, port: 80, tls: false }) => {
1686
+ const resolvedPort =
1687
+ (options.port ? options.port : options.tls ? 443 : 80) + parseInt(process.env.DEV_PROXY_PORT_OFFSET);
1688
+ const isDefaultPort = (options.tls && resolvedPort === 443) || (!options.tls && resolvedPort === 80);
1689
+ const protocol = options.includeHttp ? (options.tls ? 'https://' : 'http://') : '';
1690
+ const hostname = options.host ? options.host : 'localhost';
1691
+ return `${protocol}${hostname}${isDefaultPort ? '' : `:${resolvedPort}`}`;
1692
+ };
1508
1693
 
1509
1694
  /**
1510
1695
  * @method isTlsDevProxy
@@ -1512,7 +1697,7 @@ const devProxyHostFactory = (options = { host: 'default.net', includeHttp: false
1512
1697
  * @returns {boolean} - The TLS dev proxy status.
1513
1698
  * @memberof ServerConfBuilder
1514
1699
  */
1515
- const isTlsDevProxy = () => process.env.NODE_ENV !== 'production' && process.argv[7] === 'tls';
1700
+ const isTlsDevProxy = () => process.env.NODE_ENV !== 'production' && !!process.argv.find((arg) => arg === 'tls');
1516
1701
 
1517
1702
  /**
1518
1703
  * @method getTlsHosts
@@ -1524,17 +1709,52 @@ const isTlsDevProxy = () => process.env.NODE_ENV !== 'production' && process.arg
1524
1709
  const getTlsHosts = (confServer) =>
1525
1710
  Array.from(new Set(Object.keys(confServer).map((h) => new URL('https://' + h).hostname)));
1526
1711
 
1712
+ /**
1713
+ * Reads a `conf.server.json` file from disk, parses it, and resolves all `env:` secret
1714
+ * references using {@link resolveConfSecrets}.
1715
+ *
1716
+ * Reads and parses a `conf.server.json` file from disk. The `env:` secret
1717
+ * references are **preserved** by default so that build/deploy tooling never
1718
+ * accidentally strips them. Callers that need the actual secret values
1719
+ * (e.g. database or mailer modules) should explicitly wrap the result with
1720
+ * {@link resolveConfSecrets}.
1721
+ *
1722
+ * @method loadConfServerJson
1723
+ * @param {string} jsonPath - Absolute or relative path to the `conf.server.json` file.
1724
+ * @param {object} [options] - Optional settings.
1725
+ * @param {boolean} [options.resolve=false] - When `true`, resolves `env:` references
1726
+ * via {@link resolveConfSecrets} before returning.
1727
+ * @returns {object} The parsed server configuration object (secrets unresolved unless
1728
+ * `options.resolve` is `true`).
1729
+ * @throws {Error} If the file does not exist or cannot be parsed.
1730
+ * @memberof ServerConfBuilder
1731
+ *
1732
+ * @example
1733
+ * // Structure-only read (env: strings preserved)
1734
+ * const confServer = loadConfServerJson(`./engine-private/conf/${deployId}/conf.server.json`);
1735
+ *
1736
+ * @example
1737
+ * // Resolved read (env: strings replaced with process.env values)
1738
+ * const confServer = loadConfServerJson(`./engine-private/conf/${deployId}/conf.server.json`, { resolve: true });
1739
+ */
1740
+ const loadConfServerJson = (jsonPath, options) => {
1741
+ if (!fs.existsSync(jsonPath)) {
1742
+ throw new Error(`loadConfServerJson: configuration file not found: ${jsonPath}`);
1743
+ }
1744
+ const raw = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
1745
+ return options && options.resolve === true ? resolveConfSecrets(raw) : raw;
1746
+ };
1747
+
1527
1748
  export {
1528
- Cmd,
1529
1749
  Config,
1530
1750
  loadConf,
1531
1751
  loadReplicas,
1532
1752
  cloneConf,
1533
1753
  getCapVariableName,
1754
+ addClientConf,
1534
1755
  buildClientSrc,
1535
1756
  buildApiSrc,
1536
1757
  addApiConf,
1537
- addClientConf,
1538
1758
  addWsConf,
1539
1759
  buildWsSrc,
1540
1760
  cloneSrcComponents,
@@ -1542,7 +1762,6 @@ export {
1542
1762
  getDataDeploy,
1543
1763
  validateTemplatePath,
1544
1764
  buildReplicaId,
1545
- getCronBackUpFolder,
1546
1765
  mergeFile,
1547
1766
  getPathsSSR,
1548
1767
  buildKindPorts,
@@ -1564,4 +1783,12 @@ export {
1564
1783
  devProxyHostFactory,
1565
1784
  isTlsDevProxy,
1566
1785
  getTlsHosts,
1786
+ resolveConfSecrets,
1787
+ loadConfServerJson,
1788
+ getConfFolder,
1789
+ getConfFilePath,
1790
+ readConfJson,
1791
+ DEFAULT_DEPLOY_ID,
1792
+ loadCronDeployEnv,
1793
+ cronDeployIdResolve,
1567
1794
  };