@archznn/xavva 3.1.3 → 3.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 +221 -12
- package/package.json +3 -2
- package/src/commands/AuditCommand.ts +12 -10
- package/src/commands/BuildCommand.ts +9 -7
- package/src/commands/ChangelogCommand.ts +5 -5
- package/src/commands/CleanCommand.ts +242 -0
- package/src/commands/CompletionCommand.ts +7 -7
- package/src/commands/DbCommand.ts +33 -31
- package/src/commands/DeployCommand.ts +252 -229
- package/src/commands/DepsCommand.ts +174 -174
- package/src/commands/DockerCommand.ts +14 -14
- package/src/commands/DoctorCommand.ts +252 -239
- package/src/commands/EncodingCommand.ts +19 -19
- package/src/commands/HealthCommand.ts +7 -7
- package/src/commands/HelpCommand.ts +34 -14
- package/src/commands/HistoryCommand.ts +5 -5
- package/src/commands/HttpCommand.ts +6 -6
- package/src/commands/IdeCommand.ts +313 -0
- package/src/commands/InitCommand.ts +26 -25
- package/src/commands/LogsCommand.ts +8 -6
- package/src/commands/ProfilesCommand.ts +6 -6
- package/src/commands/RedoCommand.ts +2 -2
- package/src/commands/RunCommand.ts +64 -24
- package/src/commands/StartCommand.ts +9 -7
- package/src/commands/TestCommand.ts +4 -4
- package/src/commands/TomcatCommand.ts +219 -100
- package/src/config/versions.ts +111 -9
- package/src/di/container.ts +239 -105
- package/src/errors/ErrorHandler.ts +23 -19
- package/src/errors/errorMessages.ts +235 -0
- package/src/index.ts +11 -3
- package/src/logging/FileLogger.ts +235 -0
- package/src/logging/Logger.ts +545 -0
- package/src/logging/OperationLogger.ts +296 -0
- package/src/logging/ProgressLogger.ts +187 -0
- package/src/logging/TableLogger.ts +246 -0
- package/src/logging/colors.ts +167 -0
- package/src/logging/constants.ts +176 -0
- package/src/logging/formatters.ts +337 -0
- package/src/logging/index.ts +93 -0
- package/src/logging/types.ts +64 -0
- package/src/plugins/PluginManager.ts +325 -0
- package/src/plugins/types.ts +82 -0
- package/src/services/AuditService.ts +5 -3
- package/src/services/BuildService.ts +15 -17
- package/src/services/DashboardService.ts +14 -3
- package/src/services/DbService.ts +35 -34
- package/src/services/DependencyAnalyzerService.ts +18 -18
- package/src/services/DependencyCacheService.ts +303 -0
- package/src/services/DeployWatcher.ts +127 -23
- package/src/services/DockerService.ts +3 -3
- package/src/services/EmbeddedTomcatService.ts +13 -12
- package/src/services/FileWatcher.ts +15 -7
- package/src/services/HttpService.ts +5 -5
- package/src/services/LogAnalyzer.ts +26 -22
- package/src/services/PerformanceProfiler.ts +267 -0
- package/src/services/ProjectService.ts +3 -0
- package/src/services/TestService.ts +3 -3
- package/src/services/TomcatService.ts +46 -25
- package/src/services/tomcat/TomcatBackupManager.ts +330 -0
- package/src/services/tomcat/TomcatChecksumVerifier.ts +211 -0
- package/src/services/tomcat/TomcatCompatibilityChecker.ts +298 -0
- package/src/services/tomcat/TomcatDownloadCache.ts +250 -0
- package/src/services/tomcat/TomcatDownloadService.ts +335 -0
- package/src/services/tomcat/TomcatInstallerService.ts +474 -0
- package/src/services/tomcat/TomcatMirrorManager.ts +181 -0
- package/src/services/tomcat/index.ts +36 -0
- package/src/services/tomcat/types.ts +120 -0
- package/src/types/args.ts +68 -1
- package/src/types/configSchema.ts +174 -0
- package/src/utils/ChangelogGenerator.ts +11 -11
- package/src/utils/LoggerLevel.ts +44 -20
- package/src/utils/ProgressBar.ts +87 -46
- package/src/utils/argsParser.ts +260 -0
- package/src/utils/config.ts +340 -189
- package/src/utils/constants.ts +87 -9
- package/src/utils/dryRun.ts +192 -0
- package/src/utils/processManager.ts +23 -7
- package/src/utils/security.ts +293 -0
- package/src/utils/ui.ts +299 -428
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* DeployWatcher - Específico para watch de deploy
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Melhorias:
|
|
5
|
+
* - Debounce inteligente com batch processing
|
|
6
|
+
* - Priorização de tipos de arquivos
|
|
7
|
+
* - Rate limiting de builds
|
|
8
|
+
* - Métricas de performance
|
|
4
9
|
*/
|
|
5
10
|
|
|
6
11
|
import { FileWatcher, type FileChangeEvent } from "./FileWatcher";
|
|
7
12
|
import { DeployCommand } from "../commands/DeployCommand";
|
|
8
|
-
import { Logger } from "../
|
|
13
|
+
import { Logger } from "../logging";
|
|
9
14
|
import type { AppConfig } from "../types/config";
|
|
10
|
-
import {
|
|
15
|
+
import { TIMEOUTS } from "../config/versions";
|
|
16
|
+
|
|
17
|
+
interface WatcherMetrics {
|
|
18
|
+
filesChanged: number;
|
|
19
|
+
buildsTriggered: number;
|
|
20
|
+
lastBuildTime: number;
|
|
21
|
+
avgBuildTime: number;
|
|
22
|
+
buildTimes: number[];
|
|
23
|
+
}
|
|
11
24
|
|
|
12
25
|
export class DeployWatcher {
|
|
13
26
|
private fileWatcher: FileWatcher;
|
|
@@ -16,6 +29,29 @@ export class DeployWatcher {
|
|
|
16
29
|
private modifiedFiles = new Set<string>();
|
|
17
30
|
private pendingFiles = new Set<string>();
|
|
18
31
|
private hasPendingChanges = false;
|
|
32
|
+
private logger = Logger.getInstance();
|
|
33
|
+
|
|
34
|
+
// Debounce e batching
|
|
35
|
+
private debounceTimer: ReturnType<typeof setTimeout> | null = null;
|
|
36
|
+
private readonly DEBOUNCE_MS = TIMEOUTS.DEBOUNCE;
|
|
37
|
+
private readonly MAX_BATCH_SIZE = 50;
|
|
38
|
+
private readonly MIN_BUILD_INTERVAL_MS = 500;
|
|
39
|
+
|
|
40
|
+
// Métricas
|
|
41
|
+
private metrics: WatcherMetrics = {
|
|
42
|
+
filesChanged: 0,
|
|
43
|
+
buildsTriggered: 0,
|
|
44
|
+
lastBuildTime: 0,
|
|
45
|
+
avgBuildTime: 0,
|
|
46
|
+
buildTimes: [],
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Priorização de arquivos
|
|
50
|
+
private static readonly PRIORITY = {
|
|
51
|
+
BUILD_CONFIG: 1, // pom.xml, build.gradle
|
|
52
|
+
JAVA: 2, // .java
|
|
53
|
+
RESOURCE: 3, // .jsp, .html, etc
|
|
54
|
+
};
|
|
19
55
|
|
|
20
56
|
constructor(
|
|
21
57
|
private config: AppConfig,
|
|
@@ -23,8 +59,8 @@ export class DeployWatcher {
|
|
|
23
59
|
) {
|
|
24
60
|
this.fileWatcher = new FileWatcher({
|
|
25
61
|
recursive: true,
|
|
26
|
-
debounceMs:
|
|
27
|
-
coolingMs:
|
|
62
|
+
debounceMs: TIMEOUTS.WATCHER_DEBOUNCE,
|
|
63
|
+
coolingMs: TIMEOUTS.COOLING,
|
|
28
64
|
});
|
|
29
65
|
}
|
|
30
66
|
|
|
@@ -41,7 +77,7 @@ export class DeployWatcher {
|
|
|
41
77
|
// Inicia o watcher
|
|
42
78
|
this.fileWatcher.start();
|
|
43
79
|
|
|
44
|
-
|
|
80
|
+
this.logger.status("watch", "running", "monitorando arquivos");
|
|
45
81
|
}
|
|
46
82
|
|
|
47
83
|
/**
|
|
@@ -77,7 +113,8 @@ export class DeployWatcher {
|
|
|
77
113
|
private async handleBuildConfigChange(event: FileChangeEvent): Promise<void> {
|
|
78
114
|
if (!event.filename) return;
|
|
79
115
|
|
|
80
|
-
|
|
116
|
+
this.logger.file(event.filename, 'changed');
|
|
117
|
+
this.logger.info("Configuração de build alterada - rebuild completo necessário");
|
|
81
118
|
|
|
82
119
|
// Limpa cache quando config muda
|
|
83
120
|
const { BuildCacheService } = await import("./BuildCacheService");
|
|
@@ -87,22 +124,57 @@ export class DeployWatcher {
|
|
|
87
124
|
}
|
|
88
125
|
|
|
89
126
|
/**
|
|
90
|
-
* Trata mudança em arquivo Java
|
|
127
|
+
* Trata mudança em arquivo Java com debounce inteligente
|
|
91
128
|
*/
|
|
92
129
|
private handleJavaChange(event: FileChangeEvent): void {
|
|
93
|
-
if (!event.filename
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
130
|
+
if (!event.filename) return;
|
|
131
|
+
|
|
132
|
+
this.metrics.filesChanged++;
|
|
133
|
+
this.logger.file(event.filename, 'changed');
|
|
134
|
+
this.modifiedFiles.add(event.filename);
|
|
135
|
+
|
|
136
|
+
// Se muitos arquivos mudaram, faz full build
|
|
137
|
+
if (this.modifiedFiles.size >= this.MAX_BATCH_SIZE) {
|
|
138
|
+
this.logger.warn(`Muitos arquivos modificados (${this.modifiedFiles.size}) - forçando build completo`);
|
|
139
|
+
this.pendingFullBuild = true;
|
|
140
|
+
this.flush();
|
|
98
141
|
return;
|
|
99
142
|
}
|
|
100
143
|
|
|
101
|
-
|
|
102
|
-
this.
|
|
144
|
+
// Debounce normal
|
|
145
|
+
if (this.debounceTimer) {
|
|
146
|
+
clearTimeout(this.debounceTimer);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
this.debounceTimer = setTimeout(() => {
|
|
150
|
+
this.flush();
|
|
151
|
+
}, this.DEBOUNCE_MS);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Processa batch de arquivos modificados
|
|
156
|
+
*/
|
|
157
|
+
private flush(): void {
|
|
158
|
+
if (this.debounceTimer) {
|
|
159
|
+
clearTimeout(this.debounceTimer);
|
|
160
|
+
this.debounceTimer = null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const files = [...this.modifiedFiles];
|
|
164
|
+
this.modifiedFiles.clear();
|
|
103
165
|
|
|
104
|
-
|
|
105
|
-
|
|
166
|
+
if (files.length === 0) return;
|
|
167
|
+
|
|
168
|
+
// Rate limiting
|
|
169
|
+
const timeSinceLastBuild = Date.now() - this.metrics.lastBuildTime;
|
|
170
|
+
if (timeSinceLastBuild < this.MIN_BUILD_INTERVAL_MS) {
|
|
171
|
+
const delay = this.MIN_BUILD_INTERVAL_MS - timeSinceLastBuild;
|
|
172
|
+
setTimeout(() => this.run(this.pendingFullBuild ? false : true, files), delay);
|
|
173
|
+
} else {
|
|
174
|
+
this.run(this.pendingFullBuild ? false : true, files);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
this.pendingFullBuild = false;
|
|
106
178
|
}
|
|
107
179
|
|
|
108
180
|
/**
|
|
@@ -111,12 +183,12 @@ export class DeployWatcher {
|
|
|
111
183
|
private async handleResourceChange(event: FileChangeEvent): Promise<void> {
|
|
112
184
|
if (!event.filename) return;
|
|
113
185
|
|
|
114
|
-
|
|
186
|
+
this.logger.file(event.filename, 'changed');
|
|
115
187
|
|
|
116
188
|
try {
|
|
117
189
|
await this.deployCmd.syncResource(this.config, event.filename);
|
|
118
190
|
} catch (error) {
|
|
119
|
-
|
|
191
|
+
this.logger.error(`Falha ao sincronizar: ${event.filename}`);
|
|
120
192
|
}
|
|
121
193
|
}
|
|
122
194
|
|
|
@@ -134,12 +206,21 @@ export class DeployWatcher {
|
|
|
134
206
|
}
|
|
135
207
|
|
|
136
208
|
/**
|
|
137
|
-
* Executa o deploy
|
|
209
|
+
* Executa o deploy com métricas
|
|
138
210
|
*/
|
|
139
211
|
private async run(incremental = false, changedFiles?: string[]): Promise<void> {
|
|
140
|
-
if (this.isDeploying)
|
|
212
|
+
if (this.isDeploying) {
|
|
213
|
+
// Acumula para próximo ciclo
|
|
214
|
+
if (changedFiles) {
|
|
215
|
+
changedFiles.forEach(f => this.pendingFiles.add(f));
|
|
216
|
+
this.hasPendingChanges = true;
|
|
217
|
+
}
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
141
220
|
|
|
142
221
|
this.isDeploying = true;
|
|
222
|
+
const buildStart = performance.now();
|
|
223
|
+
this.metrics.buildsTriggered++;
|
|
143
224
|
|
|
144
225
|
try {
|
|
145
226
|
await this.deployCmd.execute(this.config, {
|
|
@@ -147,18 +228,34 @@ export class DeployWatcher {
|
|
|
147
228
|
incremental,
|
|
148
229
|
changedFiles,
|
|
149
230
|
});
|
|
231
|
+
|
|
232
|
+
// Atualiza métricas
|
|
233
|
+
const buildTime = performance.now() - buildStart;
|
|
234
|
+
this.metrics.lastBuildTime = Date.now();
|
|
235
|
+
this.metrics.buildTimes.push(buildTime);
|
|
236
|
+
|
|
237
|
+
// Mantém apenas últimos 10 builds para média
|
|
238
|
+
if (this.metrics.buildTimes.length > 10) {
|
|
239
|
+
this.metrics.buildTimes.shift();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
this.metrics.avgBuildTime =
|
|
243
|
+
this.metrics.buildTimes.reduce((a, b) => a + b, 0) / this.metrics.buildTimes.length;
|
|
244
|
+
|
|
245
|
+
this.logger.debug(`Build em ${buildTime.toFixed(0)}ms (média: ${this.metrics.avgBuildTime.toFixed(0)}ms)`);
|
|
246
|
+
|
|
150
247
|
} catch (error) {
|
|
151
248
|
// Erro já é tratado pelo comando
|
|
152
249
|
} finally {
|
|
153
250
|
this.isDeploying = false;
|
|
154
251
|
|
|
155
252
|
// Processa mudanças pendentes
|
|
156
|
-
if (this.hasPendingChanges) {
|
|
253
|
+
if (this.hasPendingChanges || this.pendingFiles.size > 0) {
|
|
157
254
|
const pending = [...this.pendingFiles];
|
|
158
255
|
this.pendingFiles.clear();
|
|
159
256
|
this.hasPendingChanges = false;
|
|
160
257
|
|
|
161
|
-
|
|
258
|
+
this.logger.info(`${pending.length} arquivo(s) pendente(s)`);
|
|
162
259
|
|
|
163
260
|
setTimeout(() => {
|
|
164
261
|
this.run(true, pending);
|
|
@@ -167,6 +264,13 @@ export class DeployWatcher {
|
|
|
167
264
|
}
|
|
168
265
|
}
|
|
169
266
|
|
|
267
|
+
/**
|
|
268
|
+
* Obtém métricas do watcher
|
|
269
|
+
*/
|
|
270
|
+
getMetrics(): WatcherMetrics {
|
|
271
|
+
return { ...this.metrics };
|
|
272
|
+
}
|
|
273
|
+
|
|
170
274
|
/**
|
|
171
275
|
* Verifica se é arquivo de recurso (não Java)
|
|
172
276
|
*/
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Gera configs e gerencia containers
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { Logger } from "../utils/ui";
|
|
6
|
+
import { Logger, C } from "../utils/ui";
|
|
7
7
|
import { spawn } from "child_process";
|
|
8
8
|
import fs from "fs";
|
|
9
9
|
import path from "path";
|
|
@@ -291,8 +291,8 @@ services:
|
|
|
291
291
|
|
|
292
292
|
Logger.divider();
|
|
293
293
|
for (const c of containers) {
|
|
294
|
-
const statusColor = c.status.includes("Up") ?
|
|
295
|
-
Logger.info(c.name, `${statusColor}${c.status}${
|
|
294
|
+
const statusColor = c.status.includes("Up") ? C.success : C.error;
|
|
295
|
+
Logger.info(c.name, `${statusColor}${c.status}${C.reset}`);
|
|
296
296
|
if (c.ports) {
|
|
297
297
|
Logger.dim(` ${c.ports}`);
|
|
298
298
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Logger } from "../
|
|
1
|
+
import { Logger } from "../logging";
|
|
2
2
|
import { ProgressBar, ThemedSpinner } from "../utils/ProgressBar";
|
|
3
3
|
import { VERSIONS, getAvailableTomcatVersions, isSupportedTomcatVersion } from "../config/versions";
|
|
4
4
|
import {
|
|
@@ -46,6 +46,7 @@ export class EmbeddedTomcatService {
|
|
|
46
46
|
private tomcatHome: string;
|
|
47
47
|
private downloadUrl: string;
|
|
48
48
|
private isInstalled: boolean = false;
|
|
49
|
+
private logger = Logger.getInstance();
|
|
49
50
|
|
|
50
51
|
// Versões agora centralizadas em src/config/versions.ts
|
|
51
52
|
|
|
@@ -110,13 +111,13 @@ export class EmbeddedTomcatService {
|
|
|
110
111
|
*/
|
|
111
112
|
async install(): Promise<boolean> {
|
|
112
113
|
if (this.checkInstallation()) {
|
|
113
|
-
|
|
114
|
+
this.logger.info(`Tomcat: Versão ${this.version} já instalada`);
|
|
114
115
|
return true;
|
|
115
116
|
}
|
|
116
117
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
118
|
+
this.logger.section("Instalando Tomcat Embutido");
|
|
119
|
+
this.logger.info(`Versão: ${this.version}`);
|
|
120
|
+
this.logger.info(`Destino: ${this.tomcatHome}`);
|
|
120
121
|
|
|
121
122
|
// Cria diretório base
|
|
122
123
|
if (!existsSync(this.baseDir)) {
|
|
@@ -152,10 +153,10 @@ export class EmbeddedTomcatService {
|
|
|
152
153
|
await this.configureContextXml();
|
|
153
154
|
|
|
154
155
|
this.isInstalled = true;
|
|
155
|
-
|
|
156
|
+
this.logger.success(`Tomcat ${this.version} instalado com sucesso!`);
|
|
156
157
|
return true;
|
|
157
158
|
} catch (error) {
|
|
158
|
-
|
|
159
|
+
this.logger.error(`Falha ao instalar Tomcat: ${error}`);
|
|
159
160
|
// Limpa arquivos parciais
|
|
160
161
|
if (existsSync(this.tomcatHome)) {
|
|
161
162
|
await fsPromises.rm(this.tomcatHome, { recursive: true, force: true });
|
|
@@ -203,7 +204,7 @@ export class EmbeddedTomcatService {
|
|
|
203
204
|
);
|
|
204
205
|
|
|
205
206
|
await fsPromises.writeFile(serverXmlPath, content, "utf-8");
|
|
206
|
-
|
|
207
|
+
this.logger.debug(`server.xml configurado na porta ${this.port}`);
|
|
207
208
|
}
|
|
208
209
|
|
|
209
210
|
/**
|
|
@@ -280,7 +281,7 @@ export class EmbeddedTomcatService {
|
|
|
280
281
|
</Context>`;
|
|
281
282
|
|
|
282
283
|
writeFileSync(contextFile, content);
|
|
283
|
-
|
|
284
|
+
this.logger.debug(`Context criado: ${contextFile}`);
|
|
284
285
|
}
|
|
285
286
|
|
|
286
287
|
/**
|
|
@@ -394,7 +395,7 @@ export class EmbeddedTomcatService {
|
|
|
394
395
|
await fsPromises.writeFile(destPath, allChunks);
|
|
395
396
|
|
|
396
397
|
const sizeMB = (received / 1024 / 1024).toFixed(1);
|
|
397
|
-
|
|
398
|
+
this.logger.info(`Download: ${sizeMB} MB baixados`);
|
|
398
399
|
} else {
|
|
399
400
|
// Sem content-length, usar spinner temático
|
|
400
401
|
const spinner = new ThemedSpinner();
|
|
@@ -406,7 +407,7 @@ export class EmbeddedTomcatService {
|
|
|
406
407
|
stop(true);
|
|
407
408
|
|
|
408
409
|
const sizeMB = (buffer.byteLength / 1024 / 1024).toFixed(1);
|
|
409
|
-
|
|
410
|
+
this.logger.info(`Download: ${sizeMB} MB baixados`);
|
|
410
411
|
} catch (error) {
|
|
411
412
|
stop(false);
|
|
412
413
|
throw error;
|
|
@@ -455,7 +456,7 @@ export class EmbeddedTomcatService {
|
|
|
455
456
|
async uninstall(): Promise<void> {
|
|
456
457
|
if (existsSync(this.tomcatHome)) {
|
|
457
458
|
await fsPromises.rm(this.tomcatHome, { recursive: true, force: true });
|
|
458
|
-
|
|
459
|
+
this.logger.info(`Tomcat: Versão ${this.version} removida`);
|
|
459
460
|
}
|
|
460
461
|
}
|
|
461
462
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { watch, type FSWatcher } from "fs";
|
|
8
|
-
import { Logger } from "../
|
|
8
|
+
import { Logger } from "../logging";
|
|
9
9
|
|
|
10
10
|
export interface FileWatcherOptions {
|
|
11
11
|
recursive?: boolean;
|
|
@@ -29,6 +29,7 @@ export class FileWatcher {
|
|
|
29
29
|
private debounceTimers: Map<string, Timer> = new Map();
|
|
30
30
|
private coolingFiles: Set<string> = new Set();
|
|
31
31
|
private isWatching = false;
|
|
32
|
+
private logger = Logger.getInstance();
|
|
32
33
|
|
|
33
34
|
private static readonly DEFAULT_OPTIONS: Required<FileWatcherOptions> = {
|
|
34
35
|
recursive: true,
|
|
@@ -74,7 +75,7 @@ export class FileWatcher {
|
|
|
74
75
|
*/
|
|
75
76
|
start(rootPath: string = process.cwd()): void {
|
|
76
77
|
if (this.isWatching) {
|
|
77
|
-
|
|
78
|
+
// Silently ignore
|
|
78
79
|
return;
|
|
79
80
|
}
|
|
80
81
|
|
|
@@ -85,7 +86,7 @@ export class FileWatcher {
|
|
|
85
86
|
);
|
|
86
87
|
|
|
87
88
|
this.isWatching = true;
|
|
88
|
-
|
|
89
|
+
// Started silently
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
/**
|
|
@@ -102,7 +103,7 @@ export class FileWatcher {
|
|
|
102
103
|
this.debounceTimers.clear();
|
|
103
104
|
|
|
104
105
|
this.isWatching = false;
|
|
105
|
-
|
|
106
|
+
// Stopped silently
|
|
106
107
|
}
|
|
107
108
|
|
|
108
109
|
/**
|
|
@@ -190,11 +191,11 @@ export class FileWatcher {
|
|
|
190
191
|
const result = handler(event);
|
|
191
192
|
if (result instanceof Promise) {
|
|
192
193
|
result.catch(err => {
|
|
193
|
-
|
|
194
|
+
this.logger.debug(`Erro em handler de watch: ${err.message}`);
|
|
194
195
|
});
|
|
195
196
|
}
|
|
196
197
|
} catch (err) {
|
|
197
|
-
|
|
198
|
+
this.logger.debug(`Erro em handler de watch: ${(err as Error).message}`);
|
|
198
199
|
}
|
|
199
200
|
}
|
|
200
201
|
}
|
|
@@ -208,7 +209,14 @@ export class FileWatcher {
|
|
|
208
209
|
if (pattern === "*") return true;
|
|
209
210
|
|
|
210
211
|
try {
|
|
211
|
-
|
|
212
|
+
// Quando um RegExp é convertido para string (ex: usado como chave de Map),
|
|
213
|
+
// ele vira algo como "/\\.java$/". Precisamos extrair o padrão real.
|
|
214
|
+
let patternStr = pattern;
|
|
215
|
+
if (pattern.length > 2 && pattern.startsWith("/") && pattern.lastIndexOf("/") > 0) {
|
|
216
|
+
const lastSlash = pattern.lastIndexOf("/");
|
|
217
|
+
patternStr = pattern.slice(1, lastSlash);
|
|
218
|
+
}
|
|
219
|
+
const regex = new RegExp(patternStr);
|
|
212
220
|
return regex.test(filename);
|
|
213
221
|
} catch {
|
|
214
222
|
// Se não for regex válido, trata como string simples
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Similar ao Postman/curl mas integrado com o projeto
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { Logger } from "../utils/ui";
|
|
6
|
+
import { Logger, C } from "../utils/ui";
|
|
7
7
|
import type { ApiEndpoint } from "../types/endpoint";
|
|
8
8
|
|
|
9
9
|
export interface HttpRequest {
|
|
@@ -195,12 +195,12 @@ export class HttpService {
|
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
private printResponse(res: HttpResponse): void {
|
|
198
|
-
const statusColor = res.status < 300 ?
|
|
199
|
-
: res.status < 400 ?
|
|
200
|
-
:
|
|
198
|
+
const statusColor = res.status < 300 ? C.success
|
|
199
|
+
: res.status < 400 ? C.warning
|
|
200
|
+
: C.error;
|
|
201
201
|
|
|
202
202
|
Logger.divider();
|
|
203
|
-
Logger.info("Status", `${statusColor}${res.status} ${res.statusText}${
|
|
203
|
+
Logger.info("Status", `${statusColor}${res.status} ${res.statusText}${C.reset}`);
|
|
204
204
|
Logger.info("Duration", `${res.duration}ms`);
|
|
205
205
|
Logger.info("Size", `${this.formatBytes(res.size)}`);
|
|
206
206
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { Logger } from "../
|
|
1
|
+
import { Logger, Colors } from "../logging";
|
|
2
2
|
import type { ProjectConfig } from "../types/config";
|
|
3
3
|
|
|
4
4
|
export class LogAnalyzer {
|
|
5
5
|
private projectPrefixes: string[] = [];
|
|
6
|
+
private logger = Logger.getInstance();
|
|
6
7
|
|
|
7
8
|
constructor(private config: ProjectConfig) {
|
|
8
9
|
// Tentamos inferir prefixos comuns do projeto.
|
|
@@ -20,18 +21,18 @@ export class LogAnalyzer {
|
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
public summarize(line: string): string {
|
|
23
|
-
if (
|
|
24
|
+
if (line.includes("CATALINA") || line.includes("Using JRE_HOME") || line.includes("Using CLASSPATH")) return "";
|
|
24
25
|
|
|
25
26
|
// Reuso da lógica existente no Logger.summarize, mas aprimorada
|
|
26
27
|
const startupMatch = line.match(/Server startup in (\[?)(.*?)(\]?)\s*ms/);
|
|
27
28
|
if (startupMatch) {
|
|
28
29
|
const time = (parseInt(startupMatch[2]) / 1000).toFixed(1);
|
|
29
|
-
return `${
|
|
30
|
+
return `${Colors.success}✔ ${Colors.bold}Servidor iniciado em ${time}s`;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
const deployMatch = line.match(/Deployment of web application archive \[(.*?)\] has finished in \[(.*?)\] ms/);
|
|
33
34
|
if (deployMatch) {
|
|
34
|
-
return `${
|
|
35
|
+
return `${Colors.success}✔ Artefatos implantados`;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
// Smart Folding para Stack Traces
|
|
@@ -40,7 +41,7 @@ export class LogAnalyzer {
|
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
if (line.includes("Caused by:")) {
|
|
43
|
-
return `${
|
|
44
|
+
return `${Colors.bgRed}${Colors.white}${Colors.bold} CAUSA RAIZ ${Colors.reset} ${Colors.error}${line.trim()}${Colors.reset}`;
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
// Hotswap
|
|
@@ -66,7 +67,7 @@ export class LogAnalyzer {
|
|
|
66
67
|
|
|
67
68
|
// Se a linha contém Exception mas não é o 'at ', destaca
|
|
68
69
|
if (line.includes("Exception:")) {
|
|
69
|
-
return `${
|
|
70
|
+
return `${Colors.error}${Colors.bold}${line.trim()}${Colors.reset}`;
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
return "";
|
|
@@ -77,9 +78,9 @@ export class LogAnalyzer {
|
|
|
77
78
|
const isProject = this.projectPrefixes.some(p => trimmed.includes(p));
|
|
78
79
|
|
|
79
80
|
if (isProject) {
|
|
80
|
-
return ` ${
|
|
81
|
+
return ` ${Colors.bold}${Colors.warning}${trimmed}${Colors.reset}`;
|
|
81
82
|
} else {
|
|
82
|
-
return ` ${
|
|
83
|
+
return ` ${Colors.dim}${trimmed}${Colors.reset}`;
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
86
|
|
|
@@ -87,7 +88,10 @@ export class LogAnalyzer {
|
|
|
87
88
|
const level = match[1];
|
|
88
89
|
let msg = match[3];
|
|
89
90
|
|
|
90
|
-
|
|
91
|
+
// Ignora mensagens de plugin inicializado (são muito verbosas)
|
|
92
|
+
if (msg.toLowerCase().includes("plugin") && msg.toLowerCase().includes("initialized")) {
|
|
93
|
+
return "";
|
|
94
|
+
}
|
|
91
95
|
|
|
92
96
|
if (msg.includes("redefinition") || msg.includes("reloaded") || level === 'RELOAD') {
|
|
93
97
|
if (msg.includes("Reloading classes [")) {
|
|
@@ -95,26 +99,26 @@ export class LogAnalyzer {
|
|
|
95
99
|
const classCount = classes.split(",").length;
|
|
96
100
|
if (classCount > 3) msg = `Reloading ${classCount} classes...`;
|
|
97
101
|
}
|
|
98
|
-
return `${
|
|
102
|
+
return `${Colors.magenta}👀 ${Colors.bold}Hotswap:${Colors.reset} ${msg.replace(/Class '.*?'/, (m) => Colors.bold + m + Colors.reset)}`;
|
|
99
103
|
}
|
|
100
104
|
|
|
101
|
-
let color =
|
|
105
|
+
let color = Colors.primary;
|
|
102
106
|
let symbol = "●";
|
|
103
|
-
if (level === "WARN") { color =
|
|
104
|
-
else if (level === "ERROR") { color =
|
|
107
|
+
if (level === "WARN") { color = Colors.warning; symbol = "▲"; }
|
|
108
|
+
else if (level === "ERROR") { color = Colors.error; symbol = "✖"; }
|
|
105
109
|
|
|
106
|
-
return `${color}${symbol} ${
|
|
110
|
+
return `${color}${symbol} ${Colors.bold}Hotswap:${Colors.reset} ${msg}`;
|
|
107
111
|
}
|
|
108
112
|
|
|
109
113
|
private formatTomcatLine(match: RegExpMatchArray): string {
|
|
110
114
|
const label = match[2];
|
|
111
115
|
let msg = match[4].trim();
|
|
112
|
-
if (
|
|
116
|
+
if (msg.includes("CATALINA") || msg.includes("Using JRE_HOME")) return "";
|
|
113
117
|
|
|
114
|
-
let color =
|
|
118
|
+
let color = Colors.dim;
|
|
115
119
|
let symbol = "ℹ";
|
|
116
|
-
if (label === "WARNING") { color =
|
|
117
|
-
else if (label === "SEVERE" || label === "ERROR") { color =
|
|
120
|
+
if (label === "WARNING") { color = Colors.warning; symbol = "▲"; }
|
|
121
|
+
else if (label === "SEVERE" || label === "ERROR") { color = Colors.error; symbol = "✖"; }
|
|
118
122
|
|
|
119
123
|
msg = msg.replace(/^(org\.apache|com\.sun|java\..*?|org\.glassfish)\.[a-zA-Z0-9.]+\s/, "").trim();
|
|
120
124
|
if (!msg) return "";
|
|
@@ -127,13 +131,13 @@ export class LogAnalyzer {
|
|
|
127
131
|
let msg = match[2].trim();
|
|
128
132
|
if (msg.includes("Total time:") || msg.includes("Finished at:") || msg.includes("Final Memory:") || msg.includes("-----------------------")) return "";
|
|
129
133
|
|
|
130
|
-
let color =
|
|
134
|
+
let color = Colors.dim;
|
|
131
135
|
let symbol = "ℹ";
|
|
132
|
-
if (label === "WARNING" || label === "WARN") { color =
|
|
133
|
-
else if (label === "SEVERE" || label === "ERROR") { color =
|
|
136
|
+
if (label === "WARNING" || label === "WARN") { color = Colors.warning; symbol = "▲"; }
|
|
137
|
+
else if (label === "SEVERE" || label === "ERROR") { color = Colors.error; symbol = "✖"; }
|
|
134
138
|
|
|
135
139
|
msg = msg.replace(/^(org\.apache|com\.sun|java\..*?)\.[a-zA-Z0-9.]+\s/, "").trim();
|
|
136
|
-
if (!msg || msg === "]"
|
|
140
|
+
if (!msg || msg === "]") return "";
|
|
137
141
|
|
|
138
142
|
return `${color}${symbol} ${msg}`;
|
|
139
143
|
}
|