cyberia 3.1.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 (208) 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 +13 -8
  10. package/CHANGELOG.md +433 -1
  11. package/CLI-HELP.md +57 -7
  12. package/Dockerfile +4 -2
  13. package/README.md +347 -22
  14. package/bin/build.js +5 -2
  15. package/bin/cyberia.js +1789 -112
  16. package/bin/deploy.js +177 -124
  17. package/bin/file.js +3 -0
  18. package/bin/index.js +1789 -112
  19. package/conf.js +64 -8
  20. package/deployment.yaml +92 -20
  21. package/hardhat/hardhat.config.js +13 -13
  22. package/hardhat/ignition/modules/ObjectLayerToken.js +1 -1
  23. package/hardhat/package-lock.json +2554 -5859
  24. package/hardhat/package.json +13 -22
  25. package/hardhat/scripts/deployObjectLayerToken.js +1 -1
  26. package/hardhat/test/ObjectLayerToken.js +4 -2
  27. package/hardhat/types/ethers-contracts/ObjectLayerToken.ts +690 -0
  28. package/hardhat/types/ethers-contracts/common.ts +92 -0
  29. package/hardhat/types/ethers-contracts/factories/ObjectLayerToken__factory.ts +1055 -0
  30. package/hardhat/types/ethers-contracts/factories/index.ts +4 -0
  31. package/hardhat/types/ethers-contracts/hardhat.d.ts +47 -0
  32. package/hardhat/types/ethers-contracts/index.ts +6 -0
  33. package/jsdoc.dd-cyberia.json +64 -55
  34. package/jsdoc.json +64 -55
  35. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +5 -4
  36. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +5 -4
  37. package/manifests/deployment/dd-cyberia-development/deployment.yaml +92 -20
  38. package/manifests/deployment/dd-cyberia-development/proxy.yaml +54 -18
  39. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  40. package/manifests/deployment/dd-test-development/deployment.yaml +88 -74
  41. package/manifests/deployment/dd-test-development/proxy.yaml +13 -4
  42. package/manifests/deployment/playwright/deployment.yaml +1 -1
  43. package/nodemon.json +1 -1
  44. package/package.json +22 -16
  45. package/proxy.yaml +54 -18
  46. package/scripts/rhel-grpc-setup.sh +56 -0
  47. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +44 -0
  48. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +16 -0
  49. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +5 -0
  50. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +80 -7
  51. package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +93 -0
  52. package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +36 -0
  53. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +29 -0
  54. package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +51 -0
  55. package/src/api/cyberia-entity/cyberia-entity.controller.js +74 -0
  56. package/src/api/cyberia-entity/cyberia-entity.model.js +24 -0
  57. package/src/api/cyberia-entity/cyberia-entity.router.js +27 -0
  58. package/src/api/cyberia-entity/cyberia-entity.service.js +42 -0
  59. package/src/api/cyberia-instance/cyberia-fallback-world.js +368 -0
  60. package/src/api/cyberia-instance/cyberia-instance.controller.js +92 -0
  61. package/src/api/cyberia-instance/cyberia-instance.model.js +84 -0
  62. package/src/api/cyberia-instance/cyberia-instance.router.js +63 -0
  63. package/src/api/cyberia-instance/cyberia-instance.service.js +191 -0
  64. package/src/api/cyberia-instance/cyberia-portal-connector.js +486 -0
  65. package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +74 -0
  66. package/src/api/cyberia-instance-conf/cyberia-instance-conf.defaults.js +413 -0
  67. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +228 -0
  68. package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +27 -0
  69. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +42 -0
  70. package/src/api/cyberia-map/cyberia-map.controller.js +79 -0
  71. package/src/api/cyberia-map/cyberia-map.model.js +30 -0
  72. package/src/api/cyberia-map/cyberia-map.router.js +40 -0
  73. package/src/api/cyberia-map/cyberia-map.service.js +74 -0
  74. package/src/api/file/file.ref.json +18 -0
  75. package/src/api/ipfs/ipfs.controller.js +4 -25
  76. package/src/api/ipfs/ipfs.model.js +43 -34
  77. package/src/api/ipfs/ipfs.router.js +8 -13
  78. package/src/api/ipfs/ipfs.service.js +54 -102
  79. package/src/api/object-layer/README.md +347 -22
  80. package/src/api/object-layer/object-layer.router.js +30 -0
  81. package/src/api/object-layer/object-layer.service.js +114 -31
  82. package/src/api/user/user.service.js +8 -7
  83. package/src/cli/cluster.js +7 -7
  84. package/src/cli/db.js +710 -827
  85. package/src/cli/deploy.js +151 -93
  86. package/src/cli/env.js +29 -0
  87. package/src/cli/fs.js +5 -2
  88. package/src/cli/index.js +48 -2
  89. package/src/cli/kubectl.js +211 -0
  90. package/src/cli/release.js +284 -0
  91. package/src/cli/repository.js +438 -75
  92. package/src/cli/run.js +195 -35
  93. package/src/cli/secrets.js +73 -0
  94. package/src/cli/test.js +3 -3
  95. package/src/client/Cryptokoyn.index.js +3 -4
  96. package/src/client/CyberiaPortal.index.js +3 -4
  97. package/src/client/Default.index.js +3 -4
  98. package/src/client/Itemledger.index.js +3 -4
  99. package/src/client/Underpost.index.js +3 -4
  100. package/src/client/components/core/AppStore.js +69 -0
  101. package/src/client/components/core/CalendarCore.js +2 -2
  102. package/src/client/components/core/DropDown.js +137 -17
  103. package/src/client/components/core/Keyboard.js +2 -2
  104. package/src/client/components/core/LogIn.js +2 -2
  105. package/src/client/components/core/LogOut.js +2 -2
  106. package/src/client/components/core/Modal.js +0 -1
  107. package/src/client/components/core/Panel.js +0 -1
  108. package/src/client/components/core/PanelForm.js +19 -19
  109. package/src/client/components/core/SocketIo.js +82 -29
  110. package/src/client/components/core/SocketIoHandler.js +75 -0
  111. package/src/client/components/core/Stream.js +143 -95
  112. package/src/client/components/core/Webhook.js +40 -7
  113. package/src/client/components/cryptokoyn/AppStoreCryptokoyn.js +5 -0
  114. package/src/client/components/cryptokoyn/LogInCryptokoyn.js +3 -3
  115. package/src/client/components/cryptokoyn/LogOutCryptokoyn.js +2 -2
  116. package/src/client/components/cryptokoyn/MenuCryptokoyn.js +3 -3
  117. package/src/client/components/cryptokoyn/SocketIoCryptokoyn.js +3 -51
  118. package/src/client/components/cyberia/InstanceEngineCyberia.js +700 -0
  119. package/src/client/components/cyberia/MapEngineCyberia.js +1359 -2
  120. package/src/client/components/cyberia/ObjectLayerEngineModal.js +17 -6
  121. package/src/client/components/cyberia/ObjectLayerEngineViewer.js +92 -54
  122. package/src/client/components/cyberia-portal/AppStoreCyberiaPortal.js +5 -0
  123. package/src/client/components/cyberia-portal/CommonCyberiaPortal.js +216 -30
  124. package/src/client/components/cyberia-portal/LogInCyberiaPortal.js +3 -3
  125. package/src/client/components/cyberia-portal/LogOutCyberiaPortal.js +2 -2
  126. package/src/client/components/cyberia-portal/MenuCyberiaPortal.js +40 -7
  127. package/src/client/components/cyberia-portal/RoutesCyberiaPortal.js +4 -0
  128. package/src/client/components/cyberia-portal/SocketIoCyberiaPortal.js +3 -49
  129. package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +4 -0
  130. package/src/client/components/default/AppStoreDefault.js +5 -0
  131. package/src/client/components/default/LogInDefault.js +3 -3
  132. package/src/client/components/default/LogOutDefault.js +2 -2
  133. package/src/client/components/default/MenuDefault.js +5 -5
  134. package/src/client/components/default/SocketIoDefault.js +3 -51
  135. package/src/client/components/itemledger/AppStoreItemledger.js +5 -0
  136. package/src/client/components/itemledger/LogInItemledger.js +3 -3
  137. package/src/client/components/itemledger/LogOutItemledger.js +2 -2
  138. package/src/client/components/itemledger/MenuItemledger.js +3 -3
  139. package/src/client/components/itemledger/SocketIoItemledger.js +3 -51
  140. package/src/client/components/underpost/AppStoreUnderpost.js +5 -0
  141. package/src/client/components/underpost/LogInUnderpost.js +3 -3
  142. package/src/client/components/underpost/LogOutUnderpost.js +2 -2
  143. package/src/client/components/underpost/MenuUnderpost.js +5 -5
  144. package/src/client/components/underpost/SocketIoUnderpost.js +3 -51
  145. package/src/client/services/core/core.service.js +20 -8
  146. package/src/client/services/cyberia-dialogue/cyberia-dialogue.service.js +105 -0
  147. package/src/client/services/cyberia-entity/cyberia-entity.management.js +57 -0
  148. package/src/client/services/cyberia-entity/cyberia-entity.service.js +105 -0
  149. package/src/client/services/cyberia-instance/cyberia-instance.management.js +194 -0
  150. package/src/client/services/cyberia-instance/cyberia-instance.service.js +122 -0
  151. package/src/client/services/cyberia-instance-conf/cyberia-instance-conf.service.js +105 -0
  152. package/src/client/services/cyberia-map/cyberia-map.management.js +193 -0
  153. package/src/client/services/cyberia-map/cyberia-map.service.js +126 -0
  154. package/src/client/services/instance/instance.management.js +2 -2
  155. package/src/client/services/ipfs/ipfs.service.js +3 -23
  156. package/src/client/services/object-layer/object-layer.management.js +3 -3
  157. package/src/client/services/object-layer/object-layer.service.js +21 -0
  158. package/src/client/services/user/user.management.js +2 -2
  159. package/src/client/ssr/pages/CyberiaServerMetrics.js +1 -1
  160. package/src/grpc/cyberia/OFF_CHAIN_ECONOMY.md +305 -0
  161. package/src/grpc/cyberia/README.md +326 -0
  162. package/src/grpc/cyberia/grpc-server.js +530 -0
  163. package/src/index.js +24 -1
  164. package/src/runtime/express/Dockerfile +4 -0
  165. package/src/runtime/express/Express.js +18 -1
  166. package/src/runtime/lampp/Dockerfile +13 -2
  167. package/src/runtime/lampp/Lampp.js +27 -4
  168. package/src/runtime/wp/Dockerfile +68 -0
  169. package/src/runtime/wp/Wp.js +639 -0
  170. package/src/server/auth.js +24 -1
  171. package/src/server/backup.js +37 -9
  172. package/src/server/client-build-docs.js +9 -2
  173. package/src/server/client-build.js +31 -31
  174. package/src/server/client-formatted.js +109 -57
  175. package/src/server/conf.js +24 -9
  176. package/src/server/cron.js +25 -23
  177. package/src/server/dns.js +2 -1
  178. package/src/server/ipfs-client.js +24 -1
  179. package/src/server/object-layer.js +149 -108
  180. package/src/server/peer.js +8 -0
  181. package/src/server/runtime.js +25 -1
  182. package/src/server/semantic-layer-generator-floor.js +359 -0
  183. package/src/server/semantic-layer-generator-skin.js +1294 -0
  184. package/src/server/semantic-layer-generator.js +116 -555
  185. package/src/server/start.js +2 -2
  186. package/src/ws/IoInterface.js +1 -10
  187. package/src/ws/IoServer.js +14 -33
  188. package/src/ws/core/channels/core.ws.chat.js +65 -20
  189. package/src/ws/core/channels/core.ws.mailer.js +113 -32
  190. package/src/ws/core/channels/core.ws.stream.js +90 -31
  191. package/src/ws/core/core.ws.connection.js +12 -33
  192. package/src/ws/core/core.ws.emit.js +10 -26
  193. package/src/ws/core/core.ws.server.js +25 -58
  194. package/src/ws/default/channels/default.ws.main.js +53 -12
  195. package/src/ws/default/default.ws.connection.js +26 -13
  196. package/src/ws/default/default.ws.server.js +30 -12
  197. package/src/client/components/cryptokoyn/CommonCryptokoyn.js +0 -29
  198. package/src/client/components/cryptokoyn/ElementsCryptokoyn.js +0 -38
  199. package/src/client/components/cyberia-portal/ElementsCyberiaPortal.js +0 -38
  200. package/src/client/components/default/ElementsDefault.js +0 -38
  201. package/src/client/components/itemledger/CommonItemledger.js +0 -29
  202. package/src/client/components/itemledger/ElementsItemledger.js +0 -38
  203. package/src/client/components/underpost/CommonUnderpost.js +0 -29
  204. package/src/client/components/underpost/ElementsUnderpost.js +0 -38
  205. package/src/ws/core/management/core.ws.chat.js +0 -8
  206. package/src/ws/core/management/core.ws.mailer.js +0 -16
  207. package/src/ws/core/management/core.ws.stream.js +0 -8
  208. package/src/ws/default/management/default.ws.main.js +0 -8
@@ -6,7 +6,6 @@
6
6
 
7
7
  import fs from 'fs-extra';
8
8
  import { loggerFactory } from './logger.js';
9
- import { shellExec } from './process.js';
10
9
  import Underpost from '../index.js';
11
10
  import { loadCronDeployEnv } from './conf.js';
12
11
 
@@ -21,16 +20,24 @@ class BackUp {
21
20
  /**
22
21
  * @method callback
23
22
  * @description Initiates a backup operation for the specified deployment list.
24
- * @param {string} deployList - The list of deployments to backup.
23
+ * Orchestrates two backup phases per deployment:
24
+ * 1. Database export (MariaDB / MongoDB dump via `node bin db --export`).
25
+ * 2. Repository backup (git commit+push inside the deployment pod via `node bin db --repo-backup`).
26
+ *
27
+ * Commands are always forwarded to the host node via SSH because the CronJob
28
+ * container itself has no kubectl access. GITHUB_TOKEN and GITHUB_USERNAME
29
+ * are passed as ephemeral inline env vars so they never touch the host filesystem.
30
+ *
31
+ * @param {string} deployList - Comma-separated list of deployment IDs.
25
32
  * @param {Object} options - The options for the backup operation.
26
33
  * @param {boolean} options.git - Whether to backup data using Git.
27
34
  * @param {boolean} [options.k3s] - Use k3s cluster context.
28
35
  * @param {boolean} [options.kind] - Use kind cluster context.
29
36
  * @param {boolean} [options.kubeadm] - Use kubeadm cluster context.
30
- * @param {boolean} [options.ssh] - Execute backup commands via SSH on the remote node.
31
37
  * @memberof UnderpostBakcUp
32
38
  */
33
39
  static callback = async function (deployList, options = { git: false }) {
40
+ const firstDeployId = deployList && deployList !== 'dd' ? deployList.split(',')[0].trim() : '';
34
41
  loadCronDeployEnv();
35
42
  if ((!deployList || deployList === 'dd') && fs.existsSync(`./engine-private/deploy/dd.router`))
36
43
  deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').trim();
@@ -43,19 +50,40 @@ class BackUp {
43
50
  for (const _deployId of deployList.split(',')) {
44
51
  const deployId = _deployId.trim();
45
52
  if (!deployId) continue;
53
+ const dbCommand = `node bin db ${options.git ? '--git --force-clone ' : ''}--export --primary-pod --preserveUUID${clusterFlag} ${deployId}`;
54
+ const repoCommand = `node bin db --repo-backup${clusterFlag} ${deployId}`;
46
55
 
47
- const command = `node bin db ${options.git ? '--git --force-clone ' : ''}--export --primary-pod${clusterFlag} ${deployId}`;
56
+ // Pass GITHUB_TOKEN and GITHUB_USERNAME ephemerally through the SSH command
57
+ // so git operations can push backups without relying on host env files.
58
+ const envPrefix = [
59
+ process.env.GITHUB_TOKEN ? `GITHUB_TOKEN=${process.env.GITHUB_TOKEN}` : '',
60
+ process.env.GITHUB_USERNAME ? `GITHUB_USERNAME=${process.env.GITHUB_USERNAME}` : '',
61
+ ]
62
+ .filter(Boolean)
63
+ .join(' ');
64
+ const prefixCmd = (cmd) => (envPrefix ? `${envPrefix} ${cmd}` : cmd);
48
65
 
49
- if (options.ssh) {
66
+ try {
50
67
  logger.info('Executing database export via SSH for', deployId);
51
- await Underpost.ssh.sshRemoteRunner(command, {
68
+ await Underpost.ssh.sshRemoteRunner(prefixCmd(dbCommand), {
52
69
  remote: true,
53
70
  useSudo: true,
54
71
  cd: '/home/dd/engine',
55
72
  });
56
- } else {
57
- logger.info('Executing database export for', deployId);
58
- shellExec(command);
73
+ } catch (err) {
74
+ logger.error(`Error during database export for ${deployId}:`, err);
75
+ }
76
+
77
+ // Repository backup: Cron container → SSH to host → host finds pod → kubectl exec git backup
78
+ try {
79
+ logger.info('Executing repository backup via SSH for', deployId);
80
+ await Underpost.ssh.sshRemoteRunner(prefixCmd(repoCommand), {
81
+ remote: true,
82
+ useSudo: true,
83
+ cd: '/home/dd/engine',
84
+ });
85
+ } catch (err) {
86
+ logger.error(`Error during repository backup for ${deployId}:`, err);
59
87
  }
60
88
  }
61
89
  };
@@ -394,6 +394,7 @@ const buildCoverage = async ({ host, path, docs }) => {
394
394
  const jsDocSourcePath = docs.jsJsonPath;
395
395
  const jsDocsConfig = JSON.parse(fs.readFileSync(jsDocSourcePath, 'utf8'));
396
396
  const coveragePath = docs.coveragePath;
397
+ const coverageOutputDir = docs.coverageOutputDir || 'coverage';
397
398
 
398
399
  const coverageOutputPath = `${coveragePath}/coverage`;
399
400
  if (!fs.existsSync(coverageOutputPath)) {
@@ -411,9 +412,15 @@ const buildCoverage = async ({ host, path, docs }) => {
411
412
  }
412
413
 
413
414
  if (fs.existsSync(coverageOutputPath) && fs.readdirSync(coverageOutputPath).length > 0) {
414
- const coverageBuildPath = `${jsDocsConfig.opts.destination}coverage`;
415
+ const coverageBuildPath = `${jsDocsConfig.opts.destination}${coverageOutputDir}`;
415
416
  fs.mkdirSync(coverageBuildPath, { recursive: true });
416
- fs.copySync(coverageOutputPath, coverageBuildPath);
417
+ // Hardhat 3 outputs HTML to coverage/html/; Hardhat 2 / c8 output directly to coverage/
418
+ const coverageHtmlSubdir = `${coverageOutputPath}/html`;
419
+ if (fs.existsSync(coverageHtmlSubdir) && fs.existsSync(`${coverageHtmlSubdir}/index.html`)) {
420
+ fs.copySync(coverageHtmlSubdir, coverageBuildPath);
421
+ } else {
422
+ fs.copySync(coverageOutputPath, coverageBuildPath);
423
+ }
417
424
  logger.warn('build coverage', coverageBuildPath);
418
425
  } else {
419
426
  logger.warn('no coverage output found, skipping', coverageOutputPath);
@@ -7,7 +7,7 @@
7
7
  'use strict';
8
8
 
9
9
  import fs from 'fs-extra';
10
- import { srcFormatted, componentFormatted, viewFormatted, JSONweb } from './client-formatted.js';
10
+ import { transformClientJs, JSONweb } from './client-formatted.js';
11
11
  import { loggerFactory } from './logger.js';
12
12
  import {
13
13
  getCapVariableName,
@@ -16,7 +16,6 @@ import {
16
16
  uniqueArray,
17
17
  } from '../client/components/core/CommonJs.js';
18
18
  import { readConfJson } from './conf.js';
19
- import UglifyJS from 'uglify-js';
20
19
  import { minify } from 'html-minifier-terser';
21
20
  import AdmZip from 'adm-zip';
22
21
  import * as dir from 'path';
@@ -317,7 +316,8 @@ const buildClient = async (
317
316
  shellExec(`cd /home/dd && git clone https://github.com/designmodo/html-website-templates.git`);
318
317
  if (!fs.existsSync(`${rootClientPath}/index.php`)) {
319
318
  fs.copySync(`/home/dd/html-website-templates/${publicClientId.split('-publicClientId-')[1]}`, rootClientPath);
320
- shellExec(`cd ${rootClientPath} && git init && git add . && git commit -m "Base template implementation"`);
319
+ Underpost.repo.initLocalRepo({ path: rootClientPath });
320
+ shellExec(`cd ${rootClientPath} && git add . && git commit -m "Base template implementation"`);
321
321
  // git remote add origin git@github.com:<username>/<repo>.git
322
322
  fs.writeFileSync(`${rootClientPath}/.git/.htaccess`, `Deny from all`, 'utf8');
323
323
  }
@@ -447,15 +447,15 @@ const buildClient = async (
447
447
 
448
448
  if (enableLiveRebuild && !options.liveClientBuildPaths.find((p) => p.srcBuildPath === jsSrcPath)) continue;
449
449
 
450
- const jsSrc = componentFormatted(
451
- await srcFormatted(fs.readFileSync(jsSrcPath, 'utf8')),
452
- module,
450
+ const jsSrc = await transformClientJs(jsSrcPath, {
453
451
  dists,
454
- path,
455
- 'components',
452
+ proxyPath: path,
453
+ basePath: 'components',
454
+ module,
456
455
  baseHost,
457
- );
458
- fs.writeFileSync(jsPublicPath, minifyBuild ? UglifyJS.minify(jsSrc).code : jsSrc, 'utf8');
456
+ minify: minifyBuild,
457
+ });
458
+ fs.writeFileSync(jsPublicPath, jsSrc, 'utf8');
459
459
  }
460
460
  }
461
461
 
@@ -469,15 +469,15 @@ const buildClient = async (
469
469
  const jsPublicPath = `${rootClientPath}/services/${module}/${module}.service.js`;
470
470
  if (enableLiveRebuild && !options.liveClientBuildPaths.find((p) => p.srcBuildPath === jsSrcPath)) continue;
471
471
 
472
- let jsSrc = componentFormatted(
473
- await srcFormatted(fs.readFileSync(jsSrcPath, 'utf8')),
474
- module,
472
+ const jsSrc = await transformClientJs(jsSrcPath, {
475
473
  dists,
476
- path,
477
- 'services',
474
+ proxyPath: path,
475
+ basePath: 'services',
476
+ module,
478
477
  baseHost,
479
- );
480
- fs.writeFileSync(jsPublicPath, minifyBuild ? UglifyJS.minify(jsSrc).code : jsSrc, 'utf8');
478
+ minify: minifyBuild,
479
+ });
480
+ fs.writeFileSync(jsPublicPath, jsSrc, 'utf8');
481
481
  }
482
482
  }
483
483
 
@@ -487,15 +487,15 @@ const buildClient = async (
487
487
  const jsPublicPath = `${rootClientPath}/services/${module}/${module}.management.js`;
488
488
  if (enableLiveRebuild && !options.liveClientBuildPaths.find((p) => p.srcBuildPath === jsSrcPath)) continue;
489
489
 
490
- const jsSrc = componentFormatted(
491
- await srcFormatted(fs.readFileSync(jsSrcPath, 'utf8')),
492
- module,
490
+ const jsSrc = await transformClientJs(jsSrcPath, {
493
491
  dists,
494
- path,
495
- 'services',
492
+ proxyPath: path,
493
+ basePath: 'services',
494
+ module,
496
495
  baseHost,
497
- );
498
- fs.writeFileSync(jsPublicPath, minifyBuild ? UglifyJS.minify(jsSrc).code : jsSrc, 'utf8');
496
+ minify: minifyBuild,
497
+ });
498
+ fs.writeFileSync(jsPublicPath, jsSrc, 'utf8');
499
499
  }
500
500
  }
501
501
  }
@@ -513,9 +513,9 @@ const buildClient = async (
513
513
  const jsPublicPath = `${rootClientPath}/sw.js`;
514
514
 
515
515
  if (!(enableLiveRebuild && !options.liveClientBuildPaths.find((p) => p.srcBuildPath === jsSrcPath))) {
516
- const jsSrc = viewFormatted(await srcFormatted(fs.readFileSync(jsSrcPath, 'utf8')), dists, path, baseHost);
516
+ const jsSrc = await transformClientJs(jsSrcPath, { dists, proxyPath: path, baseHost, minify: minifyBuild });
517
517
 
518
- fs.writeFileSync(jsPublicPath, minifyBuild ? UglifyJS.minify(jsSrc).code : jsSrc, 'utf8');
518
+ fs.writeFileSync(jsPublicPath, jsSrc, 'utf8');
519
519
  }
520
520
 
521
521
  if (
@@ -535,14 +535,14 @@ const buildClient = async (
535
535
 
536
536
  logger.info('View build', buildPath);
537
537
 
538
- const jsSrc = viewFormatted(
539
- await srcFormatted(fs.readFileSync(`./src/client/${view.client}.index.js`, 'utf8')),
538
+ const jsSrc = await transformClientJs(`./src/client/${view.client}.index.js`, {
540
539
  dists,
541
- path,
540
+ proxyPath: path,
542
541
  baseHost,
543
- );
542
+ minify: minifyBuild,
543
+ });
544
544
 
545
- fs.writeFileSync(`${buildPath}${buildId}.js`, minifyBuild ? UglifyJS.minify(jsSrc).code : jsSrc, 'utf8');
545
+ fs.writeFileSync(`${buildPath}${buildId}.js`, jsSrc, 'utf8');
546
546
  const title = metadata.title ? metadata.title : title;
547
547
 
548
548
  const canonicalURL = `https://${host}${path}${
@@ -1,87 +1,139 @@
1
1
  /**
2
- * Module for formatting client-side code
2
+ * Module for formatting client-side code using esbuild for import rewriting and minification.
3
3
  * @module src/server/client-formatted.js
4
4
  * @namespace clientFormatted
5
5
  */
6
6
 
7
7
  'use strict';
8
8
 
9
+ import * as esbuild from 'esbuild';
10
+ import fs from 'fs-extra';
11
+ import * as path from 'path';
12
+
13
+ /**
14
+ * Escapes a string for safe use inside a RegExp.
15
+ * @param {string} s - The string to escape.
16
+ * @returns {string} The escaped string.
17
+ * @memberof clientFormatted
18
+ */
19
+ const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
20
+
9
21
  /**
10
- * Formats a source code string by removing 'html`' and 'css`' tags from template literals.
22
+ * Formats a source code string by removing 'html`' and 'css`' tagged template prefixes.
23
+ * Used for SSR VM execution where the full esbuild pipeline is not needed.
11
24
  * @param {string} src - The source code string.
12
25
  * @returns {string} The formatted source code.
13
26
  * @memberof clientFormatted
14
27
  */
15
- const srcFormatted = (src) =>
16
- src
17
- .replaceAll(' html`', '`')
18
- .replaceAll(' css`', '`')
19
- .replaceAll('{html`', '{`')
20
- .replaceAll('{css`', '{`')
21
- .replaceAll('(html`', '(`')
22
- .replaceAll('(css`', '(`')
23
- .replaceAll('[html`', '[`')
24
- .replaceAll('[css`', '[`');
28
+ const srcFormatted = (src) => src.replace(/(?<=[\s({[,;=+!?:^])(html|css)`/g, '`');
25
29
 
26
30
  /**
27
31
  * Converts a JavaScript object into a string that can be embedded in client-side code
28
32
  * and parsed back into an object (e.g., 'JSON.parse(`{...}`)').
33
+ * Escapes backticks and template expression markers for safe template literal embedding.
29
34
  * @param {*} data - The data to be stringified.
30
35
  * @returns {string} A string representing the code to parse the JSON data.
31
36
  * @memberof clientFormatted
32
37
  */
33
- const JSONweb = (data) => 'JSON.parse(`' + JSON.stringify(data) + '`)';
38
+ const JSONweb = (data) => {
39
+ const json = JSON.stringify(data).replace(/`/g, '\\`').replace(/\$\{/g, '\\${');
40
+ return 'JSON.parse(`' + json + '`)';
41
+ };
34
42
 
35
43
  /**
36
- * Formats a component's source code by rewriting its import paths to be absolute for browser consumption.
37
- * @param {string} src - The source code of the component.
38
- * @param {string} module - The name of the module/component.
39
- * @param {Array<object>} dists - An array of distribution objects with import names.
40
- * @param {string} proxyPath - The proxy path for the application.
41
- * @param {string} [componentBasePath=''] - The base path for components.
42
- * @param {string} [baseHost=''] - The base host URL.
43
- * @returns {string} The formatted source code with updated import paths.
44
+ * Creates an esbuild plugin that rewrites import paths for browser consumption.
45
+ * Handles dist library imports, relative imports, and marks all remaining imports as external.
46
+ * @param {object} options
47
+ * @param {Array<object>} [options.dists=[]] - Distribution objects with import_name and import_name_build.
48
+ * @param {string} options.proxyPath - The proxy path for the application.
49
+ * @param {string} [options.basePath=''] - The base path for the module type (e.g., 'components', 'services').
50
+ * @param {string} [options.module=''] - The module/component name for relative import resolution.
51
+ * @param {string} [options.baseHost=''] - The base host URL.
52
+ * @returns {import('esbuild').Plugin}
44
53
  * @memberof clientFormatted
45
54
  */
46
- const componentFormatted = (src, module, dists, proxyPath, componentBasePath = '', baseHost = '') => {
47
- dists.map(
48
- (dist) =>
49
- (src = src.replaceAll(
50
- `from '${dist.import_name}'`,
51
- `from '${baseHost}${proxyPath !== '/' ? `${proxyPath}` : ''}${dist.import_name_build}'`,
52
- )),
53
- );
54
- return src
55
- .replaceAll(
56
- `from '../`,
57
- `from '${baseHost}${proxyPath !== '/' ? `${proxyPath}/` : '/'}${
58
- componentBasePath === '' ? `` : `${componentBasePath}/`
59
- }`,
60
- )
61
- .replaceAll(
62
- `from './`,
63
- `from '${baseHost}${proxyPath !== '/' ? `${proxyPath}/` : '/'}${
64
- componentBasePath === '' ? `` : `${componentBasePath}/`
65
- }${module}/`,
66
- );
67
- };
55
+ const importRewritePlugin = ({ dists = [], proxyPath, basePath = '', module = '', baseHost = '' }) => ({
56
+ name: 'import-rewrite',
57
+ setup(build) {
58
+ const prefix = `${baseHost}${proxyPath !== '/' ? `${proxyPath}/` : '/'}`;
59
+
60
+ // Rewrite dist library imports (e.g., '@neodrag/vanilla' '/proxyPath/dist/@neodrag-vanilla/index.js')
61
+ if (dists) {
62
+ for (const dist of dists) {
63
+ if (!dist.import_name) continue;
64
+ const filter = new RegExp(`^${escapeRegExp(dist.import_name)}$`);
65
+ build.onResolve({ filter }, () => ({
66
+ path: `${baseHost}${proxyPath !== '/' ? proxyPath : ''}${dist.import_name_build}`,
67
+ external: true,
68
+ }));
69
+ }
70
+ }
71
+
72
+ // Rewrite relative imports to absolute paths based on proxy path and module
73
+ build.onResolve({ filter: /^\.\.?\// }, (args) => {
74
+ const basePrefix = `${prefix}${basePath ? `${basePath}/` : ''}`;
75
+ if (args.path.startsWith('./')) {
76
+ return {
77
+ path: `${basePrefix}${module ? `${module}/` : ''}${args.path.slice(2)}`,
78
+ external: true,
79
+ };
80
+ }
81
+ if (args.path.startsWith('../')) {
82
+ return {
83
+ path: `${basePrefix}${args.path.slice(3)}`,
84
+ external: true,
85
+ };
86
+ }
87
+ });
88
+
89
+ // Mark any remaining imports as external
90
+ build.onResolve({ filter: /.*/ }, (args) => {
91
+ if (args.kind === 'entry-point') return;
92
+ return { path: args.path, external: true };
93
+ });
94
+ },
95
+ });
68
96
 
69
97
  /**
70
- * Formats a view's source code by rewriting its import paths.
71
- * @param {string} src - The source code of the view.
72
- * @param {Array<object>} dists - An array of distribution objects with import names.
73
- * @param {string} proxyPath - The proxy path for the application.
74
- * @param {string} [baseHost=''] - The base host URL.
75
- * @returns {string} The formatted source code with updated import paths.
98
+ * Transforms a JavaScript source file using esbuild with import path rewriting,
99
+ * tagged template stripping, and optional minification.
100
+ * Replaces the previous srcFormatted + componentFormatted/viewFormatted + UglifyJS pipeline.
101
+ * @param {string} srcPath - Path to the source file.
102
+ * @param {object} options
103
+ * @param {Array<object>} [options.dists=[]] - Distribution objects with import names.
104
+ * @param {string} options.proxyPath - The proxy path for the application.
105
+ * @param {string} [options.basePath=''] - Base path for the module type (e.g., 'components', 'services').
106
+ * @param {string} [options.module=''] - Module name for relative import resolution.
107
+ * @param {string} [options.baseHost=''] - Base host URL.
108
+ * @param {boolean} [options.minify=false] - Whether to minify the output.
109
+ * @returns {Promise<string>} The transformed source code.
76
110
  * @memberof clientFormatted
77
111
  */
78
- const viewFormatted = (src, dists, proxyPath, baseHost = '') => {
79
- dists.map(
80
- (dist) =>
81
- (src = src.replaceAll(dist.import_name, `${proxyPath !== '/' ? `${proxyPath}` : ''}${dist.import_name_build}`)),
82
- );
83
- const componentFromFormatted = `from '${baseHost}${proxyPath !== '/' ? `${proxyPath}/` : '/'}`;
84
- return src.replaceAll(`from './`, componentFromFormatted).replaceAll(`from '../`, componentFromFormatted);
112
+ const transformClientJs = async (
113
+ srcPath,
114
+ { dists = [], proxyPath, basePath = '', module = '', baseHost = '', minify: shouldMinify = false } = {},
115
+ ) => {
116
+ const src = fs.readFileSync(srcPath, 'utf8');
117
+ const stripped = srcFormatted(src);
118
+
119
+ const result = await esbuild.build({
120
+ stdin: {
121
+ contents: stripped,
122
+ loader: 'js',
123
+ resolveDir: path.dirname(path.resolve(srcPath)),
124
+ sourcefile: srcPath,
125
+ },
126
+ bundle: true,
127
+ write: false,
128
+ format: 'esm',
129
+ platform: 'browser',
130
+ target: 'esnext',
131
+ minify: shouldMinify,
132
+ logLevel: 'warning',
133
+ plugins: [importRewritePlugin({ dists, proxyPath, basePath, module, baseHost })],
134
+ });
135
+
136
+ return result.outputFiles[0].text;
85
137
  };
86
138
 
87
- export { srcFormatted, JSONweb, componentFormatted, viewFormatted };
139
+ export { srcFormatted, JSONweb, transformClientJs };
@@ -148,6 +148,23 @@ const getConfFolder = (deployId) => {
148
148
  : `./engine-private/conf/${deployId}`;
149
149
  };
150
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
+
151
168
  /**
152
169
  * Loads the deployment-specific `.env` file referenced by `engine-private/deploy/dd.cron`
153
170
  * into `process.env`. Uses `NODE_ENV` to select the environment variant
@@ -162,15 +179,12 @@ function loadCronDeployEnv() {
162
179
  const envName = process.env.NODE_ENV || 'production';
163
180
 
164
181
  // 1) Load dd.cron env (takes full precedence)
165
- const cronDeployFile = './engine-private/deploy/dd.cron';
166
- if (fs.existsSync(cronDeployFile)) {
167
- const cronDeployId = fs.readFileSync(cronDeployFile, 'utf8').trim();
168
- if (cronDeployId) {
169
- const cronEnvPath = `./engine-private/conf/${cronDeployId}/.env.${envName}`;
170
- if (fs.existsSync(cronEnvPath)) {
171
- const cronEnv = dotenv.parse(fs.readFileSync(cronEnvPath, 'utf8'));
172
- process.env = { ...process.env, ...cronEnv };
173
- }
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 };
174
188
  }
175
189
  }
176
190
 
@@ -1776,4 +1790,5 @@ export {
1776
1790
  readConfJson,
1777
1791
  DEFAULT_DEPLOY_ID,
1778
1792
  loadCronDeployEnv,
1793
+ cronDeployIdResolve,
1779
1794
  };
@@ -16,7 +16,7 @@ const volumeHostPath = '/home/dd';
16
16
  const enginePath = '/home/dd/engine';
17
17
  const cronVolumeName = 'underpost-cron-container-volume';
18
18
  const shareEnvVolumeName = 'underpost-share-env';
19
- const underpostContainerEnvPath = '/usr/lib/node_modules/underpost/.env';
19
+ const underpostContainerEnvDir = '/usr/lib/node_modules/underpost';
20
20
 
21
21
  /**
22
22
  * Generates a Kubernetes CronJob YAML manifest string.
@@ -33,7 +33,9 @@ const underpostContainerEnvPath = '/usr/lib/node_modules/underpost/.env';
33
33
  * @param {string} [params.cmd] - Optional pre-script commands to run before cron execution
34
34
  * @param {boolean} [params.suspend=false] - Whether the CronJob is suspended
35
35
  * @param {boolean} [params.dryRun=false] - Pass --dry-run flag to the cron command inside the container
36
- * @param {boolean} [params.ssh=false] - Execute backup commands via SSH on the remote node
36
+ * @param {boolean} [params.k3s=false] - Pass --k3s flag to the cron command inside the container
37
+ * @param {boolean} [params.kind=false] - Pass --kind flag to the cron command inside the container
38
+ * @param {boolean} [params.kubeadm=false] - Pass --kubeadm flag to the cron command inside the container
37
39
  * @returns {string} Kubernetes CronJob YAML manifest
38
40
  * @memberof UnderpostCron
39
41
  */
@@ -49,7 +51,9 @@ const cronJobYamlFactory = ({
49
51
  cmd,
50
52
  suspend = false,
51
53
  dryRun = false,
52
- ssh = false,
54
+ k3s = false,
55
+ kind = false,
56
+ kubeadm = false,
53
57
  }) => {
54
58
  const containerImage = image || `underpost/underpost-engine:${Underpost.version}`;
55
59
 
@@ -60,10 +64,12 @@ const cronJobYamlFactory = ({
60
64
  .replace(/^-|-$/g, '')
61
65
  .substring(0, 52);
62
66
 
63
- const cmdPart = cmd ? `${cmd} && ` : '';
64
- const cronBin = dev ? 'node bin' : 'underpost';
65
- const flags = `${git ? '--git ' : ''}${dev ? '--dev ' : ''}${dryRun ? '--dry-run ' : ''}${ssh ? '--ssh ' : ''}`;
66
- const cronCommand = `${cmdPart}${cronBin} cron ${flags}${deployList} ${jobList}`;
67
+ const cronBin = 'node bin'; // dev ? 'node bin' : 'underpost';
68
+ const flags = `${git ? '--git ' : ''}${dev ? '--dev ' : ''}${dryRun ? '--dry-run ' : ''}${k3s ? '--k3s ' : ''}${kind ? '--kind ' : ''}${kubeadm ? '--kubeadm ' : ''}`;
69
+ const commands = [`cd ${enginePath}`]; // `node bin run secret`
70
+ if (cmd) commands.push(cmd);
71
+ commands.push(`${cronBin} cron ${deployList} ${jobList} ${flags}`);
72
+ const fullCommand = commands.join(' &&\n ');
67
73
 
68
74
  return `apiVersion: batch/v1
69
75
  kind: CronJob
@@ -95,13 +101,12 @@ spec:
95
101
  - /bin/sh
96
102
  - -c
97
103
  - >
98
- ${cronCommand}
104
+ ${fullCommand}
99
105
  volumeMounts:
100
106
  - mountPath: ${enginePath}
101
107
  name: ${cronVolumeName}
102
- - mountPath: ${underpostContainerEnvPath}
108
+ - mountPath: ${underpostContainerEnvDir}
103
109
  name: ${shareEnvVolumeName}
104
- subPath: .env
105
110
  volumes:
106
111
  - hostPath:
107
112
  path: ${enginePath}
@@ -183,7 +188,6 @@ class UnderpostCron {
183
188
  * @param {boolean} [options.kubeadm] - Use kubeadm cluster context (apply directly on host)
184
189
  * @param {boolean} [options.dryRun] - Preview cron jobs without executing them
185
190
  * @param {boolean} [options.createJobNow] - After applying, immediately create a Job from each CronJob (requires --apply)
186
- * @param {boolean} [options.ssh] - Execute backup commands via SSH on the remote node
187
191
  * @memberof UnderpostCron
188
192
  */
189
193
  callback: async function (
@@ -227,7 +231,6 @@ class UnderpostCron {
227
231
  * @param {boolean} [options.k3s] - k3s cluster context (apply directly on host)
228
232
  * @param {boolean} [options.kind] - kind cluster context (apply via kind-worker container)
229
233
  * @param {boolean} [options.kubeadm] - kubeadm cluster context (apply directly on host)
230
- * @param {boolean} [options.ssh] - Execute backup commands via SSH on the remote node
231
234
  * @memberof UnderpostCron
232
235
  */
233
236
  setupDeployStart: async function (deployId, options = {}) {
@@ -269,21 +272,19 @@ class UnderpostCron {
269
272
  logger.warn(`package.json not found for deploy-id: ${deployId}`, { path: packageJsonPath });
270
273
  }
271
274
 
272
- // Generate and apply cron job manifests for this deploy-id
273
275
  await Underpost.cron.generateK8sCronJobs({
274
276
  deployId,
275
277
  namespace: options.namespace,
276
278
  image: options.image,
277
279
  apply: options.apply,
278
280
  createJobNow: options.createJobNow,
279
- git: true,
280
- dev: true,
281
- kubeadm: true,
282
- ssh: true,
283
- cmd: ` cd ${enginePath} && node bin env ${deployId} production`,
284
- k3s: false,
285
- kind: false,
286
- dryRun: false,
281
+ git: !!options.git,
282
+ dev: !!options.dev,
283
+ kubeadm: !!options.kubeadm,
284
+ cmd: options.cmd || `node bin env ${deployId} production`,
285
+ k3s: !!options.k3s,
286
+ kind: !!options.kind,
287
+ dryRun: !!options.dryRun,
287
288
  });
288
289
  },
289
290
 
@@ -305,7 +306,6 @@ class UnderpostCron {
305
306
  * @param {boolean} [options.kubeadm=false] - kubeadm cluster context (apply directly on host)
306
307
  * @param {boolean} [options.createJobNow=false] - After applying, create a Job from each CronJob immediately
307
308
  * @param {boolean} [options.dryRun=false] - Pass --dry-run=client to kubectl commands
308
- * @param {boolean} [options.ssh=false] - Execute backup commands via SSH on the remote node
309
309
  * @memberof UnderpostCron
310
310
  */
311
311
  generateK8sCronJobs: async function (options = {}) {
@@ -362,7 +362,9 @@ class UnderpostCron {
362
362
  cmd: options.cmd,
363
363
  suspend: false,
364
364
  dryRun: !!options.dryRun,
365
- ssh: !!options.ssh,
365
+ k3s: !!options.k3s,
366
+ kind: !!options.kind,
367
+ kubeadm: !!options.kubeadm,
366
368
  });
367
369
 
368
370
  const yamlFilePath = `${outputDir}/${cronJobName}.yaml`;
package/src/server/dns.js CHANGED
@@ -265,7 +265,8 @@ class Dns {
265
265
  * @returns {Promise<void>}
266
266
  */
267
267
  static async callback(deployList) {
268
- loadCronDeployEnv();
268
+ // loadCronDeployEnv();
269
+
269
270
  const isOnline = await Dns.isInternetConnection();
270
271
 
271
272
  if (!isOnline) return;