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.
Files changed (47) hide show
  1. package/dist/adapters/factory.js +8 -8
  2. package/dist/adapters/native.js +3 -3
  3. package/dist/api/console.js +1 -1
  4. package/dist/auth/client.js +5 -5
  5. package/dist/auth/components.js +2 -2
  6. package/dist/auth/core.js +2 -2
  7. package/dist/auth/react.js +4 -4
  8. package/dist/auth/routes.js +1 -1
  9. package/dist/bin/hightjs.js +32 -331
  10. package/dist/builder.js +7 -19
  11. package/dist/client/DefaultNotFound.js +1 -1
  12. package/dist/client/entry.client.js +98 -8
  13. package/dist/helpers.d.ts +1 -0
  14. package/dist/helpers.js +130 -29
  15. package/dist/hotReload.js +25 -16
  16. package/dist/index.d.ts +1 -1
  17. package/dist/index.js +26 -36
  18. package/dist/renderer.js +137 -18
  19. package/dist/router.js +133 -62
  20. package/dist/types.d.ts +81 -0
  21. package/docs/config.md +216 -0
  22. package/example/hightjs.config.ts +87 -0
  23. package/example/package-lock.json +633 -3054
  24. package/example/package.json +3 -3
  25. package/example/src/web/layout.tsx +57 -3
  26. package/example/src/web/routes/index.tsx +1 -1
  27. package/package.json +1 -1
  28. package/src/adapters/factory.ts +8 -8
  29. package/src/adapters/native.ts +3 -3
  30. package/src/api/console.ts +3 -1
  31. package/src/auth/client.ts +5 -5
  32. package/src/auth/components.tsx +2 -2
  33. package/src/auth/core.ts +2 -2
  34. package/src/auth/react.tsx +4 -4
  35. package/src/auth/routes.ts +1 -1
  36. package/src/bin/hightjs.js +33 -394
  37. package/src/builder.js +7 -20
  38. package/src/client/DefaultNotFound.tsx +1 -1
  39. package/src/client/entry.client.tsx +125 -10
  40. package/src/helpers.ts +144 -30
  41. package/src/hotReload.ts +25 -16
  42. package/src/index.ts +33 -39
  43. package/src/renderer.tsx +142 -18
  44. package/src/router.ts +142 -63
  45. package/src/types.ts +108 -0
  46. package/example/.hweb/entry.client.js +0 -24
  47. 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: 'linear-gradient(135deg, #8e2de2, #4a00e0)', // Gradiente Roxo
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: '0 4px 15px rgba(0,0,0,0.2)',
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', // Animação suave ao soltar
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
- <div ref={indicatorRef} style={getIndicatorStyle()} onMouseDown={handleMouseDown} title="Modo Dev HightJS">
276
- H
277
- </div>
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
- const initialData = (window as any).__HWEB_INITIAL_DATA__;
384
+ // 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] Dados iniciais não encontrados na página.');
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 não encontrado.');
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] Erro ao renderizar aplicação:', error);
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 ? "Rodando em modo de desenvolvimento" : null;
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: ${Colors.FgGreen}${protocol}://localhost:${options.port}${Colors.Reset}`,
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} Rede: ${Colors.FgGreen}${protocol}://${localIp}:${options.port}${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 (porta ${options.ssl?.redirectPort}) está redirecionando para HTTPS.`);
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: "Acesse em:" });
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}Iniciando HightJS na porta ${options.port}${Colors.Reset}`);
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 url = req.url || '/';
212
- if (url.length > 2048) {
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(`Erro no servidor nativo: ${error.message}`);
346
+ Console.error(`Native server error: ${error.message}`);
231
347
  } else {
232
- Console.error('Erro desconhecido no servidor nativo:', 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 (Comum a ambos)
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}Pronto na porta ${Colors.BgGreen} ${options.port} ${Colors.Reset}${Colors.Bright} em ${Date.now() - time}ms${Colors.Reset}\n`);
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("Não foi possivel achar cookie-parser");
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("Não foi possivel achar @fastify/cookie");
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("Não foi possivel achar @fastify/formbody");
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('Não foi possível verificar a versão mais recente do HightJS:', 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} uma nova versão disponível (v${latestVersion})${Colors.FgMagenta}`
517
+ message = `${Colors.FgGreen} A new version is available (v${latestVersion})${Colors.FgMagenta}`
402
518
  } else {
403
- message = `${Colors.FgGreen} Você está na versão mais recente${Colors.FgMagenta}`
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(`O framework "${framework}" foi selecionado, mas o método init() funciona com o framework "native". Iniciando servidor nativo...`);
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,`📄 Aguardando build do frontend...`);
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 concluído, recarregando frontend...`);
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 no build, recarregando mesmo assim...`);
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,`⚙️ Recarregando backend...`);
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,`🔄 Recarregando aplicação...`);
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, `Erro no listener customizado: ${error.message}`);
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, `Erro ao enviar mensagem WebSocket: ${error}`);
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; // 30 segundos max
331
- let reconnectInterval = 1000; // Começa com 1 segundo
330
+ let maxReconnectInterval = 30000;
331
+ let reconnectInterval = 1000;
332
332
  let reconnectTimer;
333
333
  let isConnected = false;
334
334
 
335
335
  function connect() {
336
- // Evita múltiplas tentativas simultâneas
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('ws://localhost:3000/hweb-hotreload/');
344
+ ws = new WebSocket(wsUrl);
343
345
 
344
346
  ws.onopen = function() {
345
- console.log('🔌 Hot-reload conectado');
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('🔄 Servidor reiniciando...');
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('❌ Erro no frontend:', message.data);
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('Erro ao criar WebSocket:', 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 customizado registrado');
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
  }