zudojs-cli 1.1.0 → 1.2.0

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 (54) hide show
  1. package/README.md +27 -0
  2. package/dist/src/adapters/databases/databaseAdapter.resolver.d.ts +21 -0
  3. package/dist/src/adapters/databases/databaseAdapter.resolver.js +40 -0
  4. package/dist/src/adapters/databases/index.d.ts +1 -0
  5. package/dist/src/adapters/databases/index.js +1 -0
  6. package/dist/src/adapters/databases/mysql.adapter.d.ts +1 -1
  7. package/dist/src/adapters/databases/mysql.adapter.js +2 -2
  8. package/dist/src/adapters/databases/postgres.adapter.d.ts +3 -2
  9. package/dist/src/adapters/databases/postgres.adapter.js +2 -2
  10. package/dist/src/adapters/databases/sqlite.adapter.d.ts +1 -1
  11. package/dist/src/adapters/databases/sqlite.adapter.js +2 -2
  12. package/dist/src/bin/zudojs.js +7 -1
  13. package/dist/src/commands/add.command.js +5 -1
  14. package/dist/src/commands/create.command.js +21 -7
  15. package/dist/src/commands/generate.command.js +37 -6
  16. package/dist/src/errors/index.d.ts +3 -14
  17. package/dist/src/errors/index.js +3 -24
  18. package/dist/src/generators/frontend/frontendPipeline.js +7 -3
  19. package/dist/src/generators/fullstack/fullstackBackend.layout.d.ts +26 -0
  20. package/dist/src/generators/fullstack/fullstackBackend.layout.js +34 -0
  21. package/dist/src/generators/fullstack/fullstackComposer.core.js +5 -1
  22. package/dist/src/generators/fullstack/index.d.ts +1 -0
  23. package/dist/src/generators/fullstack/index.js +1 -0
  24. package/dist/src/generators/infrastructure/infrastructure.generator.d.ts +5 -10
  25. package/dist/src/generators/infrastructure/infrastructure.generator.js +41 -75
  26. package/dist/src/generators/integration/integrationGenerator.core.js +8 -2
  27. package/dist/src/generators/module/index.d.ts +6 -0
  28. package/dist/src/generators/module/index.js +6 -0
  29. package/dist/src/generators/module/module.generator.d.ts +6 -1
  30. package/dist/src/generators/module/module.generator.js +15 -16
  31. package/dist/src/generators/module/module.registration.d.ts +23 -0
  32. package/dist/src/generators/module/module.registration.js +50 -0
  33. package/dist/src/resolvers/dependency/dependencyResolver.core.js +6 -1
  34. package/dist/src/resolvers/dependency/dependencyVersions.constant.d.ts +12 -0
  35. package/dist/src/resolvers/dependency/dependencyVersions.constant.js +40 -0
  36. package/dist/src/resolvers/dependency/index.d.ts +1 -0
  37. package/dist/src/resolvers/dependency/index.js +1 -0
  38. package/dist/src/templates/microservice/microservice.template.js +14 -42
  39. package/dist/src/templates/modular-monolith/modularMonolith.template.js +2 -1
  40. package/dist/src/templates/monolith/monolith.template.js +5 -2
  41. package/dist/src/templates/shared/appRuntime.template.d.ts +0 -5
  42. package/dist/src/templates/shared/appRuntime.template.js +3 -40
  43. package/dist/src/templates/shared/dockerfile.template.d.ts +28 -0
  44. package/dist/src/templates/shared/dockerfile.template.js +69 -0
  45. package/dist/src/templates/shared/index.d.ts +2 -0
  46. package/dist/src/templates/shared/index.js +2 -0
  47. package/dist/src/templates/shared/server.template.d.ts +31 -0
  48. package/dist/src/templates/shared/server.template.js +96 -0
  49. package/dist/src/utils/index.d.ts +1 -0
  50. package/dist/src/utils/index.js +1 -0
  51. package/dist/src/utils/utils.fileSystem.js +6 -0
  52. package/dist/src/utils/utils.writeGuard.d.ts +25 -0
  53. package/dist/src/utils/utils.writeGuard.js +46 -0
  54. package/package.json +6 -5
@@ -5,9 +5,15 @@
5
5
  */
6
6
  import { writeFileTree } from "../../utils/utils.fileSystem.js";
7
7
  import { CLIValidationError } from "../../errors/index.js";
8
+ import { resolveDatabaseAdapter } from "../../adapters/databases/databaseAdapter.resolver.js";
9
+ import { resolveMicroserviceServices } from "../../templates/microservice/microservice.template.js";
10
+ import { renderAppPackageDockerfile, renderWorkspaceAppDockerfile, } from "../../templates/shared/dockerfile.template.js";
8
11
  const SERVICE_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
9
12
  export class InfrastructureGenerator {
10
13
  async generate(options, basePath) {
14
+ // Rejects engines with no adapter (e.g. mongodb) instead of falling
15
+ // through to a postgres container.
16
+ resolveDatabaseAdapter(options.database);
11
17
  for (const service of options.services ?? []) {
12
18
  if (!SERVICE_NAME_PATTERN.test(service)) {
13
19
  throw new CLIValidationError(`Invalid service name: "${service}". Service names must match ${SERVICE_NAME_PATTERN}.`);
@@ -21,43 +27,34 @@ export class InfrastructureGenerator {
21
27
  if (options.architecture === "microservice") {
22
28
  files["docker-compose.yml"] = this.getDockerCompose(options);
23
29
  files[".dockerignore"] = "node_modules\ndist\n.git\n.env\n";
24
- const services = options.services ?? ["gateway"];
25
- for (const service of services) {
26
- files[`apps/services/${service}/Dockerfile`] =
27
- this.getServiceDockerfile(options);
28
- }
30
+ // The gateway lives at apps/gateway and each service at
31
+ // apps/services/<name>, as the microservice template writes them;
32
+ // each Dockerfile builds from the project root with its app path.
33
+ files["apps/gateway/Dockerfile"] = renderAppPackageDockerfile({
34
+ appPath: "apps/gateway",
35
+ port: 3000,
36
+ packageManager: options.packageManager,
37
+ });
38
+ resolveMicroserviceServices(options.services ?? []).forEach((service, i) => {
39
+ files[`apps/services/${service}/Dockerfile`] = renderAppPackageDockerfile({
40
+ appPath: `apps/services/${service}`,
41
+ port: 3001 + i,
42
+ packageManager: options.packageManager,
43
+ });
44
+ });
29
45
  }
30
46
  else {
31
47
  files["docker-compose.yml"] = this.getSimpleDockerCompose(options);
32
48
  files[".dockerignore"] = "node_modules\ndist\n.git\n.env\n";
33
- files["Dockerfile"] = this.getAppDockerfile(options);
49
+ files["Dockerfile"] = renderWorkspaceAppDockerfile({
50
+ appDirectory: options.appDirectory ?? ".",
51
+ port: 3000,
52
+ packageManager: options.packageManager,
53
+ });
34
54
  }
35
55
  files["migrations/.gitkeep"] = "";
36
56
  return files;
37
57
  }
38
- /**
39
- * Install and build commands for Dockerfiles.
40
- *
41
- * Generated projects have no lockfile yet, so never use `npm ci` or
42
- * `--frozen-lockfile`. pnpm and yarn need `corepack enable` on the bare
43
- * node:24-alpine image; bun is not available there so it falls back to npm.
44
- */
45
- getDockerCommands(options) {
46
- switch (options.packageManager) {
47
- case "pnpm":
48
- return {
49
- install: "corepack enable && pnpm install",
50
- build: "pnpm run build",
51
- };
52
- case "yarn":
53
- return {
54
- install: "corepack enable && yarn install",
55
- build: "yarn run build",
56
- };
57
- default:
58
- return { install: "npm install", build: "npm run build" };
59
- }
60
- }
61
58
  getDatabaseCompose(options) {
62
59
  if (options.database === "mysql") {
63
60
  return {
@@ -103,45 +100,13 @@ ${db.environment.map((e) => ` - ${e}`).join("\n")}
103
100
 
104
101
  volumes:
105
102
  db-data:
106
- `;
107
- }
108
- getAppDockerfile(options) {
109
- const { install, build } = this.getDockerCommands(options);
110
- return `FROM node:24-alpine AS builder
111
- WORKDIR /app
112
- COPY . .
113
- RUN ${install}
114
- RUN ${build}
115
-
116
- FROM node:24-alpine
117
- WORKDIR /app
118
- COPY --from=builder /app/dist ./dist
119
- COPY --from=builder /app/node_modules ./node_modules
120
- COPY --from=builder /app/package.json ./
121
- EXPOSE 3000
122
- CMD ["node", "dist/server.js"]
123
- `;
124
- }
125
- getServiceDockerfile(options) {
126
- const { install, build } = this.getDockerCommands(options);
127
- return `FROM node:24-alpine AS builder
128
- WORKDIR /app
129
- COPY . .
130
- RUN ${install}
131
- RUN ${build}
132
-
133
- FROM node:24-alpine
134
- WORKDIR /app
135
- COPY --from=builder /app/dist ./dist
136
- COPY --from=builder /app/node_modules ./node_modules
137
- COPY --from=builder /app/package.json ./
138
- EXPOSE 3000
139
- CMD ["node", "dist/server.js"]
140
103
  `;
141
104
  }
142
105
  getSimpleDockerCompose(options) {
143
106
  const db = this.getDatabaseCompose(options);
144
107
  const databaseUrl = this.getDatabaseUrl(options);
108
+ const appDirectory = (options.appDirectory ?? ".").replace(/^\.\/?/, "").replace(/\/$/, "");
109
+ const src = appDirectory === "" ? "src" : `${appDirectory}/src`;
145
110
  return `services:
146
111
  app:
147
112
  build: .
@@ -151,30 +116,31 @@ CMD ["node", "dist/server.js"]
151
116
  - DATABASE_URL=${databaseUrl}
152
117
  ${db ? " depends_on:\n - db\n" : ""} develop:
153
118
  watch:
154
- - path: src/
119
+ - path: ${src}/
155
120
  action: sync
156
- target: /app/src
121
+ target: /app/${src}
157
122
  ${db ? `\n${this.getDbServiceBlock(db)}` : ""}`;
158
123
  }
159
124
  getDockerCompose(options) {
160
- const services = options.services ?? ["gateway"];
125
+ const services = resolveMicroserviceServices(options.services ?? []);
161
126
  const db = this.getDatabaseCompose(options);
162
127
  const databaseUrl = this.getDatabaseUrl(options);
163
- let serviceDefs = "";
164
- for (let i = 0; i < services.length; i++) {
165
- const service = services[i];
166
- const port = 3001 + i;
167
- serviceDefs += `
168
- ${service}:
128
+ const dependsOn = db ? " depends_on:\n - db\n" : "";
129
+ const block = (name, appPath, port) => `
130
+ ${name}:
169
131
  build:
170
- context: apps/services/${service}
132
+ context: .
133
+ dockerfile: ${appPath}/Dockerfile
171
134
  ports:
172
135
  - "${port}:${port}"
173
136
  environment:
174
137
  - PORT=${port}
175
138
  - DATABASE_URL=${databaseUrl}
176
- ${db ? " depends_on:\n - db\n" : ""}`;
177
- }
139
+ ${dependsOn}`;
140
+ const serviceDefs = [
141
+ block("gateway", "apps/gateway", 3000),
142
+ ...services.map((service, i) => block(service, `apps/services/${service}`, 3001 + i)),
143
+ ].join("");
178
144
  return `services:${serviceDefs}${db ? `\n${this.getDbServiceBlock(db)}` : ""}`;
179
145
  }
180
146
  }
@@ -6,6 +6,7 @@
6
6
  import { existsSync } from "node:fs";
7
7
  import { join } from "node:path";
8
8
  import { writeFileTree } from "../../utils/utils.fileSystem.js";
9
+ import { renderDatabaseEnv } from "../../adapters/databases/databaseAdapter.resolver.js";
9
10
  /**
10
11
  * Generates integration files between frontend and backend.
11
12
  */
@@ -57,7 +58,7 @@ export class IntegrationGenerator {
57
58
  `VITE_API_URL=http://localhost:${backendPort}`,
58
59
  "",
59
60
  "# Database",
60
- "DATABASE_URL=postgresql://localhost:5432/mydb",
61
+ renderDatabaseEnv(context.project.backend?.database, "mydb"),
61
62
  "",
62
63
  ];
63
64
  if (context.project.frontend?.framework === "next") {
@@ -73,7 +74,12 @@ export class IntegrationGenerator {
73
74
  * Ends with a "/" when non-empty so it can be prefixed directly.
74
75
  */
75
76
  getBackendPath(context) {
76
- return context.project.type === "fullstack" ? "apps/api/" : "";
77
+ if (context.project.type !== "fullstack")
78
+ return "";
79
+ // A microservice backend's public entry point is the gateway (CLI-02).
80
+ return context.project.backend?.architecture === "microservice"
81
+ ? "apps/gateway/"
82
+ : "apps/api/";
77
83
  }
78
84
  generateApiClient(context) {
79
85
  const backendPort = context.backendPort ?? 3000;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * zudojs-cli — Module Generator barrel.
3
+ */
4
+ export { generateModule, type GenerateModuleOptions, } from "./module.generator.js";
5
+ export { registerModuleInApp, type ModuleRegistration, } from "./module.registration.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,6 @@
1
+ /**
2
+ * zudojs-cli — Module Generator barrel.
3
+ */
4
+ export { generateModule, } from "./module.generator.js";
5
+ export { registerModuleInApp, } from "./module.registration.js";
6
+ //# sourceMappingURL=index.js.map
@@ -1,13 +1,18 @@
1
1
  /**
2
2
  * zudojs-cli — Module Generator
3
3
  *
4
- * Generates a new feature module within a Zudojs project.
4
+ * Generates a runtime module (a `BaseModule` subclass, rendered by the same
5
+ * template `zudojs create` uses), exports it from the modules barrel and
6
+ * registers it in the sibling `app.ts`.
5
7
  */
8
+ import { type ModuleRegistration } from "./module.registration.js";
6
9
  export interface GenerateModuleOptions {
7
10
  readonly name: string;
8
11
  readonly feature?: boolean;
9
12
  readonly basePath?: string;
10
13
  readonly dryRun?: boolean;
14
+ /** Receives the registration outcome (e.g. lines to add by hand). */
15
+ readonly onRegistered?: (registration: ModuleRegistration) => void;
11
16
  }
12
17
  export declare function generateModule(options: GenerateModuleOptions, cwd: string): Promise<string[]>;
13
18
  //# sourceMappingURL=module.generator.d.ts.map
@@ -1,30 +1,26 @@
1
1
  /**
2
2
  * zudojs-cli — Module Generator
3
3
  *
4
- * Generates a new feature module within a Zudojs project.
4
+ * Generates a runtime module (a `BaseModule` subclass, rendered by the same
5
+ * template `zudojs create` uses), exports it from the modules barrel and
6
+ * registers it in the sibling `app.ts`.
5
7
  */
6
- import { writeFileTree } from "../../utils/utils.fileSystem.js";
8
+ import { basename, dirname } from "node:path";
9
+ import { mergeBarrelExport, writeFileTree } from "../../utils/utils.fileSystem.js";
7
10
  import { CLIGenerationError } from "../../errors/index.js";
8
11
  import { assertGeneratableName, toPascalCase } from "../../utils/utils.name.js";
12
+ import { moduleSpec, renderModuleFile } from "../../templates/shared/appRuntime.template.js";
13
+ import { registerModuleInApp } from "./module.registration.js";
9
14
  export async function generateModule(options, cwd) {
10
15
  const basePath = options.basePath ?? "modules";
11
16
  const name = assertGeneratableName(options.name, "module name");
12
17
  const namePascal = toPascalCase(name);
18
+ const spec = moduleSpec(name, `./${name}/index.js`);
13
19
  const files = {
14
- [`${basePath}/${name}/${name}.module.ts`]: `import { createLogger } from "@zudojs/logger";
15
-
16
- export class ${namePascal}Module {
17
- private readonly logger = createLogger({ name: "${name}-module" });
18
-
19
- id = "${name}-module";
20
-
21
- initialize() {
22
- this.logger.info("${name} module initialized");
23
- }
24
- }
25
- `,
26
- [`${basePath}/${name}/index.ts`]: `export { ${namePascal}Module } from "./${name}.module.js";
20
+ [`${basePath}/${name}/${name}.module.ts`]: renderModuleFile({ module: spec }),
21
+ [`${basePath}/${name}/index.ts`]: `export { ${spec.className} } from "./${name}.module.js";
27
22
  `,
23
+ [`${basePath}/index.ts`]: mergeBarrelExport(cwd, `${basePath}/index.ts`, `export { ${spec.className} } from "./${name}/index.js";`),
28
24
  };
29
25
  if (options.feature) {
30
26
  const featureName = `${name}.feature`;
@@ -43,10 +39,13 @@ export class ${namePascal}Feature {
43
39
  }
44
40
  try {
45
41
  await writeFileTree(cwd, files);
46
- return Object.keys(files);
47
42
  }
48
43
  catch (error) {
49
44
  throw new CLIGenerationError(`Failed to generate module: ${name}`, error);
50
45
  }
46
+ const appPath = `${dirname(basePath)}/app.ts`;
47
+ const registration = registerModuleInApp(cwd, appPath, spec.className, `./${basename(basePath)}/index.js`);
48
+ options.onRegistered?.(registration);
49
+ return registration.registered ? [...Object.keys(files), appPath] : Object.keys(files);
51
50
  }
52
51
  //# sourceMappingURL=module.generator.js.map
@@ -0,0 +1,23 @@
1
+ /**
2
+ * zudojs-cli — Registering a generated module with the runtime.
3
+ *
4
+ * `zudojs generate module` used to emit a plain class that was not a runtime
5
+ * `Module` and that nothing imported, so it never ran. The generator now
6
+ * emits a `BaseModule` subclass (the same template `zudojs create` uses),
7
+ * exports it from the modules barrel, and adds it to the module list in the
8
+ * sibling `app.ts` written by `zudojs create`.
9
+ */
10
+ /** Outcome of {@link registerModuleInApp}. */
11
+ export interface ModuleRegistration {
12
+ /** Whether app.ts now registers the module. */
13
+ readonly registered: boolean;
14
+ /** Lines to add by hand when `registered` is false. */
15
+ readonly manualSteps: readonly string[];
16
+ }
17
+ /**
18
+ * Adds `new <className>()` to the module list in `appPath` and imports it
19
+ * from `importPath`. Idempotent. Leaves a hand-edited app.ts it does not
20
+ * recognise untouched and returns the lines to add instead.
21
+ */
22
+ export declare function registerModuleInApp(cwd: string, appPath: string, className: string, importPath: string): ModuleRegistration;
23
+ //# sourceMappingURL=module.registration.d.ts.map
@@ -0,0 +1,50 @@
1
+ /**
2
+ * zudojs-cli — Registering a generated module with the runtime.
3
+ *
4
+ * `zudojs generate module` used to emit a plain class that was not a runtime
5
+ * `Module` and that nothing imported, so it never ran. The generator now
6
+ * emits a `BaseModule` subclass (the same template `zudojs create` uses),
7
+ * exports it from the modules barrel, and adds it to the module list in the
8
+ * sibling `app.ts` written by `zudojs create`.
9
+ */
10
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
11
+ import { join } from "node:path";
12
+ import { activeWriteCapture } from "../../utils/utils.writeGuard.js";
13
+ /** Marker `renderAppFile` emits at the start of the module list. */
14
+ const MODULE_LIST = " for (const module of [\n";
15
+ /**
16
+ * Adds `new <className>()` to the module list in `appPath` and imports it
17
+ * from `importPath`. Idempotent. Leaves a hand-edited app.ts it does not
18
+ * recognise untouched and returns the lines to add instead.
19
+ */
20
+ export function registerModuleInApp(cwd, appPath, className, importPath) {
21
+ const importLine = `import { ${className} } from "${importPath}";`;
22
+ const entry = ` new ${className}(),\n`;
23
+ const manualSteps = [
24
+ importLine,
25
+ `new ${className}() in the modules passed to createRuntime`,
26
+ ];
27
+ const fullPath = join(cwd, appPath);
28
+ if (!existsSync(fullPath))
29
+ return { registered: false, manualSteps };
30
+ const source = readFileSync(fullPath, "utf-8");
31
+ if (source.includes(entry))
32
+ return { registered: true, manualSteps: [] };
33
+ const listAt = source.indexOf(MODULE_LIST);
34
+ const lastImport = source.lastIndexOf("\nimport ");
35
+ if (listAt === -1 || lastImport === -1)
36
+ return { registered: false, manualSteps };
37
+ const importEnd = source.indexOf("\n", lastImport + 1);
38
+ const insertAt = listAt + MODULE_LIST.length;
39
+ const updated = source.slice(0, importEnd + 1) +
40
+ `${importLine}\n` +
41
+ source.slice(importEnd + 1, insertAt) +
42
+ entry +
43
+ source.slice(insertAt);
44
+ // During the overwrite check the generator runs with writes captured; the
45
+ // registration is an intended edit, not a conflict, so it is skipped then.
46
+ if (!activeWriteCapture())
47
+ writeFileSync(fullPath, updated);
48
+ return { registered: true, manualSteps: [] };
49
+ }
50
+ //# sourceMappingURL=module.registration.js.map
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * @module resolvers/dependency
5
5
  */
6
+ import { DEFAULT_DEPENDENCY_VERSIONS } from "./dependencyVersions.constant.js";
6
7
  /**
7
8
  * Resolves compatible dependency versions.
8
9
  */
@@ -63,9 +64,13 @@ export class DependencyResolver {
63
64
  conflicts.push(conflict);
64
65
  continue;
65
66
  }
67
+ const version = req.version ?? DEFAULT_DEPENDENCY_VERSIONS.get(req.name);
68
+ if (version === undefined) {
69
+ warnings.push(`No version range known for "${req.name}"; it is recorded as "latest", which is not reproducible.`);
70
+ }
66
71
  const resolved = {
67
72
  name: req.name,
68
- version: req.version ?? "latest",
73
+ version: version ?? "latest",
69
74
  type: req.type,
70
75
  };
71
76
  if (req.type === "dependency") {
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Default version ranges for frontend dependencies the adapters request
3
+ * without a version.
4
+ *
5
+ * Unpinned requirements used to be written as `"latest"`, so the same
6
+ * scaffold produced a different dependency set every day. Every name a
7
+ * built-in adapter requests unpinned has a caret range here (checked by
8
+ * `tests/cli.round10.*.test.ts`). Ranges were taken from the npm registry on
9
+ * 2026-09-19.
10
+ */
11
+ export declare const DEFAULT_DEPENDENCY_VERSIONS: ReadonlyMap<string, string>;
12
+ //# sourceMappingURL=dependencyVersions.constant.d.ts.map
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Default version ranges for frontend dependencies the adapters request
3
+ * without a version.
4
+ *
5
+ * Unpinned requirements used to be written as `"latest"`, so the same
6
+ * scaffold produced a different dependency set every day. Every name a
7
+ * built-in adapter requests unpinned has a caret range here (checked by
8
+ * `tests/cli.round10.*.test.ts`). Ranges were taken from the npm registry on
9
+ * 2026-09-19.
10
+ */
11
+ export const DEFAULT_DEPENDENCY_VERSIONS = new Map([
12
+ ["@angular-builders/jest", "^22.0.1"],
13
+ ["@angular/common", "^22.1.7"],
14
+ ["@angular/core", "^22.1.7"],
15
+ ["@ngrx/store", "^22.0.1"],
16
+ ["@testing-library/jest-dom", "^7.0.1"],
17
+ ["@testing-library/react", "^16.3.3"],
18
+ ["@testing-library/react-native", "^14.0.1"],
19
+ ["@testing-library/svelte", "^5.4.2"],
20
+ ["@testing-library/user-event", "^14.6.7"],
21
+ ["@vue/test-utils", "^2.5.1"],
22
+ ["eslint", "^10.11.0"],
23
+ ["eslint-plugin-react-hooks", "^7.1.1"],
24
+ ["eslint-plugin-react-refresh", "^0.5.7"],
25
+ ["eslint-plugin-svelte", "^3.23.0"],
26
+ ["eslint-plugin-vue", "^10.11.0"],
27
+ ["jest", "^30.5.2"],
28
+ ["jsdom", "^30.1.0"],
29
+ ["pinia", "^4.0.3"],
30
+ ["prettier", "^3.9.8"],
31
+ ["prettier-plugin-svelte", "^4.1.1"],
32
+ ["react", "^19.3.0"],
33
+ ["react-dom", "^19.3.0"],
34
+ ["svelte", "^5.57.1"],
35
+ ["typescript-eslint", "^8.70.0"],
36
+ ["vitest", "^5.0.1"],
37
+ ["vue", "^3.5.43"],
38
+ ["zustand", "^5.0.15"],
39
+ ]);
40
+ //# sourceMappingURL=dependencyVersions.constant.js.map
@@ -4,4 +4,5 @@
4
4
  * @module resolvers/dependency
5
5
  */
6
6
  export { DependencyResolver, type DependencyResolutionResult, type ResolvedDependency, type DependencyConflict, } from "./dependencyResolver.core.js";
7
+ export { DEFAULT_DEPENDENCY_VERSIONS } from "./dependencyVersions.constant.js";
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -4,4 +4,5 @@
4
4
  * @module resolvers/dependency
5
5
  */
6
6
  export { DependencyResolver, } from "./dependencyResolver.core.js";
7
+ export { DEFAULT_DEPENDENCY_VERSIONS } from "./dependencyVersions.constant.js";
7
8
  //# sourceMappingURL=index.js.map
@@ -23,7 +23,7 @@
23
23
  */
24
24
  import { ZUDOJS_PACKAGES_VERSION } from "../../constants/index.js";
25
25
  import { normalizeName } from "../../utils/utils.name.js";
26
- import { RUNTIME_APP_DEPENDENCIES, moduleSpec, renderAppFile, renderModuleFile, renderServerFile, renderPnpmWorkspaceFile, } from "../shared/index.js";
26
+ import { RUNTIME_APP_DEPENDENCIES, moduleSpec, renderAppPackageDockerfile, renderAppFile, renderModuleFile, renderServerFile, renderPnpmWorkspaceFile, } from "../shared/index.js";
27
27
  /** Default service names used when none are provided. */
28
28
  export const DEFAULT_MICROSERVICE_SERVICES = [
29
29
  "identity",
@@ -173,13 +173,6 @@ MIT
173
173
  `;
174
174
  // Shared types
175
175
  files["src/types/index.ts"] = ``;
176
- // Install command used inside Dockerfiles (no lockfile is generated, so
177
- // never use `npm ci`; enable corepack for pnpm/yarn on bare node images).
178
- const dockerInstall = options.packageManager === "pnpm"
179
- ? "corepack enable && pnpm install"
180
- : options.packageManager === "yarn"
181
- ? "corepack enable && yarn install"
182
- : "npm install";
183
176
  const appTsconfig = `{
184
177
  "compilerOptions": {
185
178
  "target": "ES2024",
@@ -223,22 +216,11 @@ MIT
223
216
  devDependencies: appDevDeps,
224
217
  }, null, 2) + "\n";
225
218
  files["apps/gateway/tsconfig.json"] = appTsconfig;
226
- files["apps/gateway/Dockerfile"] = `FROM node:24-alpine AS builder
227
- WORKDIR /app
228
- COPY apps/gateway/package.json ./
229
- RUN ${dockerInstall}
230
- COPY apps/gateway/tsconfig.json ./
231
- COPY apps/gateway/src ./src
232
- RUN npx tsc
233
-
234
- FROM node:24-alpine AS runtime
235
- WORKDIR /app
236
- COPY --from=builder /app/dist ./dist
237
- COPY --from=builder /app/node_modules ./node_modules
238
- COPY --from=builder /app/package.json ./
239
- EXPOSE 3000
240
- CMD ["node", "dist/server.js"]
241
- `;
219
+ files["apps/gateway/Dockerfile"] = renderAppPackageDockerfile({
220
+ appPath: "apps/gateway",
221
+ port: 3000,
222
+ packageManager: options.packageManager,
223
+ });
242
224
  files["apps/gateway/src/index.ts"] =
243
225
  `export { createApp } from "./app.js";
244
226
  `;
@@ -284,23 +266,11 @@ CMD ["node", "dist/server.js"]
284
266
  devDependencies: appDevDeps,
285
267
  }, null, 2) + "\n";
286
268
  files[`apps/services/${svcName}/tsconfig.json`] = appTsconfig;
287
- files[`apps/services/${svcName}/Dockerfile`] =
288
- `FROM node:24-alpine AS builder
289
- WORKDIR /app
290
- COPY apps/services/${svcName}/package.json ./
291
- RUN ${dockerInstall}
292
- COPY apps/services/${svcName}/tsconfig.json ./
293
- COPY apps/services/${svcName}/src ./src
294
- RUN npx tsc
295
-
296
- FROM node:24-alpine AS runtime
297
- WORKDIR /app
298
- COPY --from=builder /app/dist ./dist
299
- COPY --from=builder /app/node_modules ./node_modules
300
- COPY --from=builder /app/package.json ./
301
- EXPOSE ${port}
302
- CMD ["node", "dist/server.js"]
303
- `;
269
+ files[`apps/services/${svcName}/Dockerfile`] = renderAppPackageDockerfile({
270
+ appPath: `apps/services/${svcName}`,
271
+ port,
272
+ packageManager: options.packageManager,
273
+ });
304
274
  // Service structure (modular monolith per service)
305
275
  const svcDirs = [
306
276
  "configs",
@@ -337,7 +307,9 @@ CMD ["node", "dist/server.js"]
337
307
  renderModuleFile({ module: svcModule });
338
308
  files[`apps/services/${svcName}/src/modules/index.ts`] =
339
309
  `export { ${svcModule.className} } from "./${svcName}.module.js";\n`;
340
- files[`apps/services/${svcName}/src/server.ts`] = renderServerFile();
310
+ files[`apps/services/${svcName}/src/server.ts`] = renderServerFile("./app.js", {
311
+ defaultPort: port,
312
+ });
341
313
  for (const dir of svcDirs) {
342
314
  files[`apps/services/${svcName}/src/${dir}/index.ts`] = "";
343
315
  }
@@ -31,6 +31,7 @@
31
31
  * └── README.md
32
32
  * ```
33
33
  */
34
+ import { renderDatabaseEnv } from "../../adapters/databases/databaseAdapter.resolver.js";
34
35
  import { ZUDOJS_PACKAGES_VERSION } from "../../constants/index.js";
35
36
  import { normalizeName } from "../../utils/utils.name.js";
36
37
  import { RUNTIME_APP_DEPENDENCIES, moduleSpec, renderAppFile, renderModuleFile, renderServerFile, renderPnpmWorkspaceFile, } from "../shared/index.js";
@@ -107,7 +108,7 @@ export function generateModularMonolithFiles(options) {
107
108
  }
108
109
  files[".env.example"] = `NODE_ENV=development
109
110
  PORT=3000
110
- DATABASE_URL=postgresql://localhost:5432/${nameSlug}
111
+ ${renderDatabaseEnv(options.database, nameSlug)}
111
112
  `;
112
113
  files[".gitignore"] = `node_modules/
113
114
  dist/
@@ -37,6 +37,7 @@
37
37
  * └── README.md
38
38
  * ```
39
39
  */
40
+ import { renderDatabaseEnv } from "../../adapters/databases/databaseAdapter.resolver.js";
40
41
  import { ZUDOJS_PACKAGES_VERSION } from "../../constants/index.js";
41
42
  import { normalizeName, toPascalCase } from "../../utils/utils.name.js";
42
43
  import { RUNTIME_APP_DEPENDENCIES, moduleSpec, renderAppFile, renderModuleFile, renderServerFile, renderServiceFile, renderPnpmWorkspaceFile, } from "../shared/index.js";
@@ -124,7 +125,7 @@ export function generateMonolithFiles(options) {
124
125
  files[".env.example"] = `NODE_ENV=development
125
126
  PORT=3000
126
127
 
127
- DATABASE_URL=postgresql://localhost:5432/${nameSlug}
128
+ ${renderDatabaseEnv(options.database, nameSlug)}
128
129
  JWT_SECRET=change-this-in-production
129
130
  `;
130
131
  files[".gitignore"] = `node_modules/
@@ -179,7 +180,9 @@ MIT
179
180
  // src/ entry points
180
181
  files["src/index.ts"] = `export { createApp } from "./app.js";
181
182
  `;
182
- files["src/server.ts"] = renderServerFile();
183
+ files["src/server.ts"] = renderServerFile("./app.js", {
184
+ healthControllerImport: "./controllers/index.js",
185
+ });
183
186
  // Normalized: the name becomes a file path segment and a class name.
184
187
  const moduleName = normalizeName(options.services[0] ?? "app") || "app";
185
188
  const serviceClassName = `${toPascalCase(moduleName)}Service`;
@@ -37,11 +37,6 @@ export declare function renderAppFile(options: {
37
37
  */
38
38
  readonly port?: number;
39
39
  }): string;
40
- /**
41
- * Renders `src/server.ts`: the process entry point. Starts the runtime and
42
- * stops it cleanly on SIGINT/SIGTERM.
43
- */
44
- export declare function renderServerFile(appImportPath?: string): string;
45
40
  /**
46
41
  * Renders a module class extending `BaseModule`.
47
42
  *