@archznn/xavva 2.6.0 → 2.8.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.
@@ -30,6 +30,14 @@ export class HelpCommand implements Command {
30
30
  ${this.c("green", "profiles")} List available Maven/Gradle profiles
31
31
  ${this.c("green", "tomcat")} Manage embedded Tomcat (install, list, installed, use, status)
32
32
  ${this.c("green", "docs")} Generate endpoint documentation
33
+ ${this.c("green", "encoding")} Convert file encoding (detect, convert, fix, list)
34
+
35
+ ${this.c("cyan", "init")} Initialize project configuration (wizard)
36
+ ${this.c("cyan", "config")} View/edit configuration (--interactive)
37
+ ${this.c("cyan", "history")} Show command history
38
+ ${this.c("cyan", "redo")} Repeat last command
39
+ ${this.c("cyan", "health")} Check environment health
40
+ ${this.c("cyan", "completion")} Generate shell completions (bash/zsh/fish)
33
41
 
34
42
  ${this.c("yellow", "GENERAL OPTIONS")}
35
43
  ${this.c("cyan", "-p, --path")} <path> Tomcat installation path
@@ -65,6 +73,13 @@ export class HelpCommand implements Command {
65
73
  ${this.c("cyan", "--strict")} Fail on critical conflicts (for CI/CD)
66
74
  ${this.c("cyan", "-o, --output")} <file> Export report as JSON
67
75
 
76
+ ${this.c("yellow", "ENCODING OPTIONS")} ${this.c("dim", "(for xavva encoding)")}
77
+ ${this.c("cyan", "--from")} <encoding> Source encoding (auto-detect if not specified)
78
+ ${this.c("cyan", "--to")} <encoding> Target encoding (default: from xavva.json or UTF-8)
79
+ ${this.c("cyan", "--backup")} Create backup before conversion
80
+ ${this.c("cyan", "--dry-run")} Simulate without modifying files
81
+ ${this.c("cyan", "--src")} <path> Source directory (default: src/)
82
+
68
83
  ${this.c("yellow", "EXAMPLES")}
69
84
  ${this.c("dim", "# Development with hot reload and dashboard")}
70
85
  xavva dev --tui --watch
@@ -97,6 +112,33 @@ export class HelpCommand implements Command {
97
112
  xavva tomcat status
98
113
  xavva tomcat uninstall 9.0.115
99
114
 
115
+ ${this.c("dim", "# Convert file encoding")}
116
+ xavva encoding detect src/main/java/MinhaClasse.java
117
+ xavva encoding convert --from utf-8 --to cp1252 src/main/java/
118
+ xavva encoding convert --to cp1252 --backup src/main/java/MinhaClasse.java
119
+ xavva encoding fix src/main/java/MinhaClasse.java # Fix mojibake
120
+ xavva encoding list # List all file encodings
121
+
122
+ ${this.c("dim", "# Initialize new project")}
123
+ xavva init # Interactive wizard
124
+
125
+ ${this.c("dim", "# Manage configuration")}
126
+ xavva config # View current config
127
+ xavva config --interactive # Edit config interactively
128
+
129
+ ${this.c("dim", "# Command history")}
130
+ xavva history # Show recent commands
131
+ xavva history --clear # Clear history
132
+ xavva redo # Repeat last command
133
+
134
+ ${this.c("dim", "# Health check")}
135
+ xavva health # Check environment health
136
+
137
+ ${this.c("dim", "# Shell completions")}
138
+ xavva completion bash # Generate bash completions
139
+ xavva completion zsh # Generate zsh completions
140
+ eval "$(xavva completion bash)" # Enable in current shell
141
+
100
142
  ${this.c("yellow", "CONFIGURATION")}
101
143
  Settings are loaded from ${this.c("cyan", "xavva.json")} in the project root:
102
144
 
@@ -0,0 +1,49 @@
1
+ import type { Command } from "./Command";
2
+ import type { AppConfig, CLIArguments } from "../types/config";
3
+ import { HistoryService } from "../services/HistoryService";
4
+ import { Logger } from "../utils/ui";
5
+
6
+ export class HistoryCommand implements Command {
7
+ private historyService = new HistoryService();
8
+
9
+ async execute(_config: AppConfig, args?: CLIArguments): Promise<void> {
10
+ const clear = args?.["clear"] || false;
11
+ const limit = parseInt(String(args?.["limit"] || "10"));
12
+
13
+ if (clear) {
14
+ await this.historyService.clear();
15
+ Logger.success("Histórico limpo!");
16
+ return;
17
+ }
18
+
19
+ const entries = await this.historyService.getRecent(limit);
20
+ const stats = await this.historyService.getStats();
21
+
22
+ Logger.banner("history");
23
+ Logger.section(`Últimos ${entries.length} comandos`);
24
+
25
+ if (entries.length === 0) {
26
+ Logger.dim("Nenhum comando no histórico");
27
+ Logger.endSection();
28
+ return;
29
+ }
30
+
31
+ for (let i = 0; i < entries.length; i++) {
32
+ const entry = entries[i];
33
+ const date = new Date(entry.timestamp);
34
+ const time = date.toLocaleTimeString("pt-BR", { hour: "2-digit", minute: "2-digit" });
35
+ const icon = entry.success
36
+ ? `${Logger.C.success}✓${Logger.C.reset}`
37
+ : `${Logger.C.error}✗${Logger.C.reset}`;
38
+
39
+ const args = entry.args.length > 0 ? entry.args.join(" ") : "";
40
+ const duration = entry.duration ? `${Logger.C.gray}(${entry.duration.toFixed(1)}s)${Logger.C.reset}` : "";
41
+
42
+ Logger.log(`${Logger.C.gray}│${Logger.C.reset} ${Logger.C.dim}${time}${Logger.C.reset} ${icon} ${Logger.C.white}xavva ${entry.command}${Logger.C.reset} ${Logger.C.gray}${args}${Logger.C.reset} ${duration}`);
43
+ }
44
+
45
+ Logger.endSection();
46
+ Logger.info(`Total: ${stats.total} | Sucesso: ${stats.successful} | Falha: ${stats.failed}`);
47
+ Logger.dim("Use 'xavva redo' para repetir o último comando");
48
+ }
49
+ }
@@ -0,0 +1,148 @@
1
+ import { input, select, confirm, number } from "@inquirer/prompts";
2
+ import { writeFile, access } from "fs/promises";
3
+ import { join } from "path";
4
+ import { constants } from "fs";
5
+ import type { Command } from "./Command";
6
+ import type { AppConfig, CLIArguments } from "../types/config";
7
+ import { Logger } from "../utils/ui";
8
+
9
+ export class InitCommand implements Command {
10
+ async execute(_config: AppConfig, _args?: CLIArguments): Promise<void> {
11
+ Logger.banner("init");
12
+ Logger.section("Wizard de Configuração");
13
+ Logger.info("Vamos configurar seu projeto Xavva");
14
+ Logger.newline();
15
+
16
+ // Detectar build tool
17
+ const buildTool = await this.detectBuildTool();
18
+
19
+ // Nome da aplicação
20
+ const appName = await input({
21
+ message: "Nome da aplicação:",
22
+ default: process.cwd().split(/[/\\]/).pop() || "my-app",
23
+ validate: (value) => value.length > 0 || "Nome é obrigatório"
24
+ });
25
+
26
+ // Profile
27
+ const profile = await select({
28
+ message: "Profile padrão:",
29
+ choices: [
30
+ { name: "desenvolvimento", value: "dev" },
31
+ { name: "teste", value: "test" },
32
+ { name: "produção", value: "prod" },
33
+ { name: "customizado", value: "custom" }
34
+ ],
35
+ default: "dev"
36
+ });
37
+
38
+ const customProfile = profile === "custom" ? await input({
39
+ message: "Nome do profile:",
40
+ default: "local"
41
+ }) : profile;
42
+
43
+ // Porta do Tomcat
44
+ const port = await number({
45
+ message: "Porta do Tomcat:",
46
+ default: 8080,
47
+ validate: (value) => (value && value > 0 && value < 65536) || "Porta inválida"
48
+ }) || 8080;
49
+
50
+ // Configurações opcionais
51
+ Logger.newline();
52
+ Logger.dim("Configurações avançadas:");
53
+
54
+ const useEmbedded = await confirm({
55
+ message: "Usar Tomcat embutido (auto-download)?",
56
+ default: true
57
+ });
58
+
59
+ const enableCache = await confirm({
60
+ message: "Habilitar cache de build?",
61
+ default: true
62
+ });
63
+
64
+ const enableTui = await confirm({
65
+ message: "Habilitar dashboard TUI?",
66
+ default: true
67
+ });
68
+
69
+ const encoding = await select({
70
+ message: "Encoding:",
71
+ choices: [
72
+ { name: "UTF-8", value: "UTF-8" },
73
+ { name: "ISO-8859-1", value: "ISO-8859-1" },
74
+ { name: "Windows-1252", value: "Windows-1252" }
75
+ ],
76
+ default: "UTF-8"
77
+ });
78
+
79
+ // Montar configuração
80
+ const config: Record<string, unknown> = {
81
+ appName,
82
+ buildTool,
83
+ profile: customProfile,
84
+ port,
85
+ cache: enableCache,
86
+ tui: enableTui,
87
+ encoding
88
+ };
89
+
90
+ if (useEmbedded) {
91
+ config.embedded = true;
92
+ config.tomcatVersion = "10.1.52";
93
+ } else {
94
+ const tomcatPath = await input({
95
+ message: "Caminho do Tomcat (CATALINA_HOME):",
96
+ validate: async (value) => {
97
+ if (!value) return "Caminho é obrigatório";
98
+ try {
99
+ await access(value, constants.R_OK);
100
+ return true;
101
+ } catch {
102
+ return "Caminho não acessível";
103
+ }
104
+ }
105
+ });
106
+ config.tomcatPath = tomcatPath;
107
+ }
108
+
109
+ // Salvar arquivo
110
+ Logger.newline();
111
+ Logger.step("Salvando configuração...");
112
+
113
+ const configPath = join(process.cwd(), "xavva.json");
114
+ await writeFile(configPath, JSON.stringify(config, null, 2));
115
+
116
+ Logger.success(`Configuração salva em ${configPath}`);
117
+ Logger.newline();
118
+ Logger.ready("Projeto configurado!");
119
+ Logger.info("Próximos passos:");
120
+ Logger.log(` ${Logger.C.gray}│${Logger.C.reset} ${Logger.C.primary}xavva build${Logger.C.reset} ${Logger.C.gray}- Compilar projeto${Logger.C.reset}`);
121
+ Logger.log(` ${Logger.C.gray}│${Logger.C.reset} ${Logger.C.primary}xavva deploy${Logger.C.reset} ${Logger.C.gray}- Build + deploy${Logger.C.reset}`);
122
+ Logger.log(` ${Logger.C.gray}│${Logger.C.reset} ${Logger.C.primary}xavva doctor${Logger.C.reset} ${Logger.C.gray}- Verificar ambiente${Logger.C.reset}`);
123
+ Logger.done();
124
+ }
125
+
126
+ private async detectBuildTool(): Promise<"maven" | "gradle"> {
127
+ try {
128
+ await access(join(process.cwd(), "pom.xml"), constants.R_OK);
129
+ Logger.info("Detectado: Projeto Maven");
130
+ return "maven";
131
+ } catch {
132
+ try {
133
+ await access(join(process.cwd(), "build.gradle"), constants.R_OK);
134
+ Logger.info("Detectado: Projeto Gradle");
135
+ return "gradle";
136
+ } catch {
137
+ const choice = await select({
138
+ message: "Build tool:",
139
+ choices: [
140
+ { name: "Maven", value: "maven" },
141
+ { name: "Gradle", value: "gradle" }
142
+ ]
143
+ });
144
+ return choice;
145
+ }
146
+ }
147
+ }
148
+ }
@@ -0,0 +1,36 @@
1
+ import type { Command } from "./Command";
2
+ import type { AppConfig, CLIArguments } from "../types/config";
3
+ import { HistoryService } from "../services/HistoryService";
4
+ import { Logger } from "../utils/ui";
5
+ import { ProcessManager } from "../utils/processManager";
6
+
7
+ export class RedoCommand implements Command {
8
+ private historyService = new HistoryService();
9
+
10
+ async execute(_config: AppConfig, _args?: CLIArguments): Promise<void> {
11
+ const lastEntry = await this.historyService.getLast();
12
+
13
+ if (!lastEntry) {
14
+ Logger.error("Nenhum comando no histórico");
15
+ return;
16
+ }
17
+
18
+ const args = lastEntry.args.length > 0 ? lastEntry.args.join(" ") : "";
19
+ Logger.banner("redo");
20
+ Logger.info(`Repetindo: ${Logger.C.white}xavva ${lastEntry.command}${Logger.C.reset} ${Logger.C.gray}${args}${Logger.C.reset}`);
21
+ Logger.newline();
22
+
23
+ // Re-executar o comando
24
+ const proc = Bun.spawn([
25
+ "bun", "run", "src/index.ts",
26
+ lastEntry.command,
27
+ ...lastEntry.args
28
+ ], {
29
+ stdio: "inherit",
30
+ cwd: process.cwd()
31
+ });
32
+
33
+ await proc.exited;
34
+ ProcessManager.getInstance().shutdown(proc.exitCode || 0);
35
+ }
36
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Configurações centralizadas de versões
3
+ * Evita hardcoding espalhado pelo código
4
+ */
5
+
6
+ export const VERSIONS = {
7
+ // Versões padrão do Tomcat
8
+ TOMCAT: {
9
+ DEFAULT: "10.1.52",
10
+ AVAILABLE: {
11
+ "10.1.52": { sha512: "" },
12
+ "9.0.115": { sha512: "" },
13
+ "11.0.18": { sha512: "" },
14
+ },
15
+ },
16
+
17
+ // HotswapAgent
18
+ HOTSWAP_AGENT: {
19
+ VERSION: "2.0.3",
20
+ URL: "https://github.com/HotswapProjects/HotswapAgent/releases/download/RELEASE-{version}/hotswap-agent-{version}.jar",
21
+ },
22
+
23
+ // Configurações padrão
24
+ DEFAULTS: {
25
+ TOMCAT_PORT: 8080,
26
+ DEBUG_PORT: 5005,
27
+ DEBOUNCE_MS: 300,
28
+ COOLING_MS: 1000,
29
+ },
30
+ } as const;
31
+
32
+ // Type helpers
33
+ export type TomcatVersion = keyof typeof VERSIONS.TOMCAT.AVAILABLE;
34
+
35
+ /**
36
+ * Obtém URL de download do HotswapAgent
37
+ */
38
+ export function getHotswapAgentUrl(version: string = VERSIONS.HOTSWAP_AGENT.VERSION): string {
39
+ return VERSIONS.HOTSWAP_AGENT.URL
40
+ .replace(/{version}/g, version);
41
+ }
42
+
43
+ /**
44
+ * Verifica se versão do Tomcat é suportada
45
+ */
46
+ export function isSupportedTomcatVersion(version: string): version is TomcatVersion {
47
+ return version in VERSIONS.TOMCAT.AVAILABLE;
48
+ }
49
+
50
+ /**
51
+ * Obtém versões disponíveis do Tomcat
52
+ */
53
+ export function getAvailableTomcatVersions(): string[] {
54
+ return Object.keys(VERSIONS.TOMCAT.AVAILABLE);
55
+ }
56
+
57
+ /**
58
+ * Obtém SHA512 de uma versão do Tomcat
59
+ */
60
+ export function getTomcatSha512(version: string): string | undefined {
61
+ const info = VERSIONS.TOMCAT.AVAILABLE[version as TomcatVersion];
62
+ return info?.sha512;
63
+ }
@@ -0,0 +1,249 @@
1
+ /**
2
+ * Container de Injeção de Dependência (DI) simplificado
3
+ * Centraliza a criação e injeção de serviços
4
+ */
5
+
6
+ import type { AppConfig } from "../types/config";
7
+ import { ProjectService } from "../services/ProjectService";
8
+ import { TomcatService } from "../services/TomcatService";
9
+ import { BuildService } from "../services/BuildService";
10
+ import { BuildCacheService } from "../services/BuildCacheService";
11
+ import { AuditService } from "../services/AuditService";
12
+ import { DashboardService } from "../services/DashboardService";
13
+ import { LogAnalyzer } from "../services/LogAnalyzer";
14
+ import { DeployCommand } from "../commands/DeployCommand";
15
+ import { BuildCommand } from "../commands/BuildCommand";
16
+ import { StartCommand } from "../commands/StartCommand";
17
+ import { LogsCommand } from "../commands/LogsCommand";
18
+ import { AuditCommand } from "../commands/AuditCommand";
19
+ import { ProfilesCommand } from "../commands/ProfilesCommand";
20
+ import { RunCommand } from "../commands/RunCommand";
21
+ import { HelpCommand } from "../commands/HelpCommand";
22
+ import { DoctorCommand } from "../commands/DoctorCommand";
23
+ import { DepsCommand } from "../commands/DepsCommand";
24
+ import { TomcatCommand } from "../commands/TomcatCommand";
25
+ import { EncodingCommand } from "../commands/EncodingCommand";
26
+ import { DocsCommand } from "../commands/DocsCommand";
27
+ import { InitCommand } from "../commands/InitCommand";
28
+ import { ConfigCommand } from "../commands/ConfigCommand";
29
+ import { HistoryCommand } from "../commands/HistoryCommand";
30
+ import { RedoCommand } from "../commands/RedoCommand";
31
+ import { HealthCommand } from "../commands/HealthCommand";
32
+ import { CompletionCommand } from "../commands/CompletionCommand";
33
+ import { HistoryService } from "../services/HistoryService";
34
+ import { NotificationService } from "../services/NotificationService";
35
+ import type { Command } from "../commands/Command";
36
+ import { Logger } from "../utils/ui";
37
+
38
+ export interface Services {
39
+ projectService: ProjectService;
40
+ tomcatService: TomcatService;
41
+ buildCacheService: BuildCacheService;
42
+ buildService: BuildService;
43
+ auditService: AuditService;
44
+ dashboardService: DashboardService;
45
+ logAnalyzer: LogAnalyzer;
46
+ historyService: HistoryService;
47
+ }
48
+
49
+ export interface Commands {
50
+ deploy: DeployCommand;
51
+ dev: DeployCommand;
52
+ build: BuildCommand;
53
+ start: StartCommand;
54
+ logs: LogsCommand;
55
+ audit: AuditCommand;
56
+ profiles: ProfilesCommand;
57
+ run: RunCommand;
58
+ debug: RunCommand;
59
+ help: HelpCommand;
60
+ doctor: DoctorCommand;
61
+ deps: DepsCommand;
62
+ tomcat: TomcatCommand;
63
+ encoding: EncodingCommand;
64
+ docs: DocsCommand;
65
+ init: InitCommand;
66
+ config: ConfigCommand;
67
+ history: HistoryCommand;
68
+ redo: RedoCommand;
69
+ health: HealthCommand;
70
+ completion: CompletionCommand;
71
+ }
72
+
73
+ export class DIContainer {
74
+ private config: AppConfig;
75
+ private services: Partial<Services> = {};
76
+ private commands: Partial<Commands> = {};
77
+ private isInitialized = false;
78
+
79
+ constructor(config: AppConfig) {
80
+ this.config = config;
81
+ }
82
+
83
+ /**
84
+ * Inicializa todos os serviços e comandos
85
+ */
86
+ initialize(): void {
87
+ if (this.isInitialized) {
88
+ Logger.debug("DI Container já inicializado");
89
+ return;
90
+ }
91
+
92
+ this.initializeServices();
93
+ this.initializeCommands();
94
+ this.isInitialized = true;
95
+ }
96
+
97
+ private initializeServices(): void {
98
+ // Serviços base (sem dependências ou com dependências simples)
99
+ const projectService = new ProjectService(this.config.project);
100
+ const buildCacheService = new BuildCacheService();
101
+ const dashboardService = new DashboardService(this.config);
102
+ const logAnalyzer = new LogAnalyzer(this.config.project);
103
+
104
+ // Configura Logger com dashboard
105
+ Logger.setDashboard(dashboardService);
106
+
107
+ // Serviços com dependências
108
+ const buildService = new BuildService(
109
+ this.config.project,
110
+ this.config.tomcat,
111
+ projectService,
112
+ buildCacheService
113
+ );
114
+
115
+ const tomcatService = new TomcatService(this.config.tomcat);
116
+ tomcatService.setProjectService(projectService);
117
+
118
+ const auditService = new AuditService(this.config.tomcat);
119
+ const historyService = new HistoryService();
120
+
121
+ this.services = {
122
+ projectService,
123
+ buildCacheService,
124
+ buildService,
125
+ tomcatService,
126
+ auditService,
127
+ dashboardService,
128
+ logAnalyzer,
129
+ historyService,
130
+ };
131
+ }
132
+
133
+ private initializeCommands(): void {
134
+ const { tomcatService, buildService, auditService, dashboardService, logAnalyzer } = this.services;
135
+
136
+ if (!tomcatService || !buildService || !auditService || !dashboardService || !logAnalyzer) {
137
+ throw new Error("Serviços não inicializados corretamente");
138
+ }
139
+
140
+ // Comandos que compartilham instâncias
141
+ const deployCmd = new DeployCommand(tomcatService, buildService);
142
+ const logsCmd = new LogsCommand(dashboardService, logAnalyzer);
143
+
144
+ this.commands = {
145
+ deploy: deployCmd,
146
+ dev: deployCmd, // dev reusa deploy
147
+ build: new BuildCommand(buildService),
148
+ start: new StartCommand(tomcatService),
149
+ logs: logsCmd,
150
+ audit: new AuditCommand(auditService),
151
+ profiles: new ProfilesCommand(this.services.projectService!),
152
+ run: new RunCommand(),
153
+ debug: new RunCommand(),
154
+ help: new HelpCommand(),
155
+ doctor: new DoctorCommand(),
156
+ deps: new DepsCommand(),
157
+ tomcat: new TomcatCommand(),
158
+ encoding: new EncodingCommand(),
159
+ docs: new DocsCommand(),
160
+ init: new InitCommand(),
161
+ config: new ConfigCommand(),
162
+ history: new HistoryCommand(),
163
+ redo: new RedoCommand(),
164
+ health: new HealthCommand(),
165
+ completion: new CompletionCommand(),
166
+ };
167
+ }
168
+
169
+ /**
170
+ * Obtém um serviço pelo nome
171
+ */
172
+ getService<K extends keyof Services>(name: K): Services[K] {
173
+ if (!this.isInitialized) {
174
+ this.initialize();
175
+ }
176
+ const service = this.services[name];
177
+ if (!service) {
178
+ throw new Error(`Serviço '${name}' não encontrado no container`);
179
+ }
180
+ return service;
181
+ }
182
+
183
+ /**
184
+ * Obtém um comando pelo nome
185
+ */
186
+ getCommand<K extends keyof Commands>(name: K): Commands[K] {
187
+ if (!this.isInitialized) {
188
+ this.initialize();
189
+ }
190
+ const command = this.commands[name];
191
+ if (!command) {
192
+ throw new Error(`Comando '${name}' não encontrado no container`);
193
+ }
194
+ return command;
195
+ }
196
+
197
+ /**
198
+ * Obtém todos os serviços
199
+ */
200
+ getAllServices(): Services {
201
+ if (!this.isInitialized) {
202
+ this.initialize();
203
+ }
204
+ return this.services as Services;
205
+ }
206
+
207
+ /**
208
+ * Obtém todos os comandos
209
+ */
210
+ getAllCommands(): Commands {
211
+ if (!this.isInitialized) {
212
+ this.initialize();
213
+ }
214
+ return this.commands as Commands;
215
+ }
216
+
217
+ /**
218
+ * Registra um serviço customizado (útil para testes)
219
+ */
220
+ registerService<K extends keyof Services>(name: K, service: Services[K]): void {
221
+ this.services[name] = service;
222
+ }
223
+
224
+ /**
225
+ * Registra um comando customizado (útil para testes)
226
+ */
227
+ registerCommand<K extends keyof Commands>(name: K, command: Commands[K]): void {
228
+ this.commands[name] = command;
229
+ }
230
+ }
231
+
232
+ // Singleton para uso global
233
+ let globalContainer: DIContainer | null = null;
234
+
235
+ export function createContainer(config: AppConfig): DIContainer {
236
+ globalContainer = new DIContainer(config);
237
+ return globalContainer;
238
+ }
239
+
240
+ export function getContainer(): DIContainer {
241
+ if (!globalContainer) {
242
+ throw new Error("Container não inicializado. Chame createContainer primeiro.");
243
+ }
244
+ return globalContainer;
245
+ }
246
+
247
+ export function resetContainer(): void {
248
+ globalContainer = null;
249
+ }