zudojs-cli 2.0.1 → 2.1.1

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 (248) hide show
  1. package/README.md +59 -7
  2. package/dist/src/adapters/frontend/angular.adapter.d.ts +0 -1
  3. package/dist/src/adapters/frontend/angular.adapter.js +14 -97
  4. package/dist/src/adapters/frontend/astro.adapter.d.ts +0 -1
  5. package/dist/src/adapters/frontend/astro.adapter.js +4 -64
  6. package/dist/src/adapters/frontend/fallback/fallback.helper.d.ts +41 -0
  7. package/dist/src/adapters/frontend/fallback/fallback.helper.js +57 -0
  8. package/dist/src/adapters/frontend/fallback/framework/angular/angular.fallback.d.ts +18 -0
  9. package/dist/src/adapters/frontend/fallback/framework/angular/angular.fallback.js +107 -0
  10. package/dist/src/adapters/frontend/fallback/framework/angular/angularWorkspace.fallback.d.ts +23 -0
  11. package/dist/src/adapters/frontend/fallback/framework/angular/angularWorkspace.fallback.js +125 -0
  12. package/dist/src/adapters/frontend/fallback/framework/angular/index.d.ts +8 -0
  13. package/dist/src/adapters/frontend/fallback/framework/angular/index.js +8 -0
  14. package/dist/src/adapters/frontend/fallback/framework/astro.fallback.d.ts +14 -0
  15. package/dist/src/adapters/frontend/fallback/framework/astro.fallback.js +76 -0
  16. package/dist/src/adapters/frontend/fallback/framework/index.d.ts +13 -0
  17. package/dist/src/adapters/frontend/fallback/framework/index.js +13 -0
  18. package/dist/src/adapters/frontend/fallback/framework/next.fallback.d.ts +13 -0
  19. package/dist/src/adapters/frontend/fallback/framework/next.fallback.js +105 -0
  20. package/dist/src/adapters/frontend/fallback/framework/nuxt.fallback.d.ts +14 -0
  21. package/dist/src/adapters/frontend/fallback/framework/nuxt.fallback.js +56 -0
  22. package/dist/src/adapters/frontend/fallback/framework/sveltekit.fallback.d.ts +15 -0
  23. package/dist/src/adapters/frontend/fallback/framework/sveltekit.fallback.js +94 -0
  24. package/dist/src/adapters/frontend/fallback/index.d.ts +12 -0
  25. package/dist/src/adapters/frontend/fallback/index.js +12 -0
  26. package/dist/src/adapters/frontend/fallback/vite/index.d.ts +11 -0
  27. package/dist/src/adapters/frontend/fallback/vite/index.js +11 -0
  28. package/dist/src/adapters/frontend/fallback/vite/react.fallback.d.ts +13 -0
  29. package/dist/src/adapters/frontend/fallback/vite/react.fallback.js +77 -0
  30. package/dist/src/adapters/frontend/fallback/vite/svelte.fallback.d.ts +14 -0
  31. package/dist/src/adapters/frontend/fallback/vite/svelte.fallback.js +95 -0
  32. package/dist/src/adapters/frontend/fallback/vite/vanilla.fallback.d.ts +13 -0
  33. package/dist/src/adapters/frontend/fallback/vite/vanilla.fallback.js +71 -0
  34. package/dist/src/adapters/frontend/fallback/vite/vue.fallback.d.ts +14 -0
  35. package/dist/src/adapters/frontend/fallback/vite/vue.fallback.js +78 -0
  36. package/dist/src/adapters/frontend/next.adapter.d.ts +0 -1
  37. package/dist/src/adapters/frontend/next.adapter.js +16 -95
  38. package/dist/src/adapters/frontend/nuxt.adapter.d.ts +1 -2
  39. package/dist/src/adapters/frontend/nuxt.adapter.js +32 -43
  40. package/dist/src/adapters/frontend/react-native.adapter.js +1 -1
  41. package/dist/src/adapters/frontend/react.adapter.d.ts +0 -1
  42. package/dist/src/adapters/frontend/react.adapter.js +7 -89
  43. package/dist/src/adapters/frontend/svelte.adapter.d.ts +0 -1
  44. package/dist/src/adapters/frontend/svelte.adapter.js +4 -86
  45. package/dist/src/adapters/frontend/sveltekit.adapter.d.ts +0 -1
  46. package/dist/src/adapters/frontend/sveltekit.adapter.js +4 -57
  47. package/dist/src/adapters/frontend/vanilla.adapter.d.ts +0 -1
  48. package/dist/src/adapters/frontend/vanilla.adapter.js +4 -47
  49. package/dist/src/adapters/frontend/vue.adapter.d.ts +0 -1
  50. package/dist/src/adapters/frontend/vue.adapter.js +4 -76
  51. package/dist/src/bin/bin.menu.d.ts +22 -0
  52. package/dist/src/bin/bin.menu.js +40 -0
  53. package/dist/src/bin/bin.runner.d.ts +38 -0
  54. package/dist/src/bin/bin.runner.js +51 -0
  55. package/dist/src/bin/index.d.ts +9 -0
  56. package/dist/src/bin/index.js +9 -0
  57. package/dist/src/bin/zudojs.js +15 -4
  58. package/dist/src/cliApplication/cliApplication.core.d.ts +4 -2
  59. package/dist/src/cliApplication/cliApplication.core.js +48 -36
  60. package/dist/src/cliApplication/cliApplication.logger.d.ts +5 -0
  61. package/dist/src/cliApplication/cliApplication.logger.js +5 -3
  62. package/dist/src/cliApplication/invocation/index.d.ts +12 -0
  63. package/dist/src/cliApplication/invocation/index.js +12 -0
  64. package/dist/src/cliApplication/invocation/invocation.hint.d.ts +30 -0
  65. package/dist/src/cliApplication/invocation/invocation.hint.js +74 -0
  66. package/dist/src/cliApplication/invocation/invocation.locate.d.ts +34 -0
  67. package/dist/src/cliApplication/invocation/invocation.locate.js +53 -0
  68. package/dist/src/cliApplication/invocation/invocation.name.d.ts +37 -0
  69. package/dist/src/cliApplication/invocation/invocation.name.js +69 -0
  70. package/dist/src/cliApplication/invocation/invocation.sanitize.d.ts +19 -0
  71. package/dist/src/cliApplication/invocation/invocation.sanitize.js +32 -0
  72. package/dist/src/cliParser/cliParser.core.js +3 -1
  73. package/dist/src/cliParser/cliParser.helper.d.ts +7 -0
  74. package/dist/src/cliParser/cliParser.helper.js +19 -2
  75. package/dist/src/cliParser/cliParser.longOption.js +2 -2
  76. package/dist/src/commands/add.command.d.ts +7 -3
  77. package/dist/src/commands/add.command.js +96 -57
  78. package/dist/src/commands/create.command.js +29 -42
  79. package/dist/src/commands/doctor.command.d.ts +2 -1
  80. package/dist/src/commands/doctor.command.js +52 -4
  81. package/dist/src/commands/generate.command.js +31 -16
  82. package/dist/src/commands/info.command.js +11 -1
  83. package/dist/src/constants/index.d.ts +21 -3
  84. package/dist/src/constants/index.js +40 -5
  85. package/dist/src/constants/zudojsVersions.generated.d.ts +9 -0
  86. package/dist/src/constants/zudojsVersions.generated.js +48 -0
  87. package/dist/src/constants/zudojsVersions.helper.d.ts +21 -0
  88. package/dist/src/constants/zudojsVersions.helper.js +29 -0
  89. package/dist/src/generators/frontend/frontendGenerator.core.js +9 -10
  90. package/dist/src/generators/frontend/frontendPipeline.d.ts +6 -1
  91. package/dist/src/generators/frontend/frontendPipeline.js +9 -1
  92. package/dist/src/generators/fullstack/fullstackComposer.core.js +7 -1
  93. package/dist/src/generators/index.d.ts +1 -2
  94. package/dist/src/generators/index.js +1 -2
  95. package/dist/src/generators/infrastructure/infrastructure.generator.js +13 -5
  96. package/dist/src/generators/integration/integrationGenerator.core.d.ts +2 -0
  97. package/dist/src/generators/integration/integrationGenerator.core.js +31 -2
  98. package/dist/src/generators/module/module.generator.js +36 -4
  99. package/dist/src/generators/resource/index.d.ts +8 -0
  100. package/dist/src/generators/resource/index.js +8 -0
  101. package/dist/src/generators/resource/resource.generator.d.ts +27 -0
  102. package/dist/src/generators/resource/resource.generator.js +87 -0
  103. package/dist/src/generators/resource/resource.layout.d.ts +26 -0
  104. package/dist/src/generators/resource/resource.layout.js +46 -0
  105. package/dist/src/generators/resource/resource.plan.d.ts +27 -0
  106. package/dist/src/generators/resource/resource.plan.js +46 -0
  107. package/dist/src/generators/resource/resource.run.d.ts +25 -0
  108. package/dist/src/generators/resource/resource.run.js +52 -0
  109. package/dist/src/prompts/cancel.prompt.d.ts +5 -1
  110. package/dist/src/prompts/cancel.prompt.js +4 -0
  111. package/dist/src/prompts/menu/index.d.ts +13 -0
  112. package/dist/src/prompts/menu/index.js +13 -0
  113. package/dist/src/prompts/menu/menu.adapter.d.ts +16 -0
  114. package/dist/src/prompts/menu/menu.adapter.js +56 -0
  115. package/dist/src/prompts/menu/menu.constant.d.ts +20 -0
  116. package/dist/src/prompts/menu/menu.constant.js +29 -0
  117. package/dist/src/prompts/menu/menu.flow.d.ts +18 -0
  118. package/dist/src/prompts/menu/menu.flow.js +82 -0
  119. package/dist/src/prompts/menu/menu.select.d.ts +26 -0
  120. package/dist/src/prompts/menu/menu.select.js +118 -0
  121. package/dist/src/prompts/menu/menu.type.d.ts +66 -0
  122. package/dist/src/prompts/menu/menu.type.js +8 -0
  123. package/dist/src/recipes/apply/index.d.ts +9 -0
  124. package/dist/src/recipes/apply/index.js +9 -0
  125. package/dist/src/recipes/apply/recipeApply.app.d.ts +20 -0
  126. package/dist/src/recipes/apply/recipeApply.app.js +79 -0
  127. package/dist/src/recipes/apply/recipeApply.context.d.ts +14 -0
  128. package/dist/src/recipes/apply/recipeApply.context.js +76 -0
  129. package/dist/src/recipes/apply/recipeApply.files.d.ts +39 -0
  130. package/dist/src/recipes/apply/recipeApply.files.js +84 -0
  131. package/dist/src/recipes/apply/recipeApply.project.d.ts +11 -0
  132. package/dist/src/recipes/apply/recipeApply.project.js +50 -0
  133. package/dist/src/recipes/apply/recipeApply.report.d.ts +20 -0
  134. package/dist/src/recipes/apply/recipeApply.report.js +51 -0
  135. package/dist/src/recipes/catalog/database/database.files.d.ts +15 -0
  136. package/dist/src/recipes/catalog/database/database.files.js +98 -0
  137. package/dist/src/recipes/catalog/database/database.recipe.d.ts +9 -0
  138. package/dist/src/recipes/catalog/database/database.recipe.js +50 -0
  139. package/dist/src/recipes/catalog/database/index.d.ts +6 -0
  140. package/dist/src/recipes/catalog/database/index.js +6 -0
  141. package/dist/src/recipes/catalog/email.recipe.d.ts +9 -0
  142. package/dist/src/recipes/catalog/email.recipe.js +122 -0
  143. package/dist/src/recipes/catalog/index.d.ts +11 -0
  144. package/dist/src/recipes/catalog/index.js +11 -0
  145. package/dist/src/recipes/catalog/openapi.recipe.d.ts +8 -0
  146. package/dist/src/recipes/catalog/openapi.recipe.js +25 -0
  147. package/dist/src/recipes/catalog/platform/cache.recipe.d.ts +7 -0
  148. package/dist/src/recipes/catalog/platform/cache.recipe.js +55 -0
  149. package/dist/src/recipes/catalog/platform/index.d.ts +9 -0
  150. package/dist/src/recipes/catalog/platform/index.js +9 -0
  151. package/dist/src/recipes/catalog/platform/messaging.recipe.d.ts +7 -0
  152. package/dist/src/recipes/catalog/platform/messaging.recipe.js +46 -0
  153. package/dist/src/recipes/catalog/platform/observability.recipe.d.ts +8 -0
  154. package/dist/src/recipes/catalog/platform/observability.recipe.js +49 -0
  155. package/dist/src/recipes/catalog/platform/queue.recipe.d.ts +7 -0
  156. package/dist/src/recipes/catalog/platform/queue.recipe.js +61 -0
  157. package/dist/src/recipes/catalog/platform/scheduler.recipe.d.ts +7 -0
  158. package/dist/src/recipes/catalog/platform/scheduler.recipe.js +56 -0
  159. package/dist/src/recipes/catalog/redis.recipe.d.ts +8 -0
  160. package/dist/src/recipes/catalog/redis.recipe.js +79 -0
  161. package/dist/src/recipes/catalog/storage.recipe.d.ts +7 -0
  162. package/dist/src/recipes/catalog/storage.recipe.js +49 -0
  163. package/dist/src/recipes/catalog/websockets.recipe.d.ts +10 -0
  164. package/dist/src/recipes/catalog/websockets.recipe.js +84 -0
  165. package/dist/src/recipes/docker/docker.compose.d.ts +19 -0
  166. package/dist/src/recipes/docker/docker.compose.js +90 -0
  167. package/dist/src/recipes/docker/docker.recipe.d.ts +10 -0
  168. package/dist/src/recipes/docker/docker.recipe.js +51 -0
  169. package/dist/src/recipes/docker/docker.refresh.d.ts +13 -0
  170. package/dist/src/recipes/docker/docker.refresh.js +37 -0
  171. package/dist/src/recipes/docker/index.d.ts +7 -0
  172. package/dist/src/recipes/docker/index.js +7 -0
  173. package/dist/src/recipes/index.d.ts +8 -0
  174. package/dist/src/recipes/index.js +8 -0
  175. package/dist/src/recipes/recipe.registry.d.ts +17 -0
  176. package/dist/src/recipes/recipe.registry.js +46 -0
  177. package/dist/src/recipes/recipe.type.d.ts +93 -0
  178. package/dist/src/recipes/recipe.type.js +12 -0
  179. package/dist/src/resolvers/dependency/dependencyVersions.constant.d.ts +100 -5
  180. package/dist/src/resolvers/dependency/dependencyVersions.constant.js +103 -34
  181. package/dist/src/resolvers/dependency/index.d.ts +1 -1
  182. package/dist/src/resolvers/dependency/index.js +1 -1
  183. package/dist/src/resolvers/project.resolver.d.ts +14 -1
  184. package/dist/src/resolvers/project.resolver.js +11 -3
  185. package/dist/src/scaffolders/scaffolder.helper.js +12 -1
  186. package/dist/src/templates/backendApp/backendApp.core.d.ts +45 -0
  187. package/dist/src/templates/backendApp/backendApp.core.js +66 -0
  188. package/dist/src/templates/backendApp/backendApp.modules.d.ts +26 -0
  189. package/dist/src/templates/backendApp/backendApp.modules.js +25 -0
  190. package/dist/src/templates/backendApp/backendApp.project.d.ts +26 -0
  191. package/dist/src/templates/backendApp/backendApp.project.js +87 -0
  192. package/dist/src/templates/backendApp/index.d.ts +8 -0
  193. package/dist/src/templates/backendApp/index.js +8 -0
  194. package/dist/src/templates/backendApp/parts/config.template.d.ts +28 -0
  195. package/dist/src/templates/backendApp/parts/config.template.js +118 -0
  196. package/dist/src/templates/backendApp/parts/httpUtils.template.d.ts +10 -0
  197. package/dist/src/templates/backendApp/parts/httpUtils.template.js +103 -0
  198. package/dist/src/templates/backendApp/parts/index.d.ts +9 -0
  199. package/dist/src/templates/backendApp/parts/index.js +9 -0
  200. package/dist/src/templates/backendApp/parts/integrations.template.d.ts +16 -0
  201. package/dist/src/templates/backendApp/parts/integrations.template.js +115 -0
  202. package/dist/src/templates/backendApp/parts/wiring.template.d.ts +26 -0
  203. package/dist/src/templates/backendApp/parts/wiring.template.js +111 -0
  204. package/dist/src/templates/microservice/microservice.template.js +61 -87
  205. package/dist/src/templates/modular-monolith/modularMonolith.template.js +25 -77
  206. package/dist/src/templates/monolith/monolith.template.d.ts +11 -26
  207. package/dist/src/templates/monolith/monolith.template.js +49 -146
  208. package/dist/src/templates/resource/index.d.ts +9 -0
  209. package/dist/src/templates/resource/index.js +9 -0
  210. package/dist/src/templates/resource/layers/index.d.ts +9 -0
  211. package/dist/src/templates/resource/layers/index.js +9 -0
  212. package/dist/src/templates/resource/layers/resourceData.template.d.ts +9 -0
  213. package/dist/src/templates/resource/layers/resourceData.template.js +108 -0
  214. package/dist/src/templates/resource/layers/resourcePrisma.template.d.ts +15 -0
  215. package/dist/src/templates/resource/layers/resourcePrisma.template.js +89 -0
  216. package/dist/src/templates/resource/layers/resourceRoutes.template.d.ts +13 -0
  217. package/dist/src/templates/resource/layers/resourceRoutes.template.js +128 -0
  218. package/dist/src/templates/resource/layers/resourceService.template.d.ts +13 -0
  219. package/dist/src/templates/resource/layers/resourceService.template.js +95 -0
  220. package/dist/src/templates/resource/resource.files.d.ts +33 -0
  221. package/dist/src/templates/resource/resource.files.js +99 -0
  222. package/dist/src/templates/resource/resource.names.d.ts +38 -0
  223. package/dist/src/templates/resource/resource.names.js +60 -0
  224. package/dist/src/templates/resource/resource.type.d.ts +19 -0
  225. package/dist/src/templates/resource/resource.type.js +15 -0
  226. package/dist/src/templates/shared/appRuntime.template.js +28 -11
  227. package/dist/src/templates/shared/capability.template.d.ts +13 -4
  228. package/dist/src/templates/shared/capability.template.js +21 -5
  229. package/dist/src/templates/shared/dockerfile.template.d.ts +12 -4
  230. package/dist/src/templates/shared/dockerfile.template.js +68 -29
  231. package/dist/src/templates/shared/pnpm.template.d.ts +8 -2
  232. package/dist/src/templates/shared/pnpm.template.js +9 -1
  233. package/dist/src/templates/shared/server.template.d.ts +23 -22
  234. package/dist/src/templates/shared/server.template.js +79 -53
  235. package/dist/src/types/scaffold.type.d.ts +6 -0
  236. package/dist/src/types/scaffold.type.js +2 -1
  237. package/dist/src/utils/utils.name.js +6 -0
  238. package/dist/src/utils/utils.writeGuard.d.ts +5 -0
  239. package/dist/src/utils/utils.writeGuard.js +23 -0
  240. package/dist/src/wiring/index.d.ts +10 -0
  241. package/dist/src/wiring/index.js +10 -0
  242. package/dist/src/wiring/wiring.dependencies.d.ts +29 -0
  243. package/dist/src/wiring/wiring.dependencies.js +83 -0
  244. package/dist/src/wiring/wiring.edits.d.ts +51 -0
  245. package/dist/src/wiring/wiring.edits.js +90 -0
  246. package/dist/src/wiring/wiring.markers.d.ts +53 -0
  247. package/dist/src/wiring/wiring.markers.js +87 -0
  248. package/package.json +20 -15
@@ -1,19 +1,27 @@
1
1
  /**
2
2
  * zudojs-cli — Generated Dockerfiles.
3
3
  *
4
- * Shared by the microservice template and the InfrastructureGenerator so the
5
- * fullstack microservice project and the standalone one build the same way.
4
+ * Shared by the microservice template, the InfrastructureGenerator and
5
+ * `zudojs add docker`. Every image is multi-stage on Node 24: a build stage
6
+ * installs, compiles and prunes dev dependencies; the runtime stage copies
7
+ * only `dist`, production `node_modules` and `package.json`, runs as the
8
+ * unprivileged `node` user and declares a /health HEALTHCHECK. The images
9
+ * used to run as root and ship the whole dev toolchain.
6
10
  */
7
11
  /** The dependency install command for a package manager inside a Dockerfile. */
8
12
  export declare function dockerInstallCommand(packageManager: string): string;
9
13
  /**
10
- * A self-contained Dockerfile for one app of a microservice project. The
11
- * build context is the project root; only `appPath` is copied in.
14
+ * A self-contained Dockerfile for one app package. The build context is the
15
+ * project root; `appPath` is the app's directory (`.` for a single-app
16
+ * project). pnpm's `pnpm-workspace.yaml` is copied for its build-script
17
+ * allow-list, without which pnpm 11 refuses to install.
12
18
  */
13
19
  export declare function renderAppPackageDockerfile(options: {
14
20
  readonly appPath: string;
15
21
  readonly port: number;
16
22
  readonly packageManager: string;
23
+ /** The app has Prisma: copy the schema and config before installing. */
24
+ readonly prisma?: boolean;
17
25
  }): string;
18
26
  /**
19
27
  * A Dockerfile for a workspace whose server lives in `appDirectory`. The whole
@@ -1,8 +1,12 @@
1
1
  /**
2
2
  * zudojs-cli — Generated Dockerfiles.
3
3
  *
4
- * Shared by the microservice template and the InfrastructureGenerator so the
5
- * fullstack microservice project and the standalone one build the same way.
4
+ * Shared by the microservice template, the InfrastructureGenerator and
5
+ * `zudojs add docker`. Every image is multi-stage on Node 24: a build stage
6
+ * installs, compiles and prunes dev dependencies; the runtime stage copies
7
+ * only `dist`, production `node_modules` and `package.json`, runs as the
8
+ * unprivileged `node` user and declares a /health HEALTHCHECK. The images
9
+ * used to run as root and ship the whole dev toolchain.
6
10
  */
7
11
  /** The dependency install command for a package manager inside a Dockerfile. */
8
12
  export function dockerInstallCommand(packageManager) {
@@ -17,29 +21,61 @@ export function dockerInstallCommand(packageManager) {
17
21
  return "npm install";
18
22
  }
19
23
  }
20
- /**
21
- * A self-contained Dockerfile for one app of a microservice project. The
22
- * build context is the project root; only `appPath` is copied in.
23
- */
24
- export function renderAppPackageDockerfile(options) {
25
- const { appPath, port } = options;
26
- return `FROM node:24-alpine AS builder
27
- WORKDIR /app
28
- COPY ${appPath}/package.json ./
29
- RUN ${dockerInstallCommand(options.packageManager)}
30
- COPY ${appPath}/tsconfig.json ./
31
- COPY ${appPath}/src ./src
32
- RUN npx tsc
33
-
34
- FROM node:24-alpine AS runtime
24
+ /** Removes dev dependencies after the build (scripts off: prisma is a dev tool). */
25
+ function dockerPruneCommand(packageManager) {
26
+ switch (packageManager) {
27
+ case "pnpm":
28
+ return "pnpm prune --prod --ignore-scripts";
29
+ case "yarn":
30
+ return undefined;
31
+ default:
32
+ return "npm prune --omit=dev --ignore-scripts";
33
+ }
34
+ }
35
+ function runnerFor(packageManager) {
36
+ return packageManager === "npm" || packageManager === "bun" ? "npm" : packageManager;
37
+ }
38
+ /** The runtime stage of a single-package image. */
39
+ function runtimeStage(port, dist, from) {
40
+ return `FROM node:24-alpine AS runtime
41
+ ENV NODE_ENV=production
35
42
  WORKDIR /app
36
- COPY --from=builder /app/dist ./dist
37
- COPY --from=builder /app/node_modules ./node_modules
38
- COPY --from=builder /app/package.json ./
43
+ COPY --from=build --chown=node:node ${from}/package.json ./
44
+ COPY --from=build --chown=node:node ${from}/node_modules ./node_modules
45
+ COPY --from=build --chown=node:node ${from}/${dist} ./${dist}
46
+ USER node
39
47
  EXPOSE ${port}
40
- CMD ["node", "dist/server.js"]
48
+ HEALTHCHECK --interval=30s --timeout=5s --start-period=15s CMD ["node", "-e", "fetch('http://127.0.0.1:${port}/health').then((r) => process.exit(r.ok ? 0 : 1), () => process.exit(1))"]
49
+ CMD ["node", "${dist}/server.js"]
41
50
  `;
42
51
  }
52
+ /**
53
+ * A self-contained Dockerfile for one app package. The build context is the
54
+ * project root; `appPath` is the app's directory (`.` for a single-app
55
+ * project). pnpm's `pnpm-workspace.yaml` is copied for its build-script
56
+ * allow-list, without which pnpm 11 refuses to install.
57
+ */
58
+ export function renderAppPackageDockerfile(options) {
59
+ const app = options.appPath.replace(/^\.\/?/, "").replace(/\/$/, "");
60
+ const at = (file) => (app === "" ? file : `${app}/${file}`);
61
+ const pm = options.packageManager;
62
+ const prune = dockerPruneCommand(pm);
63
+ const lines = [
64
+ "# syntax=docker/dockerfile:1",
65
+ "FROM node:24-alpine AS build",
66
+ "WORKDIR /app",
67
+ `COPY ${at("package.json")} ./`,
68
+ ...(pm === "pnpm" ? ["COPY pnpm-workspace.yaml ./"] : []),
69
+ ...(options.prisma ? [`COPY ${at("prisma.config.ts")} ./`, `COPY ${at("prisma")} ./prisma`] : []),
70
+ `RUN ${dockerInstallCommand(pm)}`,
71
+ `COPY ${at("tsconfig.json")} ./`,
72
+ `COPY ${at("src")} ./src`,
73
+ `RUN ${runnerFor(pm)} run build`,
74
+ ...(prune === undefined ? [] : [`RUN ${prune}`]),
75
+ "",
76
+ ];
77
+ return `${lines.join("\n")}\n${runtimeStage(options.port, "dist", "/app")}`;
78
+ }
43
79
  /**
44
80
  * A Dockerfile for a workspace whose server lives in `appDirectory`. The whole
45
81
  * workspace is installed, then only that app is built and started: the root
@@ -47,22 +83,25 @@ CMD ["node", "dist/server.js"]
47
83
  */
48
84
  export function renderWorkspaceAppDockerfile(options) {
49
85
  const dir = options.appDirectory.replace(/^\.\/?/, "").replace(/\/$/, "");
50
- const install = dockerInstallCommand(options.packageManager);
51
- const runner = options.packageManager === "npm" || options.packageManager === "bun"
52
- ? "npm"
53
- : options.packageManager;
86
+ const runner = runnerFor(options.packageManager);
54
87
  const build = dir === "" ? `${runner} run build` : `cd ${dir} && ${runner} run build`;
55
88
  const dist = dir === "" ? "dist" : `${dir}/dist`;
56
- return `FROM node:24-alpine AS builder
89
+ // pnpm links workspace dependencies through the root node_modules, so
90
+ // the runtime stage takes the whole built workspace.
91
+ return `# syntax=docker/dockerfile:1
92
+ FROM node:24-alpine AS build
57
93
  WORKDIR /app
58
94
  COPY . .
59
- RUN ${install}
95
+ RUN ${dockerInstallCommand(options.packageManager)}
60
96
  RUN ${build}
61
97
 
62
- FROM node:24-alpine
98
+ FROM node:24-alpine AS runtime
99
+ ENV NODE_ENV=production
63
100
  WORKDIR /app
64
- COPY --from=builder /app /app
101
+ COPY --from=build --chown=node:node /app /app
102
+ USER node
65
103
  EXPOSE ${options.port}
104
+ HEALTHCHECK --interval=30s --timeout=5s --start-period=15s CMD ["node", "-e", "fetch('http://127.0.0.1:${options.port}/health').then((r) => process.exit(r.ok ? 0 : 1), () => process.exit(1))"]
66
105
  CMD ["node", "${dist}/server.js"]
67
106
  `;
68
107
  }
@@ -14,8 +14,14 @@
14
14
  *
15
15
  * @module templates/shared/pnpm
16
16
  */
17
- /** Dependencies whose install scripts a generated project allows. */
18
- export declare const PNPM_ALLOWED_BUILD_SCRIPTS: readonly ["esbuild", "@swc/core", "sharp", "@parcel/watcher", "@tailwindcss/oxide", "unrs-resolver"];
17
+ /**
18
+ * Dependencies whose install scripts a generated project allows.
19
+ *
20
+ * `lmdb` and `msgpackr-extract` come in through `@angular/build` (its
21
+ * persistent build cache); without them `pnpm install` in an Angular 22
22
+ * project fails with `ERR_PNPM_IGNORED_BUILDS`.
23
+ */
24
+ export declare const PNPM_ALLOWED_BUILD_SCRIPTS: readonly ["esbuild", "@swc/core", "sharp", "@parcel/watcher", "@tailwindcss/oxide", "unrs-resolver", "lmdb", "msgpackr-extract"];
19
25
  /**
20
26
  * Renders `pnpm-workspace.yaml`.
21
27
  *
@@ -14,7 +14,13 @@
14
14
  *
15
15
  * @module templates/shared/pnpm
16
16
  */
17
- /** Dependencies whose install scripts a generated project allows. */
17
+ /**
18
+ * Dependencies whose install scripts a generated project allows.
19
+ *
20
+ * `lmdb` and `msgpackr-extract` come in through `@angular/build` (its
21
+ * persistent build cache); without them `pnpm install` in an Angular 22
22
+ * project fails with `ERR_PNPM_IGNORED_BUILDS`.
23
+ */
18
24
  export const PNPM_ALLOWED_BUILD_SCRIPTS = [
19
25
  "esbuild",
20
26
  "@swc/core",
@@ -22,6 +28,8 @@ export const PNPM_ALLOWED_BUILD_SCRIPTS = [
22
28
  "@parcel/watcher",
23
29
  "@tailwindcss/oxide",
24
30
  "unrs-resolver",
31
+ "lmdb",
32
+ "msgpackr-extract",
25
33
  ];
26
34
  /**
27
35
  * Renders `pnpm-workspace.yaml`.
@@ -1,31 +1,32 @@
1
1
  /**
2
2
  * zudojs-cli — Generated `src/server.ts`
3
3
  *
4
- * The entry point used to call `runtime.start()` and nothing else. The
5
- * runtime creates no HTTP server, so `pnpm dev` logged "ready" and exited
6
- * with 0 even though `.env.example` set `PORT`, the Dockerfile exposed it
7
- * and the fullstack frontend called it.
4
+ * The entry point loads the configuration, builds a router with
5
+ * `createRouter()`, registers every route through `registerRoutes` (from
6
+ * the composition root in `container.ts`), mounts the OpenAPI document and
7
+ * `/docs` page when the `openapi` capability is on, and serves the router
8
+ * with `createHttpServer` + `createNodeHttpAdapter`.
8
9
  *
9
- * The emitted server starts the runtime, then serves HTTP with
10
- * `@zudojs/http` (`createHttpServer` + `createNodeHttpAdapter`) on `PORT`,
11
- * answers `GET /health`, and keeps the process alive until SIGINT/SIGTERM,
12
- * when it stops the HTTP server and then the runtime. `tests/
13
- * generatedProject.typecheck.test.ts` type-checks this output against the
14
- * current `@zudojs/*` sources.
10
+ * Every response passes through the @zudojs/security default headers, a
11
+ * CORS policy that allows only `CORS_ORIGINS` (none by default) and a
12
+ * per-client rate limit (`RATE_LIMIT_MAX` per `RATE_LIMIT_WINDOW_MS`). Exposed
13
+ * 4xx errors are answered with their status; anything else is a generic
14
+ * 500. On SIGINT/SIGTERM integrations drain, HTTP stops, then the runtime
15
+ * stops. `tests/generatedProject.typecheck.test.ts` type-checks the output
16
+ * against the current `@zudojs/*` sources.
15
17
  */
16
18
  /** Options for {@link renderServerFile}. */
17
19
  export interface ServerFileOptions {
18
- /** Port used when `PORT` is unset. Defaults to 3000. */
19
- readonly defaultPort?: number;
20
- /**
21
- * Import path of a `HealthController` whose `check()` answers `/health`,
22
- * relative to server.ts. When omitted, `/health` is answered inline.
23
- */
24
- readonly healthControllerImport?: string;
20
+ /** Title of the OpenAPI document. */
21
+ readonly title: string;
22
+ /** Whether `/openapi.json` and `/docs` are mounted. */
23
+ readonly openapi: boolean;
25
24
  }
26
- /**
27
- * Renders `src/server.ts`: starts the runtime, serves HTTP on `PORT` with a
28
- * `/health` route, and shuts both down cleanly on SIGINT/SIGTERM.
29
- */
30
- export declare function renderServerFile(appImportPath?: string, options?: ServerFileOptions): string;
25
+ /** The lines `zudojs add openapi` (or `create`) puts in server.ts. */
26
+ export declare function openApiServerLines(title: string): {
27
+ readonly importLine: string;
28
+ readonly mountLine: string;
29
+ };
30
+ /** Renders `src/server.ts`. */
31
+ export declare function renderServerFile(options: ServerFileOptions): string;
31
32
  //# sourceMappingURL=server.template.d.ts.map
@@ -1,86 +1,112 @@
1
1
  /**
2
2
  * zudojs-cli — Generated `src/server.ts`
3
3
  *
4
- * The entry point used to call `runtime.start()` and nothing else. The
5
- * runtime creates no HTTP server, so `pnpm dev` logged "ready" and exited
6
- * with 0 even though `.env.example` set `PORT`, the Dockerfile exposed it
7
- * and the fullstack frontend called it.
4
+ * The entry point loads the configuration, builds a router with
5
+ * `createRouter()`, registers every route through `registerRoutes` (from
6
+ * the composition root in `container.ts`), mounts the OpenAPI document and
7
+ * `/docs` page when the `openapi` capability is on, and serves the router
8
+ * with `createHttpServer` + `createNodeHttpAdapter`.
8
9
  *
9
- * The emitted server starts the runtime, then serves HTTP with
10
- * `@zudojs/http` (`createHttpServer` + `createNodeHttpAdapter`) on `PORT`,
11
- * answers `GET /health`, and keeps the process alive until SIGINT/SIGTERM,
12
- * when it stops the HTTP server and then the runtime. `tests/
13
- * generatedProject.typecheck.test.ts` type-checks this output against the
14
- * current `@zudojs/*` sources.
10
+ * Every response passes through the @zudojs/security default headers, a
11
+ * CORS policy that allows only `CORS_ORIGINS` (none by default) and a
12
+ * per-client rate limit (`RATE_LIMIT_MAX` per `RATE_LIMIT_WINDOW_MS`). Exposed
13
+ * 4xx errors are answered with their status; anything else is a generic
14
+ * 500. On SIGINT/SIGTERM integrations drain, HTTP stops, then the runtime
15
+ * stops. `tests/generatedProject.typecheck.test.ts` type-checks the output
16
+ * against the current `@zudojs/*` sources.
15
17
  */
18
+ import { MARKERS, renderMarkerBlock } from "../../wiring/index.js";
16
19
  /** Emits a string as a TypeScript string literal that cannot break out. */
17
20
  function literal(value) {
18
21
  return JSON.stringify(value);
19
22
  }
20
- /**
21
- * Renders `src/server.ts`: starts the runtime, serves HTTP on `PORT` with a
22
- * `/health` route, and shuts both down cleanly on SIGINT/SIGTERM.
23
- */
24
- export function renderServerFile(appImportPath = "./app.js", options = {}) {
25
- const defaultPort = options.defaultPort ?? 3000;
26
- const controller = options.healthControllerImport;
27
- const controllerImport = controller
28
- ? `import { HealthController } from ${literal(controller)};\n`
29
- : "";
30
- const healthBody = controller
31
- ? "new HealthController().check()"
32
- : `{ status: "ok", timestamp: new Date().toISOString() }`;
33
- return `import {
23
+ /** The lines `zudojs add openapi` (or `create`) puts in server.ts. */
24
+ export function openApiServerLines(title) {
25
+ return {
26
+ importLine: `import { mountOpenAPI } from "@zudojs/http";`,
27
+ mountLine: `mountOpenAPI(router, { info: { title: ${literal(title)}, version: "0.1.0" } });`,
28
+ };
29
+ }
30
+ /** Renders `src/server.ts`. */
31
+ export function renderServerFile(options) {
32
+ const openapi = openApiServerLines(options.title);
33
+ return `import { createServer } from "node:http";
34
+
35
+ import {
36
+ HttpMiddlewarePipeline,
37
+ createCorsMiddleware,
34
38
  createHttpServer,
35
39
  createNodeHttpAdapter,
40
+ createRateLimitMiddleware,
36
41
  createResponseContext,
42
+ createRouter,
43
+ type HttpMiddleware,
37
44
  type HttpRequestContext,
38
45
  } from "@zudojs/http";
46
+ ${renderMarkerBlock(MARKERS.serverImports, options.openapi ? [openapi.importLine] : [])}
47
+
48
+ import { createApp } from "./app.js";
49
+ import { loadConfig } from "./configs/index.js";
50
+ import { createDependencies } from "./container.js";
51
+ import { checkIntegrations, drainIntegrations, integrations } from "./integrations/index.js";
52
+ import { registerRoutes } from "./routes/index.js";
53
+ import { errorResponse, securityHeaders } from "./utils/http.js";
39
54
 
40
- import { createApp } from ${literal(appImportPath)};
41
- ${controllerImport}
42
- const DEFAULT_PORT = ${defaultPort};
55
+ const config = await loadConfig();
56
+ const httpServer = createServer();
57
+ const runtime = createApp({ config, httpServer });
43
58
 
44
- /** Reads PORT, refusing values that are not a TCP port. */
45
- function resolvePort(): number {
46
- const raw = process.env["PORT"];
47
- if (raw === undefined || raw.trim() === "") return DEFAULT_PORT;
48
- const port = Number(raw);
49
- if (!Number.isInteger(port) || port < 0 || port > 65535) {
50
- throw new Error(\`PORT must be an integer between 0 and 65535, got "\${raw}".\`);
59
+ const router = createRouter();
60
+ registerRoutes(
61
+ router,
62
+ createDependencies({
63
+ health: async () => {
64
+ const checks = await checkIntegrations(integrations);
65
+ const ready =
66
+ runtime.state === "running" && Object.values(checks).every((check) => check === "up");
67
+ return { ready, checks };
68
+ },
69
+ }),
70
+ );
71
+ ${renderMarkerBlock(MARKERS.serverMounts, options.openapi ? [openapi.mountLine] : [])}
72
+
73
+ const dispatch: HttpMiddleware = async (context) => {
74
+ try {
75
+ return (await router.dispatch(context.request, { signal: context.signal })).response;
76
+ } catch (error) {
77
+ const response = errorResponse(error);
78
+ if (response === undefined) throw error;
79
+ return response;
51
80
  }
52
- return port;
53
- }
81
+ };
82
+
83
+ const pipeline = new HttpMiddlewarePipeline({
84
+ middlewares: [
85
+ securityHeaders(),
86
+ createCorsMiddleware({ allowOrigin: config.corsOrigins }),
87
+ createRateLimitMiddleware({ windowMs: config.rateLimit.windowMs, max: config.rateLimit.max }),
88
+ ${renderMarkerBlock(MARKERS.serverMiddleware, [], " ")}
89
+ dispatch,
90
+ ],
91
+ });
54
92
 
55
- const runtime = createApp();
56
93
  await runtime.start();
57
94
 
58
95
  const server = createHttpServer({
59
- adapter: createNodeHttpAdapter({
60
- host: process.env["HOST"] ?? "0.0.0.0",
61
- port: resolvePort(),
62
- }),
63
- handler: (request: HttpRequestContext) => {
64
- if (request.path === "/health") {
65
- if (runtime.state !== "running") {
66
- return createResponseContext({ status: 503 }).json({ status: "unavailable" });
67
- }
68
- return ${healthBody};
69
- }
70
- return createResponseContext({ status: 404 }).json({ error: "Not Found" });
71
- },
96
+ adapter: createNodeHttpAdapter({ server: httpServer, host: config.host, port: config.port }),
97
+ handler: (request: HttpRequestContext) => pipeline.execute(request, createResponseContext()),
72
98
  });
73
99
 
74
100
  await server.start();
75
- console.log(\`Listening on port \${server.address?.port ?? resolvePort()}\`);
101
+ console.log(\`Listening on http://\${config.host}:\${server.address?.port ?? config.port}\`);
76
102
 
77
103
  let stopping = false;
78
104
  for (const signal of ["SIGINT", "SIGTERM"] as const) {
79
105
  process.once(signal, () => {
80
106
  if (stopping) return;
81
107
  stopping = true;
82
- void server
83
- .stop()
108
+ void drainIntegrations(integrations)
109
+ .then(() => server.stop())
84
110
  .then(() => runtime.stop())
85
111
  .then(() => {
86
112
  process.exit(0);
@@ -35,6 +35,12 @@ export interface ScaffoldOptions {
35
35
  readonly enableOpenAPI: boolean;
36
36
  readonly enableDatabase: boolean;
37
37
  readonly enableQueue: boolean;
38
+ /**
39
+ * Every capability id that was selected, including those without an
40
+ * `enable*` flag of their own (`events`, `security`). Read through
41
+ * `resolveProjectCapabilities`, never directly.
42
+ */
43
+ readonly capabilities?: readonly string[];
38
44
  readonly enableDocker: boolean;
39
45
  readonly installDeps: boolean;
40
46
  readonly initGit: boolean;
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Type definitions for the Zudojs CLI scaffolding system.
3
3
  */
4
+ import { resolveProjectCapabilities } from "../templates/shared/capability.template.js";
4
5
  /**
5
6
  * Converts ScaffoldOptions to ProjectConfiguration for runtime use.
6
7
  */
@@ -26,7 +27,7 @@ export function toProjectConfiguration(options) {
26
27
  workspace: {
27
28
  packageManager: options.packageManager,
28
29
  },
29
- features: options.services,
30
+ features: resolveProjectCapabilities(options),
30
31
  };
31
32
  }
32
33
  //# sourceMappingURL=scaffold.type.js.map
@@ -2,6 +2,12 @@ import { CLIValidationError } from "../errors/index.js";
2
2
  export function normalizeName(name) {
3
3
  return name
4
4
  .trim()
5
+ // Split camelCase and acronym boundaries before lowercasing, so
6
+ // `createBook` becomes `create-book` (class `CreateBookCommand`) rather
7
+ // than `createbook` (`CreatebookCommand`), and `HTTPServer` becomes
8
+ // `http-server`.
9
+ .replace(/([a-z0-9])([A-Z])/g, "$1-$2")
10
+ .replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2")
5
11
  .toLowerCase()
6
12
  .replace(/[^a-z0-9-]+/g, "-")
7
13
  // Trim leading/trailing dashes without the quadratic `-+$` scan: the
@@ -10,6 +10,11 @@
10
10
  * barrel merge done by `mergeBarrelExport`); anything else is a conflict
11
11
  * unless `--force` is passed.
12
12
  */
13
+ /**
14
+ * Records that `fullPath` is edited in place on purpose (see
15
+ * {@link intendedEdits}). A no-op outside `captureWrites`.
16
+ */
17
+ export declare function recordIntendedEdit(fullPath: string): void;
13
18
  /** The capture map of the current `captureWrites` call, if any. */
14
19
  export declare function activeWriteCapture(): Map<string, string> | undefined;
15
20
  /**
@@ -14,6 +14,24 @@ import { AsyncLocalStorage } from "node:async_hooks";
14
14
  import { existsSync, readFileSync } from "node:fs";
15
15
  import { relative } from "node:path";
16
16
  const captured = new AsyncLocalStorage();
17
+ /**
18
+ * Files a capture is allowed to change in place: marker insertions into
19
+ * `routes/index.ts`, `container.ts` and friends. They are edits by design,
20
+ * not overwrites, so the conflict check does not report them.
21
+ */
22
+ const intendedEdits = new WeakMap();
23
+ /**
24
+ * Records that `fullPath` is edited in place on purpose (see
25
+ * {@link intendedEdits}). A no-op outside `captureWrites`.
26
+ */
27
+ export function recordIntendedEdit(fullPath) {
28
+ const capture = captured.getStore();
29
+ if (!capture)
30
+ return;
31
+ const edits = intendedEdits.get(capture) ?? new Set();
32
+ edits.add(fullPath);
33
+ intendedEdits.set(capture, edits);
34
+ }
17
35
  /** The capture map of the current `captureWrites` call, if any. */
18
36
  export function activeWriteCapture() {
19
37
  return captured.getStore();
@@ -33,7 +51,12 @@ export async function captureWrites(run) {
33
51
  */
34
52
  export function findWriteConflicts(cwd, files) {
35
53
  const conflicts = [];
54
+ const edits = files instanceof Map
55
+ ? intendedEdits.get(files)
56
+ : undefined;
36
57
  for (const [path, content] of files) {
58
+ if (edits?.has(path))
59
+ continue;
37
60
  if (!existsSync(path))
38
61
  continue;
39
62
  const existing = readFileSync(path, "utf-8");
@@ -0,0 +1,10 @@
1
+ /**
2
+ * zudojs-cli — Wiring
3
+ *
4
+ * Marker comments in generated projects and the edits `generate` and `add`
5
+ * make between them, and the dependencies generated code needs.
6
+ */
7
+ export * from "./wiring.markers.js";
8
+ export * from "./wiring.edits.js";
9
+ export * from "./wiring.dependencies.js";
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * zudojs-cli — Wiring
3
+ *
4
+ * Marker comments in generated projects and the edits `generate` and `add`
5
+ * make between them, and the dependencies generated code needs.
6
+ */
7
+ export * from "./wiring.markers.js";
8
+ export * from "./wiring.edits.js";
9
+ export * from "./wiring.dependencies.js";
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,29 @@
1
+ /**
2
+ * zudojs-cli — Dependencies of generated code
3
+ *
4
+ * `generate command` and `generate query` wrote files importing
5
+ * `@zudojs/cqrs` into projects created without the cqrs capability, so the
6
+ * project stopped compiling (TS2307) and nothing said why. After a schematic
7
+ * writes its files, every `@zudojs/*` package they import is added to the
8
+ * nearest package.json that lacks it.
9
+ */
10
+ /** One package.json edited by {@link ensureZudojsDependencies}. */
11
+ export interface AddedDependencies {
12
+ /** package.json path, relative to the project root. */
13
+ readonly packageJson: string;
14
+ /** `@zudojs/*` names added to its dependencies. */
15
+ readonly added: readonly string[];
16
+ }
17
+ /** `@zudojs/*` package names imported by `source`. */
18
+ export declare function importedZudojsPackages(source: string): string[];
19
+ /**
20
+ * Adds every `@zudojs/*` package imported by `files` (paths relative to
21
+ * `root`, or absolute) to the dependencies of the package.json that owns
22
+ * each file, when it is missing. Existing entries are never changed.
23
+ */
24
+ export declare function ensureZudojsDependencies(root: string, files: readonly string[]): AddedDependencies[];
25
+ /** Human-readable lines describing {@link ensureZudojsDependencies} edits. */
26
+ export declare function describeAddedDependencies(edits: readonly AddedDependencies[]): string[];
27
+ /** "1 file" / "3 files". */
28
+ export declare function fileCount(count: number): string;
29
+ //# sourceMappingURL=wiring.dependencies.d.ts.map