hightjs 0.3.4 → 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.
@@ -185,7 +185,7 @@ class Console {
185
185
  }
186
186
  }
187
187
  static logWithout(level, colors, ...args) {
188
- const color = colors || level === Levels.ERROR ? Colors.BgRed : level === Levels.WARN ? Colors.BgYellow : level === Levels.INFO ? Colors.BgMagenta : level === Levels.SUCCESS ? Colors.BgGreen : Colors.BgCyan;
188
+ const color = colors ? colors : level === Levels.ERROR ? Colors.BgRed : level === Levels.WARN ? Colors.BgYellow : level === Levels.INFO ? Colors.BgMagenta : level === Levels.SUCCESS ? Colors.BgGreen : Colors.BgCyan;
189
189
  let output = "";
190
190
  for (const arg of args) {
191
191
  let msg = (arg instanceof Error) ? arg.stack : (typeof arg === 'string') ? arg : JSON.stringify(arg, null, 2);
@@ -113,11 +113,11 @@ program
113
113
  const app = teste.default({ dev: false, port: 3000, hostname: '0.0.0.0', framework: 'native' });
114
114
  await app.prepare();
115
115
  console.log('✅ Build complete\n');
116
- // 3. Copia a pasta hweb-dist para exported
117
- const distDir = path.join(projectDir, 'hweb-dist');
116
+ // 3. Copia a pasta .hight para exported
117
+ const distDir = path.join(projectDir, '.hight');
118
118
  if (fs.existsSync(distDir)) {
119
119
  console.log('📦 Copying JavaScript files...');
120
- const exportDistDir = path.join(exportDir, 'hweb-dist');
120
+ const exportDistDir = path.join(exportDir, '.hight');
121
121
  fs.mkdirSync(exportDistDir, { recursive: true });
122
122
  const files = fs.readdirSync(distDir);
123
123
  files.forEach(file => {
@@ -157,10 +157,49 @@ const DEV_INDICATOR_CORNERS = [
157
157
  function DevIndicator() {
158
158
  const [corner, setCorner] = (0, react_1.useState)(3); // Canto atual (0-3)
159
159
  const [isDragging, setIsDragging] = (0, react_1.useState)(false); // Estado de arrastar
160
+ const [isBuilding, setIsBuilding] = (0, react_1.useState)(false); // Estado de build
160
161
  // Posição visual do indicador durante o arraste
161
162
  const [position, setPosition] = (0, react_1.useState)({ top: 0, left: 0 });
162
163
  const indicatorRef = (0, react_1.useRef)(null);
163
164
  const dragStartRef = (0, react_1.useRef)(null);
165
+ // Escuta eventos de hot reload para mostrar estado de build
166
+ (0, react_1.useEffect)(() => {
167
+ if (typeof window === 'undefined')
168
+ return;
169
+ const handleHotReloadMessage = (event) => {
170
+ try {
171
+ const message = JSON.parse(event.data);
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
+ // Quando o build termina ou servidor fica pronto, desativa loading
179
+ if (message.type === 'server-ready' || message.type === 'build-complete') {
180
+ setIsBuilding(false);
181
+ }
182
+ }
183
+ catch (e) {
184
+ // Ignora mensagens que não são JSON
185
+ }
186
+ };
187
+ // Intercepta mensagens WebSocket
188
+ const originalWebSocket = window.WebSocket;
189
+ window.WebSocket = class extends originalWebSocket {
190
+ constructor(url, protocols) {
191
+ super(url, protocols);
192
+ this.addEventListener('message', (event) => {
193
+ if (url.toString().includes('hweb-hotreload')) {
194
+ handleHotReloadMessage(event);
195
+ }
196
+ });
197
+ }
198
+ };
199
+ return () => {
200
+ window.WebSocket = originalWebSocket;
201
+ };
202
+ }, []);
164
203
  // --- Estilos Dinâmicos ---
165
204
  const getIndicatorStyle = () => {
166
205
  const baseStyle = {
@@ -169,17 +208,22 @@ function DevIndicator() {
169
208
  width: DEV_INDICATOR_SIZE,
170
209
  height: DEV_INDICATOR_SIZE,
171
210
  borderRadius: '50%',
172
- background: 'linear-gradient(135deg, #8e2de2, #4a00e0)', // Gradiente Roxo
211
+ background: isBuilding
212
+ ? 'linear-gradient(135deg, #f093fb, #f5576c)' // Gradiente Rosa/Vermelho quando building
213
+ : 'linear-gradient(135deg, #8e2de2, #4a00e0)', // Gradiente Roxo normal
173
214
  color: 'white',
174
215
  fontWeight: 'bold',
175
216
  fontSize: 28,
176
- boxShadow: '0 4px 15px rgba(0,0,0,0.2)',
217
+ boxShadow: isBuilding
218
+ ? '0 4px 25px rgba(245, 87, 108, 0.6)' // Shadow mais forte quando building
219
+ : '0 4px 15px rgba(0,0,0,0.2)',
177
220
  display: 'flex',
178
221
  alignItems: 'center',
179
222
  justifyContent: 'center',
180
223
  cursor: isDragging ? 'grabbing' : 'grab',
181
224
  userSelect: 'none',
182
- transition: isDragging ? 'none' : 'all 0.3s ease-out', // Animação suave ao soltar
225
+ transition: isDragging ? 'none' : 'all 0.3s ease-out',
226
+ animation: isBuilding ? 'hweb-pulse 1.5s ease-in-out infinite' : 'none',
183
227
  };
184
228
  if (isDragging) {
185
229
  return {
@@ -259,13 +303,59 @@ function DevIndicator() {
259
303
  window.removeEventListener('mouseup', handleMouseUp);
260
304
  };
261
305
  }, [isDragging, handleMouseMove, handleMouseUp]);
262
- return ((0, jsx_runtime_1.jsx)("div", { ref: indicatorRef, style: getIndicatorStyle(), onMouseDown: handleMouseDown, title: "Modo Dev HightJS", children: "H" }));
306
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("style", { children: `
307
+ @keyframes hweb-pulse {
308
+ 0%, 100% {
309
+ transform: scale(1);
310
+ opacity: 1;
311
+ }
312
+ 50% {
313
+ transform: scale(1.1);
314
+ opacity: 0.8;
315
+ }
316
+ }
317
+
318
+ @keyframes hweb-spin {
319
+ from {
320
+ transform: rotate(0deg);
321
+ }
322
+ to {
323
+ transform: rotate(360deg);
324
+ }
325
+ }
326
+ ` }), (0, jsx_runtime_1.jsx)("div", { ref: indicatorRef, style: getIndicatorStyle(), onMouseDown: handleMouseDown, title: isBuilding ? "Building..." : "Modo Dev HightJS", children: isBuilding ? ((0, jsx_runtime_1.jsx)("span", { style: { animation: 'hweb-spin 1s linear infinite' }, children: "\u27F3" })) : ('H') })] }));
263
327
  }
264
328
  // --- Inicialização do Cliente (CSR - Client-Side Rendering) ---
329
+ function deobfuscateData(obfuscated) {
330
+ try {
331
+ // Remove o hash fake
332
+ const parts = obfuscated.split('.');
333
+ const base64 = parts.length > 1 ? parts[1] : parts[0];
334
+ // Decodifica base64
335
+ const jsonStr = atob(base64);
336
+ // Parse JSON
337
+ return JSON.parse(jsonStr);
338
+ }
339
+ catch (error) {
340
+ console.error('[hweb] Failed to decode data:', error);
341
+ return null;
342
+ }
343
+ }
265
344
  function initializeClient() {
266
- const initialData = window.__HWEB_INITIAL_DATA__;
345
+ // os dados do atributo data-h
346
+ const dataElement = document.getElementById('__hight_data__');
347
+ if (!dataElement) {
348
+ console.error('[hweb] Initial data script not found.');
349
+ return;
350
+ }
351
+ const obfuscated = dataElement.getAttribute('data-h');
352
+ if (!obfuscated) {
353
+ console.error('[hweb] Data attribute not found.');
354
+ return;
355
+ }
356
+ const initialData = deobfuscateData(obfuscated);
267
357
  if (!initialData) {
268
- console.error('[hweb] Initial data not found on page.');
358
+ console.error('[hweb] Failed to parse initial data.');
269
359
  return;
270
360
  }
271
361
  // Cria o mapa de componentes dinamicamente a partir dos módulos carregados
package/dist/helpers.js CHANGED
@@ -119,6 +119,7 @@ async function loadHightConfig(projectDir, phase) {
119
119
  serverTimeout: 35000,
120
120
  individualRequestTimeout: 30000,
121
121
  maxUrlLength: 2048,
122
+ accessLogging: true,
122
123
  };
123
124
  try {
124
125
  // Tenta primeiro .ts, depois .js
@@ -250,6 +251,9 @@ async function initNativeServer(hwebApp, options, port, hostname) {
250
251
  // Extraímos a lógica principal para uma variável
251
252
  // para que possa ser usada tanto pelo servidor HTTP quanto HTTPS.
252
253
  const requestListener = async (req, res) => {
254
+ const requestStartTime = Date.now();
255
+ const method = req.method || 'GET';
256
+ const url = req.url || '/';
253
257
  // Configurações de segurança básicas
254
258
  res.setHeader('X-Content-Type-Options', 'nosniff');
255
259
  res.setHeader('X-Frame-Options', 'DENY');
@@ -264,10 +268,45 @@ async function initNativeServer(hwebApp, options, port, hostname) {
264
268
  req.setTimeout(hightConfig.individualRequestTimeout || 30000, () => {
265
269
  res.statusCode = 408; // Request Timeout
266
270
  res.end('Request timeout');
271
+ // Log de timeout
272
+ if (hightConfig.accessLogging) {
273
+ const duration = Date.now() - requestStartTime;
274
+ console_1.default.info(`${console_1.Colors.FgYellow}${method}${console_1.Colors.Reset} ${url} ${console_1.Colors.FgRed}408${console_1.Colors.Reset} ${console_1.Colors.FgGray}${duration}ms${console_1.Colors.Reset}`);
275
+ }
267
276
  });
277
+ // Intercepta o método end() para logar quando a resposta for enviada
278
+ const originalEnd = res.end.bind(res);
279
+ let hasEnded = false;
280
+ res.end = function (...args) {
281
+ if (!hasEnded && hightConfig.accessLogging) {
282
+ hasEnded = true;
283
+ const duration = Date.now() - requestStartTime;
284
+ const statusCode = res.statusCode || 200;
285
+ // Define cor baseada no status code
286
+ let statusColor = console_1.Colors.FgGreen; // 2xx
287
+ if (statusCode >= 500)
288
+ statusColor = console_1.Colors.FgRed; // 5xx
289
+ else if (statusCode >= 400)
290
+ statusColor = console_1.Colors.FgYellow; // 4xx
291
+ else if (statusCode >= 300)
292
+ statusColor = console_1.Colors.FgCyan; // 3xx
293
+ // Formata o método com cor
294
+ let methodColor = console_1.Colors.BgCyan;
295
+ if (method === 'POST')
296
+ methodColor = console_1.Colors.BgGreen;
297
+ else if (method === 'PUT')
298
+ methodColor = console_1.Colors.BgYellow;
299
+ else if (method === 'DELETE')
300
+ methodColor = console_1.Colors.BgRed;
301
+ else if (method === 'PATCH')
302
+ methodColor = console_1.Colors.BgMagenta;
303
+ console_1.default.logCustomLevel(method, true, methodColor, `${url} ${statusColor}${statusCode}${console_1.Colors.Reset} ${console_1.Colors.FgGray}${duration}ms${console_1.Colors.Reset}`);
304
+ }
305
+ // @ts-ignore
306
+ return originalEnd.apply(this, args);
307
+ };
268
308
  try {
269
309
  // Validação básica de URL (usa configuração personalizada)
270
- const url = req.url || '/';
271
310
  const maxUrlLength = hightConfig.maxUrlLength || 2048;
272
311
  if (url.length > maxUrlLength) {
273
312
  res.statusCode = 414; // URI Too Long
package/dist/hotReload.js CHANGED
@@ -312,19 +312,21 @@ class HotReloadManager {
312
312
  if (typeof window !== 'undefined') {
313
313
  let ws;
314
314
  let reconnectAttempts = 0;
315
- let maxReconnectInterval = 30000; // 30 segundos max
316
- let reconnectInterval = 1000; // Começa com 1 segundo
315
+ let maxReconnectInterval = 30000;
316
+ let reconnectInterval = 1000;
317
317
  let reconnectTimer;
318
318
  let isConnected = false;
319
319
 
320
320
  function connect() {
321
- // Evita múltiplas tentativas simultâneas
321
+ const url = window.location; // Objeto com info da URL atual
322
+ const protocol = url.protocol === "https:" ? "wss:" : "ws:"; // Usa wss se for https
323
+ const wsUrl = protocol + '//' + url.host + '/hweb-hotreload/';
322
324
  if (ws && (ws.readyState === WebSocket.CONNECTING || ws.readyState === WebSocket.OPEN)) {
323
325
  return;
324
326
  }
325
327
 
326
328
  try {
327
- ws = new WebSocket('ws://localhost:3000/hweb-hotreload/');
329
+ ws = new WebSocket(wsUrl);
328
330
 
329
331
  ws.onopen = function() {
330
332
  console.log('🔌 Hot-reload connected');
@@ -454,6 +456,13 @@ class HotReloadManager {
454
456
  this.buildCompleteResolve = null;
455
457
  }
456
458
  this.isBuilding = false;
459
+ // Notifica os clientes que o build terminou
460
+ if (success) {
461
+ this.notifyClients('build-complete', { success: true });
462
+ }
463
+ else {
464
+ this.notifyClients('build-error', { success: false });
465
+ }
457
466
  }
458
467
  }
459
468
  exports.HotReloadManager = HotReloadManager;
package/dist/index.js CHANGED
@@ -260,7 +260,7 @@ function hweb(options) {
260
260
  // Carrega layout.tsx ANTES de criar o entry file
261
261
  const layout = (0, router_1.loadLayout)(userWebDir);
262
262
  const notFound = (0, router_1.loadNotFound)(userWebDir);
263
- const outDir = path_1.default.join(dir, 'hweb-dist');
263
+ const outDir = path_1.default.join(dir, '.hight');
264
264
  fs_1.default.mkdirSync(outDir, { recursive: true });
265
265
  entryPoint = createEntryFile(dir, frontendRoutes);
266
266
  clearInterval(spinner1);
@@ -280,6 +280,10 @@ function hweb(options) {
280
280
  clearInterval(spinner); // para o spinner
281
281
  time.update(""); // limpa a linha
282
282
  time.end(` ${console_1.Colors.BgGreen} build ${console_1.Colors.Reset} Client build completed in ${elapsed}ms`);
283
+ // Notifica o hot reload manager que o build foi concluído
284
+ if (hotReloadManager) {
285
+ hotReloadManager.onBuildComplete(true);
286
+ }
283
287
  }
284
288
  else {
285
289
  const time = console_1.default.dynamicLine(` ${console_1.Colors.BgYellow} watcher ${console_1.Colors.Reset} Starting client watch`);
@@ -330,7 +334,7 @@ function hweb(options) {
330
334
  return;
331
335
  }
332
336
  // 2. Primeiro verifica se é um arquivo estático da pasta public
333
- if (pathname !== '/' && !pathname.startsWith('/api/') && !pathname.startsWith('/hweb-')) {
337
+ if (pathname !== '/' && !pathname.startsWith('/api/') && !pathname.startsWith('/.hight')) {
334
338
  const publicDir = path_1.default.join(dir, 'public');
335
339
  const filePath = path_1.default.join(publicDir, pathname);
336
340
  if (fs_1.default.existsSync(filePath) && fs_1.default.statSync(filePath).isFile()) {
@@ -372,10 +376,10 @@ function hweb(options) {
372
376
  return;
373
377
  }
374
378
  }
375
- // 3. Verifica se é um arquivo estático do hweb-dist
376
- if (pathname.startsWith('/hweb-dist/')) {
377
- const staticPath = path_1.default.join(dir, 'hweb-dist');
378
- const filePath = path_1.default.join(staticPath, pathname.replace('/hweb-dist/', ''));
379
+ // 3. Verifica se é um arquivo estático do .hight
380
+ if (pathname.startsWith('/_hight/')) {
381
+ const staticPath = path_1.default.join(dir, '.hight');
382
+ const filePath = path_1.default.join(staticPath, pathname.replace('/_hight/', ''));
379
383
  if (fs_1.default.existsSync(filePath)) {
380
384
  const ext = path_1.default.extname(filePath).toLowerCase();
381
385
  const contentTypes = {
package/dist/renderer.js CHANGED
@@ -7,18 +7,123 @@ exports.render = render;
7
7
  const router_1 = require("./router");
8
8
  const fs_1 = __importDefault(require("fs"));
9
9
  const path_1 = __importDefault(require("path"));
10
- // Funções para codificar/decodificar dados (disfarça o JSON no HTML)
11
- function encodeInitialData(data) {
12
- // Converte para JSON, depois para base64, e adiciona um prefixo fake
10
+ // Função para gerar todas as meta tags
11
+ function generateMetaTags(metadata) {
12
+ const tags = [];
13
+ // Charset
14
+ tags.push(`<meta charset="${metadata.charset || 'UTF-8'}">`);
15
+ // Viewport
16
+ tags.push(`<meta name="viewport" content="${metadata.viewport || 'width=device-width, initial-scale=1.0'}">`);
17
+ // Description
18
+ if (metadata.description) {
19
+ tags.push(`<meta name="description" content="${metadata.description}">`);
20
+ }
21
+ // Keywords
22
+ if (metadata.keywords) {
23
+ const keywordsStr = Array.isArray(metadata.keywords)
24
+ ? metadata.keywords.join(', ')
25
+ : metadata.keywords;
26
+ tags.push(`<meta name="keywords" content="${keywordsStr}">`);
27
+ }
28
+ // Author
29
+ if (metadata.author) {
30
+ tags.push(`<meta name="author" content="${metadata.author}">`);
31
+ }
32
+ // Theme color
33
+ if (metadata.themeColor) {
34
+ tags.push(`<meta name="theme-color" content="${metadata.themeColor}">`);
35
+ }
36
+ // Robots
37
+ if (metadata.robots) {
38
+ tags.push(`<meta name="robots" content="${metadata.robots}">`);
39
+ }
40
+ // Canonical
41
+ if (metadata.canonical) {
42
+ tags.push(`<link rel="canonical" href="${metadata.canonical}">`);
43
+ }
44
+ // Favicon
45
+ if (metadata.favicon) {
46
+ tags.push(`<link rel="icon" href="${metadata.favicon}">`);
47
+ }
48
+ // Apple Touch Icon
49
+ if (metadata.appleTouchIcon) {
50
+ tags.push(`<link rel="apple-touch-icon" href="${metadata.appleTouchIcon}">`);
51
+ }
52
+ // Manifest
53
+ if (metadata.manifest) {
54
+ tags.push(`<link rel="manifest" href="${metadata.manifest}">`);
55
+ }
56
+ // Open Graph
57
+ if (metadata.openGraph) {
58
+ const og = metadata.openGraph;
59
+ if (og.title)
60
+ tags.push(`<meta property="og:title" content="${og.title}">`);
61
+ if (og.description)
62
+ tags.push(`<meta property="og:description" content="${og.description}">`);
63
+ if (og.type)
64
+ tags.push(`<meta property="og:type" content="${og.type}">`);
65
+ if (og.url)
66
+ tags.push(`<meta property="og:url" content="${og.url}">`);
67
+ if (og.siteName)
68
+ tags.push(`<meta property="og:site_name" content="${og.siteName}">`);
69
+ if (og.locale)
70
+ tags.push(`<meta property="og:locale" content="${og.locale}">`);
71
+ if (og.image) {
72
+ if (typeof og.image === 'string') {
73
+ tags.push(`<meta property="og:image" content="${og.image}">`);
74
+ }
75
+ else {
76
+ tags.push(`<meta property="og:image" content="${og.image.url}">`);
77
+ if (og.image.width)
78
+ tags.push(`<meta property="og:image:width" content="${og.image.width}">`);
79
+ if (og.image.height)
80
+ tags.push(`<meta property="og:image:height" content="${og.image.height}">`);
81
+ if (og.image.alt)
82
+ tags.push(`<meta property="og:image:alt" content="${og.image.alt}">`);
83
+ }
84
+ }
85
+ }
86
+ // Twitter Card
87
+ if (metadata.twitter) {
88
+ const tw = metadata.twitter;
89
+ if (tw.card)
90
+ tags.push(`<meta name="twitter:card" content="${tw.card}">`);
91
+ if (tw.site)
92
+ tags.push(`<meta name="twitter:site" content="${tw.site}">`);
93
+ if (tw.creator)
94
+ tags.push(`<meta name="twitter:creator" content="${tw.creator}">`);
95
+ if (tw.title)
96
+ tags.push(`<meta name="twitter:title" content="${tw.title}">`);
97
+ if (tw.description)
98
+ tags.push(`<meta name="twitter:description" content="${tw.description}">`);
99
+ if (tw.image)
100
+ tags.push(`<meta name="twitter:image" content="${tw.image}">`);
101
+ if (tw.imageAlt)
102
+ tags.push(`<meta name="twitter:image:alt" content="${tw.imageAlt}">`);
103
+ }
104
+ // Custom meta tags
105
+ if (metadata.other) {
106
+ for (const [key, value] of Object.entries(metadata.other)) {
107
+ tags.push(`<meta name="${key}" content="${value}">`);
108
+ }
109
+ }
110
+ return tags.join('\n');
111
+ }
112
+ // Função para ofuscar dados (não é criptografia, apenas ofuscação)
113
+ function obfuscateData(data) {
114
+ // 1. Serializa para JSON minificado
13
115
  const jsonStr = JSON.stringify(data);
116
+ // 2. Converte para base64
14
117
  const base64 = Buffer.from(jsonStr).toString('base64');
15
- return `hweb_${base64}_config`;
118
+ // 3. Adiciona um hash fake no início para parecer um token
119
+ const hash = Buffer.from(Date.now().toString()).toString('base64').substring(0, 8);
120
+ return `${hash}.${base64}`;
16
121
  }
17
- function createDecodeScript() {
18
- return `
19
-
20
- window.__HWEB_DECODE__ = function(encoded) { const base64 = encoded.replace('hweb_', '').replace('_config', ''); const jsonStr = atob(base64); return JSON.parse(jsonStr); };
21
- `;
122
+ // Função para criar script ofuscado
123
+ function createInitialDataScript(data) {
124
+ const obfuscated = obfuscateData(data);
125
+ // Usa um atributo data-* ao invés de JSON visível
126
+ return `<script id="__hight_data__" type="text/plain" data-h="${obfuscated}"></script>`;
22
127
  }
23
128
  async function render({ req, route, params, allRoutes }) {
24
129
  const { generateMetadata } = route;
@@ -43,22 +148,36 @@ async function render({ req, route, params, allRoutes }) {
43
148
  initialComponentPath: route.componentPath,
44
149
  initialParams: params,
45
150
  };
46
- // Codifica os dados para disfarçar
47
- const encodedData = encodeInitialData(initialData);
151
+ // Cria script JSON limpo
152
+ const initialDataScript = createInitialDataScript(initialData);
48
153
  // Script de hot reload apenas em desenvolvimento
49
154
  const hotReloadScript = !isProduction && hotReloadManager
50
155
  ? hotReloadManager.getClientScript()
51
156
  : '';
52
- const favicon = metadata.favicon ? `<link rel="icon" href="${metadata.favicon}">` : '';
157
+ // Gera todas as meta tags
158
+ const metaTags = generateMetaTags(metadata);
53
159
  // Determina quais arquivos JavaScript carregar
54
160
  const jsFiles = getJavaScriptFiles(req);
161
+ const htmlLang = metadata.language || 'pt-BR';
55
162
  // HTML base sem SSR - apenas o container e scripts para client-side rendering
56
- 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>`;
163
+ return `<!DOCTYPE html>
164
+ <html lang="${htmlLang}">
165
+ <head>
166
+ ${metaTags}
167
+ <title>${metadata.title || 'App hweb'}</title>
168
+ </head>
169
+ <body>
170
+ <div id="root"></div>
171
+ ${initialDataScript}
172
+ ${jsFiles}
173
+ ${hotReloadScript}
174
+ </body>
175
+ </html>`;
57
176
  }
58
177
  // Função para determinar quais arquivos JavaScript carregar
59
178
  function getJavaScriptFiles(req) {
60
179
  const projectDir = process.cwd();
61
- const distDir = path_1.default.join(projectDir, 'hweb-dist');
180
+ const distDir = path_1.default.join(projectDir, '.hight');
62
181
  try {
63
182
  // Verifica se existe um manifesto de chunks (gerado pelo ESBuild com splitting)
64
183
  const manifestPath = path_1.default.join(distDir, 'manifest.json');
@@ -67,7 +186,7 @@ function getJavaScriptFiles(req) {
67
186
  const manifest = JSON.parse(fs_1.default.readFileSync(manifestPath, 'utf8'));
68
187
  const scripts = Object.values(manifest)
69
188
  .filter((file) => file.endsWith('.js'))
70
- .map((file) => `<script src="/hweb-dist/${file}"></script>`)
189
+ .map((file) => `<script src="/_hight/${file}"></script>`)
71
190
  .join('');
72
191
  return scripts;
73
192
  }
@@ -91,17 +210,17 @@ function getJavaScriptFiles(req) {
91
210
  if (jsFiles.length >= 1) {
92
211
  // Modo chunks sem manifesto
93
212
  return jsFiles
94
- .map(file => `<script src="/hweb-dist/${file}"></script>`)
213
+ .map(file => `<script src="/_hight/${file}"></script>`)
95
214
  .join('');
96
215
  }
97
216
  else {
98
217
  // Modo tradicional - único arquivo
99
- return '<script src="/hweb-dist/main.js"></script>';
218
+ return '<script src="/_hight/main.js"></script>';
100
219
  }
101
220
  }
102
221
  }
103
222
  catch (error) {
104
223
  // Fallback para o modo tradicional
105
- return '<script src="/hweb-dist/main.js"></script>';
224
+ return '<script src="/_hight/main.js"></script>';
106
225
  }
107
226
  }
package/dist/types.d.ts CHANGED
@@ -62,6 +62,11 @@ export interface HightConfig {
62
62
  * Padrão: 2048
63
63
  */
64
64
  maxUrlLength?: number;
65
+ /**
66
+ * Habilita o log de acesso HTTP (ex: GET /api/users 200 15ms).
67
+ * Padrão: false
68
+ */
69
+ accessLogging?: boolean;
65
70
  }
66
71
  /**
67
72
  * Tipo da função de configuração que pode ser exportada no hightjs.config.js
@@ -72,7 +77,41 @@ export type HightConfigFunction = (phase: string, context: {
72
77
  export interface Metadata {
73
78
  title?: string;
74
79
  description?: string;
80
+ keywords?: string | string[];
81
+ author?: string;
75
82
  favicon?: string;
83
+ viewport?: string;
84
+ themeColor?: string;
85
+ canonical?: string;
86
+ robots?: string;
87
+ openGraph?: {
88
+ title?: string;
89
+ description?: string;
90
+ type?: string;
91
+ url?: string;
92
+ image?: string | {
93
+ url: string;
94
+ width?: number;
95
+ height?: number;
96
+ alt?: string;
97
+ };
98
+ siteName?: string;
99
+ locale?: string;
100
+ };
101
+ twitter?: {
102
+ card?: 'summary' | 'summary_large_image' | 'app' | 'player';
103
+ site?: string;
104
+ creator?: string;
105
+ title?: string;
106
+ description?: string;
107
+ image?: string;
108
+ imageAlt?: string;
109
+ };
110
+ language?: string;
111
+ charset?: string;
112
+ appleTouchIcon?: string;
113
+ manifest?: string;
114
+ other?: Record<string, string>;
76
115
  }
77
116
  export interface RouteConfig {
78
117
  pattern: string;
package/docs/config.md CHANGED
@@ -63,9 +63,18 @@ export default config;
63
63
  | `serverTimeout` | `number` | `35000` | Timeout geral do servidor em ms |
64
64
  | `individualRequestTimeout` | `number` | `30000` | Timeout por requisição individual em ms |
65
65
  | `maxUrlLength` | `number` | `2048` | Tamanho máximo da URL em caracteres |
66
+ | `accessLogging` | `boolean` | `false` | Habilita logs de acesso HTTP (ex: `GET /api/users 200 15ms`) |
66
67
 
67
68
  ## 🎯 Casos de Uso
68
69
 
70
+ ### Habilitar Logs de Acesso
71
+
72
+ ```typescript
73
+ const config: HightConfig = {
74
+ accessLogging: true, // Mostra: GET /api/users 200 15ms
75
+ };
76
+ ```
77
+
69
78
  ### Aumentar Timeouts para Debugging
70
79
 
71
80
  ```typescript
@@ -153,6 +162,9 @@ const hightConfig: HightConfigFunction = (phase, { defaultConfig }) => {
153
162
 
154
163
  // Ajustes por ambiente
155
164
  if (phase === 'development') {
165
+ // Habilita logs de acesso em dev
166
+ config.accessLogging = true;
167
+
156
168
  // Timeouts maiores para debugging
157
169
  config.requestTimeout = 120000;
158
170
  config.individualRequestTimeout = 120000;
@@ -160,6 +172,9 @@ const hightConfig: HightConfigFunction = (phase, { defaultConfig }) => {
160
172
  // URLs longas para testes
161
173
  config.maxUrlLength = 4096;
162
174
  } else if (phase === 'production') {
175
+ // Logs de acesso também podem ser úteis em produção
176
+ config.accessLogging = true;
177
+
163
178
  // Mais restritivo em produção
164
179
  config.maxHeadersCount = 50;
165
180
  config.maxUrlLength = 1024;
@@ -47,6 +47,12 @@ const hightConfig: HightConfigFunction = (phase, { defaultConfig }) => {
47
47
  * Default: 2048
48
48
  */
49
49
  maxUrlLength: 2048,
50
+
51
+ /**
52
+ * Enable HTTP access logging (e.g., GET /api/users 200 15ms)
53
+ * Default: false
54
+ */
55
+ accessLogging: true,
50
56
  };
51
57
 
52
58
  // You can customize settings based on the phase
@@ -25,7 +25,7 @@
25
25
  },
26
26
  "..": {
27
27
  "name": "hightjs",
28
- "version": "0.3.3",
28
+ "version": "0.3.4",
29
29
  "license": "Apache-2.0",
30
30
  "dependencies": {
31
31
  "@fastify/cookie": "^11.0.2",
@@ -4,8 +4,8 @@
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
- "start": "hight start --ssl --port 443",
8
- "dev": "hight dev --ssl --port 443"
7
+ "start": "hight start --port 80",
8
+ "dev": "hight dev --port 80"
9
9
  },
10
10
  "author": "itsmuzin",
11
11
  "license": "ISC",