hightjs 0.3.3 → 0.3.5
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/factory.js +8 -8
- package/dist/adapters/native.js +3 -3
- package/dist/api/console.js +1 -1
- package/dist/auth/client.js +5 -5
- package/dist/auth/components.js +2 -2
- package/dist/auth/core.js +2 -2
- package/dist/auth/react.js +4 -4
- package/dist/auth/routes.js +1 -1
- package/dist/bin/hightjs.js +32 -331
- package/dist/builder.js +7 -19
- package/dist/client/DefaultNotFound.js +1 -1
- package/dist/client/entry.client.js +98 -8
- package/dist/helpers.d.ts +1 -0
- package/dist/helpers.js +130 -29
- package/dist/hotReload.js +25 -16
- package/dist/index.d.ts +1 -1
- package/dist/index.js +26 -36
- package/dist/renderer.js +137 -18
- package/dist/router.js +133 -62
- package/dist/types.d.ts +81 -0
- package/docs/config.md +216 -0
- package/example/hightjs.config.ts +87 -0
- package/example/package-lock.json +633 -3054
- package/example/package.json +3 -3
- package/example/src/web/layout.tsx +57 -3
- package/example/src/web/routes/index.tsx +1 -1
- package/package.json +1 -1
- package/src/adapters/factory.ts +8 -8
- package/src/adapters/native.ts +3 -3
- package/src/api/console.ts +3 -1
- package/src/auth/client.ts +5 -5
- package/src/auth/components.tsx +2 -2
- package/src/auth/core.ts +2 -2
- package/src/auth/react.tsx +4 -4
- package/src/auth/routes.ts +1 -1
- package/src/bin/hightjs.js +33 -394
- package/src/builder.js +7 -20
- package/src/client/DefaultNotFound.tsx +1 -1
- package/src/client/entry.client.tsx +125 -10
- package/src/helpers.ts +144 -30
- package/src/hotReload.ts +25 -16
- package/src/index.ts +33 -39
- package/src/renderer.tsx +142 -18
- package/src/router.ts +142 -63
- package/src/types.ts +108 -0
- package/example/.hweb/entry.client.js +0 -24
- package/example/hweb-dist/main-5KKAYNUU.js +0 -1137
|
@@ -153,6 +153,7 @@ const DEV_INDICATOR_CORNERS = [
|
|
|
153
153
|
function DevIndicator() {
|
|
154
154
|
const [corner, setCorner] = useState(3); // Canto atual (0-3)
|
|
155
155
|
const [isDragging, setIsDragging] = useState(false); // Estado de arrastar
|
|
156
|
+
const [isBuilding, setIsBuilding] = useState(false); // Estado de build
|
|
156
157
|
|
|
157
158
|
// Posição visual do indicador durante o arraste
|
|
158
159
|
const [position, setPosition] = useState<{ top: number; left: number }>({ top: 0, left: 0 });
|
|
@@ -160,6 +161,49 @@ function DevIndicator() {
|
|
|
160
161
|
const indicatorRef = useRef<HTMLDivElement>(null);
|
|
161
162
|
const dragStartRef = useRef<{ x: number; y: number; moved: boolean } | null>(null);
|
|
162
163
|
|
|
164
|
+
// Escuta eventos de hot reload para mostrar estado de build
|
|
165
|
+
useEffect(() => {
|
|
166
|
+
if (typeof window === 'undefined') return;
|
|
167
|
+
|
|
168
|
+
const handleHotReloadMessage = (event: MessageEvent) => {
|
|
169
|
+
try {
|
|
170
|
+
const message = JSON.parse(event.data);
|
|
171
|
+
|
|
172
|
+
// Quando detecta mudança em arquivo, ativa loading
|
|
173
|
+
if (message.type === 'frontend-reload' ||
|
|
174
|
+
message.type === 'backend-api-reload' ||
|
|
175
|
+
message.type === 'src-reload') {
|
|
176
|
+
setIsBuilding(true);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Quando o build termina ou servidor fica pronto, desativa loading
|
|
180
|
+
if (message.type === 'server-ready' || message.type === 'build-complete') {
|
|
181
|
+
setIsBuilding(false);
|
|
182
|
+
}
|
|
183
|
+
} catch (e) {
|
|
184
|
+
// Ignora mensagens que não são JSON
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
// Intercepta mensagens WebSocket
|
|
189
|
+
const originalWebSocket = window.WebSocket;
|
|
190
|
+
window.WebSocket = class extends originalWebSocket {
|
|
191
|
+
constructor(url: string | URL, protocols?: string | string[]) {
|
|
192
|
+
super(url, protocols);
|
|
193
|
+
|
|
194
|
+
this.addEventListener('message', (event) => {
|
|
195
|
+
if (url.toString().includes('hweb-hotreload')) {
|
|
196
|
+
handleHotReloadMessage(event);
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
} as any;
|
|
201
|
+
|
|
202
|
+
return () => {
|
|
203
|
+
window.WebSocket = originalWebSocket;
|
|
204
|
+
};
|
|
205
|
+
}, []);
|
|
206
|
+
|
|
163
207
|
// --- Estilos Dinâmicos ---
|
|
164
208
|
const getIndicatorStyle = (): React.CSSProperties => {
|
|
165
209
|
const baseStyle: React.CSSProperties = {
|
|
@@ -168,17 +212,22 @@ function DevIndicator() {
|
|
|
168
212
|
width: DEV_INDICATOR_SIZE,
|
|
169
213
|
height: DEV_INDICATOR_SIZE,
|
|
170
214
|
borderRadius: '50%',
|
|
171
|
-
background:
|
|
215
|
+
background: isBuilding
|
|
216
|
+
? 'linear-gradient(135deg, #f093fb, #f5576c)' // Gradiente Rosa/Vermelho quando building
|
|
217
|
+
: 'linear-gradient(135deg, #8e2de2, #4a00e0)', // Gradiente Roxo normal
|
|
172
218
|
color: 'white',
|
|
173
219
|
fontWeight: 'bold',
|
|
174
220
|
fontSize: 28,
|
|
175
|
-
boxShadow:
|
|
221
|
+
boxShadow: isBuilding
|
|
222
|
+
? '0 4px 25px rgba(245, 87, 108, 0.6)' // Shadow mais forte quando building
|
|
223
|
+
: '0 4px 15px rgba(0,0,0,0.2)',
|
|
176
224
|
display: 'flex',
|
|
177
225
|
alignItems: 'center',
|
|
178
226
|
justifyContent: 'center',
|
|
179
227
|
cursor: isDragging ? 'grabbing' : 'grab',
|
|
180
228
|
userSelect: 'none',
|
|
181
|
-
transition: isDragging ? 'none' : 'all 0.3s ease-out',
|
|
229
|
+
transition: isDragging ? 'none' : 'all 0.3s ease-out',
|
|
230
|
+
animation: isBuilding ? 'hweb-pulse 1.5s ease-in-out infinite' : 'none',
|
|
182
231
|
};
|
|
183
232
|
|
|
184
233
|
if (isDragging) {
|
|
@@ -272,19 +321,85 @@ function DevIndicator() {
|
|
|
272
321
|
|
|
273
322
|
|
|
274
323
|
return (
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
324
|
+
<>
|
|
325
|
+
<style>
|
|
326
|
+
{`
|
|
327
|
+
@keyframes hweb-pulse {
|
|
328
|
+
0%, 100% {
|
|
329
|
+
transform: scale(1);
|
|
330
|
+
opacity: 1;
|
|
331
|
+
}
|
|
332
|
+
50% {
|
|
333
|
+
transform: scale(1.1);
|
|
334
|
+
opacity: 0.8;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
@keyframes hweb-spin {
|
|
339
|
+
from {
|
|
340
|
+
transform: rotate(0deg);
|
|
341
|
+
}
|
|
342
|
+
to {
|
|
343
|
+
transform: rotate(360deg);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
`}
|
|
347
|
+
</style>
|
|
348
|
+
<div
|
|
349
|
+
ref={indicatorRef}
|
|
350
|
+
style={getIndicatorStyle()}
|
|
351
|
+
onMouseDown={handleMouseDown}
|
|
352
|
+
title={isBuilding ? "Building..." : "Modo Dev HightJS"}
|
|
353
|
+
>
|
|
354
|
+
{isBuilding ? (
|
|
355
|
+
<span style={{ animation: 'hweb-spin 1s linear infinite' }}>⟳</span>
|
|
356
|
+
) : (
|
|
357
|
+
'H'
|
|
358
|
+
)}
|
|
359
|
+
</div>
|
|
360
|
+
</>
|
|
278
361
|
);
|
|
279
362
|
}
|
|
280
363
|
|
|
281
364
|
// --- Inicialização do Cliente (CSR - Client-Side Rendering) ---
|
|
282
365
|
|
|
366
|
+
function deobfuscateData(obfuscated: string): any {
|
|
367
|
+
try {
|
|
368
|
+
// Remove o hash fake
|
|
369
|
+
const parts = obfuscated.split('.');
|
|
370
|
+
const base64 = parts.length > 1 ? parts[1] : parts[0];
|
|
371
|
+
|
|
372
|
+
// Decodifica base64
|
|
373
|
+
const jsonStr = atob(base64);
|
|
374
|
+
|
|
375
|
+
// Parse JSON
|
|
376
|
+
return JSON.parse(jsonStr);
|
|
377
|
+
} catch (error) {
|
|
378
|
+
console.error('[hweb] Failed to decode data:', error);
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
283
383
|
function initializeClient() {
|
|
284
|
-
|
|
384
|
+
// Lê os dados do atributo data-h
|
|
385
|
+
const dataElement = document.getElementById('__hight_data__');
|
|
386
|
+
|
|
387
|
+
if (!dataElement) {
|
|
388
|
+
console.error('[hweb] Initial data script not found.');
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const obfuscated = dataElement.getAttribute('data-h');
|
|
393
|
+
|
|
394
|
+
if (!obfuscated) {
|
|
395
|
+
console.error('[hweb] Data attribute not found.');
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const initialData = deobfuscateData(obfuscated);
|
|
285
400
|
|
|
286
401
|
if (!initialData) {
|
|
287
|
-
console.error('[hweb]
|
|
402
|
+
console.error('[hweb] Failed to parse initial data.');
|
|
288
403
|
return;
|
|
289
404
|
}
|
|
290
405
|
|
|
@@ -298,7 +413,7 @@ function initializeClient() {
|
|
|
298
413
|
|
|
299
414
|
const container = document.getElementById('root');
|
|
300
415
|
if (!container) {
|
|
301
|
-
console.error('[hweb] Container #root
|
|
416
|
+
console.error('[hweb] Container #root not found.');
|
|
302
417
|
return;
|
|
303
418
|
}
|
|
304
419
|
|
|
@@ -316,7 +431,7 @@ function initializeClient() {
|
|
|
316
431
|
/>
|
|
317
432
|
);
|
|
318
433
|
} catch (error) {
|
|
319
|
-
console.error('[hweb]
|
|
434
|
+
console.error('[hweb] Error rendering application:', error);
|
|
320
435
|
}
|
|
321
436
|
}
|
|
322
437
|
|
package/src/helpers.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
1
3
|
/*
|
|
2
4
|
* This file is part of the HightJS Project.
|
|
3
5
|
* Copyright (c) 2025 itsmuzin
|
|
@@ -19,9 +21,10 @@
|
|
|
19
21
|
import http, {IncomingMessage, Server, ServerResponse} from 'http';
|
|
20
22
|
import os from 'os';
|
|
21
23
|
import {URLSearchParams} from 'url'; // API moderna, substitui 'querystring'
|
|
24
|
+
import path from 'path';
|
|
22
25
|
// Helpers para integração com diferentes frameworks
|
|
23
26
|
import hweb, {FrameworkAdapterFactory} from './index'; // Importando o tipo
|
|
24
|
-
import type {HightJSOptions} from './types';
|
|
27
|
+
import type {HightJSOptions, HightConfig, HightConfigFunction} from './types';
|
|
25
28
|
import Console, {Colors} from "./api/console";
|
|
26
29
|
import https, { Server as HttpsServer } from 'https'; // <-- ADICIONAR
|
|
27
30
|
import fs from 'fs'; // <-- ADICIONAR
|
|
@@ -66,7 +69,7 @@ function getLocalExternalIp(): string {
|
|
|
66
69
|
}
|
|
67
70
|
|
|
68
71
|
const sendBox = (options: HightJSOptions) => {
|
|
69
|
-
const isDev = options.dev ? "
|
|
72
|
+
const isDev = options.dev ? "Running in development mode" : null;
|
|
70
73
|
|
|
71
74
|
// 1. Verifica se o SSL está ativado (baseado na mesma lógica do initNativeServer)
|
|
72
75
|
const isSSL = options.ssl && options.ssl.key && options.ssl.cert;
|
|
@@ -75,12 +78,12 @@ const sendBox = (options: HightJSOptions) => {
|
|
|
75
78
|
|
|
76
79
|
// 2. Monta as mensagens com o protocolo correto
|
|
77
80
|
const messages = [
|
|
78
|
-
` ${Colors.FgGray}┃${Colors.Reset} Local:
|
|
81
|
+
` ${Colors.FgGray}┃${Colors.Reset} Local: ${Colors.FgGreen}${protocol}://localhost:${options.port}${Colors.Reset}`,
|
|
79
82
|
];
|
|
80
83
|
|
|
81
84
|
// Só adiciona a rede se o IP local for encontrado
|
|
82
85
|
if (localIp) {
|
|
83
|
-
messages.push(` ${Colors.FgGray}┃${Colors.Reset}
|
|
86
|
+
messages.push(` ${Colors.FgGray}┃${Colors.Reset} Network: ${Colors.FgGreen}${protocol}://${localIp}:${options.port}${Colors.Reset}`);
|
|
84
87
|
}
|
|
85
88
|
|
|
86
89
|
if (isDev) {
|
|
@@ -89,10 +92,79 @@ const sendBox = (options: HightJSOptions) => {
|
|
|
89
92
|
|
|
90
93
|
// Adiciona aviso de redirecionamento se estiver em modo SSL
|
|
91
94
|
if (isSSL && options.ssl?.redirectPort) {
|
|
92
|
-
messages.push(` ${Colors.FgGray}┃${Colors.Reset} HTTP (
|
|
95
|
+
messages.push(` ${Colors.FgGray}┃${Colors.Reset} HTTP (port ${options.ssl?.redirectPort}) is redirecting to HTTPS.`);
|
|
93
96
|
}
|
|
94
97
|
|
|
95
|
-
Console.box(messages.join("\n"), { title: "
|
|
98
|
+
Console.box(messages.join("\n"), { title: "Access on:" });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Carrega o arquivo de configuração hightjs.config.ts ou hightjs.config.js do projeto
|
|
103
|
+
* @param projectDir Diretório raiz do projeto
|
|
104
|
+
* @param phase Fase de execução ('development' ou 'production')
|
|
105
|
+
* @returns Configuração mesclada com os valores padrão
|
|
106
|
+
*/
|
|
107
|
+
async function loadHightConfig(projectDir: string, phase: string): Promise<HightConfig> {
|
|
108
|
+
const defaultConfig: HightConfig = {
|
|
109
|
+
maxHeadersCount: 100,
|
|
110
|
+
headersTimeout: 60000,
|
|
111
|
+
requestTimeout: 30000,
|
|
112
|
+
serverTimeout: 35000,
|
|
113
|
+
individualRequestTimeout: 30000,
|
|
114
|
+
maxUrlLength: 2048,
|
|
115
|
+
accessLogging: true,
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
// Tenta primeiro .ts, depois .js
|
|
120
|
+
const possiblePaths = [
|
|
121
|
+
path.join(projectDir, 'hightjs.config.ts'),
|
|
122
|
+
path.join(projectDir, 'hightjs.config.js'),
|
|
123
|
+
];
|
|
124
|
+
|
|
125
|
+
let configPath: string | null = null;
|
|
126
|
+
for (const p of possiblePaths) {
|
|
127
|
+
if (fs.existsSync(p)) {
|
|
128
|
+
configPath = p;
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (!configPath) {
|
|
134
|
+
return defaultConfig;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Remove do cache para permitir hot reload da configuração em dev
|
|
138
|
+
delete require.cache[require.resolve(configPath)];
|
|
139
|
+
|
|
140
|
+
const configModule = require(configPath);
|
|
141
|
+
const configExport = configModule.default || configModule;
|
|
142
|
+
|
|
143
|
+
let userConfig: HightConfig;
|
|
144
|
+
|
|
145
|
+
if (typeof configExport === 'function') {
|
|
146
|
+
// Suporta tanto função síncrona quanto assíncrona
|
|
147
|
+
userConfig = await Promise.resolve(
|
|
148
|
+
(configExport as HightConfigFunction)(phase, { defaultConfig })
|
|
149
|
+
);
|
|
150
|
+
} else {
|
|
151
|
+
userConfig = configExport;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Mescla a configuração do usuário com a padrão
|
|
155
|
+
const mergedConfig = { ...defaultConfig, ...userConfig };
|
|
156
|
+
|
|
157
|
+
const configFileName = path.basename(configPath);
|
|
158
|
+
Console.info(`${Colors.FgCyan}[Config]${Colors.Reset} Loaded ${configFileName}`);
|
|
159
|
+
|
|
160
|
+
return mergedConfig;
|
|
161
|
+
} catch (error) {
|
|
162
|
+
if (error instanceof Error) {
|
|
163
|
+
Console.warn(`${Colors.FgYellow}[Config]${Colors.Reset} Error loading hightjs.config: ${error.message}`);
|
|
164
|
+
Console.warn(`${Colors.FgYellow}[Config]${Colors.Reset} Using default configuration`);
|
|
165
|
+
}
|
|
166
|
+
return defaultConfig;
|
|
167
|
+
}
|
|
96
168
|
}
|
|
97
169
|
|
|
98
170
|
/**
|
|
@@ -181,13 +253,23 @@ async function initNativeServer(hwebApp: HWebApp, options: HightJSOptions, port:
|
|
|
181
253
|
const time = Date.now();
|
|
182
254
|
|
|
183
255
|
await hwebApp.prepare();
|
|
256
|
+
|
|
257
|
+
// Carrega a configuração do arquivo hightjs.config.js
|
|
258
|
+
const projectDir = options.dir || process.cwd();
|
|
259
|
+
const phase = options.dev ? 'development' : 'production';
|
|
260
|
+
const hightConfig = await loadHightConfig(projectDir, phase);
|
|
261
|
+
|
|
184
262
|
const handler = hwebApp.getRequestHandler();
|
|
185
|
-
const msg = Console.dynamicLine(` ${Colors.BgYellow} ready ${Colors.Reset} ${Colors.Bright}
|
|
263
|
+
const msg = Console.dynamicLine(` ${Colors.BgYellow} ready ${Colors.Reset} ${Colors.Bright}Starting HightJS on port ${options.port}${Colors.Reset}`);
|
|
186
264
|
|
|
187
265
|
// --- LÓGICA DO LISTENER (REUTILIZÁVEL) ---
|
|
188
266
|
// Extraímos a lógica principal para uma variável
|
|
189
267
|
// para que possa ser usada tanto pelo servidor HTTP quanto HTTPS.
|
|
190
268
|
const requestListener = async (req: HWebIncomingMessage, res: ServerResponse) => {
|
|
269
|
+
const requestStartTime = Date.now();
|
|
270
|
+
const method = req.method || 'GET';
|
|
271
|
+
const url = req.url || '/';
|
|
272
|
+
|
|
191
273
|
// Configurações de segurança básicas
|
|
192
274
|
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
193
275
|
res.setHeader('X-Frame-Options', 'DENY');
|
|
@@ -200,16 +282,50 @@ async function initNativeServer(hwebApp: HWebApp, options: HightJSOptions, port:
|
|
|
200
282
|
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
|
|
201
283
|
}
|
|
202
284
|
|
|
203
|
-
// Timeout por requisição
|
|
204
|
-
req.setTimeout(30000, () => {
|
|
285
|
+
// Timeout por requisição (usa configuração personalizada)
|
|
286
|
+
req.setTimeout(hightConfig.individualRequestTimeout || 30000, () => {
|
|
205
287
|
res.statusCode = 408; // Request Timeout
|
|
206
288
|
res.end('Request timeout');
|
|
289
|
+
|
|
290
|
+
// Log de timeout
|
|
291
|
+
if (hightConfig.accessLogging) {
|
|
292
|
+
const duration = Date.now() - requestStartTime;
|
|
293
|
+
Console.info(`${Colors.FgYellow}${method}${Colors.Reset} ${url} ${Colors.FgRed}408${Colors.Reset} ${Colors.FgGray}${duration}ms${Colors.Reset}`);
|
|
294
|
+
}
|
|
207
295
|
});
|
|
208
296
|
|
|
297
|
+
// Intercepta o método end() para logar quando a resposta for enviada
|
|
298
|
+
const originalEnd = res.end.bind(res);
|
|
299
|
+
let hasEnded = false;
|
|
300
|
+
|
|
301
|
+
res.end = function(this: ServerResponse, ...args: any[]): any {
|
|
302
|
+
if (!hasEnded && hightConfig.accessLogging) {
|
|
303
|
+
hasEnded = true;
|
|
304
|
+
const duration = Date.now() - requestStartTime;
|
|
305
|
+
const statusCode = res.statusCode || 200;
|
|
306
|
+
|
|
307
|
+
// Define cor baseada no status code
|
|
308
|
+
let statusColor = Colors.FgGreen; // 2xx
|
|
309
|
+
if (statusCode >= 500) statusColor = Colors.FgRed; // 5xx
|
|
310
|
+
else if (statusCode >= 400) statusColor = Colors.FgYellow; // 4xx
|
|
311
|
+
else if (statusCode >= 300) statusColor = Colors.FgCyan; // 3xx
|
|
312
|
+
|
|
313
|
+
// Formata o método com cor
|
|
314
|
+
let methodColor = Colors.BgCyan;
|
|
315
|
+
if (method === 'POST') methodColor = Colors.BgGreen;
|
|
316
|
+
else if (method === 'PUT') methodColor = Colors.BgYellow;
|
|
317
|
+
else if (method === 'DELETE') methodColor = Colors.BgRed;
|
|
318
|
+
else if (method === 'PATCH') methodColor = Colors.BgMagenta;
|
|
319
|
+
Console.logCustomLevel(method, true, methodColor, `${url} ${statusColor}${statusCode}${Colors.Reset} ${Colors.FgGray}${duration}ms${Colors.Reset}`);
|
|
320
|
+
}
|
|
321
|
+
// @ts-ignore
|
|
322
|
+
return originalEnd.apply(this, args);
|
|
323
|
+
} as any;
|
|
324
|
+
|
|
209
325
|
try {
|
|
210
|
-
// Validação básica de URL
|
|
211
|
-
const
|
|
212
|
-
if (url.length >
|
|
326
|
+
// Validação básica de URL (usa configuração personalizada)
|
|
327
|
+
const maxUrlLength = hightConfig.maxUrlLength || 2048;
|
|
328
|
+
if (url.length > maxUrlLength) {
|
|
213
329
|
res.statusCode = 414; // URI Too Long
|
|
214
330
|
res.end('URL too long');
|
|
215
331
|
return;
|
|
@@ -227,9 +343,9 @@ async function initNativeServer(hwebApp: HWebApp, options: HightJSOptions, port:
|
|
|
227
343
|
} catch (error) {
|
|
228
344
|
// Log do erro no servidor
|
|
229
345
|
if (error instanceof Error) {
|
|
230
|
-
Console.error(`
|
|
346
|
+
Console.error(`Native server error: ${error.message}`);
|
|
231
347
|
} else {
|
|
232
|
-
Console.error('
|
|
348
|
+
Console.error('Unknown native server error:', error);
|
|
233
349
|
}
|
|
234
350
|
|
|
235
351
|
// Tratamento de erro (idêntico ao seu original)
|
|
@@ -297,15 +413,15 @@ async function initNativeServer(hwebApp: HWebApp, options: HightJSOptions, port:
|
|
|
297
413
|
server = http.createServer(requestListener as any); // (any para contornar HWebIncomingMessage)
|
|
298
414
|
}
|
|
299
415
|
|
|
300
|
-
// Configurações de segurança do servidor (
|
|
301
|
-
server.setTimeout(35000); // Timeout geral do servidor
|
|
302
|
-
server.maxHeadersCount = 100; // Limita número de headers
|
|
303
|
-
server.headersTimeout = 60000; // Timeout para headers
|
|
304
|
-
server.requestTimeout = 30000; // Timeout para requisições
|
|
416
|
+
// Configurações de segurança do servidor (usa configuração personalizada)
|
|
417
|
+
server.setTimeout(hightConfig.serverTimeout || 35000); // Timeout geral do servidor
|
|
418
|
+
server.maxHeadersCount = hightConfig.maxHeadersCount || 100; // Limita número de headers
|
|
419
|
+
server.headersTimeout = hightConfig.headersTimeout || 60000; // Timeout para headers
|
|
420
|
+
server.requestTimeout = hightConfig.requestTimeout || 30000; // Timeout para requisições
|
|
305
421
|
|
|
306
422
|
server.listen(port, hostname, () => {
|
|
307
423
|
sendBox({ ...options, port });
|
|
308
|
-
msg.end(` ${Colors.BgGreen} ready ${Colors.Reset} ${Colors.Bright}
|
|
424
|
+
msg.end(` ${Colors.BgGreen} ready ${Colors.Reset} ${Colors.Bright}Ready on port ${Colors.BgGreen} ${options.port} ${Colors.Reset}${Colors.Bright} in ${Date.now() - time}ms${Colors.Reset}\n`);
|
|
309
425
|
});
|
|
310
426
|
|
|
311
427
|
// Configura WebSocket para hot reload (Comum a ambos)
|
|
@@ -344,7 +460,7 @@ export function app(options: HightJSOptions = {}) {
|
|
|
344
460
|
const cookieParser = require('cookie-parser');
|
|
345
461
|
serverApp.use(cookieParser());
|
|
346
462
|
} catch (e) {
|
|
347
|
-
Console.error("
|
|
463
|
+
Console.error("Could not find cookie-parser");
|
|
348
464
|
}
|
|
349
465
|
serverApp.use(express.json());
|
|
350
466
|
serverApp.use(express.urlencoded({ extended: true }));
|
|
@@ -355,12 +471,12 @@ export function app(options: HightJSOptions = {}) {
|
|
|
355
471
|
try {
|
|
356
472
|
await serverApp.register(require('@fastify/cookie'));
|
|
357
473
|
} catch (e) {
|
|
358
|
-
Console.error("
|
|
474
|
+
Console.error("Could not find @fastify/cookie");
|
|
359
475
|
}
|
|
360
476
|
try {
|
|
361
477
|
await serverApp.register(require('@fastify/formbody'));
|
|
362
478
|
} catch (e) {
|
|
363
|
-
Console.error("
|
|
479
|
+
Console.error("Could not find @fastify/formbody");
|
|
364
480
|
}
|
|
365
481
|
await serverApp.register(async (fastify: any) => {
|
|
366
482
|
fastify.all('*', handler);
|
|
@@ -390,7 +506,7 @@ export function app(options: HightJSOptions = {}) {
|
|
|
390
506
|
const data = await response.json();
|
|
391
507
|
return data.version;
|
|
392
508
|
} catch (error) {
|
|
393
|
-
Console.error('
|
|
509
|
+
Console.error('Could not check for the latest HightJS version:', error);
|
|
394
510
|
return currentVersion; // Retorna a versão atual em caso de erro
|
|
395
511
|
}
|
|
396
512
|
}
|
|
@@ -398,23 +514,21 @@ export function app(options: HightJSOptions = {}) {
|
|
|
398
514
|
const isUpToDate = latestVersion === currentVersion;
|
|
399
515
|
let message;
|
|
400
516
|
if (!isUpToDate) {
|
|
401
|
-
message = `${Colors.FgGreen}
|
|
517
|
+
message = `${Colors.FgGreen} A new version is available (v${latestVersion})${Colors.FgMagenta}`
|
|
402
518
|
} else {
|
|
403
|
-
message = `${Colors.FgGreen}
|
|
519
|
+
message = `${Colors.FgGreen} You are on the latest version${Colors.FgMagenta}`
|
|
404
520
|
}
|
|
405
521
|
console.log(`${Colors.FgMagenta}
|
|
406
522
|
__ ___ ${Colors.FgGreen} __ ${Colors.FgMagenta}
|
|
407
523
|
|__| | / _\` |__| | ${Colors.FgGreen} | /__\` ${Colors.FgMagenta} ${Colors.FgMagenta}HightJS ${Colors.FgGray}(v${require('../package.json').version}) - itsmuzin${Colors.FgMagenta}
|
|
408
|
-
| | | \\__> | | | ${Colors.FgGreen}\\__/ .__/ ${message}
|
|
409
|
-
|
|
410
|
-
|
|
524
|
+
| | | \\__> | | | ${Colors.FgGreen}\\__/ .__/ ${message}
|
|
411
525
|
${Colors.Reset}`);
|
|
412
526
|
|
|
413
527
|
const actualPort = options.port || 3000;
|
|
414
528
|
const actualHostname = options.hostname || "0.0.0.0";
|
|
415
529
|
|
|
416
530
|
if (framework !== 'native') {
|
|
417
|
-
Console.warn(`
|
|
531
|
+
Console.warn(`The "${framework}" framework was selected, but the init() method only works with the "native" framework. Starting native server...`);
|
|
418
532
|
}
|
|
419
533
|
|
|
420
534
|
|
package/src/hotReload.ts
CHANGED
|
@@ -204,7 +204,7 @@ export class HotReloadManager {
|
|
|
204
204
|
|
|
205
205
|
// Se for arquivo de frontend, aguarda o build terminar antes de recarregar
|
|
206
206
|
if (isFrontendFile) {
|
|
207
|
-
Console.logWithout(Levels.INFO, Colors.BgRed,`📄
|
|
207
|
+
Console.logWithout(Levels.INFO, Colors.BgRed,`📄 Waiting for frontend build...`);
|
|
208
208
|
|
|
209
209
|
// Marca que estamos esperando um build
|
|
210
210
|
this.isBuilding = true;
|
|
@@ -221,11 +221,11 @@ export class HotReloadManager {
|
|
|
221
221
|
|
|
222
222
|
try {
|
|
223
223
|
await Promise.race([buildPromise, timeoutPromise]);
|
|
224
|
-
Console.logWithout(Levels.INFO, Colors.BgRed,`✅ Build
|
|
224
|
+
Console.logWithout(Levels.INFO, Colors.BgRed,`✅ Build complete, reloading frontend...`);
|
|
225
225
|
this.frontendChangeCallback?.();
|
|
226
226
|
this.notifyClients('frontend-reload', { file: filePath, event: 'change' });
|
|
227
227
|
} catch (error) {
|
|
228
|
-
Console.logWithout(Levels.ERROR, Colors.BgRed,`⚠️ Timeout
|
|
228
|
+
Console.logWithout(Levels.ERROR, Colors.BgRed,`⚠️ Timeout in build, reloading anyway...`);
|
|
229
229
|
this.frontendChangeCallback?.();
|
|
230
230
|
this.notifyClients('frontend-reload', { file: filePath, event: 'change' });
|
|
231
231
|
} finally {
|
|
@@ -236,14 +236,14 @@ export class HotReloadManager {
|
|
|
236
236
|
|
|
237
237
|
// Se for arquivo de backend, recarrega o módulo e notifica
|
|
238
238
|
if (isBackendFile) {
|
|
239
|
-
Console.logWithout(Levels.INFO, Colors.BgRed,`⚙️
|
|
239
|
+
Console.logWithout(Levels.INFO, Colors.BgRed,`⚙️ Reloading backend...`);
|
|
240
240
|
this.backendApiChangeCallback?.();
|
|
241
241
|
this.notifyClients('backend-api-reload', { file: filePath, event: 'change' });
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
// Fallback: se não for nem frontend nem backend detectado, recarrega tudo
|
|
245
245
|
if (!isFrontendFile && !isBackendFile) {
|
|
246
|
-
Console.logWithout(Levels.INFO, Colors.BgRed,`🔄
|
|
246
|
+
Console.logWithout(Levels.INFO, Colors.BgRed,`🔄 Reloading application...`);
|
|
247
247
|
this.frontendChangeCallback?.();
|
|
248
248
|
this.backendApiChangeCallback?.();
|
|
249
249
|
this.notifyClients('src-reload', { file: filePath, event: 'change' });
|
|
@@ -255,7 +255,7 @@ export class HotReloadManager {
|
|
|
255
255
|
await this.customHotReloadListener(filePath);
|
|
256
256
|
} catch (error) {
|
|
257
257
|
// @ts-ignore
|
|
258
|
-
Console.logWithout(Levels.ERROR, `
|
|
258
|
+
Console.logWithout(Levels.ERROR, `Error in custom listener: ${error.message}`);
|
|
259
259
|
}
|
|
260
260
|
}
|
|
261
261
|
}
|
|
@@ -273,7 +273,7 @@ export class HotReloadManager {
|
|
|
273
273
|
try {
|
|
274
274
|
ws.send(message);
|
|
275
275
|
} catch (error) {
|
|
276
|
-
Console.logWithout(Levels.ERROR, Colors.BgRed, `
|
|
276
|
+
Console.logWithout(Levels.ERROR, Colors.BgRed, `Error sending WebSocket message: ${error}`);
|
|
277
277
|
deadClients.push(ws);
|
|
278
278
|
}
|
|
279
279
|
} else {
|
|
@@ -327,22 +327,24 @@ export class HotReloadManager {
|
|
|
327
327
|
if (typeof window !== 'undefined') {
|
|
328
328
|
let ws;
|
|
329
329
|
let reconnectAttempts = 0;
|
|
330
|
-
let maxReconnectInterval = 30000;
|
|
331
|
-
let reconnectInterval = 1000;
|
|
330
|
+
let maxReconnectInterval = 30000;
|
|
331
|
+
let reconnectInterval = 1000;
|
|
332
332
|
let reconnectTimer;
|
|
333
333
|
let isConnected = false;
|
|
334
334
|
|
|
335
335
|
function connect() {
|
|
336
|
-
//
|
|
336
|
+
const url = window.location; // Objeto com info da URL atual
|
|
337
|
+
const protocol = url.protocol === "https:" ? "wss:" : "ws:"; // Usa wss se for https
|
|
338
|
+
const wsUrl = protocol + '//' + url.host + '/hweb-hotreload/';
|
|
337
339
|
if (ws && (ws.readyState === WebSocket.CONNECTING || ws.readyState === WebSocket.OPEN)) {
|
|
338
340
|
return;
|
|
339
341
|
}
|
|
340
342
|
|
|
341
343
|
try {
|
|
342
|
-
ws = new WebSocket(
|
|
344
|
+
ws = new WebSocket(wsUrl);
|
|
343
345
|
|
|
344
346
|
ws.onopen = function() {
|
|
345
|
-
console.log('🔌 Hot-reload
|
|
347
|
+
console.log('🔌 Hot-reload connected');
|
|
346
348
|
isConnected = true;
|
|
347
349
|
reconnectAttempts = 0;
|
|
348
350
|
reconnectInterval = 1000;
|
|
@@ -362,13 +364,13 @@ export class HotReloadManager {
|
|
|
362
364
|
window.location.reload();
|
|
363
365
|
break;
|
|
364
366
|
case 'server-restart':
|
|
365
|
-
console.log('🔄
|
|
367
|
+
console.log('🔄 Server restarting...');
|
|
366
368
|
break;
|
|
367
369
|
case 'server-ready':
|
|
368
370
|
setTimeout(() => window.location.reload(), 500);
|
|
369
371
|
break;
|
|
370
372
|
case 'frontend-error':
|
|
371
|
-
console.error('❌
|
|
373
|
+
console.error('❌ Frontend error:', message.data);
|
|
372
374
|
break;
|
|
373
375
|
}
|
|
374
376
|
} catch (e) {
|
|
@@ -393,7 +395,7 @@ export class HotReloadManager {
|
|
|
393
395
|
};
|
|
394
396
|
|
|
395
397
|
} catch (error) {
|
|
396
|
-
console.error('
|
|
398
|
+
console.error('Error creating WebSocket:', error);
|
|
397
399
|
scheduleReconnect();
|
|
398
400
|
}
|
|
399
401
|
}
|
|
@@ -463,7 +465,7 @@ export class HotReloadManager {
|
|
|
463
465
|
|
|
464
466
|
setHotReloadListener(listener: (file: string) => Promise<void> | void) {
|
|
465
467
|
this.customHotReloadListener = listener;
|
|
466
|
-
Console.info('🔌 Hot reload listener
|
|
468
|
+
Console.info('🔌 Hot reload custom listener registered');
|
|
467
469
|
}
|
|
468
470
|
|
|
469
471
|
removeHotReloadListener() {
|
|
@@ -476,5 +478,12 @@ export class HotReloadManager {
|
|
|
476
478
|
this.buildCompleteResolve = null;
|
|
477
479
|
}
|
|
478
480
|
this.isBuilding = false;
|
|
481
|
+
|
|
482
|
+
// Notifica os clientes que o build terminou
|
|
483
|
+
if (success) {
|
|
484
|
+
this.notifyClients('build-complete', { success: true });
|
|
485
|
+
} else {
|
|
486
|
+
this.notifyClients('build-error', { success: false });
|
|
487
|
+
}
|
|
479
488
|
}
|
|
480
489
|
}
|