hightjs 0.5.1 → 0.5.3
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/package.json +1 -1
- package/src/builder.js +8 -8
- package/src/hotReload.ts +4 -7
- package/src/router.ts +15 -13
- package/dist/adapters/express.d.ts +0 -7
- package/dist/adapters/express.js +0 -63
- package/dist/adapters/factory.d.ts +0 -23
- package/dist/adapters/factory.js +0 -122
- package/dist/adapters/fastify.d.ts +0 -25
- package/dist/adapters/fastify.js +0 -61
- package/dist/adapters/native.d.ts +0 -8
- package/dist/adapters/native.js +0 -198
- package/dist/api/console.d.ts +0 -94
- package/dist/api/console.js +0 -294
- package/dist/api/http.d.ts +0 -180
- package/dist/api/http.js +0 -469
- package/dist/bin/hightjs.d.ts +0 -2
- package/dist/bin/hightjs.js +0 -214
- package/dist/builder.d.ts +0 -32
- package/dist/builder.js +0 -581
- package/dist/client/DefaultNotFound.d.ts +0 -1
- package/dist/client/DefaultNotFound.js +0 -79
- package/dist/client/client.d.ts +0 -3
- package/dist/client/client.js +0 -24
- package/dist/client/clientRouter.d.ts +0 -58
- package/dist/client/clientRouter.js +0 -132
- package/dist/client/entry.client.d.ts +0 -1
- package/dist/client/entry.client.js +0 -455
- package/dist/components/Link.d.ts +0 -7
- package/dist/components/Link.js +0 -13
- package/dist/global/global.d.ts +0 -117
- package/dist/global/global.js +0 -17
- package/dist/helpers.d.ts +0 -20
- package/dist/helpers.js +0 -583
- package/dist/hotReload.d.ts +0 -32
- package/dist/hotReload.js +0 -548
- package/dist/index.d.ts +0 -18
- package/dist/index.js +0 -494
- package/dist/loaders.d.ts +0 -1
- package/dist/loaders.js +0 -46
- package/dist/renderer.d.ts +0 -14
- package/dist/renderer.js +0 -380
- package/dist/router.d.ts +0 -101
- package/dist/router.js +0 -653
- package/dist/types/framework.d.ts +0 -37
- package/dist/types/framework.js +0 -2
- package/dist/types.d.ts +0 -192
- package/dist/types.js +0 -2
package/dist/adapters/native.js
DELETED
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.NativeAdapter = void 0;
|
|
4
|
-
const url_1 = require("url");
|
|
5
|
-
// --- Funções Auxiliares de Segurança ---
|
|
6
|
-
/**
|
|
7
|
-
* Remove caracteres de quebra de linha (\r, \n) de uma string para prevenir
|
|
8
|
-
* ataques de HTTP Header Injection (CRLF Injection).
|
|
9
|
-
* @param value O valor a ser sanitizado.
|
|
10
|
-
* @returns A string sanitizada.
|
|
11
|
-
*/
|
|
12
|
-
function sanitizeHeaderValue(value) {
|
|
13
|
-
return String(value).replace(/[\r\n]/g, '');
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Valida se o nome de um cookie contém apenas caracteres permitidos pela RFC 6265.
|
|
17
|
-
* Isso previne a criação de cookies com nomes inválidos ou maliciosos.
|
|
18
|
-
* @param name O nome do cookie a ser validado.
|
|
19
|
-
* @returns `true` se o nome for válido, `false` caso contrário.
|
|
20
|
-
*/
|
|
21
|
-
function isValidCookieName(name) {
|
|
22
|
-
// A RFC 6265 define 'token' como 1 ou mais caracteres que não são controle nem separadores.
|
|
23
|
-
// Separadores: ( ) < > @ , ; : \ " / [ ] ? = { }
|
|
24
|
-
const validCookieNameRegex = /^[a-zA-Z0-9!#$%&'*+-.^_`|~]+$/;
|
|
25
|
-
return validCookieNameRegex.test(name);
|
|
26
|
-
}
|
|
27
|
-
class NativeAdapter {
|
|
28
|
-
constructor() {
|
|
29
|
-
this.type = 'native';
|
|
30
|
-
}
|
|
31
|
-
parseRequest(req) {
|
|
32
|
-
const url = (0, url_1.parse)(req.url || '', true);
|
|
33
|
-
return {
|
|
34
|
-
method: req.method || 'GET',
|
|
35
|
-
url: req.url || '/',
|
|
36
|
-
headers: req.headers,
|
|
37
|
-
// Adicionado fallback para null para maior segurança caso o body parser não tenha rodado.
|
|
38
|
-
body: req.body ?? null,
|
|
39
|
-
// Tipo mais específico para a query.
|
|
40
|
-
query: url.query,
|
|
41
|
-
params: {}, // Será preenchido pelo roteador
|
|
42
|
-
cookies: this.parseCookies(req.headers.cookie || ''),
|
|
43
|
-
raw: req
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
createResponse(res) {
|
|
47
|
-
return new NativeResponseWrapper(res);
|
|
48
|
-
}
|
|
49
|
-
parseCookies(cookieHeader) {
|
|
50
|
-
const cookies = {};
|
|
51
|
-
if (!cookieHeader)
|
|
52
|
-
return cookies;
|
|
53
|
-
cookieHeader.split(';').forEach(cookie => {
|
|
54
|
-
const [name, ...rest] = cookie.trim().split('=');
|
|
55
|
-
if (name && rest.length > 0) {
|
|
56
|
-
try {
|
|
57
|
-
// Tenta decodificar o valor do cookie.
|
|
58
|
-
cookies[name] = decodeURIComponent(rest.join('='));
|
|
59
|
-
}
|
|
60
|
-
catch (e) {
|
|
61
|
-
// Prevenção de crash: Ignora cookies com valores malformados (e.g., URI inválida).
|
|
62
|
-
console.error(`Warning: Malformed cookie with name "${name}" was ignored.`);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
return cookies;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
exports.NativeAdapter = NativeAdapter;
|
|
70
|
-
class NativeResponseWrapper {
|
|
71
|
-
constructor(res) {
|
|
72
|
-
this.res = res;
|
|
73
|
-
this.statusCode = 200;
|
|
74
|
-
this.headers = {};
|
|
75
|
-
this.cookiesToSet = []; // Array para lidar corretamente com múltiplos cookies.
|
|
76
|
-
this.finished = false;
|
|
77
|
-
}
|
|
78
|
-
get raw() {
|
|
79
|
-
return this.res;
|
|
80
|
-
}
|
|
81
|
-
status(code) {
|
|
82
|
-
this.statusCode = code;
|
|
83
|
-
return this;
|
|
84
|
-
}
|
|
85
|
-
header(name, value) {
|
|
86
|
-
// Medida de segurança CRÍTICA: Previne HTTP Header Injection (CRLF Injection).
|
|
87
|
-
// Sanitiza tanto o nome quanto o valor do header para remover quebras de linha.
|
|
88
|
-
const sanitizedName = sanitizeHeaderValue(name);
|
|
89
|
-
const sanitizedValue = sanitizeHeaderValue(value);
|
|
90
|
-
if (name !== sanitizedName || String(value) !== sanitizedValue) {
|
|
91
|
-
console.warn(`Warning: Potential HTTP Header Injection attempt detected and sanitized. Original header: "${name}"`);
|
|
92
|
-
}
|
|
93
|
-
this.headers[sanitizedName] = sanitizedValue;
|
|
94
|
-
return this;
|
|
95
|
-
}
|
|
96
|
-
cookie(name, value, options) {
|
|
97
|
-
// Medida de segurança: Valida o nome do cookie.
|
|
98
|
-
if (!isValidCookieName(name)) {
|
|
99
|
-
console.error(`Error: Invalid cookie name "${name}". The cookie will not be set.`);
|
|
100
|
-
return this;
|
|
101
|
-
}
|
|
102
|
-
let cookieString = `${name}=${encodeURIComponent(value)}`;
|
|
103
|
-
if (options) {
|
|
104
|
-
// Sanitiza as opções que são strings para prevenir Header Injection.
|
|
105
|
-
if (options.domain)
|
|
106
|
-
cookieString += `; Domain=${sanitizeHeaderValue(options.domain)}`;
|
|
107
|
-
if (options.path)
|
|
108
|
-
cookieString += `; Path=${sanitizeHeaderValue(options.path)}`;
|
|
109
|
-
if (options.expires)
|
|
110
|
-
cookieString += `; Expires=${options.expires.toUTCString()}`;
|
|
111
|
-
if (options.maxAge)
|
|
112
|
-
cookieString += `; Max-Age=${options.maxAge}`;
|
|
113
|
-
if (options.httpOnly)
|
|
114
|
-
cookieString += '; HttpOnly';
|
|
115
|
-
if (options.secure)
|
|
116
|
-
cookieString += '; Secure';
|
|
117
|
-
if (options.sameSite) {
|
|
118
|
-
const sameSiteValue = typeof options.sameSite === 'boolean' ? 'Strict' : options.sameSite;
|
|
119
|
-
cookieString += `; SameSite=${sanitizeHeaderValue(sameSiteValue)}`;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
this.cookiesToSet.push(cookieString);
|
|
123
|
-
return this;
|
|
124
|
-
}
|
|
125
|
-
clearCookie(name, options) {
|
|
126
|
-
const clearOptions = { ...options, expires: new Date(0), maxAge: 0 };
|
|
127
|
-
return this.cookie(name, '', clearOptions);
|
|
128
|
-
}
|
|
129
|
-
writeHeaders() {
|
|
130
|
-
if (this.finished)
|
|
131
|
-
return;
|
|
132
|
-
this.res.statusCode = this.statusCode;
|
|
133
|
-
Object.entries(this.headers).forEach(([name, value]) => {
|
|
134
|
-
this.res.setHeader(name, value);
|
|
135
|
-
});
|
|
136
|
-
// CORREÇÃO: Envia múltiplos cookies corretamente como headers 'Set-Cookie' separados.
|
|
137
|
-
// O método antigo de juntar com vírgula estava incorreto.
|
|
138
|
-
if (this.cookiesToSet.length > 0) {
|
|
139
|
-
this.res.setHeader('Set-Cookie', this.cookiesToSet);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
json(data) {
|
|
143
|
-
if (this.finished)
|
|
144
|
-
return;
|
|
145
|
-
this.header('Content-Type', 'application/json; charset=utf-8');
|
|
146
|
-
this.writeHeaders();
|
|
147
|
-
const jsonString = JSON.stringify(data);
|
|
148
|
-
this.res.end(jsonString);
|
|
149
|
-
this.finished = true;
|
|
150
|
-
}
|
|
151
|
-
text(data) {
|
|
152
|
-
if (this.finished)
|
|
153
|
-
return;
|
|
154
|
-
this.header('Content-Type', 'text/plain; charset=utf-8');
|
|
155
|
-
this.writeHeaders();
|
|
156
|
-
this.res.end(data);
|
|
157
|
-
this.finished = true;
|
|
158
|
-
}
|
|
159
|
-
send(data) {
|
|
160
|
-
if (this.finished)
|
|
161
|
-
return;
|
|
162
|
-
const existingContentType = this.headers['Content-Type'];
|
|
163
|
-
if (typeof data === 'string') {
|
|
164
|
-
if (!existingContentType) {
|
|
165
|
-
this.header('Content-Type', 'text/plain; charset=utf-8');
|
|
166
|
-
}
|
|
167
|
-
this.writeHeaders();
|
|
168
|
-
this.res.end(data);
|
|
169
|
-
}
|
|
170
|
-
else if (Buffer.isBuffer(data)) {
|
|
171
|
-
this.writeHeaders();
|
|
172
|
-
this.res.end(data);
|
|
173
|
-
}
|
|
174
|
-
else if (data !== null && typeof data === 'object') {
|
|
175
|
-
this.json(data); // Reutiliza o método json para consistência
|
|
176
|
-
return; // O método json já finaliza a resposta
|
|
177
|
-
}
|
|
178
|
-
else {
|
|
179
|
-
if (!existingContentType) {
|
|
180
|
-
this.header('Content-Type', 'text/plain; charset=utf-8');
|
|
181
|
-
}
|
|
182
|
-
this.writeHeaders();
|
|
183
|
-
this.res.end(String(data));
|
|
184
|
-
}
|
|
185
|
-
this.finished = true;
|
|
186
|
-
}
|
|
187
|
-
redirect(url) {
|
|
188
|
-
if (this.finished)
|
|
189
|
-
return;
|
|
190
|
-
this.status(302);
|
|
191
|
-
// A sanitização no método .header() previne que um URL manipulado
|
|
192
|
-
// cause um ataque de Open Redirect via Header Injection.
|
|
193
|
-
this.header('Location', url);
|
|
194
|
-
this.writeHeaders();
|
|
195
|
-
this.res.end();
|
|
196
|
-
this.finished = true;
|
|
197
|
-
}
|
|
198
|
-
}
|
package/dist/api/console.d.ts
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { Options as BoxenOptions } from 'boxen';
|
|
2
|
-
/**
|
|
3
|
-
* Um "handle" para uma linha dinâmica. As instâncias desta classe
|
|
4
|
-
* são retornadas por `Console.dynamicLine()` e usadas para controlar
|
|
5
|
-
* o conteúdo da linha.
|
|
6
|
-
*/
|
|
7
|
-
export declare class DynamicLine {
|
|
8
|
-
private readonly _id;
|
|
9
|
-
constructor(initialContent: string);
|
|
10
|
-
/**
|
|
11
|
-
* Atualiza o conteúdo da linha no console.
|
|
12
|
-
* @param newContent O novo texto a ser exibido.
|
|
13
|
-
*/
|
|
14
|
-
update(newContent: string): void;
|
|
15
|
-
/**
|
|
16
|
-
* Finaliza a linha, opcionalmente com um texto final, e a torna estática.
|
|
17
|
-
* @param finalContent O texto final a ser exibido.
|
|
18
|
-
*/
|
|
19
|
-
end(finalContent: string): void;
|
|
20
|
-
}
|
|
21
|
-
export declare enum Colors {
|
|
22
|
-
Reset = "\u001B[0m",
|
|
23
|
-
Bright = "\u001B[1m",
|
|
24
|
-
Dim = "\u001B[2m",
|
|
25
|
-
Underscore = "\u001B[4m",
|
|
26
|
-
Blink = "\u001B[5m",
|
|
27
|
-
Reverse = "\u001B[7m",
|
|
28
|
-
Hidden = "\u001B[8m",
|
|
29
|
-
FgBlack = "\u001B[30m",
|
|
30
|
-
FgRed = "\u001B[31m",
|
|
31
|
-
FgGreen = "\u001B[32m",
|
|
32
|
-
FgYellow = "\u001B[33m",
|
|
33
|
-
FgBlue = "\u001B[34m",
|
|
34
|
-
FgMagenta = "\u001B[35m",
|
|
35
|
-
FgCyan = "\u001B[36m",
|
|
36
|
-
FgWhite = "\u001B[37m",
|
|
37
|
-
FgGray = "\u001B[90m",// ← adicionado
|
|
38
|
-
BgBlack = "\u001B[40m",
|
|
39
|
-
BgRed = "\u001B[41m",
|
|
40
|
-
BgGreen = "\u001B[42m",
|
|
41
|
-
BgYellow = "\u001B[43m",
|
|
42
|
-
BgBlue = "\u001B[44m",
|
|
43
|
-
BgMagenta = "\u001B[45m",
|
|
44
|
-
BgCyan = "\u001B[46m",
|
|
45
|
-
BgWhite = "\u001B[47m",
|
|
46
|
-
BgGray = "\u001B[100m"
|
|
47
|
-
}
|
|
48
|
-
export declare enum Levels {
|
|
49
|
-
ERROR = "ERROR",
|
|
50
|
-
WARN = "WARN",
|
|
51
|
-
INFO = "INFO",
|
|
52
|
-
DEBUG = "DEBUG",
|
|
53
|
-
SUCCESS = "SUCCESS"
|
|
54
|
-
}
|
|
55
|
-
export default class Console {
|
|
56
|
-
private static activeLines;
|
|
57
|
-
private static lastRenderedLines;
|
|
58
|
-
/**
|
|
59
|
-
* Limpa todas as linhas dinâmicas da tela e as redesenha com o conteúdo atualizado.
|
|
60
|
-
* Observação: usamos lastRenderedLines para saber quantas linhas mover
|
|
61
|
-
* o cursor para cima (isso evita mover para cima demais quando uma nova
|
|
62
|
-
* linha foi registrada).
|
|
63
|
-
*/
|
|
64
|
-
private static redrawDynamicLines;
|
|
65
|
-
/**
|
|
66
|
-
* Envolve a escrita de texto estático (logs normais) para não interferir
|
|
67
|
-
* com as linhas dinâmicas.
|
|
68
|
-
*/
|
|
69
|
-
private static writeStatic;
|
|
70
|
-
private static registerDynamicLine;
|
|
71
|
-
private static updateDynamicLine;
|
|
72
|
-
private static endDynamicLine;
|
|
73
|
-
static error(...args: any[]): void;
|
|
74
|
-
static warn(...args: any[]): void;
|
|
75
|
-
static info(...args: any[]): void;
|
|
76
|
-
static success(...args: any[]): void;
|
|
77
|
-
static debug(...args: any[]): void;
|
|
78
|
-
static logCustomLevel(levelName: string, without?: boolean, color?: Colors, ...args: any[]): void;
|
|
79
|
-
static logWithout(level: Levels, colors?: Colors, ...args: any[]): void;
|
|
80
|
-
static log(level: Levels, colors?: Colors, ...args: any[]): void;
|
|
81
|
-
static ask(question: string, defaultValue?: string): Promise<string>;
|
|
82
|
-
static confirm(message: string, defaultYes?: boolean): Promise<boolean>;
|
|
83
|
-
static table(data: Record<string, any> | Array<{
|
|
84
|
-
Field: string;
|
|
85
|
-
Value: any;
|
|
86
|
-
}>): void;
|
|
87
|
-
static box(content: string, options?: BoxenOptions): void;
|
|
88
|
-
/**
|
|
89
|
-
* Cria e retorna um controlador para uma linha dinâmica no console.
|
|
90
|
-
* @param initialContent O conteúdo inicial a ser exibido.
|
|
91
|
-
* @returns Uma instância de DynamicLine para controlar a linha.
|
|
92
|
-
*/
|
|
93
|
-
static dynamicLine(initialContent: string): DynamicLine;
|
|
94
|
-
}
|
package/dist/api/console.js
DELETED
|
@@ -1,294 +0,0 @@
|
|
|
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.Levels = exports.Colors = exports.DynamicLine = void 0;
|
|
7
|
-
/*
|
|
8
|
-
* This file is part of the HightJS Project.
|
|
9
|
-
* Copyright (c) 2025 itsmuzin
|
|
10
|
-
*
|
|
11
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
12
|
-
* you may not use this file except in compliance with the License.
|
|
13
|
-
* You may obtain a copy of the License at
|
|
14
|
-
*
|
|
15
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
16
|
-
*
|
|
17
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
18
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
19
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
20
|
-
* See the License for the specific language governing permissions and
|
|
21
|
-
* limitations under the License.
|
|
22
|
-
*/
|
|
23
|
-
const boxen_1 = __importDefault(require("boxen"));
|
|
24
|
-
const node_readline_1 = __importDefault(require("node:readline"));
|
|
25
|
-
/**
|
|
26
|
-
* Um "handle" para uma linha dinâmica. As instâncias desta classe
|
|
27
|
-
* são retornadas por `Console.dynamicLine()` e usadas para controlar
|
|
28
|
-
* o conteúdo da linha.
|
|
29
|
-
*/
|
|
30
|
-
class DynamicLine {
|
|
31
|
-
constructor(initialContent) {
|
|
32
|
-
// A ID é usada internamente pela classe Console para rastrear esta linha.
|
|
33
|
-
this._id = Symbol();
|
|
34
|
-
// Registra esta nova linha na classe Console para que ela seja renderizada.
|
|
35
|
-
Console['registerDynamicLine'](this._id, initialContent);
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Atualiza o conteúdo da linha no console.
|
|
39
|
-
* @param newContent O novo texto a ser exibido.
|
|
40
|
-
*/
|
|
41
|
-
update(newContent) {
|
|
42
|
-
Console['updateDynamicLine'](this._id, newContent);
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Finaliza a linha, opcionalmente com um texto final, e a torna estática.
|
|
46
|
-
* @param finalContent O texto final a ser exibido.
|
|
47
|
-
*/
|
|
48
|
-
end(finalContent) {
|
|
49
|
-
Console['endDynamicLine'](this._id, finalContent);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
exports.DynamicLine = DynamicLine;
|
|
53
|
-
var Colors;
|
|
54
|
-
(function (Colors) {
|
|
55
|
-
Colors["Reset"] = "\u001B[0m";
|
|
56
|
-
Colors["Bright"] = "\u001B[1m";
|
|
57
|
-
Colors["Dim"] = "\u001B[2m";
|
|
58
|
-
Colors["Underscore"] = "\u001B[4m";
|
|
59
|
-
Colors["Blink"] = "\u001B[5m";
|
|
60
|
-
Colors["Reverse"] = "\u001B[7m";
|
|
61
|
-
Colors["Hidden"] = "\u001B[8m";
|
|
62
|
-
Colors["FgBlack"] = "\u001B[30m";
|
|
63
|
-
Colors["FgRed"] = "\u001B[31m";
|
|
64
|
-
Colors["FgGreen"] = "\u001B[32m";
|
|
65
|
-
Colors["FgYellow"] = "\u001B[33m";
|
|
66
|
-
Colors["FgBlue"] = "\u001B[34m";
|
|
67
|
-
Colors["FgMagenta"] = "\u001B[35m";
|
|
68
|
-
Colors["FgCyan"] = "\u001B[36m";
|
|
69
|
-
Colors["FgWhite"] = "\u001B[37m";
|
|
70
|
-
Colors["FgGray"] = "\u001B[90m";
|
|
71
|
-
Colors["BgBlack"] = "\u001B[40m";
|
|
72
|
-
Colors["BgRed"] = "\u001B[41m";
|
|
73
|
-
Colors["BgGreen"] = "\u001B[42m";
|
|
74
|
-
Colors["BgYellow"] = "\u001B[43m";
|
|
75
|
-
Colors["BgBlue"] = "\u001B[44m";
|
|
76
|
-
Colors["BgMagenta"] = "\u001B[45m";
|
|
77
|
-
Colors["BgCyan"] = "\u001B[46m";
|
|
78
|
-
Colors["BgWhite"] = "\u001B[47m";
|
|
79
|
-
Colors["BgGray"] = "\u001B[100m";
|
|
80
|
-
})(Colors || (exports.Colors = Colors = {}));
|
|
81
|
-
var Levels;
|
|
82
|
-
(function (Levels) {
|
|
83
|
-
Levels["ERROR"] = "ERROR";
|
|
84
|
-
Levels["WARN"] = "WARN";
|
|
85
|
-
Levels["INFO"] = "INFO";
|
|
86
|
-
Levels["DEBUG"] = "DEBUG";
|
|
87
|
-
Levels["SUCCESS"] = "SUCCESS";
|
|
88
|
-
})(Levels || (exports.Levels = Levels = {}));
|
|
89
|
-
class Console {
|
|
90
|
-
// --- MÉTODOS PRIVADOS PARA GERENCIAR A RENDERIZAÇÃO ---
|
|
91
|
-
/**
|
|
92
|
-
* Limpa todas as linhas dinâmicas da tela e as redesenha com o conteúdo atualizado.
|
|
93
|
-
* Observação: usamos lastRenderedLines para saber quantas linhas mover
|
|
94
|
-
* o cursor para cima (isso evita mover para cima demais quando uma nova
|
|
95
|
-
* linha foi registrada).
|
|
96
|
-
*/
|
|
97
|
-
static redrawDynamicLines() {
|
|
98
|
-
const stream = process.stdout;
|
|
99
|
-
// Se anteriormente renderizamos N linhas, movemos o cursor para cima N
|
|
100
|
-
// para chegar ao topo do bloco dinâmico. Se N for 0, não movemos.
|
|
101
|
-
if (this.lastRenderedLines > 0) {
|
|
102
|
-
try {
|
|
103
|
-
node_readline_1.default.moveCursor(stream, 0, -this.lastRenderedLines);
|
|
104
|
-
}
|
|
105
|
-
catch (_e) {
|
|
106
|
-
// Em terminais estranhos a movimentação pode falhar — ignoramos.
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
// Posiciona no início da linha e limpa tudo abaixo (removendo o bloco antigo).
|
|
110
|
-
node_readline_1.default.cursorTo(stream, 0);
|
|
111
|
-
node_readline_1.default.clearScreenDown(stream);
|
|
112
|
-
// Reescreve as linhas com o conteúdo atualizado
|
|
113
|
-
if (this.activeLines.length > 0) {
|
|
114
|
-
stream.write(this.activeLines.map(l => l.content).join('\n') + '\n');
|
|
115
|
-
}
|
|
116
|
-
// Atualiza o contador de linhas que agora estão renderizadas.
|
|
117
|
-
this.lastRenderedLines = this.activeLines.length;
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Envolve a escrita de texto estático (logs normais) para não interferir
|
|
121
|
-
* com as linhas dinâmicas.
|
|
122
|
-
*/
|
|
123
|
-
static writeStatic(content) {
|
|
124
|
-
const stream = process.stdout;
|
|
125
|
-
// Se há um bloco dinâmico na tela, removemos (limpamos) ele antes de
|
|
126
|
-
// escrever o conteúdo estático — assim evitamos misturar a saída.
|
|
127
|
-
if (this.lastRenderedLines > 0) {
|
|
128
|
-
try {
|
|
129
|
-
node_readline_1.default.moveCursor(stream, 0, -this.lastRenderedLines);
|
|
130
|
-
}
|
|
131
|
-
catch (_e) { }
|
|
132
|
-
node_readline_1.default.cursorTo(stream, 0);
|
|
133
|
-
node_readline_1.default.clearScreenDown(stream);
|
|
134
|
-
}
|
|
135
|
-
// Garante que o conteúdo termine com quebra de linha.
|
|
136
|
-
if (!content.endsWith('\n'))
|
|
137
|
-
content += '\n';
|
|
138
|
-
stream.write(content);
|
|
139
|
-
// Depois de escrever o log estático, redesenhamos o bloco dinâmico abaixo dele.
|
|
140
|
-
if (this.activeLines.length > 0) {
|
|
141
|
-
stream.write(this.activeLines.map(l => l.content).join('\n') + '\n');
|
|
142
|
-
this.lastRenderedLines = this.activeLines.length;
|
|
143
|
-
}
|
|
144
|
-
else {
|
|
145
|
-
this.lastRenderedLines = 0;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
// --- MÉTODOS CHAMADOS PELA CLASSE DynamicLine ---
|
|
149
|
-
static registerDynamicLine(id, content) {
|
|
150
|
-
// Adiciona a nova linha à lista de linhas ativas
|
|
151
|
-
this.activeLines.push({ id, content });
|
|
152
|
-
// Redesenha todo o bloco (usa lastRenderedLines internamente)
|
|
153
|
-
this.redrawDynamicLines();
|
|
154
|
-
}
|
|
155
|
-
static updateDynamicLine(id, newContent) {
|
|
156
|
-
const line = this.activeLines.find(l => l.id === id);
|
|
157
|
-
if (line) {
|
|
158
|
-
line.content = newContent;
|
|
159
|
-
this.redrawDynamicLines();
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
static endDynamicLine(id, finalContent) {
|
|
163
|
-
const lineIndex = this.activeLines.findIndex(l => l.id === id);
|
|
164
|
-
if (lineIndex > -1) {
|
|
165
|
-
// Remove a linha da lista de ativas.
|
|
166
|
-
this.activeLines.splice(lineIndex, 1);
|
|
167
|
-
// Escreve o conteúdo final como uma linha estática.
|
|
168
|
-
// writeStatic cuidará de limpar e redesenhar as linhas dinâmicas
|
|
169
|
-
// restantes no lugar certo.
|
|
170
|
-
this.writeStatic(finalContent + '\n');
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
// --- MÉTODOS DE LOG PÚBLICOS (MODIFICADOS) ---
|
|
174
|
-
static error(...args) { this.log(Levels.ERROR, ...args); }
|
|
175
|
-
static warn(...args) { this.log(Levels.WARN, ...args); }
|
|
176
|
-
static info(...args) { this.log(Levels.INFO, ...args); }
|
|
177
|
-
static success(...args) { this.log(Levels.SUCCESS, ...args); }
|
|
178
|
-
static debug(...args) { this.log(Levels.DEBUG, ...args); }
|
|
179
|
-
static logCustomLevel(levelName, without = true, color, ...args) {
|
|
180
|
-
if (without) {
|
|
181
|
-
this.logWithout(levelName, color, ...args);
|
|
182
|
-
}
|
|
183
|
-
else {
|
|
184
|
-
this.log(levelName, color, ...args);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
static logWithout(level, colors, ...args) {
|
|
188
|
-
const color = colors ? colors : level === Levels.ERROR ? Colors.BgRed : level === Levels.WARN ? Colors.BgYellow : level === Levels.INFO ? Colors.BgMagenta : level === Levels.SUCCESS ? Colors.BgGreen : Colors.BgCyan;
|
|
189
|
-
let output = "";
|
|
190
|
-
for (const arg of args) {
|
|
191
|
-
let msg = (arg instanceof Error) ? arg.stack : (typeof arg === 'string') ? arg : JSON.stringify(arg, null, 2);
|
|
192
|
-
if (msg)
|
|
193
|
-
output += ` ${color} ${level} ${Colors.Reset} ${msg}\n`;
|
|
194
|
-
}
|
|
195
|
-
this.writeStatic(output);
|
|
196
|
-
}
|
|
197
|
-
static log(level, colors, ...args) {
|
|
198
|
-
const color = colors || level === Levels.ERROR ? Colors.BgRed : level === Levels.WARN ? Colors.BgYellow : level === Levels.INFO ? Colors.BgMagenta : level === Levels.SUCCESS ? Colors.BgGreen : Colors.BgCyan;
|
|
199
|
-
let output = "\n";
|
|
200
|
-
for (const arg of args) {
|
|
201
|
-
let msg = (arg instanceof Error) ? arg.stack : (typeof arg === 'string') ? arg : JSON.stringify(arg, null, 2);
|
|
202
|
-
if (msg)
|
|
203
|
-
output += ` ${color} ${level} ${Colors.Reset} ${msg}\n`;
|
|
204
|
-
}
|
|
205
|
-
this.writeStatic(output);
|
|
206
|
-
}
|
|
207
|
-
// --- OUTROS MÉTODOS ---
|
|
208
|
-
static async ask(question, defaultValue) {
|
|
209
|
-
// Garantimos que o bloco dinâmico atual seja temporariamente removido
|
|
210
|
-
// (redesenhado depois) para não poluir o prompt.
|
|
211
|
-
const stream = process.stdout;
|
|
212
|
-
if (this.lastRenderedLines > 0) {
|
|
213
|
-
try {
|
|
214
|
-
node_readline_1.default.moveCursor(stream, 0, -this.lastRenderedLines);
|
|
215
|
-
}
|
|
216
|
-
catch (_e) { }
|
|
217
|
-
node_readline_1.default.cursorTo(stream, 0);
|
|
218
|
-
node_readline_1.default.clearScreenDown(stream);
|
|
219
|
-
}
|
|
220
|
-
const readlineInterface = node_readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
221
|
-
const coloredQuestion = ` ${Colors.FgMagenta}${question}${Colors.Reset}`;
|
|
222
|
-
const defaultValuePart = defaultValue ? ` [${Colors.Reset}${Colors.FgCyan}${defaultValue}${Colors.Reset}]` : '';
|
|
223
|
-
const fullPrompt = `${coloredQuestion}${defaultValuePart}:\n > `;
|
|
224
|
-
return new Promise(resolve => {
|
|
225
|
-
readlineInterface.question(fullPrompt, ans => {
|
|
226
|
-
readlineInterface.close();
|
|
227
|
-
const value = ans.trim();
|
|
228
|
-
// Redesenha as linhas dinâmicas após o prompt
|
|
229
|
-
this.redrawDynamicLines();
|
|
230
|
-
resolve(value === '' && defaultValue !== undefined ? defaultValue : value);
|
|
231
|
-
});
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
static async confirm(message, defaultYes = false) {
|
|
235
|
-
const suffix = defaultYes ? 'yes' : 'no';
|
|
236
|
-
while (true) {
|
|
237
|
-
const ans = (await this.ask(message + " (yes/no)", suffix)).toLowerCase();
|
|
238
|
-
if (!ans)
|
|
239
|
-
return defaultYes;
|
|
240
|
-
if (['y', 'yes'].includes(ans))
|
|
241
|
-
return true;
|
|
242
|
-
if (['n', 'no'].includes(ans))
|
|
243
|
-
return false;
|
|
244
|
-
this.warn('Resposta inválida, digite y ou n.');
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
static table(data) {
|
|
248
|
-
let rows;
|
|
249
|
-
if (Array.isArray(data)) {
|
|
250
|
-
rows = data.map(row => ({ Field: String(row.Field), Value: String(row.Value) }));
|
|
251
|
-
}
|
|
252
|
-
else {
|
|
253
|
-
rows = Object.entries(data).map(([Field, Value]) => ({ Field, Value: String(Value) }));
|
|
254
|
-
}
|
|
255
|
-
const fieldLen = Math.max(...rows.map(r => r.Field.length), 'Field'.length);
|
|
256
|
-
const valueLen = Math.max(...rows.map(r => r.Value.length), 'Value'.length);
|
|
257
|
-
const sep = `+${'-'.repeat(fieldLen + 2)}+${'-'.repeat(valueLen + 2)}+`;
|
|
258
|
-
let output = sep + '\n';
|
|
259
|
-
output += `| ${Colors.FgGreen}${'Field'.padEnd(fieldLen)}${Colors.Reset} | ${Colors.FgGreen}${'Value'.padEnd(valueLen)}${Colors.Reset} |\n`;
|
|
260
|
-
output += sep + '\n';
|
|
261
|
-
for (const row of rows) {
|
|
262
|
-
output += `| ${row.Field.padEnd(fieldLen)} | ${row.Value.padEnd(valueLen)} |\n`;
|
|
263
|
-
}
|
|
264
|
-
output += sep + '\n';
|
|
265
|
-
this.writeStatic(output);
|
|
266
|
-
}
|
|
267
|
-
static box(content, options) {
|
|
268
|
-
const defaultOptions = {
|
|
269
|
-
padding: 1,
|
|
270
|
-
margin: 1,
|
|
271
|
-
borderStyle: 'round',
|
|
272
|
-
borderColor: 'whiteBright',
|
|
273
|
-
titleAlignment: 'left',
|
|
274
|
-
};
|
|
275
|
-
const finalOptions = { ...defaultOptions, ...options };
|
|
276
|
-
const boxedContent = (0, boxen_1.default)(content, finalOptions);
|
|
277
|
-
this.writeStatic(boxedContent + '\n');
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* Cria e retorna um controlador para uma linha dinâmica no console.
|
|
281
|
-
* @param initialContent O conteúdo inicial a ser exibido.
|
|
282
|
-
* @returns Uma instância de DynamicLine para controlar a linha.
|
|
283
|
-
*/
|
|
284
|
-
static dynamicLine(initialContent) {
|
|
285
|
-
return new DynamicLine(initialContent);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
// Armazena o estado de todas as linhas dinâmicas ativas
|
|
289
|
-
Console.activeLines = [];
|
|
290
|
-
// Quantas linhas foram efetivamente renderizadas na última operação.
|
|
291
|
-
// Usamos esse contador para evitar off-by-one ao mover o cursor para
|
|
292
|
-
// redesenhar o bloco dinâmico.
|
|
293
|
-
Console.lastRenderedLines = 0;
|
|
294
|
-
exports.default = Console;
|