@expressots/cli 3.0.0 → 4.0.0-preview.2

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 (180) hide show
  1. package/README.md +41 -95
  2. package/bin/cicd/cli.d.ts +6 -0
  3. package/bin/cicd/cli.js +126 -0
  4. package/bin/cicd/form.d.ts +29 -0
  5. package/bin/cicd/form.js +345 -0
  6. package/bin/cicd/generators/azure-devops.d.ts +2 -0
  7. package/bin/cicd/generators/azure-devops.js +370 -0
  8. package/bin/cicd/generators/bitbucket.d.ts +2 -0
  9. package/bin/cicd/generators/bitbucket.js +217 -0
  10. package/bin/cicd/generators/circleci.d.ts +2 -0
  11. package/bin/cicd/generators/circleci.js +274 -0
  12. package/bin/cicd/generators/github-actions.d.ts +14 -0
  13. package/bin/cicd/generators/github-actions.js +426 -0
  14. package/bin/cicd/generators/gitlab-ci.d.ts +2 -0
  15. package/bin/cicd/generators/gitlab-ci.js +237 -0
  16. package/bin/cicd/generators/index.d.ts +6 -0
  17. package/bin/cicd/generators/index.js +15 -0
  18. package/bin/cicd/generators/jenkins.d.ts +2 -0
  19. package/bin/cicd/generators/jenkins.js +248 -0
  20. package/bin/cicd/generators/template-loader.d.ts +17 -0
  21. package/bin/cicd/generators/template-loader.js +128 -0
  22. package/bin/cicd/index.d.ts +1 -0
  23. package/bin/cicd/index.js +5 -0
  24. package/bin/cli.d.ts +1 -1
  25. package/bin/cli.js +18 -3
  26. package/bin/commands/project.commands.d.ts +19 -6
  27. package/bin/commands/project.commands.js +390 -61
  28. package/bin/config/index.d.ts +5 -0
  29. package/bin/config/index.js +10 -0
  30. package/bin/config/manager.d.ts +98 -0
  31. package/bin/config/manager.js +222 -0
  32. package/bin/containerize/analyzers/bootstrap-analyzer.d.ts +46 -0
  33. package/bin/containerize/analyzers/bootstrap-analyzer.js +187 -0
  34. package/bin/containerize/analyzers/project-analyzer.d.ts +20 -0
  35. package/bin/containerize/analyzers/project-analyzer.js +150 -0
  36. package/bin/containerize/cli.d.ts +4 -0
  37. package/bin/containerize/cli.js +113 -0
  38. package/bin/containerize/form.d.ts +15 -0
  39. package/bin/containerize/form.js +154 -0
  40. package/bin/containerize/generators/ci-generator.d.ts +31 -0
  41. package/bin/containerize/generators/ci-generator.js +936 -0
  42. package/bin/containerize/generators/docker-compose-generator.d.ts +8 -0
  43. package/bin/containerize/generators/docker-compose-generator.js +186 -0
  44. package/bin/containerize/generators/dockerfile-generator.d.ts +8 -0
  45. package/bin/containerize/generators/dockerfile-generator.js +635 -0
  46. package/bin/containerize/generators/kubernetes-generator.d.ts +8 -0
  47. package/bin/containerize/generators/kubernetes-generator.js +133 -0
  48. package/bin/containerize/generators/template-loader.d.ts +36 -0
  49. package/bin/containerize/generators/template-loader.js +129 -0
  50. package/bin/containerize/index.d.ts +4 -0
  51. package/bin/containerize/index.js +13 -0
  52. package/bin/containerize/presets/preset-registry.d.ts +20 -0
  53. package/bin/containerize/presets/preset-registry.js +102 -0
  54. package/bin/costs/cli.d.ts +5 -0
  55. package/bin/costs/cli.js +183 -0
  56. package/bin/costs/form.d.ts +44 -0
  57. package/bin/costs/form.js +412 -0
  58. package/bin/costs/index.d.ts +4 -0
  59. package/bin/costs/index.js +25 -0
  60. package/bin/costs/pricing-manager.d.ts +84 -0
  61. package/bin/costs/pricing-manager.js +342 -0
  62. package/bin/costs/providers/index.d.ts +32 -0
  63. package/bin/costs/providers/index.js +153 -0
  64. package/bin/costs/sources/api-source.d.ts +10 -0
  65. package/bin/costs/sources/api-source.js +32 -0
  66. package/bin/costs/sources/index.d.ts +6 -0
  67. package/bin/costs/sources/index.js +15 -0
  68. package/bin/costs/sources/local-json-source.d.ts +23 -0
  69. package/bin/costs/sources/local-json-source.js +59 -0
  70. package/bin/costs/sources/remote-json-source.d.ts +11 -0
  71. package/bin/costs/sources/remote-json-source.js +53 -0
  72. package/bin/costs/types.d.ts +53 -0
  73. package/bin/costs/types.js +5 -0
  74. package/bin/dev/cli.d.ts +4 -0
  75. package/bin/dev/cli.js +134 -0
  76. package/bin/dev/form.d.ts +36 -0
  77. package/bin/dev/form.js +254 -0
  78. package/bin/dev/index.d.ts +1 -0
  79. package/bin/dev/index.js +5 -0
  80. package/bin/generate/cli.js +29 -2
  81. package/bin/generate/form.d.ts +5 -1
  82. package/bin/generate/form.js +3 -3
  83. package/bin/generate/templates/nonopinionated/config.tpl +12 -0
  84. package/bin/generate/templates/nonopinionated/event.tpl +10 -0
  85. package/bin/generate/templates/nonopinionated/guard.tpl +18 -0
  86. package/bin/generate/templates/nonopinionated/handler.tpl +12 -0
  87. package/bin/generate/templates/nonopinionated/interceptor.tpl +27 -0
  88. package/bin/generate/templates/opinionated/config.tpl +47 -0
  89. package/bin/generate/templates/opinionated/entity.tpl +1 -8
  90. package/bin/generate/templates/opinionated/event.tpl +15 -0
  91. package/bin/generate/templates/opinionated/guard.tpl +41 -0
  92. package/bin/generate/templates/opinionated/handler.tpl +23 -0
  93. package/bin/generate/templates/opinionated/interceptor.tpl +50 -0
  94. package/bin/generate/utils/command-utils.d.ts +7 -3
  95. package/bin/generate/utils/command-utils.js +95 -31
  96. package/bin/generate/utils/nonopininated-cmd.d.ts +10 -1
  97. package/bin/generate/utils/nonopininated-cmd.js +100 -1
  98. package/bin/generate/utils/opinionated-cmd.d.ts +10 -1
  99. package/bin/generate/utils/opinionated-cmd.js +112 -7
  100. package/bin/generate/utils/string-utils.d.ts +6 -0
  101. package/bin/generate/utils/string-utils.js +13 -1
  102. package/bin/help/form.js +11 -3
  103. package/bin/migrate/analyzers/platform-detector.d.ts +14 -0
  104. package/bin/migrate/analyzers/platform-detector.js +116 -0
  105. package/bin/migrate/cli.d.ts +6 -0
  106. package/bin/migrate/cli.js +96 -0
  107. package/bin/migrate/form.d.ts +25 -0
  108. package/bin/migrate/form.js +347 -0
  109. package/bin/migrate/generators/compose-to-k8s.d.ts +2 -0
  110. package/bin/migrate/generators/compose-to-k8s.js +324 -0
  111. package/bin/migrate/generators/compose-to-railway.d.ts +2 -0
  112. package/bin/migrate/generators/compose-to-railway.js +138 -0
  113. package/bin/migrate/generators/compose-to-render.d.ts +2 -0
  114. package/bin/migrate/generators/compose-to-render.js +148 -0
  115. package/bin/migrate/generators/generic-migration.d.ts +9 -0
  116. package/bin/migrate/generators/generic-migration.js +221 -0
  117. package/bin/migrate/generators/heroku-to-fly.d.ts +2 -0
  118. package/bin/migrate/generators/heroku-to-fly.js +291 -0
  119. package/bin/migrate/generators/heroku-to-railway.d.ts +2 -0
  120. package/bin/migrate/generators/heroku-to-railway.js +283 -0
  121. package/bin/migrate/generators/heroku-to-render.d.ts +2 -0
  122. package/bin/migrate/generators/heroku-to-render.js +148 -0
  123. package/bin/migrate/generators/index.d.ts +7 -0
  124. package/bin/migrate/generators/index.js +17 -0
  125. package/bin/migrate/generators/template-loader.d.ts +21 -0
  126. package/bin/migrate/generators/template-loader.js +59 -0
  127. package/bin/migrate/index.d.ts +1 -0
  128. package/bin/migrate/index.js +5 -0
  129. package/bin/new/cli.js +21 -6
  130. package/bin/new/form.d.ts +25 -4
  131. package/bin/new/form.js +285 -70
  132. package/bin/profile/analyzers/dockerfile-analyzer.d.ts +27 -0
  133. package/bin/profile/analyzers/dockerfile-analyzer.js +122 -0
  134. package/bin/profile/analyzers/image-analyzer.d.ts +19 -0
  135. package/bin/profile/analyzers/image-analyzer.js +85 -0
  136. package/bin/profile/cli.d.ts +4 -0
  137. package/bin/profile/cli.js +92 -0
  138. package/bin/profile/form.d.ts +56 -0
  139. package/bin/profile/form.js +400 -0
  140. package/bin/profile/index.d.ts +1 -0
  141. package/bin/profile/index.js +5 -0
  142. package/bin/profile/optimizers/index.d.ts +19 -0
  143. package/bin/profile/optimizers/index.js +137 -0
  144. package/bin/providers/add/form.d.ts +1 -1
  145. package/bin/providers/add/form.js +27 -6
  146. package/bin/providers/create/form.js +2 -1
  147. package/bin/scripts/form.js +27 -5
  148. package/bin/studio/cli.d.ts +15 -0
  149. package/bin/studio/cli.js +166 -0
  150. package/bin/studio/index.d.ts +5 -0
  151. package/bin/studio/index.js +9 -0
  152. package/bin/templates/cache.d.ts +54 -0
  153. package/bin/templates/cache.js +180 -0
  154. package/bin/templates/cli.d.ts +8 -0
  155. package/bin/templates/cli.js +292 -0
  156. package/bin/templates/fetcher.d.ts +49 -0
  157. package/bin/templates/fetcher.js +208 -0
  158. package/bin/templates/index.d.ts +11 -0
  159. package/bin/templates/index.js +37 -0
  160. package/bin/templates/manager.d.ts +116 -0
  161. package/bin/templates/manager.js +323 -0
  162. package/bin/templates/renderer.d.ts +49 -0
  163. package/bin/templates/renderer.js +204 -0
  164. package/bin/templates/types.d.ts +51 -0
  165. package/bin/templates/types.js +5 -0
  166. package/bin/utils/add-module-to-container.d.ts +2 -2
  167. package/bin/utils/add-module-to-container.js +15 -5
  168. package/bin/utils/cli-ui.d.ts +30 -3
  169. package/bin/utils/cli-ui.js +95 -13
  170. package/bin/utils/index.d.ts +4 -0
  171. package/bin/utils/index.js +4 -0
  172. package/bin/utils/input-validation.d.ts +50 -0
  173. package/bin/utils/input-validation.js +143 -0
  174. package/bin/utils/package-manager-commands.d.ts +24 -0
  175. package/bin/utils/package-manager-commands.js +50 -0
  176. package/bin/utils/safe-spawn.d.ts +35 -0
  177. package/bin/utils/safe-spawn.js +51 -0
  178. package/bin/utils/update-tsconfig-paths.d.ts +35 -0
  179. package/bin/utils/update-tsconfig-paths.js +286 -0
  180. package/package.json +154 -154
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ /**
3
+ * Remote JSON pricing source - fetches from GitHub-hosted pricing file
4
+ */
5
+ var __importDefault = (this && this.__importDefault) || function (mod) {
6
+ return (mod && mod.__esModule) ? mod : { "default": mod };
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.createRemoteJSONPricingSource = exports.RemoteJSONPricingSource = void 0;
10
+ const https_1 = __importDefault(require("https"));
11
+ const DEFAULT_URL = "https://raw.githubusercontent.com/expressots/pricing/main/pricing.json";
12
+ const FETCH_TIMEOUT = 10000;
13
+ class RemoteJSONPricingSource {
14
+ constructor(url) {
15
+ this.name = "remote";
16
+ this.url = url || DEFAULT_URL;
17
+ }
18
+ async fetch() {
19
+ return new Promise((resolve) => {
20
+ const request = https_1.default.get(this.url, { timeout: FETCH_TIMEOUT }, (response) => {
21
+ if (response.statusCode !== 200) {
22
+ resolve(null);
23
+ return;
24
+ }
25
+ let data = "";
26
+ response.on("data", (chunk) => {
27
+ data += chunk;
28
+ });
29
+ response.on("end", () => {
30
+ try {
31
+ const pricing = JSON.parse(data);
32
+ resolve(pricing);
33
+ }
34
+ catch {
35
+ resolve(null);
36
+ }
37
+ });
38
+ });
39
+ request.on("error", () => {
40
+ resolve(null);
41
+ });
42
+ request.on("timeout", () => {
43
+ request.destroy();
44
+ resolve(null);
45
+ });
46
+ });
47
+ }
48
+ }
49
+ exports.RemoteJSONPricingSource = RemoteJSONPricingSource;
50
+ function createRemoteJSONPricingSource(url) {
51
+ return new RemoteJSONPricingSource(url);
52
+ }
53
+ exports.createRemoteJSONPricingSource = createRemoteJSONPricingSource;
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Pricing types and interfaces
3
+ */
4
+ export type CloudProvider = "aws" | "gcp" | "azure" | "railway" | "render" | "fly" | "digitalocean" | "heroku";
5
+ export type PricingModel = "per-hour" | "per-month" | "usage";
6
+ export interface ProviderPricing {
7
+ serviceName: string;
8
+ model: PricingModel;
9
+ basePrice: number;
10
+ cpuPerHour: number;
11
+ memoryPerGbHour: number;
12
+ storagePerGb: number;
13
+ bandwidthPerGb: number;
14
+ freeBandwidth: number;
15
+ freeCredits?: number;
16
+ notes?: string;
17
+ source?: string;
18
+ lastVerified?: string;
19
+ }
20
+ export interface PricingData {
21
+ version: string;
22
+ updated: string;
23
+ providers: Record<CloudProvider, ProviderPricing>;
24
+ }
25
+ export interface PricingSource {
26
+ name: string;
27
+ fetch(): Promise<PricingData | null>;
28
+ }
29
+ export interface ResourceEstimate {
30
+ instances: number;
31
+ cpu: number;
32
+ memory: number;
33
+ storage: number;
34
+ bandwidth: number;
35
+ hours: number;
36
+ }
37
+ export interface CostEstimate {
38
+ provider: CloudProvider;
39
+ monthlyCost: number;
40
+ breakdown: {
41
+ compute: number;
42
+ storage: number;
43
+ bandwidth: number;
44
+ base: number;
45
+ };
46
+ currency: string;
47
+ notes?: string;
48
+ }
49
+ export interface PricingCacheEntry {
50
+ data: PricingData;
51
+ timestamp: number;
52
+ source: string;
53
+ }
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ /**
3
+ * Pricing types and interfaces
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,4 @@
1
+ import { CommandModule } from "yargs";
2
+ type CommandModuleArgs = {};
3
+ declare const devContainerCommand: () => CommandModule<CommandModuleArgs, any>;
4
+ export { devContainerCommand };
package/bin/dev/cli.js ADDED
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.devContainerCommand = void 0;
4
+ const form_1 = require("./form");
5
+ const devContainerCommand = () => {
6
+ return {
7
+ command: "container-dev [action]",
8
+ describe: "Develop inside Docker containers with hot reload.",
9
+ aliases: ["cdev", "docker-dev"],
10
+ builder: (yargs) => {
11
+ yargs.positional("action", {
12
+ choices: [
13
+ "start",
14
+ "stop",
15
+ "attach",
16
+ "shell",
17
+ "status",
18
+ "logs",
19
+ ],
20
+ describe: "Action to perform",
21
+ type: "string",
22
+ default: "start",
23
+ });
24
+ yargs.option("container", {
25
+ describe: "Run development in Docker container",
26
+ type: "boolean",
27
+ alias: "c",
28
+ default: false,
29
+ });
30
+ yargs.option("service", {
31
+ describe: "Docker Compose service name",
32
+ type: "string",
33
+ alias: "s",
34
+ default: "app",
35
+ });
36
+ yargs.option("compose-file", {
37
+ describe: "Path to docker-compose file",
38
+ type: "string",
39
+ alias: "f",
40
+ default: "docker-compose.development.yml",
41
+ });
42
+ yargs.option("build", {
43
+ describe: "Rebuild container before starting",
44
+ type: "boolean",
45
+ alias: "b",
46
+ default: false,
47
+ });
48
+ yargs.option("detach", {
49
+ describe: "Run in background",
50
+ type: "boolean",
51
+ alias: "d",
52
+ default: false,
53
+ });
54
+ yargs.option("port", {
55
+ describe: "Override application port",
56
+ type: "number",
57
+ alias: "p",
58
+ });
59
+ yargs.option("debug-port", {
60
+ describe: "Debug port for Node.js inspector",
61
+ type: "number",
62
+ default: 9229,
63
+ });
64
+ yargs.option("watch", {
65
+ describe: "Enable file watching for hot reload",
66
+ type: "boolean",
67
+ alias: "w",
68
+ default: true,
69
+ });
70
+ yargs.option("follow", {
71
+ describe: "Follow logs (for logs action)",
72
+ type: "boolean",
73
+ default: true,
74
+ });
75
+ yargs.option("tail", {
76
+ describe: "Number of log lines to show",
77
+ type: "number",
78
+ default: 100,
79
+ });
80
+ return yargs;
81
+ },
82
+ handler: async (argv) => {
83
+ const { action, container, service, composeFile, build, detach, port, debugPort, watch, follow, tail, } = argv;
84
+ const options = {
85
+ container,
86
+ service,
87
+ composeFile,
88
+ build,
89
+ detach,
90
+ port,
91
+ debugPort,
92
+ watch,
93
+ follow,
94
+ tail,
95
+ };
96
+ // If --container flag is set without action, start container dev
97
+ if (container && action === "start") {
98
+ await (0, form_1.startDevContainer)(options);
99
+ return;
100
+ }
101
+ switch (action) {
102
+ case "start":
103
+ if (container) {
104
+ await (0, form_1.startDevContainer)(options);
105
+ }
106
+ else {
107
+ // Regular dev command (non-container) - delegate to existing
108
+ console.log("Starting local development...");
109
+ console.log("Use --container flag to develop inside Docker.");
110
+ console.log("Or run: npm run dev");
111
+ }
112
+ break;
113
+ case "stop":
114
+ await (0, form_1.stopDevContainer)(options);
115
+ break;
116
+ case "attach":
117
+ await (0, form_1.attachToContainer)(options);
118
+ break;
119
+ case "shell":
120
+ await (0, form_1.openShell)(options);
121
+ break;
122
+ case "status":
123
+ await (0, form_1.showStatus)(options);
124
+ break;
125
+ case "logs":
126
+ await (0, form_1.showLogs)(options);
127
+ break;
128
+ default:
129
+ console.log(`Unknown action: ${action}`);
130
+ }
131
+ },
132
+ };
133
+ };
134
+ exports.devContainerCommand = devContainerCommand;
@@ -0,0 +1,36 @@
1
+ export interface DevOptions {
2
+ container: boolean;
3
+ service: string;
4
+ composeFile: string;
5
+ build: boolean;
6
+ detach: boolean;
7
+ port?: number;
8
+ debugPort: number;
9
+ watch: boolean;
10
+ follow: boolean;
11
+ tail: number;
12
+ }
13
+ /**
14
+ * Start development container with hot reload
15
+ */
16
+ export declare function startDevContainer(options: DevOptions): Promise<void>;
17
+ /**
18
+ * Stop development containers
19
+ */
20
+ export declare function stopDevContainer(options: DevOptions): Promise<void>;
21
+ /**
22
+ * Attach to running container
23
+ */
24
+ export declare function attachToContainer(options: DevOptions): Promise<void>;
25
+ /**
26
+ * Open shell in container
27
+ */
28
+ export declare function openShell(options: DevOptions): Promise<void>;
29
+ /**
30
+ * Show status of development containers
31
+ */
32
+ export declare function showStatus(options: DevOptions): Promise<void>;
33
+ /**
34
+ * Show container logs
35
+ */
36
+ export declare function showLogs(options: DevOptions): Promise<void>;
@@ -0,0 +1,254 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.showLogs = exports.showStatus = exports.openShell = exports.attachToContainer = exports.stopDevContainer = exports.startDevContainer = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const child_process_1 = require("child_process");
11
+ /**
12
+ * Start development container with hot reload
13
+ */
14
+ async function startDevContainer(options) {
15
+ console.log(chalk_1.default.cyan("\n🐳 ExpressoTS Container Development\n"));
16
+ const cwd = process.cwd();
17
+ const composeFile = path_1.default.join(cwd, options.composeFile);
18
+ // Check if docker-compose file exists
19
+ if (!fs_1.default.existsSync(composeFile)) {
20
+ console.log(chalk_1.default.yellow(`⚠️ ${options.composeFile} not found.`));
21
+ console.log(chalk_1.default.gray("Generating development Docker configuration..."));
22
+ // Try to generate if containerize is available
23
+ console.log(chalk_1.default.gray("\nRun the following to generate development config:"));
24
+ console.log(chalk_1.default.white(` expressots containerize docker --env development`));
25
+ console.log();
26
+ return;
27
+ }
28
+ // Check if Docker is running
29
+ if (!isDockerRunning()) {
30
+ console.log(chalk_1.default.red("Error: Docker is not running."));
31
+ console.log(chalk_1.default.gray("Please start Docker Desktop or Docker daemon."));
32
+ return;
33
+ }
34
+ console.log(chalk_1.default.yellow(`📄 Using ${options.composeFile}`));
35
+ // Build arguments
36
+ const args = ["-f", composeFile];
37
+ if (options.build) {
38
+ console.log(chalk_1.default.yellow("🔨 Building containers..."));
39
+ runDockerCompose([...args, "build"], { cwd });
40
+ }
41
+ // Start containers
42
+ console.log(chalk_1.default.yellow("🚀 Starting development containers..."));
43
+ const upArgs = [...args, "up"];
44
+ if (options.detach) {
45
+ upArgs.push("-d");
46
+ }
47
+ // Set environment variables
48
+ const env = {
49
+ ...process.env,
50
+ };
51
+ if (options.port) {
52
+ env.PORT = String(options.port);
53
+ }
54
+ env.DEBUG_PORT = String(options.debugPort);
55
+ if (options.detach) {
56
+ runDockerCompose(upArgs, { cwd, env });
57
+ console.log(chalk_1.default.green("\n✅ Development containers started in background.\n"));
58
+ printDevInfo(options);
59
+ }
60
+ else {
61
+ console.log(chalk_1.default.green("\n✅ Starting development environment...\n"));
62
+ printDevInfo(options);
63
+ console.log(chalk_1.default.gray("Press Ctrl+C to stop\n"));
64
+ // Run in foreground
65
+ spawnDockerCompose(upArgs, { cwd, env, stdio: "inherit" });
66
+ }
67
+ }
68
+ exports.startDevContainer = startDevContainer;
69
+ /**
70
+ * Stop development containers
71
+ */
72
+ async function stopDevContainer(options) {
73
+ console.log(chalk_1.default.cyan("\n🛑 Stopping development containers...\n"));
74
+ const cwd = process.cwd();
75
+ const composeFile = path_1.default.join(cwd, options.composeFile);
76
+ if (!fs_1.default.existsSync(composeFile)) {
77
+ // Try default compose file
78
+ const defaultCompose = path_1.default.join(cwd, "docker-compose.yml");
79
+ if (fs_1.default.existsSync(defaultCompose)) {
80
+ runDockerCompose(["-f", defaultCompose, "down"], { cwd });
81
+ }
82
+ else {
83
+ console.log(chalk_1.default.yellow("No docker-compose file found."));
84
+ }
85
+ return;
86
+ }
87
+ runDockerCompose(["-f", composeFile, "down"], { cwd });
88
+ console.log(chalk_1.default.green("✅ Development containers stopped."));
89
+ }
90
+ exports.stopDevContainer = stopDevContainer;
91
+ /**
92
+ * Attach to running container
93
+ */
94
+ async function attachToContainer(options) {
95
+ console.log(chalk_1.default.cyan(`\n🔗 Attaching to ${options.service}...\n`));
96
+ const cwd = process.cwd();
97
+ const composeFile = path_1.default.join(cwd, options.composeFile);
98
+ if (!fs_1.default.existsSync(composeFile)) {
99
+ console.log(chalk_1.default.red(`Error: ${options.composeFile} not found.`));
100
+ return;
101
+ }
102
+ spawnDockerCompose(["-f", composeFile, "attach", options.service], {
103
+ cwd,
104
+ stdio: "inherit",
105
+ });
106
+ }
107
+ exports.attachToContainer = attachToContainer;
108
+ /**
109
+ * Open shell in container
110
+ */
111
+ async function openShell(options) {
112
+ console.log(chalk_1.default.cyan(`\n🐚 Opening shell in ${options.service}...\n`));
113
+ const cwd = process.cwd();
114
+ const composeFile = path_1.default.join(cwd, options.composeFile);
115
+ if (!fs_1.default.existsSync(composeFile)) {
116
+ console.log(chalk_1.default.red(`Error: ${options.composeFile} not found.`));
117
+ return;
118
+ }
119
+ // Try sh first (Alpine), fall back to bash
120
+ spawnDockerCompose(["-f", composeFile, "exec", options.service, "sh"], {
121
+ cwd,
122
+ stdio: "inherit",
123
+ });
124
+ }
125
+ exports.openShell = openShell;
126
+ /**
127
+ * Show status of development containers
128
+ */
129
+ async function showStatus(options) {
130
+ console.log(chalk_1.default.cyan("\n📊 Development Container Status\n"));
131
+ const cwd = process.cwd();
132
+ const composeFile = path_1.default.join(cwd, options.composeFile);
133
+ if (!fs_1.default.existsSync(composeFile)) {
134
+ // Try default compose
135
+ const defaultCompose = path_1.default.join(cwd, "docker-compose.yml");
136
+ if (fs_1.default.existsSync(defaultCompose)) {
137
+ runDockerCompose(["-f", defaultCompose, "ps"], { cwd });
138
+ }
139
+ else {
140
+ console.log(chalk_1.default.yellow("No docker-compose file found."));
141
+ }
142
+ return;
143
+ }
144
+ runDockerCompose(["-f", composeFile, "ps"], { cwd });
145
+ // Show resource usage
146
+ console.log(chalk_1.default.bold("\nResource Usage:"));
147
+ try {
148
+ // Use double quotes for cross-platform compatibility (Windows + Unix)
149
+ const output = (0, child_process_1.execSync)('docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"', {
150
+ encoding: "utf-8",
151
+ stdio: ["pipe", "pipe", "pipe"],
152
+ });
153
+ console.log(output);
154
+ }
155
+ catch {
156
+ console.log(chalk_1.default.gray(" Unable to get resource stats"));
157
+ }
158
+ }
159
+ exports.showStatus = showStatus;
160
+ /**
161
+ * Show container logs
162
+ */
163
+ async function showLogs(options) {
164
+ console.log(chalk_1.default.cyan(`\n📜 Logs for ${options.service}\n`));
165
+ const cwd = process.cwd();
166
+ const composeFile = path_1.default.join(cwd, options.composeFile);
167
+ if (!fs_1.default.existsSync(composeFile)) {
168
+ console.log(chalk_1.default.red(`Error: ${options.composeFile} not found.`));
169
+ return;
170
+ }
171
+ const args = ["-f", composeFile, "logs"];
172
+ if (options.follow) {
173
+ args.push("-f");
174
+ }
175
+ args.push("--tail", String(options.tail));
176
+ args.push(options.service);
177
+ spawnDockerCompose(args, { cwd, stdio: "inherit" });
178
+ }
179
+ exports.showLogs = showLogs;
180
+ /**
181
+ * Check if Docker is running
182
+ */
183
+ function isDockerRunning() {
184
+ try {
185
+ (0, child_process_1.execSync)("docker info", { stdio: ["pipe", "pipe", "pipe"] });
186
+ return true;
187
+ }
188
+ catch {
189
+ return false;
190
+ }
191
+ }
192
+ /**
193
+ * Run docker-compose command synchronously
194
+ */
195
+ function runDockerCompose(args, options) {
196
+ try {
197
+ // Try docker compose (v2) first
198
+ (0, child_process_1.execSync)(`docker compose ${args.join(" ")}`, {
199
+ cwd: options.cwd,
200
+ env: options.env || process.env,
201
+ stdio: "inherit",
202
+ });
203
+ }
204
+ catch {
205
+ // Fall back to docker-compose (v1)
206
+ try {
207
+ (0, child_process_1.execSync)(`docker-compose ${args.join(" ")}`, {
208
+ cwd: options.cwd,
209
+ env: options.env || process.env,
210
+ stdio: "inherit",
211
+ });
212
+ }
213
+ catch (error) {
214
+ console.log(chalk_1.default.red("Error running docker-compose"));
215
+ throw error;
216
+ }
217
+ }
218
+ }
219
+ /**
220
+ * Spawn docker-compose command (for interactive/streaming)
221
+ */
222
+ function spawnDockerCompose(args, options) {
223
+ // Try docker compose (v2) first
224
+ const proc = (0, child_process_1.spawn)("docker", ["compose", ...args], {
225
+ ...options,
226
+ shell: true,
227
+ });
228
+ proc.on("error", () => {
229
+ // Fall back to docker-compose (v1)
230
+ (0, child_process_1.spawn)("docker-compose", args, {
231
+ ...options,
232
+ shell: true,
233
+ });
234
+ });
235
+ }
236
+ /**
237
+ * Print development info
238
+ */
239
+ function printDevInfo(options) {
240
+ console.log(chalk_1.default.bold("Development Environment:"));
241
+ console.log(` 🌐 App: http://localhost:${options.port || 3000}`);
242
+ console.log(` 🔍 Debug: localhost:${options.debugPort}`);
243
+ console.log(` 📁 Service: ${options.service}`);
244
+ console.log();
245
+ console.log(chalk_1.default.bold("Available Commands:"));
246
+ console.log(` ${chalk_1.default.gray("expressots container-dev status")} Show container status`);
247
+ console.log(` ${chalk_1.default.gray("expressots container-dev logs")} View logs`);
248
+ console.log(` ${chalk_1.default.gray("expressots container-dev shell")} Open shell in container`);
249
+ console.log(` ${chalk_1.default.gray("expressots container-dev stop")} Stop containers`);
250
+ console.log();
251
+ if (options.watch) {
252
+ console.log(chalk_1.default.green("🔄 Hot reload is enabled - edit files to see changes"));
253
+ }
254
+ }
@@ -0,0 +1 @@
1
+ export { devContainerCommand } from "./cli";
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.devContainerCommand = void 0;
4
+ var cli_1 = require("./cli");
5
+ Object.defineProperty(exports, "devContainerCommand", { enumerable: true, get: function () { return cli_1.devContainerCommand; } });
@@ -20,6 +20,17 @@ const coerceSchematicAliases = (arg) => {
20
20
  return "module";
21
21
  case "mi":
22
22
  return "middleware";
23
+ // NEW v4.0 schematics
24
+ case "i":
25
+ return "interceptor";
26
+ case "ev":
27
+ return "event";
28
+ case "h":
29
+ return "handler";
30
+ case "gu":
31
+ return "guard";
32
+ case "cfg":
33
+ return "config";
23
34
  default:
24
35
  return arg;
25
36
  }
@@ -40,6 +51,12 @@ const generateProject = () => {
40
51
  "entity",
41
52
  "module",
42
53
  "middleware",
54
+ // NEW v4.0 schematics
55
+ "interceptor",
56
+ "event",
57
+ "handler",
58
+ "guard",
59
+ "config",
43
60
  ],
44
61
  describe: "The schematic to generate",
45
62
  type: "string",
@@ -56,10 +73,20 @@ const generateProject = () => {
56
73
  type: "string",
57
74
  alias: "m",
58
75
  });
76
+ // NEW: Options for v4.0 schematics
77
+ yargs.option("event", {
78
+ describe: "Event class name for handler generation",
79
+ type: "string",
80
+ });
81
+ yargs.option("priority", {
82
+ describe: "Priority for interceptors/handlers (lower = earlier execution)",
83
+ type: "number",
84
+ default: 10,
85
+ });
59
86
  return yargs;
60
87
  },
61
- handler: async ({ schematic, path, method }) => {
62
- await (0, form_1.createTemplate)({ schematic, path, method });
88
+ handler: async ({ schematic, path, method, event, priority }) => {
89
+ await (0, form_1.createTemplate)({ schematic, path, method, event, priority });
63
90
  },
64
91
  };
65
92
  };
@@ -3,11 +3,15 @@
3
3
  * @param schematic
4
4
  * @param path
5
5
  * @param method
6
+ * @param event - Event class name (for handler generation)
7
+ * @param priority - Priority for interceptors/handlers
6
8
  */
7
9
  type CreateTemplateProps = {
8
10
  schematic: string;
9
11
  path: string;
10
12
  method: string;
13
+ event?: string;
14
+ priority?: number;
11
15
  };
12
16
  /**
13
17
  * Create a template based on the schematic
@@ -16,5 +20,5 @@ type CreateTemplateProps = {
16
20
  * @param method - the http method
17
21
  * @returns the file created
18
22
  */
19
- export declare const createTemplate: ({ schematic, path: target, method, }: CreateTemplateProps) => Promise<string>;
23
+ export declare const createTemplate: ({ schematic, path: target, method, event, priority, }: CreateTemplateProps) => Promise<string>;
20
24
  export {};
@@ -15,15 +15,15 @@ const opinionated_cmd_1 = require("./utils/opinionated-cmd");
15
15
  * @param method - the http method
16
16
  * @returns the file created
17
17
  */
18
- const createTemplate = async ({ schematic, path: target, method, }) => {
18
+ const createTemplate = async ({ schematic, path: target, method, event, priority = 10, }) => {
19
19
  const config = await compiler_1.default.loadConfig();
20
20
  const pathStyle = (0, command_utils_1.checkPathStyle)(target);
21
21
  let returnFile = "";
22
22
  if (config.opinionated) {
23
- returnFile = await (0, opinionated_cmd_1.opinionatedProcess)(schematic, target, method, config, pathStyle);
23
+ returnFile = await (0, opinionated_cmd_1.opinionatedProcess)(schematic, target, method, config, pathStyle, { event, priority });
24
24
  }
25
25
  else {
26
- returnFile = await (0, nonopininated_cmd_1.nonOpinionatedProcess)(schematic, target, method, config);
26
+ returnFile = await (0, nonopininated_cmd_1.nonOpinionatedProcess)(schematic, target, method, config, { event, priority });
27
27
  }
28
28
  return returnFile;
29
29
  };
@@ -0,0 +1,12 @@
1
+ import { defineConfig, Env, loadEnvSync } from "@expressots/core";
2
+
3
+ loadEnvSync({ files: { development: ".env.local", production: ".env.prod" } });
4
+
5
+ export const {{moduleName}}Config = defineConfig({
6
+ enabled: Env.boolean("{{envPrefix}}_ENABLED", { default: true }),
7
+ // Add more config options as needed
8
+ });
9
+
10
+ export const config = {{moduleName}}Config.values;
11
+ export type {{className}}Config = typeof config;
12
+
@@ -0,0 +1,10 @@
1
+ /**
2
+ * {{className}} Event
3
+ */
4
+ export class {{className}}Event {
5
+ constructor(
6
+ public readonly data: Record<string, unknown>,
7
+ public readonly timestamp: Date = new Date(),
8
+ ) {}
9
+ }
10
+