hightjs 0.2.53 → 0.3.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/dist/adapters/express.js +1 -1
- package/dist/api/console.d.ts +6 -4
- package/dist/api/console.js +27 -9
- package/dist/auth/components.js +16 -0
- package/dist/auth/core.js +16 -0
- package/dist/auth/index.js +16 -0
- package/dist/auth/jwt.js +16 -0
- package/dist/auth/providers/index.js +16 -0
- package/dist/auth/providers.js +16 -0
- package/dist/auth/react/index.js +16 -0
- package/dist/auth/react.js +16 -0
- package/dist/auth/routes.js +16 -0
- package/dist/auth/types.js +16 -0
- package/dist/bin/hightjs.js +68 -11
- package/dist/builder.js +16 -0
- package/dist/client/clientRouter.js +16 -0
- package/dist/client/entry.client.js +16 -0
- package/dist/client.js +16 -0
- package/dist/helpers.d.ts +7 -6
- package/dist/helpers.js +284 -198
- package/dist/hotReload.js +23 -7
- package/dist/index.js +49 -25
- package/dist/router.js +16 -0
- package/dist/types.d.ts +6 -0
- package/docs/cli.md +4 -5
- package/package.json +1 -1
- package/src/adapters/express.ts +2 -1
- package/src/api/console.ts +12 -10
- package/src/auth/routes.ts +1 -0
- package/src/bin/hightjs.js +61 -11
- package/src/helpers.ts +311 -216
- package/src/hotReload.ts +8 -8
- package/src/index.ts +38 -25
- package/src/types.ts +6 -0
package/dist/helpers.js
CHANGED
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* This file is part of the HightJS Project.
|
|
4
|
+
* Copyright (c) 2025 itsmuzin
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
2
18
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
19
|
if (k2 === undefined) k2 = k;
|
|
4
20
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
@@ -37,234 +53,157 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
37
53
|
};
|
|
38
54
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
55
|
exports.app = app;
|
|
40
|
-
//
|
|
41
|
-
const
|
|
56
|
+
// Imports Nativos do Node.js (movidos para o topo)
|
|
57
|
+
const http_1 = __importDefault(require("http"));
|
|
42
58
|
const os_1 = __importDefault(require("os"));
|
|
59
|
+
const url_1 = require("url"); // API moderna, substitui 'querystring'
|
|
60
|
+
// Helpers para integração com diferentes frameworks
|
|
61
|
+
const index_1 = __importStar(require("./index")); // Importando o tipo
|
|
43
62
|
const console_1 = __importStar(require("./api/console"));
|
|
63
|
+
const https_1 = __importDefault(require("https")); // <-- ADICIONAR
|
|
64
|
+
const fs_1 = __importDefault(require("fs")); // <-- ADICIONAR
|
|
65
|
+
// --- Helpers ---
|
|
66
|
+
/**
|
|
67
|
+
* Encontra o IP externo local (rede)
|
|
68
|
+
*/
|
|
44
69
|
function getLocalExternalIp() {
|
|
45
70
|
const interfaces = os_1.default.networkInterfaces();
|
|
46
71
|
for (const name of Object.keys(interfaces)) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
72
|
+
const ifaceList = interfaces[name];
|
|
73
|
+
if (ifaceList) {
|
|
74
|
+
for (const iface of ifaceList) {
|
|
75
|
+
if (iface.family === 'IPv4' && !iface.internal) {
|
|
76
|
+
return iface.address;
|
|
77
|
+
}
|
|
50
78
|
}
|
|
51
79
|
}
|
|
52
80
|
}
|
|
53
|
-
return 'localhost';
|
|
81
|
+
return 'localhost'; // Fallback
|
|
54
82
|
}
|
|
55
83
|
const sendBox = (options) => {
|
|
56
84
|
const isDev = options.dev ? "Rodando em modo de desenvolvimento" : null;
|
|
85
|
+
// 1. Verifica se o SSL está ativado (baseado na mesma lógica do initNativeServer)
|
|
86
|
+
const isSSL = options.ssl && options.ssl.key && options.ssl.cert;
|
|
87
|
+
const protocol = isSSL ? 'https' : 'http';
|
|
88
|
+
const localIp = getLocalExternalIp(); // Assume que getLocalExternalIp() existe
|
|
89
|
+
// 2. Monta as mensagens com o protocolo correto
|
|
57
90
|
const messages = [
|
|
58
|
-
` ${console_1.Colors.
|
|
59
|
-
` ${console_1.Colors.FgMagenta}● ${console_1.Colors.Reset}Rede: ${console_1.Colors.FgGreen}http://${getLocalExternalIp()}:${options.port}${console_1.Colors.Reset}`,
|
|
91
|
+
` ${console_1.Colors.FgGray}┃${console_1.Colors.Reset} Local: ${console_1.Colors.FgGreen}${protocol}://localhost:${options.port}${console_1.Colors.Reset}`,
|
|
60
92
|
];
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
console_1.default.box(messages.join("\n"), { title: "Acesse o HightJS em:" });
|
|
65
|
-
};
|
|
66
|
-
exports.default = app;
|
|
67
|
-
function app(options = {}) {
|
|
68
|
-
const framework = options.framework || 'native'; // Mudando o padrão para 'native'
|
|
69
|
-
index_1.FrameworkAdapterFactory.setFramework(framework);
|
|
70
|
-
const hwebApp = (0, index_1.default)(options);
|
|
71
|
-
return {
|
|
72
|
-
...hwebApp,
|
|
73
|
-
/**
|
|
74
|
-
* Integra com uma aplicação de qualquer framework (Express, Fastify, etc)
|
|
75
|
-
*/
|
|
76
|
-
integrate: async (serverApp) => {
|
|
77
|
-
await hwebApp.prepare();
|
|
78
|
-
const handler = hwebApp.getRequestHandler();
|
|
79
|
-
if (framework === 'express') {
|
|
80
|
-
// Express integration
|
|
81
|
-
serverApp.use(handler);
|
|
82
|
-
hwebApp.setupWebSocket(serverApp);
|
|
83
|
-
}
|
|
84
|
-
else if (framework === 'fastify') {
|
|
85
|
-
// Fastify integration
|
|
86
|
-
await serverApp.register(async (fastify) => {
|
|
87
|
-
fastify.all('*', handler);
|
|
88
|
-
});
|
|
89
|
-
hwebApp.setupWebSocket(serverApp);
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
92
|
-
// Generic integration - assume Express-like
|
|
93
|
-
serverApp.use(handler);
|
|
94
|
-
hwebApp.setupWebSocket(serverApp);
|
|
95
|
-
}
|
|
96
|
-
hwebApp.executeInstrumentation();
|
|
97
|
-
return serverApp;
|
|
98
|
-
},
|
|
99
|
-
/**
|
|
100
|
-
* Inicia um servidor HightJS fechado (o usuário não tem acesso ao framework)
|
|
101
|
-
*/
|
|
102
|
-
init: async () => {
|
|
103
|
-
console.log(`${console_1.Colors.FgMagenta}
|
|
104
|
-
_ _ _ _ _ _ _____
|
|
105
|
-
| | | (_) | | | | | |/ ____|
|
|
106
|
-
| |__| |_ __ _| |__ | |_ | | (___
|
|
107
|
-
| __ | |/ _\` | '_ \\| __| _ | |\\___ \\
|
|
108
|
-
| | | | | (_| | | | | |_ | |__| |____) |
|
|
109
|
-
|_| |_|_|\\__, |_| |_|\\__| \\____/|_____/
|
|
110
|
-
__/ |
|
|
111
|
-
|___/ ${console_1.Colors.Reset}`);
|
|
112
|
-
const actualPort = options.port || 3000;
|
|
113
|
-
const actualHostname = options.hostname || "0.0.0.0";
|
|
114
|
-
if (framework === 'express') {
|
|
115
|
-
return await initExpressServer(hwebApp, options, actualPort, actualHostname);
|
|
116
|
-
}
|
|
117
|
-
else if (framework === 'fastify') {
|
|
118
|
-
return await initFastifyServer(hwebApp, options, actualPort, actualHostname);
|
|
119
|
-
}
|
|
120
|
-
else {
|
|
121
|
-
// Default to Native
|
|
122
|
-
return await initNativeServer(hwebApp, options, actualPort, actualHostname);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Inicializa servidor Express fechado
|
|
129
|
-
*/
|
|
130
|
-
async function initExpressServer(hwebApp, options, port, hostname) {
|
|
131
|
-
const msg = console_1.default.dynamicLine(` ${console_1.Colors.FgCyan}● ${console_1.Colors.Reset}Iniciando HightJS com Express...`);
|
|
132
|
-
const express = require('express');
|
|
133
|
-
const app = express();
|
|
134
|
-
// Middlewares básicos para Express
|
|
135
|
-
app.use(express.json());
|
|
136
|
-
app.use(express.urlencoded({ extended: true }));
|
|
137
|
-
// Cookie parser se disponível
|
|
138
|
-
try {
|
|
139
|
-
const cookieParser = require('cookie-parser');
|
|
140
|
-
app.use(cookieParser());
|
|
141
|
-
}
|
|
142
|
-
catch (e) {
|
|
143
|
-
console_1.default.error("Não foi possivel achar cookie-parser");
|
|
144
|
-
}
|
|
145
|
-
await hwebApp.prepare();
|
|
146
|
-
const handler = hwebApp.getRequestHandler();
|
|
147
|
-
app.use(handler);
|
|
148
|
-
const server = app.listen(port, hostname, () => {
|
|
149
|
-
sendBox({ ...options, port });
|
|
150
|
-
msg.end(` ${console_1.Colors.FgCyan}● ${console_1.Colors.Reset}Servidor Express iniciado (compatibilidade)`);
|
|
151
|
-
});
|
|
152
|
-
// Configura WebSocket para hot reload
|
|
153
|
-
hwebApp.setupWebSocket(server);
|
|
154
|
-
hwebApp.executeInstrumentation();
|
|
155
|
-
return server;
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* Inicializa servidor Fastify fechado
|
|
159
|
-
*/
|
|
160
|
-
async function initFastifyServer(hwebApp, options, port, hostname) {
|
|
161
|
-
const msg = console_1.default.dynamicLine(` ${console_1.Colors.FgCyan}● ${console_1.Colors.Reset}Iniciando HightJS com Fastify...`);
|
|
162
|
-
const fastify = require('fastify')({ logger: false });
|
|
163
|
-
// Registra plugins básicos para Fastify
|
|
164
|
-
try {
|
|
165
|
-
await fastify.register(require('@fastify/cookie'));
|
|
166
|
-
}
|
|
167
|
-
catch (e) {
|
|
168
|
-
console_1.default.error("Não foi possivel achar @fastify/cookie");
|
|
93
|
+
// Só adiciona a rede se o IP local for encontrado
|
|
94
|
+
if (localIp) {
|
|
95
|
+
messages.push(` ${console_1.Colors.FgGray}┃${console_1.Colors.Reset} Rede: ${console_1.Colors.FgGreen}${protocol}://${localIp}:${options.port}${console_1.Colors.Reset}`);
|
|
169
96
|
}
|
|
170
|
-
|
|
171
|
-
|
|
97
|
+
if (isDev) {
|
|
98
|
+
messages.push(` ${console_1.Colors.FgGray}┃${console_1.Colors.Reset} ${isDev}`);
|
|
172
99
|
}
|
|
173
|
-
|
|
174
|
-
|
|
100
|
+
// Adiciona aviso de redirecionamento se estiver em modo SSL
|
|
101
|
+
if (isSSL && options.ssl?.redirectPort) {
|
|
102
|
+
messages.push(` ${console_1.Colors.FgGray}┃${console_1.Colors.Reset} HTTP (porta ${options.ssl?.redirectPort}) está redirecionando para HTTPS.`);
|
|
175
103
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
// Registra o handler do hweb
|
|
179
|
-
await fastify.register(async (fastify) => {
|
|
180
|
-
fastify.all('*', handler);
|
|
181
|
-
});
|
|
182
|
-
hwebApp.setupWebSocket(fastify);
|
|
183
|
-
const address = await fastify.listen({ port, host: hostname });
|
|
184
|
-
sendBox({ ...options, port });
|
|
185
|
-
msg.end(` ${console_1.Colors.FgCyan}● ${console_1.Colors.Reset}Servidor Fastify iniciado (compatibilidade)`);
|
|
186
|
-
hwebApp.executeInstrumentation();
|
|
187
|
-
return fastify;
|
|
188
|
-
}
|
|
104
|
+
console_1.default.box(messages.join("\n"), { title: "Acesse em:" });
|
|
105
|
+
};
|
|
189
106
|
/**
|
|
190
|
-
*
|
|
107
|
+
* Middleware para parsing do body com proteções de segurança (versão melhorada).
|
|
108
|
+
* Rejeita a promise em caso de erro de parsing ou estouro de limite.
|
|
191
109
|
*/
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const
|
|
195
|
-
const
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
110
|
+
const parseBody = (req) => {
|
|
111
|
+
// Constantes para limites de segurança
|
|
112
|
+
const MAX_BODY_SIZE = 10 * 1024 * 1024; // 10MB limite total
|
|
113
|
+
const MAX_JSON_SIZE = 1 * 1024 * 1024; // 1MB limite para JSON
|
|
114
|
+
const BODY_TIMEOUT = 30000; // 30 segundos
|
|
115
|
+
return new Promise((resolve, reject) => {
|
|
116
|
+
if (req.method === 'GET' || req.method === 'HEAD') {
|
|
117
|
+
resolve(null);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
let body = '';
|
|
121
|
+
let totalSize = 0;
|
|
122
|
+
// Timeout para requisições lentas
|
|
123
|
+
const timeout = setTimeout(() => {
|
|
124
|
+
req.destroy();
|
|
125
|
+
reject(new Error('Request body timeout'));
|
|
126
|
+
}, BODY_TIMEOUT);
|
|
127
|
+
req.on('data', (chunk) => {
|
|
128
|
+
totalSize += chunk.length;
|
|
129
|
+
// Proteção contra DoS (Payload Too Large)
|
|
130
|
+
if (totalSize > MAX_BODY_SIZE) {
|
|
131
|
+
clearTimeout(timeout);
|
|
132
|
+
req.destroy();
|
|
133
|
+
reject(new Error('Request body too large'));
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
body += chunk.toString();
|
|
137
|
+
});
|
|
138
|
+
req.on('end', () => {
|
|
139
|
+
clearTimeout(timeout);
|
|
140
|
+
if (!body) {
|
|
203
141
|
resolve(null);
|
|
204
142
|
return;
|
|
205
143
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
reject(new Error('Request timeout'));
|
|
213
|
-
}, 30000); // 30 segundos
|
|
214
|
-
req.on('data', (chunk) => {
|
|
215
|
-
totalSize += chunk.length;
|
|
216
|
-
// Proteção contra ataques de DoS por body muito grande
|
|
217
|
-
if (totalSize > maxBodySize) {
|
|
218
|
-
clearTimeout(timeout);
|
|
219
|
-
req.destroy();
|
|
220
|
-
reject(new Error('Request body too large'));
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
body += chunk.toString();
|
|
224
|
-
});
|
|
225
|
-
req.on('end', () => {
|
|
226
|
-
clearTimeout(timeout);
|
|
227
|
-
try {
|
|
228
|
-
const contentType = req.headers['content-type'] || '';
|
|
229
|
-
if (contentType.includes('application/json')) {
|
|
230
|
-
// Validação adicional para JSON
|
|
231
|
-
if (body.length > 1024 * 1024) { // 1MB limite para JSON
|
|
232
|
-
reject(new Error('JSON body too large'));
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
resolve(JSON.parse(body));
|
|
144
|
+
try {
|
|
145
|
+
const contentType = req.headers['content-type'] || '';
|
|
146
|
+
if (contentType.includes('application/json')) {
|
|
147
|
+
if (body.length > MAX_JSON_SIZE) {
|
|
148
|
+
reject(new Error('JSON body too large'));
|
|
149
|
+
return;
|
|
236
150
|
}
|
|
237
|
-
|
|
238
|
-
|
|
151
|
+
// Rejeita promise se o JSON for inválido
|
|
152
|
+
try {
|
|
153
|
+
resolve(JSON.parse(body));
|
|
239
154
|
}
|
|
240
|
-
|
|
241
|
-
|
|
155
|
+
catch (e) {
|
|
156
|
+
reject(new Error('Invalid JSON body'));
|
|
242
157
|
}
|
|
243
158
|
}
|
|
244
|
-
|
|
245
|
-
|
|
159
|
+
else if (contentType.includes('application/x-www-form-urlencoded')) {
|
|
160
|
+
// Usa API moderna URLSearchParams (segura contra prototype pollution)
|
|
161
|
+
resolve(Object.fromEntries(new url_1.URLSearchParams(body)));
|
|
246
162
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
163
|
+
else {
|
|
164
|
+
resolve(body); // Fallback para texto plano
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
// Pega qualquer outro erro síncrono
|
|
250
169
|
reject(error);
|
|
251
|
-
}
|
|
170
|
+
}
|
|
252
171
|
});
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
172
|
+
req.on('error', (error) => {
|
|
173
|
+
clearTimeout(timeout);
|
|
174
|
+
reject(error);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* Inicializa servidor nativo do HightJS usando HTTP ou HTTPS
|
|
180
|
+
*/
|
|
181
|
+
async function initNativeServer(hwebApp, options, port, hostname) {
|
|
182
|
+
const time = Date.now();
|
|
183
|
+
await hwebApp.prepare();
|
|
184
|
+
const handler = hwebApp.getRequestHandler();
|
|
185
|
+
const msg = console_1.default.dynamicLine(` ${console_1.Colors.BgYellow} ready ${console_1.Colors.Reset} ${console_1.Colors.Bright}Iniciando HightJS na porta ${options.port}${console_1.Colors.Reset}`);
|
|
186
|
+
// --- LÓGICA DO LISTENER (REUTILIZÁVEL) ---
|
|
187
|
+
// Extraímos a lógica principal para uma variável
|
|
188
|
+
// para que possa ser usada tanto pelo servidor HTTP quanto HTTPS.
|
|
189
|
+
const requestListener = async (req, res) => {
|
|
256
190
|
// Configurações de segurança básicas
|
|
257
191
|
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
258
192
|
res.setHeader('X-Frame-Options', 'DENY');
|
|
259
193
|
res.setHeader('X-XSS-Protection', '1; mode=block');
|
|
260
194
|
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
|
|
261
|
-
//
|
|
195
|
+
// IMPORTANTE: Adiciona HSTS (Strict-Transport-Security) se estiver em modo SSL
|
|
196
|
+
// Isso força o navegador a usar HTTPS no futuro.
|
|
197
|
+
if (options.ssl) {
|
|
198
|
+
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
|
|
199
|
+
}
|
|
200
|
+
// Timeout por requisição
|
|
262
201
|
req.setTimeout(30000, () => {
|
|
263
202
|
res.statusCode = 408; // Request Timeout
|
|
264
203
|
res.end('Request timeout');
|
|
265
204
|
});
|
|
266
205
|
try {
|
|
267
|
-
// Validação básica de URL
|
|
206
|
+
// Validação básica de URL
|
|
268
207
|
const url = req.url || '/';
|
|
269
208
|
if (url.length > 2048) {
|
|
270
209
|
res.statusCode = 414; // URI Too Long
|
|
@@ -272,16 +211,22 @@ async function initNativeServer(hwebApp, options, port, hostname) {
|
|
|
272
211
|
return;
|
|
273
212
|
}
|
|
274
213
|
// Parse do body com proteções
|
|
275
|
-
req.body = await parseBody(req);
|
|
276
|
-
// Adiciona host se não existir
|
|
214
|
+
req.body = await parseBody(req); // Assumindo que parseBody existe
|
|
215
|
+
// Adiciona host se não existir (necessário para `new URL`)
|
|
277
216
|
req.headers.host = req.headers.host || `localhost:${port}`;
|
|
278
217
|
// Chama o handler do HightJS
|
|
279
218
|
await handler(req, res);
|
|
280
219
|
}
|
|
281
220
|
catch (error) {
|
|
282
|
-
|
|
221
|
+
// Log do erro no servidor
|
|
222
|
+
if (error instanceof Error) {
|
|
223
|
+
console_1.default.error(`Erro no servidor nativo: ${error.message}`);
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
console_1.default.error('Erro desconhecido no servidor nativo:', error);
|
|
227
|
+
}
|
|
228
|
+
// Tratamento de erro (idêntico ao seu original)
|
|
283
229
|
if (!res.headersSent) {
|
|
284
|
-
res.statusCode = 500;
|
|
285
230
|
res.setHeader('Content-Type', 'text/plain');
|
|
286
231
|
if (error instanceof Error) {
|
|
287
232
|
if (error.message.includes('too large')) {
|
|
@@ -292,27 +237,168 @@ async function initNativeServer(hwebApp, options, port, hostname) {
|
|
|
292
237
|
res.statusCode = 408; // Request Timeout
|
|
293
238
|
res.end('Request timeout');
|
|
294
239
|
}
|
|
240
|
+
else if (error.message.includes('Invalid JSON')) {
|
|
241
|
+
res.statusCode = 400; // Bad Request
|
|
242
|
+
res.end('Invalid JSON body');
|
|
243
|
+
}
|
|
295
244
|
else {
|
|
245
|
+
res.statusCode = 500;
|
|
296
246
|
res.end('Internal server error');
|
|
297
247
|
}
|
|
298
248
|
}
|
|
299
249
|
else {
|
|
250
|
+
res.statusCode = 500;
|
|
300
251
|
res.end('Internal server error');
|
|
301
252
|
}
|
|
302
253
|
}
|
|
303
254
|
}
|
|
304
|
-
}
|
|
305
|
-
//
|
|
255
|
+
};
|
|
256
|
+
// --- FIM DO LISTENER ---
|
|
257
|
+
let server; // O tipo do servidor pode variar
|
|
258
|
+
const isSSL = options.ssl && options.ssl.key && options.ssl.cert;
|
|
259
|
+
if (isSSL && options.ssl) {
|
|
260
|
+
const sslOptions = {
|
|
261
|
+
key: fs_1.default.readFileSync(options.ssl.key),
|
|
262
|
+
cert: fs_1.default.readFileSync(options.ssl.cert),
|
|
263
|
+
ca: options.ssl.ca ? fs_1.default.readFileSync(options.ssl.ca) : undefined
|
|
264
|
+
};
|
|
265
|
+
// 1. Cria o servidor HTTPS principal
|
|
266
|
+
server = https_1.default.createServer(sslOptions, requestListener); // (any para contornar HWebIncomingMessage)
|
|
267
|
+
// 2. Cria o servidor de REDIRECIONAMENTO (HTTP -> HTTPS)
|
|
268
|
+
const httpRedirectPort = options.ssl.redirectPort;
|
|
269
|
+
http_1.default.createServer((req, res) => {
|
|
270
|
+
const host = req.headers['host'] || hostname;
|
|
271
|
+
// Remove a porta do host (ex: meusite.com:80)
|
|
272
|
+
const hostWithoutPort = host.split(':')[0];
|
|
273
|
+
// Monta a URL de redirecionamento
|
|
274
|
+
let redirectUrl = `https://${hostWithoutPort}`;
|
|
275
|
+
// Adiciona a porta HTTPS apenas se não for a padrão (443)
|
|
276
|
+
if (port !== 443) {
|
|
277
|
+
redirectUrl += `:${port}`;
|
|
278
|
+
}
|
|
279
|
+
redirectUrl += req.url || '/';
|
|
280
|
+
res.writeHead(301, { 'Location': redirectUrl });
|
|
281
|
+
res.end();
|
|
282
|
+
}).listen(httpRedirectPort, hostname, () => { });
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
// --- MODO HTTP (Original) ---
|
|
286
|
+
// Cria o servidor HTTP nativo
|
|
287
|
+
server = http_1.default.createServer(requestListener); // (any para contornar HWebIncomingMessage)
|
|
288
|
+
}
|
|
289
|
+
// Configurações de segurança do servidor (Comum a ambos)
|
|
306
290
|
server.setTimeout(35000); // Timeout geral do servidor
|
|
307
291
|
server.maxHeadersCount = 100; // Limita número de headers
|
|
308
292
|
server.headersTimeout = 60000; // Timeout para headers
|
|
309
293
|
server.requestTimeout = 30000; // Timeout para requisições
|
|
310
294
|
server.listen(port, hostname, () => {
|
|
311
295
|
sendBox({ ...options, port });
|
|
312
|
-
msg.end(` ${console_1.Colors.
|
|
296
|
+
msg.end(` ${console_1.Colors.BgGreen} ready ${console_1.Colors.Reset} ${console_1.Colors.Bright}Pronto na porta ${console_1.Colors.BgGreen} ${options.port} ${console_1.Colors.Reset}${console_1.Colors.Bright} em ${Date.now() - time}ms${console_1.Colors.Reset}\n`);
|
|
313
297
|
});
|
|
314
|
-
// Configura WebSocket para hot reload
|
|
298
|
+
// Configura WebSocket para hot reload (Comum a ambos)
|
|
315
299
|
hwebApp.setupWebSocket(server);
|
|
316
300
|
hwebApp.executeInstrumentation();
|
|
317
301
|
return server;
|
|
318
302
|
}
|
|
303
|
+
// --- Função Principal ---
|
|
304
|
+
function app(options = {}) {
|
|
305
|
+
const framework = options.framework || 'native';
|
|
306
|
+
index_1.FrameworkAdapterFactory.setFramework(framework);
|
|
307
|
+
// Tipando a app principal do hweb
|
|
308
|
+
const hwebApp = (0, index_1.default)(options);
|
|
309
|
+
return {
|
|
310
|
+
...hwebApp,
|
|
311
|
+
/**
|
|
312
|
+
* Integra com uma aplicação de qualquer framework (Express, Fastify, etc)
|
|
313
|
+
* O 'serverApp: any' é mantido para flexibilidade, já que pode ser de tipos diferentes.
|
|
314
|
+
*/
|
|
315
|
+
integrate: async (serverApp) => {
|
|
316
|
+
await hwebApp.prepare();
|
|
317
|
+
const handler = hwebApp.getRequestHandler();
|
|
318
|
+
// O framework é setado nas opções do hweb, que deve
|
|
319
|
+
// retornar o handler correto em getRequestHandler()
|
|
320
|
+
// A lógica de integração original parece correta.
|
|
321
|
+
if (framework === 'express') {
|
|
322
|
+
const express = require('express');
|
|
323
|
+
try {
|
|
324
|
+
const cookieParser = require('cookie-parser');
|
|
325
|
+
serverApp.use(cookieParser());
|
|
326
|
+
}
|
|
327
|
+
catch (e) {
|
|
328
|
+
console_1.default.error("Não foi possivel achar cookie-parser");
|
|
329
|
+
}
|
|
330
|
+
serverApp.use(express.json());
|
|
331
|
+
serverApp.use(express.urlencoded({ extended: true }));
|
|
332
|
+
serverApp.use(handler);
|
|
333
|
+
hwebApp.setupWebSocket(serverApp);
|
|
334
|
+
}
|
|
335
|
+
else if (framework === 'fastify') {
|
|
336
|
+
try {
|
|
337
|
+
await serverApp.register(require('@fastify/cookie'));
|
|
338
|
+
}
|
|
339
|
+
catch (e) {
|
|
340
|
+
console_1.default.error("Não foi possivel achar @fastify/cookie");
|
|
341
|
+
}
|
|
342
|
+
try {
|
|
343
|
+
await serverApp.register(require('@fastify/formbody'));
|
|
344
|
+
}
|
|
345
|
+
catch (e) {
|
|
346
|
+
console_1.default.error("Não foi possivel achar @fastify/formbody");
|
|
347
|
+
}
|
|
348
|
+
await serverApp.register(async (fastify) => {
|
|
349
|
+
fastify.all('*', handler);
|
|
350
|
+
});
|
|
351
|
+
hwebApp.setupWebSocket(serverApp);
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
// Generic integration (assume Express-like)
|
|
355
|
+
serverApp.use(handler);
|
|
356
|
+
hwebApp.setupWebSocket(serverApp);
|
|
357
|
+
}
|
|
358
|
+
hwebApp.executeInstrumentation();
|
|
359
|
+
return serverApp;
|
|
360
|
+
},
|
|
361
|
+
/**
|
|
362
|
+
* Inicia um servidor HightJS fechado (o usuário não tem acesso ao framework)
|
|
363
|
+
*/
|
|
364
|
+
init: async () => {
|
|
365
|
+
const currentVersion = require('../package.json').version;
|
|
366
|
+
async function verifyVersion() {
|
|
367
|
+
// node fetch
|
|
368
|
+
try {
|
|
369
|
+
const response = await fetch('https://registry.npmjs.org/hightjs/latest');
|
|
370
|
+
const data = await response.json();
|
|
371
|
+
return data.version;
|
|
372
|
+
}
|
|
373
|
+
catch (error) {
|
|
374
|
+
console_1.default.error('Não foi possível verificar a versão mais recente do HightJS:', error);
|
|
375
|
+
return currentVersion; // Retorna a versão atual em caso de erro
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
const latestVersion = await verifyVersion();
|
|
379
|
+
const isUpToDate = latestVersion === currentVersion;
|
|
380
|
+
let message;
|
|
381
|
+
if (!isUpToDate) {
|
|
382
|
+
message = `${console_1.Colors.FgGreen} Há uma nova versão disponível (v${latestVersion})${console_1.Colors.FgMagenta}`;
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
message = `${console_1.Colors.FgGreen} Você está na versão mais recente${console_1.Colors.FgMagenta}`;
|
|
386
|
+
}
|
|
387
|
+
console.log(`${console_1.Colors.FgMagenta}
|
|
388
|
+
__ ___ ${console_1.Colors.FgGreen} __ ${console_1.Colors.FgMagenta}
|
|
389
|
+
|__| | / _\` |__| | ${console_1.Colors.FgGreen} | /__\` ${console_1.Colors.FgMagenta} ${console_1.Colors.FgMagenta}HightJS ${console_1.Colors.FgGray}(v${require('../package.json').version}) - itsmuzin${console_1.Colors.FgMagenta}
|
|
390
|
+
| | | \\__> | | | ${console_1.Colors.FgGreen}\\__/ .__/ ${message}
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
${console_1.Colors.Reset}`);
|
|
394
|
+
const actualPort = options.port || 3000;
|
|
395
|
+
const actualHostname = options.hostname || "0.0.0.0";
|
|
396
|
+
if (framework !== 'native') {
|
|
397
|
+
console_1.default.warn(`O framework "${framework}" foi selecionado, mas o método init() só funciona com o framework "native". Iniciando servidor nativo...`);
|
|
398
|
+
}
|
|
399
|
+
return await initNativeServer(hwebApp, options, actualPort, actualHostname);
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
// Exporta a função 'app' como nomeada e também como padrão
|
|
404
|
+
exports.default = app;
|
package/dist/hotReload.js
CHANGED
|
@@ -34,6 +34,22 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.HotReloadManager = void 0;
|
|
37
|
+
/*
|
|
38
|
+
* This file is part of the HightJS Project.
|
|
39
|
+
* Copyright (c) 2025 itsmuzin
|
|
40
|
+
*
|
|
41
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
42
|
+
* you may not use this file except in compliance with the License.
|
|
43
|
+
* You may obtain a copy of the License at
|
|
44
|
+
*
|
|
45
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
46
|
+
*
|
|
47
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
48
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
49
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
50
|
+
* See the License for the specific language governing permissions and
|
|
51
|
+
* limitations under the License.
|
|
52
|
+
*/
|
|
37
53
|
const ws_1 = require("ws");
|
|
38
54
|
const chokidar = __importStar(require("chokidar"));
|
|
39
55
|
const path = __importStar(require("path"));
|
|
@@ -108,10 +124,10 @@ class HotReloadManager {
|
|
|
108
124
|
this.cleanupClient(ws);
|
|
109
125
|
});
|
|
110
126
|
ws.on('error', (error) => {
|
|
111
|
-
console_1.default.logWithout(console_1.Levels.ERROR, `WebSocket error: ${error.message}`);
|
|
127
|
+
console_1.default.logWithout(console_1.Levels.ERROR, console_1.Colors.BgRed, `WebSocket error: ${error.message}`);
|
|
112
128
|
this.cleanupClient(ws);
|
|
113
129
|
});
|
|
114
|
-
console_1.default.logWithout(console_1.Levels.INFO, '🔌 Hot-reload cliente conectado');
|
|
130
|
+
console_1.default.logWithout(console_1.Levels.INFO, console_1.Colors.BgRed, '🔌 Hot-reload cliente conectado');
|
|
115
131
|
});
|
|
116
132
|
}
|
|
117
133
|
cleanupClient(ws) {
|
|
@@ -170,7 +186,7 @@ class HotReloadManager {
|
|
|
170
186
|
};
|
|
171
187
|
}
|
|
172
188
|
async handleAnySrcChange(filePath) {
|
|
173
|
-
console_1.default.logWithout(console_1.Levels.INFO, `🔄 Arquivo alterado: ${path.basename(filePath)}`);
|
|
189
|
+
console_1.default.logWithout(console_1.Levels.INFO, console_1.Colors.BgRed, `🔄 Arquivo alterado: ${path.basename(filePath)}`);
|
|
174
190
|
// Detecta se é arquivo de frontend ou backend
|
|
175
191
|
const isFrontendFile = filePath.includes(path.join('src', 'web', 'routes')) ||
|
|
176
192
|
filePath.includes(path.join('src', 'web', 'components')) ||
|
|
@@ -194,19 +210,19 @@ class HotReloadManager {
|
|
|
194
210
|
}
|
|
195
211
|
// Se for arquivo de frontend, notifica o cliente para recarregar a página
|
|
196
212
|
if (isFrontendFile) {
|
|
197
|
-
console_1.default.logWithout(console_1.Levels.INFO, `📄 Recarregando frontend...`);
|
|
213
|
+
console_1.default.logWithout(console_1.Levels.INFO, console_1.Colors.BgRed, `📄 Recarregando frontend...`);
|
|
198
214
|
this.frontendChangeCallback?.();
|
|
199
215
|
this.notifyClients('frontend-reload', { file: filePath, event: 'change' });
|
|
200
216
|
}
|
|
201
217
|
// Se for arquivo de backend, recarrega o módulo e notifica
|
|
202
218
|
if (isBackendFile) {
|
|
203
|
-
console_1.default.logWithout(console_1.Levels.INFO, `⚙️ Recarregando backend...`);
|
|
219
|
+
console_1.default.logWithout(console_1.Levels.INFO, console_1.Colors.BgRed, `⚙️ Recarregando backend...`);
|
|
204
220
|
this.backendApiChangeCallback?.();
|
|
205
221
|
this.notifyClients('backend-api-reload', { file: filePath, event: 'change' });
|
|
206
222
|
}
|
|
207
223
|
// Fallback: se não for nem frontend nem backend detectado, recarrega tudo
|
|
208
224
|
if (!isFrontendFile && !isBackendFile) {
|
|
209
|
-
console_1.default.logWithout(console_1.Levels.INFO, `🔄 Recarregando aplicação...`);
|
|
225
|
+
console_1.default.logWithout(console_1.Levels.INFO, console_1.Colors.BgRed, `🔄 Recarregando aplicação...`);
|
|
210
226
|
this.frontendChangeCallback?.();
|
|
211
227
|
this.backendApiChangeCallback?.();
|
|
212
228
|
this.notifyClients('src-reload', { file: filePath, event: 'change' });
|
|
@@ -234,7 +250,7 @@ class HotReloadManager {
|
|
|
234
250
|
ws.send(message);
|
|
235
251
|
}
|
|
236
252
|
catch (error) {
|
|
237
|
-
console_1.default.logWithout(console_1.Levels.ERROR, `Erro ao enviar mensagem WebSocket: ${error}`);
|
|
253
|
+
console_1.default.logWithout(console_1.Levels.ERROR, console_1.Colors.BgRed, `Erro ao enviar mensagem WebSocket: ${error}`);
|
|
238
254
|
deadClients.push(ws);
|
|
239
255
|
}
|
|
240
256
|
}
|