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
package/src/index.ts CHANGED
@@ -61,6 +61,9 @@ export { app } from './helpers';
61
61
  // Exporta o sistema de WebSocket
62
62
  export type { WebSocketContext, WebSocketHandler } from './types';
63
63
 
64
+ // Exporta os tipos de configuração
65
+ export type { HightConfig, HightConfigFunction } from './types';
66
+
64
67
  // Função para verificar se o projeto é grande o suficiente para se beneficiar de chunks
65
68
  function isLargeProject(projectDir: string): boolean {
66
69
  try {
@@ -254,7 +257,7 @@ export default function hweb(options: HightJSOptions) {
254
257
 
255
258
  // Adiciona callback para recarregar TUDO quando qualquer arquivo mudar
256
259
  hotReloadManager.onBackendApiChange(() => {
257
- Console.info('🔄 Recarregando backend e dependências...');
260
+
258
261
 
259
262
  loadBackendRoutes(userBackendRoutesDir);
260
263
  processWebSocketRoutes(); // Processa rotas WS após recarregar backend
@@ -262,18 +265,16 @@ export default function hweb(options: HightJSOptions) {
262
265
 
263
266
  // Adiciona callback para regenerar entry file quando frontend mudar
264
267
  hotReloadManager.onFrontendChange(() => {
265
- Console.info('🔄 Regenerando frontend...');
266
268
  regenerateEntryFile();
267
269
  });
268
270
  }
269
271
  const now = Date.now();
270
- const timee = Console.dynamicLine(` ${Colors.BgYellow} router ${Colors.Reset} Carregando rotas e componentes do frontend e backend`);
272
+ const timee = Console.dynamicLine(` ${Colors.BgYellow} router ${Colors.Reset} Loading routes and components`);
271
273
  const spinnerFrames1 = ['|', '/', '-', '\\'];
272
274
  let frameIndex1 = 0;
273
275
 
274
276
  const spinner1 = setInterval(() => {
275
- Console.info('aaaa')
276
- timee.update(` ${Colors.FgYellow}${spinnerFrames1[frameIndex1]}${Colors.Reset} Carregando rotas e componentes...`);
277
+ timee.update(` ${Colors.FgYellow}${spinnerFrames1[frameIndex1]}${Colors.Reset} Loading routes and components...`);
277
278
  frameIndex1 = (frameIndex1 + 1) % spinnerFrames1.length;
278
279
  }, 100); // muda a cada 100ms
279
280
  // ORDEM IMPORTANTE: Carrega TUDO antes de criar o arquivo de entrada
@@ -288,23 +289,23 @@ export default function hweb(options: HightJSOptions) {
288
289
 
289
290
  const notFound = loadNotFound(userWebDir);
290
291
 
291
- const outDir = path.join(dir, 'hweb-dist');
292
+ const outDir = path.join(dir, '.hight');
292
293
  fs.mkdirSync(outDir, {recursive: true});
293
294
 
294
295
  entryPoint = createEntryFile(dir, frontendRoutes);
295
296
  clearInterval(spinner1)
296
- timee.end(` ${Colors.BgGreen} router ${Colors.Reset} Rotas e componentes carregados em ${Date.now() - now}ms`);
297
+ timee.end(` ${Colors.BgGreen} router ${Colors.Reset} Routes and components loaded in ${Date.now() - now}ms`);
297
298
 
298
299
 
299
300
  if (isProduction) {
300
- const time = Console.dynamicLine(` ${Colors.BgYellow} build ${Colors.Reset} Iniciando build do cliente para produção`);
301
+ const time = Console.dynamicLine(` ${Colors.BgYellow} build ${Colors.Reset} Starting client build`);
301
302
 
302
303
  // Spinner
303
304
  const spinnerFrames = ['|', '/', '-', '\\'];
304
305
  let frameIndex = 0;
305
306
 
306
307
  const spinner = setInterval(() => {
307
- time.update(` ${Colors.FgYellow}${spinnerFrames[frameIndex]}${Colors.Reset} Buildando para produção...`);
308
+ time.update(` ${Colors.FgYellow}${spinnerFrames[frameIndex]}${Colors.Reset} Building...`);
308
309
  frameIndex = (frameIndex + 1) % spinnerFrames.length;
309
310
  }, 100); // muda a cada 100ms
310
311
 
@@ -314,14 +315,19 @@ export default function hweb(options: HightJSOptions) {
314
315
 
315
316
  clearInterval(spinner); // para o spinner
316
317
  time.update(""); // limpa a linha
317
- time.end(` ${Colors.BgGreen} build ${Colors.Reset} Build do cliente concluído em ${elapsed}ms`);
318
+ time.end(` ${Colors.BgGreen} build ${Colors.Reset} Client build completed in ${elapsed}ms`);
319
+
320
+ // Notifica o hot reload manager que o build foi concluído
321
+ if (hotReloadManager) {
322
+ hotReloadManager.onBuildComplete(true);
323
+ }
318
324
 
319
325
  } else {
320
- const time = Console.dynamicLine(` ${Colors.BgYellow} watcher ${Colors.Reset} Iniciando watch do cliente`);
326
+ const time = Console.dynamicLine(` ${Colors.BgYellow} watcher ${Colors.Reset} Starting client watch`);
321
327
  watchWithChunks(entryPoint, outDir, hotReloadManager!).catch(err => {
322
- Console.error(`Erro ao iniciar o watch`, err);
328
+ Console.error(`Error starting watch`, err);
323
329
  });
324
- time.end(` ${Colors.BgGreen} watcher ${Colors.Reset} Watch do cliente iniciado`);
330
+ time.end(` ${Colors.BgGreen} watcher ${Colors.Reset} Client Watch started`);
325
331
  }
326
332
 
327
333
  },
@@ -339,7 +345,6 @@ export default function hweb(options: HightJSOptions) {
339
345
  if (instrumentation.hotReloadListener && typeof instrumentation.hotReloadListener === 'function') {
340
346
  if (hotReloadManager) {
341
347
  hotReloadManager.setHotReloadListener(instrumentation.hotReloadListener);
342
- Console.info('✅ Hot reload listener registrado');
343
348
  }
344
349
  }
345
350
 
@@ -348,7 +353,7 @@ export default function hweb(options: HightJSOptions) {
348
353
  } else if (typeof instrumentation.default === 'function') {
349
354
  instrumentation.default();
350
355
  } else {
351
- Console.warn(`O arquivo de instrumentação ${instrumentationFile} não exporta uma função padrão.`);
356
+ Console.warn(`The instrumentation file ${instrumentationFile} does not export a default function.`);
352
357
  }
353
358
  }
354
359
  },
@@ -373,7 +378,7 @@ export default function hweb(options: HightJSOptions) {
373
378
  }
374
379
 
375
380
  // 2. Primeiro verifica se é um arquivo estático da pasta public
376
- if (pathname !== '/' && !pathname.startsWith('/api/') && !pathname.startsWith('/hweb-')) {
381
+ if (pathname !== '/' && !pathname.startsWith('/api/') && !pathname.startsWith('/.hight')) {
377
382
  const publicDir = path.join(dir, 'public');
378
383
  const filePath = path.join(publicDir, pathname);
379
384
 
@@ -417,12 +422,14 @@ export default function hweb(options: HightJSOptions) {
417
422
  }
418
423
  }
419
424
 
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/', ''));
425
+ // 3. Verifica se é um arquivo estático do .hight
426
+ if (pathname.startsWith('/_hight/')) {
427
+
428
+ const staticPath = path.join(dir, '.hight');
429
+ const filePath = path.join(staticPath, pathname.replace('/_hight/', ''));
424
430
 
425
431
  if (fs.existsSync(filePath)) {
432
+
426
433
  const ext = path.extname(filePath).toLowerCase();
427
434
  const contentTypes: Record<string, string> = {
428
435
  '.js': 'application/javascript',
@@ -470,8 +477,8 @@ export default function hweb(options: HightJSOptions) {
470
477
  return;
471
478
  }
472
479
  } catch (error) {
473
- Console.error(`Erro na rota de API ${pathname}:`, error);
474
- genericRes.status(500).text('Erro interno do servidor na API');
480
+ Console.error(`API route error ${pathname}:`, error);
481
+ genericRes.status(500).text('Internal server error in API');
475
482
  return;
476
483
  }
477
484
  }
@@ -498,8 +505,8 @@ export default function hweb(options: HightJSOptions) {
498
505
  genericRes.status(404).header('Content-Type', 'text/html').send(html);
499
506
  return;
500
507
  } catch (error) {
501
- Console.error(`Erro ao renderizar página 404:`, error);
502
- genericRes.status(404).text('Página não encontrada');
508
+ Console.error(`Error rendering page 404:`, error);
509
+ genericRes.status(404).text('Page not found');
503
510
  return;
504
511
  }
505
512
  }
@@ -513,8 +520,8 @@ export default function hweb(options: HightJSOptions) {
513
520
  });
514
521
  genericRes.status(200).header('Content-Type', 'text/html').send(html);
515
522
  } catch (error) {
516
- Console.error(`Erro ao renderizar a página ${pathname}:`, error);
517
- genericRes.status(500).text('Erro interno do servidor');
523
+ Console.error(`Error rendering page ${pathname}:`, error);
524
+ genericRes.status(500).text('Internal server error');
518
525
  }
519
526
  };
520
527
  },
@@ -529,19 +536,6 @@ export default function hweb(options: HightJSOptions) {
529
536
  setupWebSocketUpgrade(actualServer, hotReloadManager);
530
537
  },
531
538
 
532
- build: async () => {
533
- const msg = Console.dynamicLine(` ${Colors.FgYellow}● ${Colors.Reset}Iniciando build do cliente para produção`);
534
- const outDir = path.join(dir, 'hweb-dist');
535
- fs.mkdirSync(outDir, {recursive: true});
536
-
537
- const routes = loadRoutes(userWebRoutesDir);
538
- const entryPoint = createEntryFile(dir, routes);
539
- const outfile = path.join(outDir, 'main.js');
540
-
541
- await build(entryPoint, outfile, true); // Força produção no build manual
542
-
543
- msg.end(` ${Colors.FgGreen}● ${Colors.Reset}Build do cliente concluído: ${outfile}`);
544
- },
545
539
 
546
540
  stop: () => {
547
541
  if (hotReloadManager) {
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
  }