@archznn/xavva 2.9.0 → 3.1.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.
@@ -38,6 +38,12 @@ export class HelpCommand implements Command {
38
38
  ${this.c("cyan", "redo")} Repeat last command
39
39
  ${this.c("cyan", "health")} Check environment health
40
40
  ${this.c("cyan", "completion")} Generate shell completions (bash/zsh/fish)
41
+ ${this.c("cyan", "changelog")} Generate changelog from conventional commits
42
+
43
+ ${this.c("magenta", "test")} Run tests (JUnit/TestNG)
44
+ ${this.c("magenta", "db")} Database migrations (Flyway/Liquibase)
45
+ ${this.c("magenta", "http")} HTTP client for API testing
46
+ ${this.c("magenta", "docker")} Docker integration (build, run, compose)
41
47
 
42
48
  ${this.c("yellow", "GENERAL OPTIONS")}
43
49
  ${this.c("cyan", "-p, --path")} <path> Tomcat installation path
@@ -56,6 +62,7 @@ export class HelpCommand implements Command {
56
62
  ${this.c("cyan", "-s, --no-build")} Skip initial build
57
63
  ${this.c("cyan", "-q, --quiet")} Minimal output
58
64
  ${this.c("cyan", "-V, --verbose")} Detailed output
65
+ ${this.c("cyan", "--debug-level")} <lvl> Debug level: error|warn|info|verbose|trace|silly
59
66
  ${this.c("cyan", "-h, --help")} Show this help
60
67
  ${this.c("cyan", "-v, --version")} Show version
61
68
 
@@ -139,6 +146,43 @@ export class HelpCommand implements Command {
139
146
  xavva completion zsh # Generate zsh completions
140
147
  eval "$(xavva completion bash)" # Enable in current shell
141
148
 
149
+ ${this.c("dim", "# Changelog")}
150
+ xavva changelog generate # Generate CHANGELOG.md
151
+ xavva changelog check # Validate conventional commits
152
+ xavva changelog preview # Preview without saving
153
+
154
+ ${this.c("dim", "# Debug levels")}
155
+ xavva deploy --debug-level verbose # Verbose logging
156
+ xavva deploy --debug-level trace # Trace all operations
157
+ xavva deploy --debug-level silly # Everything including config
158
+
159
+ ${this.c("dim", "# Multi-environment")}
160
+ xavva deploy --env staging # Deploy to staging environment
161
+ xavva dev --env dev # Use dev environment config
162
+
163
+ ${this.c("dim", "# Test runner")}
164
+ xavva test # Run all tests
165
+ xavva test --watch # Watch mode
166
+ xavva test --coverage # Generate coverage report
167
+ xavva test UserServiceTest # Run specific test class
168
+
169
+ ${this.c("dim", "# Database migrations")}
170
+ xavva db status # Show migration status
171
+ xavva db migrate # Run pending migrations
172
+ xavva db reset --force # Reset database (drops all!)
173
+ xavva db seed # Populate with test data
174
+
175
+ ${this.c("dim", "# HTTP Client")}
176
+ xavva http GET /api/users # Test endpoint
177
+ xavva http POST /api/users --body '{"name":"John"}'
178
+ xavva http GET /api/users --param page=1 --param size=10
179
+
180
+ ${this.c("dim", "# Docker")}
181
+ xavva docker init # Generate Dockerfile & compose
182
+ xavva docker build # Build image
183
+ xavva docker up # Start with docker-compose
184
+ xavva docker run # Run dev container
185
+
142
186
  ${this.c("yellow", "CONFIGURATION")}
143
187
  Settings are loaded from ${this.c("cyan", "xavva.json")} in the project root:
144
188
 
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Comando de HTTP Client
3
+ * xavva http <method> <url> [options]
4
+ * xavva http --interactive
5
+ */
6
+
7
+ import type { Command } from "./Command";
8
+ import type { AppConfig, CLIArguments } from "../types/config";
9
+ import { HttpService, type HttpRequest } from "../services/HttpService";
10
+ import { EndpointService } from "../services/EndpointService";
11
+ import { Logger } from "../utils/ui";
12
+ import { ProcessManager } from "../utils/processManager";
13
+ import fs from "fs";
14
+ import path from "path";
15
+
16
+ export class HttpCommand implements Command {
17
+ async execute(config: AppConfig, args?: CLIArguments, positionals?: string[]): Promise<void> {
18
+ const processManager = ProcessManager.getInstance();
19
+
20
+ // Modo interativo
21
+ if (args?.interactive || positionals?.length === 1) {
22
+ const baseUrl = `http://localhost:${config.tomcat.port}`;
23
+ const service = new HttpService(baseUrl);
24
+ await service.interactive();
25
+ return;
26
+ }
27
+
28
+ // Extrai método e URL
29
+ const method = (positionals?.[1] || "GET").toUpperCase() as HttpRequest["method"];
30
+ let url = positionals?.[2] || "/";
31
+
32
+ // Se URL não começar com http, adiciona base
33
+ const baseUrl = args?.["base-url"] || `http://localhost:${config.tomcat.port}`;
34
+ if (!url.startsWith("http")) {
35
+ url = `${baseUrl}${url.startsWith("/") ? url : `/${url}`}`;
36
+ }
37
+
38
+ // Parse headers
39
+ const headers = this.parseHeaders(args);
40
+
41
+ // Parse body
42
+ let body: string | object | undefined;
43
+ if (args?.body) {
44
+ body = this.parseBody(args.body as string);
45
+ } else if (args?.file) {
46
+ const filePath = args.file as string;
47
+ if (fs.existsSync(filePath)) {
48
+ body = fs.readFileSync(filePath, "utf-8");
49
+ } else {
50
+ Logger.error(`File not found: ${filePath}`);
51
+ await processManager.shutdown(1);
52
+ return;
53
+ }
54
+ }
55
+
56
+ // Parse query params
57
+ const params = this.parseParams(args);
58
+
59
+ // Cria request
60
+ const request: HttpRequest = {
61
+ method,
62
+ url,
63
+ headers,
64
+ body,
65
+ params,
66
+ timeout: args?.timeout ? parseInt(args.timeout as string) : 30000
67
+ };
68
+
69
+ // Executa
70
+ const service = new HttpService();
71
+
72
+ try {
73
+ await service.request(request);
74
+ } catch (error) {
75
+ await processManager.shutdown(1);
76
+ }
77
+ }
78
+
79
+ private parseHeaders(args?: CLIArguments): Record<string, string> | undefined {
80
+ const headers: Record<string, string> = {};
81
+
82
+ // Formato: --header "Authorization: Bearer token"
83
+ const headerArgs = args?.header;
84
+ if (headerArgs) {
85
+ const headerList = Array.isArray(headerArgs) ? headerArgs : [headerArgs];
86
+ for (const h of headerList) {
87
+ const match = h.match(/^([^:]+):\s*(.+)$/);
88
+ if (match) {
89
+ headers[match[1]] = match[2];
90
+ }
91
+ }
92
+ }
93
+
94
+ // Content-Type shorthand
95
+ if (args?.["content-type"]) {
96
+ headers["Content-Type"] = args["content-type"] as string;
97
+ }
98
+
99
+ // Accept shorthand
100
+ if (args?.accept) {
101
+ headers["Accept"] = args.accept as string;
102
+ }
103
+
104
+ return Object.keys(headers).length > 0 ? headers : undefined;
105
+ }
106
+
107
+ private parseBody(bodyStr: string): string | object {
108
+ // Tenta parsear como JSON
109
+ try {
110
+ return JSON.parse(bodyStr);
111
+ } catch {
112
+ // Retorna como string
113
+ return bodyStr;
114
+ }
115
+ }
116
+
117
+ private parseParams(args?: CLIArguments): Record<string, string> | undefined {
118
+ const params: Record<string, string> = {};
119
+
120
+ // Formato: --param "key=value"
121
+ const paramArgs = args?.param;
122
+ if (paramArgs) {
123
+ const paramList = Array.isArray(paramArgs) ? paramArgs : [paramArgs];
124
+ for (const p of paramList) {
125
+ const [key, value] = p.split("=");
126
+ if (key && value !== undefined) {
127
+ params[key] = value;
128
+ }
129
+ }
130
+ }
131
+
132
+ return Object.keys(params).length > 0 ? params : undefined;
133
+ }
134
+ }
@@ -104,6 +104,12 @@ export class InitCommand implements Command {
104
104
  default: "UTF-8"
105
105
  });
106
106
 
107
+ // Multi-environment setup
108
+ const enableMultiEnv = await confirm({
109
+ message: "Configure multiple environments?",
110
+ default: false
111
+ });
112
+
107
113
  // Build config object
108
114
  const config: Record<string, unknown> = {
109
115
  appName,
@@ -142,6 +148,70 @@ export class InitCommand implements Command {
142
148
  config.tomcatPath = tomcatPath;
143
149
  }
144
150
 
151
+ // Add environments if enabled
152
+ if (enableMultiEnv) {
153
+ Logger.newline();
154
+ Logger.dim("Environment Configuration:");
155
+
156
+ const environments: Record<string, unknown> = {};
157
+
158
+ // Dev environment
159
+ const devPort = await number({
160
+ message: "Dev environment port:",
161
+ default: port
162
+ }) || port;
163
+ environments.dev = {
164
+ port: devPort,
165
+ profile: "dev"
166
+ };
167
+
168
+ // Test environment
169
+ const testPort = await number({
170
+ message: "Test environment port:",
171
+ default: port + 1
172
+ }) || port + 1;
173
+ environments.test = {
174
+ port: testPort,
175
+ profile: "test"
176
+ };
177
+
178
+ // Staging environment
179
+ const hasStaging = await confirm({
180
+ message: "Add staging environment?",
181
+ default: true
182
+ });
183
+
184
+ if (hasStaging) {
185
+ const stagingPort = await number({
186
+ message: "Staging environment port:",
187
+ default: port + 2
188
+ }) || port + 2;
189
+ environments.staging = {
190
+ port: stagingPort,
191
+ profile: "staging"
192
+ };
193
+ }
194
+
195
+ config.environments = environments;
196
+
197
+ // Add DB config example
198
+ const addDbExample = await confirm({
199
+ message: "Add database configuration example?",
200
+ default: true
201
+ });
202
+
203
+ if (addDbExample) {
204
+ environments.dev = {
205
+ ...environments.dev,
206
+ db: {
207
+ url: "jdbc:h2:mem:devdb",
208
+ username: "sa",
209
+ password: ""
210
+ }
211
+ };
212
+ }
213
+ }
214
+
145
215
  // Save file
146
216
  Logger.newline();
147
217
  Logger.step("Saving configuration...");
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Comando de execução de testes
3
+ * xavva test [options] [filter]
4
+ */
5
+
6
+ import type { Command } from "./Command";
7
+ import type { AppConfig, CLIArguments } from "../types/config";
8
+ import { TestService } from "../services/TestService";
9
+ import { Logger } from "../utils/ui";
10
+ import { ProcessManager } from "../utils/processManager";
11
+
12
+ export class TestCommand implements Command {
13
+ private service: TestService | null = null;
14
+
15
+ async execute(config: AppConfig, args?: CLIArguments, positionals?: string[]): Promise<void> {
16
+ const processManager = ProcessManager.getInstance();
17
+
18
+ // Extrai filtros de teste dos positionals (após o comando "test")
19
+ const filter = positionals?.slice(1).join(" ") || undefined;
20
+
21
+ // Opções
22
+ const watch = args?.watch || false;
23
+ const coverage = args?.coverage || false;
24
+ const verbose = args?.verbose || false;
25
+ const failFast = args?.["fail-fast"] || false;
26
+ const parallel = args?.parallel || false;
27
+
28
+ this.service = new TestService(config.project.buildTool);
29
+
30
+ try {
31
+ if (watch) {
32
+ this.service.startWatch({
33
+ coverage,
34
+ filter,
35
+ verbose,
36
+ failFast,
37
+ parallel
38
+ });
39
+
40
+ // Mantém processo rodando
41
+ process.on("SIGINT", () => {
42
+ this.service?.stopWatch();
43
+ processManager.shutdown(0);
44
+ });
45
+ } else {
46
+ const result = await this.service.runTests({
47
+ coverage,
48
+ filter,
49
+ verbose,
50
+ failFast,
51
+ parallel
52
+ });
53
+
54
+ if (!result.success) {
55
+ await processManager.shutdown(1);
56
+ }
57
+ }
58
+ } catch (error) {
59
+ Logger.error(`Test execution failed: ${(error as Error).message}`);
60
+ await processManager.shutdown(1);
61
+ }
62
+ }
63
+ }
@@ -30,7 +30,12 @@ import { HistoryCommand } from "../commands/HistoryCommand";
30
30
  import { RedoCommand } from "../commands/RedoCommand";
31
31
  import { HealthCommand } from "../commands/HealthCommand";
32
32
  import { CompletionCommand } from "../commands/CompletionCommand";
33
+ import { ChangelogCommand } from "../commands/ChangelogCommand";
33
34
  import { HistoryService } from "../services/HistoryService";
35
+ import { TestCommand } from "../commands/TestCommand";
36
+ import { DbCommand } from "../commands/DbCommand";
37
+ import { HttpCommand } from "../commands/HttpCommand";
38
+ import { DockerCommand } from "../commands/DockerCommand";
34
39
  import { NotificationService } from "../services/NotificationService";
35
40
  import type { Command } from "../commands/Command";
36
41
  import { Logger } from "../utils/ui";
@@ -68,6 +73,11 @@ export interface Commands {
68
73
  redo: RedoCommand;
69
74
  health: HealthCommand;
70
75
  completion: CompletionCommand;
76
+ changelog: ChangelogCommand;
77
+ test: TestCommand;
78
+ db: DbCommand;
79
+ http: HttpCommand;
80
+ docker: DockerCommand;
71
81
  }
72
82
 
73
83
  export class DIContainer {
@@ -163,6 +173,11 @@ export class DIContainer {
163
173
  redo: new RedoCommand(),
164
174
  health: new HealthCommand(),
165
175
  completion: new CompletionCommand(),
176
+ changelog: new ChangelogCommand(),
177
+ test: new TestCommand(),
178
+ db: new DbCommand(),
179
+ http: new HttpCommand(),
180
+ docker: new DockerCommand(),
166
181
  };
167
182
  }
168
183
 
package/src/index.ts CHANGED
@@ -5,6 +5,7 @@ import { createContainer, type DIContainer } from "./di/container";
5
5
  import { DeployWatcher } from "./services/DeployWatcher";
6
6
  import { ErrorHandler } from "./errors/ErrorHandler";
7
7
  import { ProcessManager } from "./utils/processManager";
8
+ import { LoggerLevel } from "./utils/LoggerLevel";
8
9
  import pkg from "../package.json";
9
10
  import { Logger } from "./utils/ui";
10
11
  import type { CLIArguments } from "./types/args";
@@ -19,12 +20,19 @@ async function main() {
19
20
  await processManager.shutdown(0);
20
21
  }
21
22
 
23
+ // Configura debug level
24
+ if (values["debug-level"]) {
25
+ LoggerLevel.setLevel(values["debug-level"]);
26
+ LoggerLevel.verbose(`Debug level set to: ${values["debug-level"]}`, {});
27
+ }
28
+
22
29
  // Identifica comando
23
30
  const commandNames = [
24
31
  "deploy", "build", "start", "dev", "doctor", "run",
25
32
  "debug", "logs", "docs", "audit", "profiles",
26
33
  "deps", "tomcat", "encoding", "init", "config",
27
- "history", "redo", "health", "completion", "help"
34
+ "history", "redo", "health", "completion", "changelog",
35
+ "test", "db", "http", "docker", "help"
28
36
  ];
29
37
  const commandName = positionals.find(p => commandNames.includes(p)) || "deploy";
30
38
 
@@ -103,6 +111,11 @@ async function main() {
103
111
  registry.register("redo", commands.redo);
104
112
  registry.register("health", commands.health);
105
113
  registry.register("completion", commands.completion);
114
+ registry.register("changelog", commands.changelog);
115
+ registry.register("test", commands.test);
116
+ registry.register("db", commands.db);
117
+ registry.register("http", commands.http);
118
+ registry.register("docker", commands.docker);
106
119
 
107
120
  // Configura flags específicas
108
121
  if (commandName === "debug") values.debug = true;