@archznn/xavva 2.0.3 → 2.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.
- package/README.md +1 -78
- package/package.json +1 -1
- package/src/commands/AuditCommand.ts +3 -2
- package/src/commands/BuildCommand.ts +5 -3
- package/src/commands/CommandRegistry.ts +9 -5
- package/src/commands/DeployCommand.ts +36 -30
- package/src/commands/DepsCommand.ts +123 -0
- package/src/commands/DoctorCommand.ts +5 -4
- package/src/commands/HelpCommand.ts +94 -40
- package/src/commands/RunCommand.ts +13 -12
- package/src/commands/StartCommand.ts +5 -3
- package/src/index.ts +15 -5
- package/src/services/AuditService.ts +30 -4
- package/src/services/BuildCacheService.ts +2 -1
- package/src/services/BuildService.ts +8 -1
- package/src/services/DashboardService.ts +181 -118
- package/src/services/DependencyAnalyzerService.ts +538 -0
- package/src/services/ProjectService.ts +3 -3
- package/src/services/TomcatService.ts +11 -11
- package/src/services/WatcherService.ts +3 -2
- package/src/types/config.ts +4 -0
- package/src/utils/config.ts +7 -2
- package/src/utils/constants.ts +54 -0
- package/src/utils/processManager.ts +145 -0
- package/src/utils/ui.ts +237 -311
package/src/utils/config.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { parseArgs } from "util";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import fs from "fs";
|
|
4
|
+
import { DEFAULT_TOMCAT_PORT, DEFAULT_DEBUG_PORT } from "./constants";
|
|
4
5
|
import type { AppConfig, CLIArguments, CommandContext } from "../types/config";
|
|
5
6
|
|
|
6
7
|
export class ConfigManager {
|
|
@@ -25,9 +26,12 @@ export class ConfigManager {
|
|
|
25
26
|
profile: { type: "string", short: "P" },
|
|
26
27
|
grep: { type: "string", short: "G" },
|
|
27
28
|
verbose: { type: "boolean", short: "V" },
|
|
29
|
+
encoding: { type: "string", short: "e" },
|
|
28
30
|
dp: { type: "string" },
|
|
29
31
|
fix: { type: "boolean" },
|
|
30
32
|
tui: { type: "boolean" },
|
|
33
|
+
output: { type: "string", short: "o" },
|
|
34
|
+
strict: { type: "boolean" },
|
|
31
35
|
},
|
|
32
36
|
strict: false,
|
|
33
37
|
allowPositionals: true,
|
|
@@ -63,7 +67,7 @@ export class ConfigManager {
|
|
|
63
67
|
const config: AppConfig = {
|
|
64
68
|
tomcat: {
|
|
65
69
|
path: String(cliValues.path || xavvaJson.path || envTomcatPath),
|
|
66
|
-
port: parseInt(String(cliValues.port || xavvaJson.port ||
|
|
70
|
+
port: parseInt(String(cliValues.port || xavvaJson.port || String(DEFAULT_TOMCAT_PORT))),
|
|
67
71
|
webapps: "webapps",
|
|
68
72
|
grep: cliValues.grep || xavvaJson.grep ? String(cliValues.grep || xavvaJson.grep) : "",
|
|
69
73
|
},
|
|
@@ -78,9 +82,10 @@ export class ConfigManager {
|
|
|
78
82
|
quiet: (cliValues.verbose ?? xavvaJson.verbose) ? false : true,
|
|
79
83
|
verbose: !!(cliValues.verbose ?? xavvaJson.verbose),
|
|
80
84
|
debug: !!(cliValues.debug ?? xavvaJson.debug ?? isDev ?? isRun),
|
|
81
|
-
debugPort: parseInt(String(cliValues.dp || xavvaJson.dp ||
|
|
85
|
+
debugPort: parseInt(String(cliValues.dp || xavvaJson.dp || String(DEFAULT_DEBUG_PORT))),
|
|
82
86
|
grep: runClass || (cliValues.grep || xavvaJson.grep ? String(cliValues.grep || xavvaJson.grep) : ""),
|
|
83
87
|
tui: !!(cliValues.tui ?? xavvaJson.tui),
|
|
88
|
+
encoding: cliValues.encoding || xavvaJson.encoding || "",
|
|
84
89
|
}
|
|
85
90
|
};
|
|
86
91
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constantes da aplicação XAVVA CLI
|
|
3
|
+
*
|
|
4
|
+
* Centraliza valores mágicos para facilitar manutenção e configuração.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Ports padrão
|
|
8
|
+
export const DEFAULT_TOMCAT_PORT = 8080;
|
|
9
|
+
export const DEFAULT_DEBUG_PORT = 5005;
|
|
10
|
+
|
|
11
|
+
// Timeouts (em milissegundos)
|
|
12
|
+
export const TIMEOUT_SHUTDOWN_MS = 5000;
|
|
13
|
+
export const WATCHER_DEBOUNCE_MS = 1000;
|
|
14
|
+
export const WATCHER_COOLING_MS = 500;
|
|
15
|
+
export const BROWSER_OPEN_DELAY_MS = 800;
|
|
16
|
+
export const DEPLOY_HEALTH_CHECK_DELAY_MS = 1500;
|
|
17
|
+
export const HOTSWAP_DELAY_MS = 500;
|
|
18
|
+
export const TOMCAT_CLEAN_RETRY_DELAY_MS = 50;
|
|
19
|
+
|
|
20
|
+
// Tamanhos e limites
|
|
21
|
+
export const MAX_LOG_SCROLLBUFFER = 1000;
|
|
22
|
+
export const MAX_BUILD_ERRORS_SHOWN = 15;
|
|
23
|
+
export const MAX_HISTORY_ITEMS = 10;
|
|
24
|
+
export const JAR_INTEGRITY_BUFFER_SIZE = 1024;
|
|
25
|
+
export const JAR_MIN_VALID_SIZE = 1000;
|
|
26
|
+
export const ZIP_EOCD_SIGNATURE_SIZE = 4;
|
|
27
|
+
|
|
28
|
+
// Dashboard
|
|
29
|
+
export const DASHBOARD_REFRESH_INTERVAL_MS = 1000;
|
|
30
|
+
export const DASHBOARD_LOG_SLICE_LINES = 1000;
|
|
31
|
+
|
|
32
|
+
// Build
|
|
33
|
+
export const MAVEN_PARALLEL_THREADS = "1C";
|
|
34
|
+
export const JVM_MEMORY_OPTS = "-Xms512m -Xmx1024m -XX:+UseParallelGC";
|
|
35
|
+
export const GRADLE_MEMORY_OPTS = "-Xmx1024m -Dorg.gradle.daemon=true";
|
|
36
|
+
|
|
37
|
+
// Exit codes
|
|
38
|
+
export const EXIT_SUCCESS = 0;
|
|
39
|
+
export const EXIT_GENERIC_ERROR = 1;
|
|
40
|
+
export const EXIT_INVALID_COMMAND = 2;
|
|
41
|
+
export const EXIT_BUILD_FAILED = 3;
|
|
42
|
+
export const EXIT_DEPLOY_FAILED = 4;
|
|
43
|
+
export const EXIT_SIGINT = 130;
|
|
44
|
+
|
|
45
|
+
// File patterns
|
|
46
|
+
export const JAVA_FILE_PATTERN = "**/*.java";
|
|
47
|
+
export const WAR_EXTENSION = ".war";
|
|
48
|
+
export const JAR_EXTENSION = ".jar";
|
|
49
|
+
|
|
50
|
+
// Directories
|
|
51
|
+
export const XAVVA_DIR = ".xavva";
|
|
52
|
+
export const TARGET_DIR = "target";
|
|
53
|
+
export const BUILD_DIR = "build";
|
|
54
|
+
export const WEBAPP_DIR = "webapps";
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ProcessManager - Centraliza o controle do ciclo de vida da aplicação.
|
|
3
|
+
*
|
|
4
|
+
* Evita chamadas diretas a process.exit() espalhadas pelo código,
|
|
5
|
+
* facilitando testes e permitindo graceful shutdown.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
EXIT_SUCCESS,
|
|
10
|
+
EXIT_GENERIC_ERROR,
|
|
11
|
+
EXIT_INVALID_COMMAND,
|
|
12
|
+
EXIT_BUILD_FAILED,
|
|
13
|
+
EXIT_DEPLOY_FAILED,
|
|
14
|
+
EXIT_SIGINT,
|
|
15
|
+
TIMEOUT_SHUTDOWN_MS
|
|
16
|
+
} from "./constants";
|
|
17
|
+
|
|
18
|
+
export type ExitCode =
|
|
19
|
+
| typeof EXIT_SUCCESS
|
|
20
|
+
| typeof EXIT_GENERIC_ERROR
|
|
21
|
+
| typeof EXIT_INVALID_COMMAND
|
|
22
|
+
| typeof EXIT_BUILD_FAILED
|
|
23
|
+
| typeof EXIT_DEPLOY_FAILED
|
|
24
|
+
| typeof EXIT_SIGINT;
|
|
25
|
+
|
|
26
|
+
interface ShutdownHandler {
|
|
27
|
+
(): Promise<void> | void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class ProcessManager {
|
|
31
|
+
private static instance: ProcessManager;
|
|
32
|
+
private shutdownHandlers: Set<ShutdownHandler> = new Set();
|
|
33
|
+
private isShuttingDown = false;
|
|
34
|
+
private exitCode: ExitCode = 0;
|
|
35
|
+
|
|
36
|
+
private constructor() {
|
|
37
|
+
this.setupSignalHandlers();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
static getInstance(): ProcessManager {
|
|
41
|
+
if (!ProcessManager.instance) {
|
|
42
|
+
ProcessManager.instance = new ProcessManager();
|
|
43
|
+
}
|
|
44
|
+
return ProcessManager.instance;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Registra um handler para ser executado no shutdown.
|
|
49
|
+
* Útil para liberar recursos (fechar conexões, limpar arquivos temp, etc).
|
|
50
|
+
*/
|
|
51
|
+
onShutdown(handler: ShutdownHandler): () => void {
|
|
52
|
+
this.shutdownHandlers.add(handler);
|
|
53
|
+
// Retorna função para remover o handler
|
|
54
|
+
return () => this.shutdownHandlers.delete(handler);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Define o código de saída sem encerrar imediatamente.
|
|
59
|
+
* O código real será usado quando shutdown() for chamado.
|
|
60
|
+
*/
|
|
61
|
+
setExitCode(code: ExitCode): void {
|
|
62
|
+
this.exitCode = code;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Obtém o código de saída atual.
|
|
67
|
+
*/
|
|
68
|
+
getExitCode(): ExitCode {
|
|
69
|
+
return this.exitCode;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Executa graceful shutdown com todos os handlers registrados.
|
|
74
|
+
* Por padrão chama process.exit(), mas pode ser mockado em testes.
|
|
75
|
+
*/
|
|
76
|
+
async shutdown(code?: ExitCode): Promise<never> {
|
|
77
|
+
if (this.isShuttingDown) {
|
|
78
|
+
// Evita chamadas duplicadas
|
|
79
|
+
return new Promise(() => {}) as never;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this.isShuttingDown = true;
|
|
83
|
+
if (code !== undefined) {
|
|
84
|
+
this.exitCode = code;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Executa handlers em paralelo com timeout
|
|
88
|
+
const timeoutMs = TIMEOUT_SHUTDOWN_MS;
|
|
89
|
+
const handlerPromises = Array.from(this.shutdownHandlers).map(async (handler) => {
|
|
90
|
+
try {
|
|
91
|
+
const timeoutPromise = new Promise((_, reject) =>
|
|
92
|
+
setTimeout(() => reject(new Error('Shutdown handler timeout')), timeoutMs)
|
|
93
|
+
);
|
|
94
|
+
await Promise.race([handler(), timeoutPromise]);
|
|
95
|
+
} catch (e) {
|
|
96
|
+
console.error('Erro em shutdown handler:', e);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
await Promise.all(handlerPromises);
|
|
101
|
+
|
|
102
|
+
// Em ambiente de teste, não chama process.exit()
|
|
103
|
+
if (process.env.NODE_ENV === 'test' || process.env.BUN_ENV === 'test') {
|
|
104
|
+
throw new ProcessExitError(this.exitCode);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
process.exit(this.exitCode);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Encerra imediatamente sem graceful shutdown.
|
|
112
|
+
* Use apenas em casos críticos onde não é seguro continuar.
|
|
113
|
+
*/
|
|
114
|
+
exit(code: ExitCode): never {
|
|
115
|
+
if (process.env.NODE_ENV === 'test' || process.env.BUN_ENV === 'test') {
|
|
116
|
+
throw new ProcessExitError(code);
|
|
117
|
+
}
|
|
118
|
+
process.exit(code);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private setupSignalHandlers(): void {
|
|
122
|
+
process.on('SIGINT', () => this.shutdown(EXIT_SIGINT));
|
|
123
|
+
process.on('SIGTERM', () => this.shutdown(0));
|
|
124
|
+
|
|
125
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
126
|
+
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
127
|
+
this.shutdown(1);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
process.on('uncaughtException', (error) => {
|
|
131
|
+
console.error('Uncaught Exception:', error);
|
|
132
|
+
this.shutdown(1);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Erro especial para identificar chamadas a process.exit() em testes.
|
|
139
|
+
*/
|
|
140
|
+
export class ProcessExitError extends Error {
|
|
141
|
+
constructor(public readonly code: ExitCode) {
|
|
142
|
+
super(`Process exited with code ${code}`);
|
|
143
|
+
this.name = 'ProcessExitError';
|
|
144
|
+
}
|
|
145
|
+
}
|