hightjs 0.3.4 → 0.4.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/src/hotReload.ts CHANGED
@@ -195,7 +195,7 @@ export class HotReloadManager {
195
195
  filePath.endsWith('.tsx') ||
196
196
  filePath.endsWith('.jsx');
197
197
 
198
- const isBackendFile = filePath.includes(path.join('src', 'web', 'backend')) ||
198
+ const isBackendFile = filePath.includes(path.join('src', 'backend')) ||
199
199
  (filePath.includes(path.join('src', 'web')) && !isFrontendFile);
200
200
 
201
201
  // Limpa o cache do arquivo alterado
@@ -327,19 +327,21 @@ 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
347
  console.log('🔌 Hot-reload connected');
@@ -355,10 +357,11 @@ export class HotReloadManager {
355
357
 
356
358
  switch(message.type) {
357
359
  case 'frontend-reload':
358
- window.location.reload();
360
+ handleFrontendReload(message.data);
359
361
  break;
360
362
  case 'backend-api-reload':
361
- // Recarrega apenas se necessário
363
+ // Backend sempre precisa recarregar
364
+ console.log('🔄 Backend changed, reloading...');
362
365
  window.location.reload();
363
366
  break;
364
367
  case 'server-restart':
@@ -370,12 +373,91 @@ export class HotReloadManager {
370
373
  case 'frontend-error':
371
374
  console.error('❌ Frontend error:', message.data);
372
375
  break;
376
+ case 'hmr-update':
377
+ handleHMRUpdate(message.data);
378
+ break;
373
379
  }
374
380
  } catch (e) {
375
381
  console.error('Erro ao processar mensagem do hot-reload:', e);
376
382
  }
377
383
  };
378
384
 
385
+ function handleFrontendReload(data) {
386
+ if (!data || !data.file) {
387
+ window.location.reload();
388
+ return;
389
+ }
390
+
391
+ const file = data.file.toLowerCase();
392
+
393
+ // Mudanças que exigem reload completo
394
+ const needsFullReload =
395
+ file.includes('layout.tsx') ||
396
+ file.includes('layout.jsx') ||
397
+ file.includes('not-found.tsx') ||
398
+ file.includes('not-found.jsx') ||
399
+ file.endsWith('.css');
400
+
401
+ if (needsFullReload) {
402
+ console.log('⚡ Layout/CSS changed, full reload...');
403
+ window.location.reload();
404
+ return;
405
+ }
406
+
407
+ // Mudanças em rotas: tenta HMR
408
+ if (file.includes('/routes/') || file.includes('\\\\routes\\\\')) {
409
+ console.log('⚡ Route component changed, hot reloading...');
410
+
411
+ // Dispara evento para forçar re-render
412
+ const event = new CustomEvent('hmr:component-update', {
413
+ detail: { file: data.file, timestamp: Date.now() }
414
+ });
415
+ window.dispatchEvent(event);
416
+
417
+ // Aguarda 500ms para ver se o HMR foi bem-sucedido
418
+ setTimeout(() => {
419
+ const hmrSuccess = window.__HMR_SUCCESS__;
420
+ if (!hmrSuccess) {
421
+ console.log('⚠️ HMR failed, falling back to full reload');
422
+ window.location.reload();
423
+ } else {
424
+ console.log('✅ HMR successful!');
425
+ }
426
+ }, 500);
427
+ } else {
428
+ // Outros arquivos: reload completo por segurança
429
+ window.location.reload();
430
+ }
431
+ }
432
+
433
+ function handleHMRUpdate(data) {
434
+ console.log('🔥 HMR Update:', data);
435
+
436
+ // Dispara evento customizado para o React capturar
437
+ const event = new CustomEvent('hmr:update', {
438
+ detail: data
439
+ });
440
+ window.dispatchEvent(event);
441
+ }
442
+
443
+ function attemptHMR(changedFile) {
444
+ // Tenta fazer Hot Module Replacement
445
+ // Dispara evento para o React App capturar
446
+ const event = new CustomEvent('hmr:component-update', {
447
+ detail: { file: changedFile, timestamp: Date.now() }
448
+ });
449
+ window.dispatchEvent(event);
450
+
451
+ // Fallback: se após 2s não houve sucesso, reload
452
+ setTimeout(() => {
453
+ const hmrSuccess = window.__HMR_SUCCESS__;
454
+ if (!hmrSuccess) {
455
+ console.log('⚠️ HMR failed, falling back to full reload');
456
+ window.location.reload();
457
+ }
458
+ }, 2000);
459
+ }
460
+
379
461
  ws.onclose = function(event) {
380
462
  isConnected = false;
381
463
 
@@ -476,5 +558,12 @@ export class HotReloadManager {
476
558
  this.buildCompleteResolve = null;
477
559
  }
478
560
  this.isBuilding = false;
561
+
562
+ // Notifica os clientes que o build terminou
563
+ if (success) {
564
+ this.notifyClients('build-complete', { success: true });
565
+ } else {
566
+ this.notifyClients('build-error', { success: false });
567
+ }
479
568
  }
480
569
  }
package/src/index.ts CHANGED
@@ -102,60 +102,61 @@ function isLargeProject(projectDir: string): boolean {
102
102
 
103
103
  // Função para gerar o arquivo de entrada para o esbuild
104
104
  function createEntryFile(projectDir: string, routes: (RouteConfig & { componentPath: string })[]): string {
105
- const tempDir = path.join(projectDir, '.hweb');
106
- fs.mkdirSync(tempDir, { recursive: true });
107
-
108
- const entryFilePath = path.join(tempDir, 'entry.client.js');
109
-
110
- // Verifica se layout
111
- const layout = getLayout();
112
-
113
- // Verifica se há notFound personalizado
114
- const notFound = getNotFound();
115
-
116
- // Gera imports dinâmicos para cada componente
117
- const imports = routes
118
- .map((route, index) => {
119
- const relativePath = path.relative(tempDir, route.componentPath).replace(/\\/g, '/');
120
- return `import route${index} from '${relativePath}';`;
121
- })
122
- .join('\n');
123
-
124
- // Import do layout se existir
125
- const layoutImport = layout
126
- ? `import LayoutComponent from '${path.relative(tempDir, layout.componentPath).replace(/\\/g, '/')}';`
127
- : '';
128
-
129
- // Import do notFound se existir
130
- const notFoundImport = notFound
131
- ? `import NotFoundComponent from '${path.relative(tempDir, notFound.componentPath).replace(/\\/g, '/')}';`
132
- : '';
133
-
134
- // Registra os componentes no window para o cliente acessar
135
- const componentRegistration = routes
136
- .map((route, index) => ` '${route.componentPath}': route${index}.component || route${index}.default?.component,`)
137
- .join('\n');
138
-
139
- // Registra o layout se existir
140
- const layoutRegistration = layout
141
- ? `window.__HWEB_LAYOUT__ = LayoutComponent.default || LayoutComponent;`
142
- : `window.__HWEB_LAYOUT__ = null;`;
143
-
144
- // Registra o notFound se existir
145
- const notFoundRegistration = notFound
146
- ? `window.__HWEB_NOT_FOUND__ = NotFoundComponent.default || NotFoundComponent;`
147
- : `window.__HWEB_NOT_FOUND__ = null;`;
148
-
149
- // Caminho correto para o entry.client.tsx
150
- const sdkDir = path.dirname(__dirname); // Vai para a pasta pai de src (onde está o hweb-sdk)
151
- const entryClientPath = path.join(sdkDir, 'src', 'client', 'entry.client.tsx');
152
- const relativeEntryPath = path.relative(tempDir, entryClientPath).replace(/\\/g, '/');
153
-
154
- // Import do DefaultNotFound do SDK
155
- const defaultNotFoundPath = path.join(sdkDir, 'src', 'client', 'DefaultNotFound.tsx');
156
- const relativeDefaultNotFoundPath = path.relative(tempDir, defaultNotFoundPath).replace(/\\/g, '/');
157
-
158
- const entryContent = `// Arquivo gerado automaticamente pelo hweb
105
+ try {
106
+ const tempDir = path.join(projectDir, '.hight', 'temp');
107
+
108
+ fs.mkdirSync(tempDir, { recursive: true });
109
+
110
+ const entryFilePath = path.join(tempDir, 'entry.client.js');
111
+ // Verifica se há layout
112
+ const layout = getLayout();
113
+
114
+ // Verifica se há notFound personalizado
115
+ const notFound = getNotFound();
116
+
117
+ // Gera imports dinâmicos para cada componente
118
+ const imports = routes
119
+ .map((route, index) => {
120
+ const relativePath = path.relative(tempDir, route.componentPath).replace(/\\/g, '/');
121
+ return `import route${index} from '${relativePath}';`;
122
+ })
123
+ .join('\n');
124
+
125
+ // Import do layout se existir
126
+ const layoutImport = layout
127
+ ? `import LayoutComponent from '${path.relative(tempDir, layout.componentPath).replace(/\\/g, '/')}';`
128
+ : '';
129
+
130
+ // Import do notFound se existir
131
+ const notFoundImport = notFound
132
+ ? `import NotFoundComponent from '${path.relative(tempDir, notFound.componentPath).replace(/\\/g, '/')}';`
133
+ : '';
134
+
135
+ // Registra os componentes no window para o cliente acessar
136
+ const componentRegistration = routes
137
+ .map((route, index) => ` '${route.componentPath}': route${index}.component || route${index}.default?.component,`)
138
+ .join('\n');
139
+
140
+ // Registra o layout se existir
141
+ const layoutRegistration = layout
142
+ ? `window.__HWEB_LAYOUT__ = LayoutComponent.default || LayoutComponent;`
143
+ : `window.__HWEB_LAYOUT__ = null;`;
144
+
145
+ // Registra o notFound se existir
146
+ const notFoundRegistration = notFound
147
+ ? `window.__HWEB_NOT_FOUND__ = NotFoundComponent.default || NotFoundComponent;`
148
+ : `window.__HWEB_NOT_FOUND__ = null;`;
149
+
150
+ // Caminho correto para o entry.client.tsx
151
+ const sdkDir = path.dirname(__dirname); // Vai para a pasta pai de src (onde está o hweb-sdk)
152
+ const entryClientPath = path.join(sdkDir, 'src', 'client', 'entry.client.tsx');
153
+ const relativeEntryPath = path.relative(tempDir, entryClientPath).replace(/\\/g, '/');
154
+
155
+ // Import do DefaultNotFound do SDK
156
+ const defaultNotFoundPath = path.join(sdkDir, 'src', 'client', 'DefaultNotFound.tsx');
157
+ const relativeDefaultNotFoundPath = path.relative(tempDir, defaultNotFoundPath).replace(/\\/g, '/');
158
+
159
+ const entryContent = `// Arquivo gerado automaticamente pelo hweb
159
160
  ${imports}
160
161
  ${layoutImport}
161
162
  ${notFoundImport}
@@ -179,9 +180,17 @@ window.__HWEB_DEFAULT_NOT_FOUND__ = DefaultNotFound;
179
180
  import '${relativeEntryPath}';
180
181
  `;
181
182
 
182
- fs.writeFileSync(entryFilePath, entryContent);
183
+ try {
184
+ fs.writeFileSync(entryFilePath, entryContent);
185
+ } catch (e) {
186
+ console.error("sdfijnsdfnijfsdijnfsdnijsdfnijfsdnijfsdnijfsdn", e)
187
+ }
183
188
 
184
- return entryFilePath;
189
+ return entryFilePath;
190
+ }catch (e){
191
+ Console.error("Error creating entry file:", e);
192
+ throw e;
193
+ }
185
194
  }
186
195
 
187
196
  export default function hweb(options: HightJSOptions) {
@@ -190,7 +199,7 @@ export default function hweb(options: HightJSOptions) {
190
199
  process.hight = options;
191
200
  const userWebDir = path.join(dir, 'src', 'web');
192
201
  const userWebRoutesDir = path.join(userWebDir, 'routes');
193
- const userBackendRoutesDir = path.join(userWebDir, 'backend', 'routes');
202
+ const userBackendRoutesDir = path.join(dir, 'src', 'backend', 'routes');
194
203
 
195
204
  /**
196
205
  * Executa middlewares sequencialmente e depois o handler final
@@ -289,7 +298,7 @@ export default function hweb(options: HightJSOptions) {
289
298
 
290
299
  const notFound = loadNotFound(userWebDir);
291
300
 
292
- const outDir = path.join(dir, 'hweb-dist');
301
+ const outDir = path.join(dir, '.hight');
293
302
  fs.mkdirSync(outDir, {recursive: true});
294
303
 
295
304
  entryPoint = createEntryFile(dir, frontendRoutes);
@@ -317,6 +326,11 @@ export default function hweb(options: HightJSOptions) {
317
326
  time.update(""); // limpa a linha
318
327
  time.end(` ${Colors.BgGreen} build ${Colors.Reset} Client build completed in ${elapsed}ms`);
319
328
 
329
+ // Notifica o hot reload manager que o build foi concluído
330
+ if (hotReloadManager) {
331
+ hotReloadManager.onBuildComplete(true);
332
+ }
333
+
320
334
  } else {
321
335
  const time = Console.dynamicLine(` ${Colors.BgYellow} watcher ${Colors.Reset} Starting client watch`);
322
336
  watchWithChunks(entryPoint, outDir, hotReloadManager!).catch(err => {
@@ -373,7 +387,7 @@ export default function hweb(options: HightJSOptions) {
373
387
  }
374
388
 
375
389
  // 2. Primeiro verifica se é um arquivo estático da pasta public
376
- if (pathname !== '/' && !pathname.startsWith('/api/') && !pathname.startsWith('/hweb-')) {
390
+ if (pathname !== '/' && !pathname.startsWith('/api/') && !pathname.startsWith('/.hight')) {
377
391
  const publicDir = path.join(dir, 'public');
378
392
  const filePath = path.join(publicDir, pathname);
379
393
 
@@ -417,12 +431,14 @@ export default function hweb(options: HightJSOptions) {
417
431
  }
418
432
  }
419
433
 
420
- // 3. Verifica se é um arquivo estático do hweb-dist
421
- if (pathname.startsWith('/hweb-dist/')) {
422
- const staticPath = path.join(dir, 'hweb-dist');
423
- const filePath = path.join(staticPath, pathname.replace('/hweb-dist/', ''));
434
+ // 3. Verifica se é um arquivo estático do .hight
435
+ if (pathname.startsWith('/_hight/')) {
436
+
437
+ const staticPath = path.join(dir, '.hight');
438
+ const filePath = path.join(staticPath, pathname.replace('/_hight/', ''));
424
439
 
425
440
  if (fs.existsSync(filePath)) {
441
+
426
442
  const ext = path.extname(filePath).toLowerCase();
427
443
  const contentTypes: Record<string, string> = {
428
444
  '.js': 'application/javascript',
package/src/renderer.tsx CHANGED
@@ -21,19 +21,128 @@ import type { GenericRequest } from './types/framework';
21
21
  import fs from 'fs';
22
22
  import path from 'path';
23
23
 
24
- // Funções para codificar/decodificar dados (disfarça o JSON no HTML)
25
- function encodeInitialData(data: any): string {
26
- // Converte para JSON, depois para base64, e adiciona um prefixo fake
24
+ // Função para gerar todas as meta tags
25
+ function generateMetaTags(metadata: Metadata): string {
26
+ const tags: string[] = [];
27
+
28
+ // Charset
29
+ tags.push(`<meta charset="${metadata.charset || 'UTF-8'}">`);
30
+
31
+ // Viewport
32
+ tags.push(`<meta name="viewport" content="${metadata.viewport || 'width=device-width, initial-scale=1.0'}">`);
33
+
34
+ // Description
35
+ if (metadata.description) {
36
+ tags.push(`<meta name="description" content="${metadata.description}">`);
37
+ }
38
+
39
+ // Keywords
40
+ if (metadata.keywords) {
41
+ const keywordsStr = Array.isArray(metadata.keywords)
42
+ ? metadata.keywords.join(', ')
43
+ : metadata.keywords;
44
+ tags.push(`<meta name="keywords" content="${keywordsStr}">`);
45
+ }
46
+
47
+ // Author
48
+ if (metadata.author) {
49
+ tags.push(`<meta name="author" content="${metadata.author}">`);
50
+ }
51
+
52
+ // Theme color
53
+ if (metadata.themeColor) {
54
+ tags.push(`<meta name="theme-color" content="${metadata.themeColor}">`);
55
+ }
56
+
57
+ // Robots
58
+ if (metadata.robots) {
59
+ tags.push(`<meta name="robots" content="${metadata.robots}">`);
60
+ }
61
+
62
+ // Canonical
63
+ if (metadata.canonical) {
64
+ tags.push(`<link rel="canonical" href="${metadata.canonical}">`);
65
+ }
66
+
67
+ // Favicon
68
+ if (metadata.favicon) {
69
+ tags.push(`<link rel="icon" href="${metadata.favicon}">`);
70
+ }
71
+
72
+ // Apple Touch Icon
73
+ if (metadata.appleTouchIcon) {
74
+ tags.push(`<link rel="apple-touch-icon" href="${metadata.appleTouchIcon}">`);
75
+ }
76
+
77
+ // Manifest
78
+ if (metadata.manifest) {
79
+ tags.push(`<link rel="manifest" href="${metadata.manifest}">`);
80
+ }
81
+
82
+ // Open Graph
83
+ if (metadata.openGraph) {
84
+ const og = metadata.openGraph;
85
+ if (og.title) tags.push(`<meta property="og:title" content="${og.title}">`);
86
+ if (og.description) tags.push(`<meta property="og:description" content="${og.description}">`);
87
+ if (og.type) tags.push(`<meta property="og:type" content="${og.type}">`);
88
+ if (og.url) tags.push(`<meta property="og:url" content="${og.url}">`);
89
+ if (og.siteName) tags.push(`<meta property="og:site_name" content="${og.siteName}">`);
90
+ if (og.locale) tags.push(`<meta property="og:locale" content="${og.locale}">`);
91
+
92
+ if (og.image) {
93
+ if (typeof og.image === 'string') {
94
+ tags.push(`<meta property="og:image" content="${og.image}">`);
95
+ } else {
96
+ tags.push(`<meta property="og:image" content="${og.image.url}">`);
97
+ if (og.image.width) tags.push(`<meta property="og:image:width" content="${og.image.width}">`);
98
+ if (og.image.height) tags.push(`<meta property="og:image:height" content="${og.image.height}">`);
99
+ if (og.image.alt) tags.push(`<meta property="og:image:alt" content="${og.image.alt}">`);
100
+ }
101
+ }
102
+ }
103
+
104
+ // Twitter Card
105
+ if (metadata.twitter) {
106
+ const tw = metadata.twitter;
107
+ if (tw.card) tags.push(`<meta name="twitter:card" content="${tw.card}">`);
108
+ if (tw.site) tags.push(`<meta name="twitter:site" content="${tw.site}">`);
109
+ if (tw.creator) tags.push(`<meta name="twitter:creator" content="${tw.creator}">`);
110
+ if (tw.title) tags.push(`<meta name="twitter:title" content="${tw.title}">`);
111
+ if (tw.description) tags.push(`<meta name="twitter:description" content="${tw.description}">`);
112
+ if (tw.image) tags.push(`<meta name="twitter:image" content="${tw.image}">`);
113
+ if (tw.imageAlt) tags.push(`<meta name="twitter:image:alt" content="${tw.imageAlt}">`);
114
+ }
115
+
116
+ // Custom meta tags
117
+ if (metadata.other) {
118
+ for (const [key, value] of Object.entries(metadata.other)) {
119
+ tags.push(`<meta name="${key}" content="${value}">`);
120
+ }
121
+ }
122
+
123
+ return tags.join('\n');
124
+ }
125
+
126
+ // Função para ofuscar dados (não é criptografia, apenas ofuscação)
127
+ function obfuscateData(data: any): string {
128
+ // 1. Serializa para JSON minificado
27
129
  const jsonStr = JSON.stringify(data);
130
+
131
+ // 2. Converte para base64
28
132
  const base64 = Buffer.from(jsonStr).toString('base64');
29
- return `hweb_${base64}_config`;
133
+
134
+ // 3. Adiciona um hash fake no início para parecer um token
135
+ const hash = Buffer.from(Date.now().toString()).toString('base64').substring(0, 8);
136
+
137
+ return `${hash}.${base64}`;
30
138
  }
31
139
 
32
- function createDecodeScript(): string {
33
- return `
34
-
35
- window.__HWEB_DECODE__ = function(encoded) { const base64 = encoded.replace('hweb_', '').replace('_config', ''); const jsonStr = atob(base64); return JSON.parse(jsonStr); };
36
- `;
140
+ // Função para criar script ofuscado
141
+ function createInitialDataScript(data: any): string {
142
+ const obfuscated = obfuscateData(data);
143
+
144
+ // Usa um atributo data-* ao invés de JSON visível
145
+ return `<script id="__hight_data__" type="text/plain" data-h="${obfuscated}"></script>`;
37
146
  }
38
147
 
39
148
  // Interface para opções de renderização apenas do cliente
@@ -74,27 +183,42 @@ export async function render({ req, route, params, allRoutes }: RenderOptions):
74
183
  initialParams: params,
75
184
  };
76
185
 
77
- // Codifica os dados para disfarçar
78
- const encodedData = encodeInitialData(initialData);
186
+ // Cria script JSON limpo
187
+ const initialDataScript = createInitialDataScript(initialData);
79
188
 
80
189
  // Script de hot reload apenas em desenvolvimento
81
190
  const hotReloadScript = !isProduction && hotReloadManager
82
191
  ? hotReloadManager.getClientScript()
83
192
  : '';
84
193
 
85
- const favicon = metadata.favicon ? `<link rel="icon" href="${metadata.favicon}">` : '';
194
+ // Gera todas as meta tags
195
+ const metaTags = generateMetaTags(metadata);
86
196
 
87
197
  // Determina quais arquivos JavaScript carregar
88
198
  const jsFiles = getJavaScriptFiles(req);
89
199
 
200
+ const htmlLang = metadata.language || 'pt-BR';
201
+
90
202
  // HTML base sem SSR - apenas o container e scripts para client-side rendering
91
- return `<!DOCTYPE html><html lang="pt-BR"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>${metadata.title || 'App hweb'}</title>${metadata.description ? `<meta name="description" content="${metadata.description}">` : ''}${favicon}</head><body><div id="root"></div><script>${createDecodeScript()}window.__HWEB_INITIAL_DATA__ = window.__HWEB_DECODE__('${encodedData}');</script>${jsFiles}${hotReloadScript}</body></html>`;
203
+ return `<!DOCTYPE html>
204
+ <html lang="${htmlLang}">
205
+ <head>
206
+ ${metaTags}
207
+ <title>${metadata.title || 'App hweb'}</title>
208
+ </head>
209
+ <body>
210
+ <div id="root"></div>
211
+ ${initialDataScript}
212
+ ${jsFiles}
213
+ ${hotReloadScript}
214
+ </body>
215
+ </html>`;
92
216
  }
93
217
 
94
218
  // Função para determinar quais arquivos JavaScript carregar
95
219
  function getJavaScriptFiles(req: GenericRequest): string {
96
220
  const projectDir = process.cwd();
97
- const distDir = path.join(projectDir, 'hweb-dist');
221
+ const distDir = path.join(projectDir, '.hight');
98
222
 
99
223
  try {
100
224
  // Verifica se existe um manifesto de chunks (gerado pelo ESBuild com splitting)
@@ -105,7 +229,7 @@ function getJavaScriptFiles(req: GenericRequest): string {
105
229
  const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
106
230
  const scripts = Object.values(manifest)
107
231
  .filter((file: any) => file.endsWith('.js'))
108
- .map((file: any) => `<script src="/hweb-dist/${file}"></script>`)
232
+ .map((file: any) => `<script src="/_hight/${file}"></script>`)
109
233
  .join('');
110
234
  return scripts;
111
235
  } else {
@@ -125,15 +249,15 @@ function getJavaScriptFiles(req: GenericRequest): string {
125
249
  if (jsFiles.length >= 1) {
126
250
  // Modo chunks sem manifesto
127
251
  return jsFiles
128
- .map(file => `<script src="/hweb-dist/${file}"></script>`)
252
+ .map(file => `<script src="/_hight/${file}"></script>`)
129
253
  .join('');
130
254
  } else {
131
255
  // Modo tradicional - único arquivo
132
- return '<script src="/hweb-dist/main.js"></script>';
256
+ return '<script src="/_hight/main.js"></script>';
133
257
  }
134
258
  }
135
259
  } catch (error) {
136
260
  // Fallback para o modo tradicional
137
- return '<script src="/hweb-dist/main.js"></script>';
261
+ return '<script src="/_hight/main.js"></script>';
138
262
  }
139
263
  }
package/src/router.ts CHANGED
@@ -671,6 +671,7 @@ export function setupWebSocketUpgrade(server: any, hotReloadManager?: any) {
671
671
  // Se não há listeners, ou se o hot-reload ainda não foi configurado, adiciona o nosso
672
672
  if (existingListeners.length === 0) {
673
673
  server.on('upgrade', (request: any, socket: any, head: Buffer) => {
674
+
674
675
  handleWebSocketUpgrade(request, socket, head, hotReloadManager);
675
676
  });
676
677
  }
@@ -689,6 +690,7 @@ function handleWebSocketUpgrade(request: any, socket: any, head: Buffer, hotRelo
689
690
 
690
691
  // Prioridade 1: Hot reload (sistema interno)
691
692
  if (pathname === '/hweb-hotreload/') {
693
+
692
694
  if (hotReloadManager) {
693
695
  hotReloadManager.handleUpgrade(request, socket, head);
694
696
  } else {
package/src/types.ts CHANGED
@@ -90,6 +90,12 @@ export interface HightConfig {
90
90
  * Padrão: 2048
91
91
  */
92
92
  maxUrlLength?: number;
93
+
94
+ /**
95
+ * Habilita o log de acesso HTTP (ex: GET /api/users 200 15ms).
96
+ * Padrão: false
97
+ */
98
+ accessLogging?: boolean;
93
99
  }
94
100
 
95
101
  /**
@@ -99,11 +105,61 @@ export type HightConfigFunction = (
99
105
  phase: string,
100
106
  context: { defaultConfig: HightConfig }
101
107
  ) => HightConfig | Promise<HightConfig>;
108
+
102
109
  export interface Metadata {
110
+ // Basic metadata
103
111
  title?: string;
104
112
  description?: string;
113
+ keywords?: string | string[];
114
+ author?: string;
105
115
  favicon?: string;
116
+
117
+ // Viewport and mobile
118
+ viewport?: string;
119
+ themeColor?: string;
120
+
121
+ // SEO
122
+ canonical?: string;
123
+ robots?: string;
124
+
125
+ // Open Graph (Facebook, LinkedIn, etc.)
126
+ openGraph?: {
127
+ title?: string;
128
+ description?: string;
129
+ type?: string;
130
+ url?: string;
131
+ image?: string | {
132
+ url: string;
133
+ width?: number;
134
+ height?: number;
135
+ alt?: string;
136
+ };
137
+ siteName?: string;
138
+ locale?: string;
139
+ };
140
+
141
+ // Twitter Card
142
+ twitter?: {
143
+ card?: 'summary' | 'summary_large_image' | 'app' | 'player';
144
+ site?: string;
145
+ creator?: string;
146
+ title?: string;
147
+ description?: string;
148
+ image?: string;
149
+ imageAlt?: string;
150
+ };
151
+
152
+ // Additional metadata
153
+ language?: string;
154
+ charset?: string;
155
+ appleTouchIcon?: string;
156
+ manifest?: string;
157
+
158
+ // Custom meta tags
159
+ other?: Record<string, string>;
106
160
  }
161
+
162
+
107
163
  export interface RouteConfig {
108
164
  pattern: string;
109
165
  component: ComponentType<any>;
@@ -1,3 +0,0 @@
1
- import {authRoutes} from "../../../auth";
2
-
3
- export default authRoutes
File without changes