vatts 1.0.2-alpha.1 → 1.0.2-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/builder.js CHANGED
@@ -58,14 +58,14 @@ const markdownPlugin = () => {
58
58
  if (id.endsWith('.md')) {
59
59
  return {
60
60
  code: `export default ${JSON.stringify(code)};`,
61
- map: null
61
+ map: null // Null map economiza memória se não precisa debugar markdown
62
62
  };
63
63
  }
64
64
  }
65
65
  };
66
66
  };
67
67
  /**
68
- * Plugin para CSS/PostCSS Manual (Smart Extraction + Tailwind Fix)
68
+ * Plugin para CSS/PostCSS Manual (Otimizado para RAM)
69
69
  */
70
70
  const customPostCssPlugin = (isProduction) => {
71
71
  let cachedProcessor = null;
@@ -95,7 +95,9 @@ const customPostCssPlugin = (isProduction) => {
95
95
  }
96
96
  }
97
97
  if (postcss) {
98
- delete require.cache[require.resolve(configPath)];
98
+ // OTIMIZAÇÃO DE RAM: Removido 'delete require.cache'.
99
+ // Limpar o cache constantemente fragmenta a memória heap do V8 em processos longos (watch).
100
+ // Se o usuário alterar o config, ele deve reiniciar o processo.
99
101
  const config = require(configPath);
100
102
  const postcssConfig = config.default || config;
101
103
  const plugins = [];
@@ -174,36 +176,24 @@ const customPostCssPlugin = (isProduction) => {
174
176
  Console.warn(`PostCSS process error:`, e.message);
175
177
  }
176
178
  }
177
- // ESTRATÉGIA DE EXTRAÇÃO INTELIGENTE
178
- if (isProduction) {
179
- // Emite arquivo físico (Melhora Cache e Tamanho do JS)
180
- const referenceId = this.emitFile({
181
- type: 'asset',
182
- name: path.basename(filePath),
183
- source: processedCss
184
- });
185
- // Retorna código JS que auto-injeta o <link>
186
- // Isso mantém compatibilidade (não quebra o app) mas usa arquivo externo
187
- return `
188
- const cssUrl = import.meta.ROLLUP_FILE_URL_${referenceId};
189
- if (typeof document !== 'undefined') {
190
- const link = document.createElement('link');
191
- link.rel = 'stylesheet';
192
- link.href = cssUrl;
193
- document.head.appendChild(link);
194
- }
195
- export default cssUrl;
196
- `;
197
- }
198
- // Modo DEV (Inline para Hot Reload mais rápido)
179
+ // OTIMIZAÇÃO: Emite arquivo físico sempre que possível.
180
+ // Strings gigantes de CSS inline consomem muita RAM no bundle JS.
181
+ const referenceId = this.emitFile({
182
+ type: 'asset',
183
+ name: path.basename(filePath),
184
+ source: processedCss
185
+ });
186
+ // Lógica unificada: Usa arquivo externo tanto em Dev quanto Prod.
187
+ // Isso libera a memória que seria usada para stringificar o CSS dentro do JS.
199
188
  return `
200
- const css = ${JSON.stringify(processedCss)};
189
+ const cssUrl = import.meta.ROLLUP_FILE_URL_${referenceId};
201
190
  if (typeof document !== 'undefined') {
202
- const style = document.createElement('style');
203
- style.textContent = css;
204
- document.head.appendChild(style);
191
+ const link = document.createElement('link');
192
+ link.rel = 'stylesheet';
193
+ link.href = cssUrl;
194
+ document.head.appendChild(link);
205
195
  }
206
- export default css;
196
+ export default cssUrl;
207
197
  `;
208
198
  }
209
199
  return null;
@@ -211,12 +201,13 @@ const customPostCssPlugin = (isProduction) => {
211
201
  };
212
202
  };
213
203
  /**
214
- * Plugin Inteligente para Assets (Substitui forceBase64Plugin)
215
- * - Dev: Base64 (Rápido)
216
- * - Prod: Arquivos > 4KB viram URL (Melhora LCP), < 4KB Base64 (Menos requests)
204
+ * Plugin Inteligente para Assets (Otimizado para RAM)
205
+ * - Agora utiliza emissão de arquivos também em DEV para arquivos grandes.
217
206
  */
218
207
  const smartAssetPlugin = (isProduction) => {
219
- const INLINE_LIMIT = 4096; // 4KB
208
+ // 4KB - Arquivos maiores que isso viram referência externa.
209
+ // Manter isso baixo economiza MUITA RAM, pois evita strings Base64 gigantes no JS.
210
+ const INLINE_LIMIT = 4096;
220
211
  return {
221
212
  name: 'smart-asset-loader',
222
213
  async load(id) {
@@ -238,42 +229,23 @@ const smartAssetPlugin = (isProduction) => {
238
229
  const type = mimeTypes[ext];
239
230
  if (!type)
240
231
  return null;
241
- // Text files always strings
232
+ // Text files always strings (geralmente pequenos)
242
233
  if (type === 'txt') {
243
234
  const content = await fs.promises.readFile(cleanId, 'utf8');
244
235
  return `export default ${JSON.stringify(content)};`;
245
236
  }
246
- const buffer = await fs.promises.readFile(cleanId);
237
+ let buffer = await fs.promises.readFile(cleanId);
247
238
  const size = buffer.length;
248
- // MODO PRODUÇÃO: Otimização de Assets
249
- if (isProduction) {
250
- // SVG: Se for pequeno inlina, se grande emite arquivo
251
- if (type === 'svg') {
252
- if (size < INLINE_LIMIT) {
253
- const content = buffer.toString('utf8');
254
- const base64 = buffer.toString('base64');
255
- return `
256
- export default "data:image/svg+xml;base64,${base64}";
257
- export const svgContent = ${JSON.stringify(content)};
258
- `;
259
- }
260
- else {
261
- // Emite arquivo físico
262
- const referenceId = this.emitFile({
263
- type: 'asset',
264
- name: path.basename(cleanId),
265
- source: buffer
266
- });
267
- const content = buffer.toString('utf8');
268
- return `
269
- export default import.meta.ROLLUP_FILE_URL_${referenceId};
270
- export const svgContent = ${JSON.stringify(content)};
271
- `;
272
- }
273
- }
274
- // Outros assets
239
+ // Tratamento especial para SVG (inline SVG vs URL)
240
+ if (type === 'svg') {
275
241
  if (size < INLINE_LIMIT) {
276
- return `export default "data:${type};base64,${buffer.toString('base64')}";`;
242
+ const content = buffer.toString('utf8');
243
+ const base64 = buffer.toString('base64');
244
+ buffer = null; // GC Hint
245
+ return `
246
+ export default "data:image/svg+xml;base64,${base64}";
247
+ export const svgContent = ${JSON.stringify(content)};
248
+ `;
277
249
  }
278
250
  else {
279
251
  const referenceId = this.emitFile({
@@ -281,19 +253,32 @@ const smartAssetPlugin = (isProduction) => {
281
253
  name: path.basename(cleanId),
282
254
  source: buffer
283
255
  });
284
- return `export default import.meta.ROLLUP_FILE_URL_${referenceId};`;
256
+ const content = buffer.toString('utf8');
257
+ buffer = null; // GC Hint
258
+ return `
259
+ export default import.meta.ROLLUP_FILE_URL_${referenceId};
260
+ export const svgContent = ${JSON.stringify(content)};
261
+ `;
285
262
  }
286
263
  }
287
- // MODO DESENVOLVIMENTO: Tudo Base64 para performance de build
288
- if (type === 'svg') {
289
- const content = buffer.toString('utf8');
264
+ // Para outros assets:
265
+ // Se for pequeno, Base64 (reduz requests HTTP)
266
+ // Se for grande, Arquivo (reduz uso de RAM e tamanho do bundle JS)
267
+ // Essa lógica agora aplica para DEV e PROD. Base64 em Dev para arquivos grandes era o vilão da RAM.
268
+ if (size < INLINE_LIMIT) {
290
269
  const base64 = buffer.toString('base64');
291
- return `
292
- export default "data:image/svg+xml;base64,${base64}";
293
- export const svgContent = ${JSON.stringify(content)};
294
- `;
270
+ buffer = null; // Libera memória do buffer bruto imediatamente
271
+ return `export default "data:${type};base64,${base64}";`;
272
+ }
273
+ else {
274
+ const referenceId = this.emitFile({
275
+ type: 'asset',
276
+ name: path.basename(cleanId),
277
+ source: buffer
278
+ });
279
+ buffer = null; // Libera memória
280
+ return `export default import.meta.ROLLUP_FILE_URL_${referenceId};`;
295
281
  }
296
- return `export default "data:${type};base64,${buffer.toString('base64')}";`;
297
282
  }
298
283
  };
299
284
  };
@@ -303,25 +288,25 @@ const smartAssetPlugin = (isProduction) => {
303
288
  function createRollupConfig(entryPoint, outdir, isProduction) {
304
289
  return {
305
290
  input: entryPoint,
306
- // Para evitar bare imports no browser (sem import map), em DEV também bundle React/ReactDOM.
307
- // O HMR evita "Invalid hook call" removendo o script antigo e recarregando main.js.
308
291
  external: nodeBuiltIns,
309
- // Otimização: Em prod usa 'recommended' para limpar código morto
310
- treeshake: isProduction ? 'recommended' : false,
311
- // CORREÇÃO CRÍTICA: Desativa cache em desenvolvimento (watch mode)
312
- // Isso previne que o Rollup emita "sucesso" baseado em um cache obsoleto quando o arquivo ainda tem erro.
292
+ // Otimização: Treeshake limpa memória removendo nós da AST não usados
293
+ treeshake: {
294
+ moduleSideEffects: 'no-external', // Mais agressivo, economiza memória
295
+ preset: isProduction ? 'recommended' : 'smallest'
296
+ },
297
+ // Cache desativado em DEV conforme solicitado anteriormente,
298
+ // o que ajuda na RAM pois não mantém a AST antiga em memória.
313
299
  cache: isProduction ? true : false,
314
300
  perf: false,
301
+ // Limita execuções paralelas de leitura de arquivo internas do Rollup
302
+ maxParallelFileOps: 20,
315
303
  plugins: [
316
- // CRÍTICO: 'replace' deve vir PRIMEIRO para injetar NODE_ENV antes que
317
- // libs como React decidam qual bundle importar.
318
304
  replace({
319
305
  preventAssignment: true,
320
306
  values: {
321
307
  'process.env.NODE_ENV': JSON.stringify(isProduction ? 'production' : 'development')
322
308
  }
323
309
  }),
324
- // Precisa vir antes do nodeResolve
325
310
  tsconfigPathsPlugin(process.cwd()),
326
311
  nodeResolve({
327
312
  extensions: ['.mjs', '.js', '.json', '.node', '.jsx', '.tsx', '.ts'],
@@ -331,20 +316,21 @@ function createRollupConfig(entryPoint, outdir, isProduction) {
331
316
  }),
332
317
  commonjs({
333
318
  sourceMap: !isProduction,
334
- requireReturnsDefault: 'auto'
319
+ requireReturnsDefault: 'auto',
320
+ // Ignora try-catch dinâmicos para economizar análise
321
+ ignoreTryCatch: true
335
322
  }),
336
323
  markdownPlugin(),
337
- // Passamos isProduction para ativar a Extração Inteligente
324
+ // PostCSS Otimizado
338
325
  customPostCssPlugin(isProduction),
339
- // Substitui forceBase64Plugin pelo Smart
326
+ // Assets Otimizados (menos Base64)
340
327
  smartAssetPlugin(isProduction),
341
328
  esbuild({
342
329
  include: /\.[jt]sx?$/,
343
330
  exclude: /node_modules/,
344
331
  sourceMap: !isProduction,
345
332
  minify: isProduction,
346
- // Otimização: Remove comentários legais em produção
347
- legalComments: isProduction ? 'none' : 'eof',
333
+ legalComments: 'none', // Remove comentários para limpar buffer
348
334
  treeShaking: isProduction,
349
335
  target: isProduction ? 'es2020' : 'esnext',
350
336
  jsx: 'automatic',
@@ -357,6 +343,9 @@ function createRollupConfig(entryPoint, outdir, isProduction) {
357
343
  return;
358
344
  if (warning.code === 'THIS_IS_UNDEFINED')
359
345
  return;
346
+ // Ignora avisos circulares comuns que enchem o log/buffer
347
+ if (warning.code === 'CIRCULAR_DEPENDENCY' && warning.message.includes('node_modules'))
348
+ return;
360
349
  warn(warning);
361
350
  }
362
351
  };
@@ -371,39 +360,31 @@ async function buildWithChunks(entryPoint, outdir, isProduction = false) {
371
360
  const outputOptions = {
372
361
  dir: outdir,
373
362
  format: 'es',
374
- // Padrão de nomes mantido, mas com estrutura para vários arquivos
375
363
  entryFileNames: isProduction ? 'main-[hash].js' : 'main.js',
376
364
  chunkFileNames: 'chunks/[name]-[hash].js',
377
365
  assetFileNames: 'assets/[name]-[hash][extname]',
378
366
  sourcemap: !isProduction,
379
- // OTIMIZAÇÃO: Separação granular para melhor Cache e Load Time
367
+ // Compacta output para economizar memória de escrita
368
+ compact: isProduction,
380
369
  manualChunks(id) {
381
370
  if (id.includes('node_modules')) {
382
- // Normaliza separadores para garantir funcionamento em Windows/Linux
383
371
  const normalizedId = id.replace(/\\/g, '/');
384
- // React Core isolado
385
- // IMPORTANTE: Uso de Regex para garantir que pegamos APENAS os pacotes do core
386
- // e não pacotes que tenham 'react' no nome (ex: react-router, react-icons),
387
- // pois isso causa dependências circulares com o chunk 'vendor'.
388
372
  if (/\/node_modules\/(react|react-dom|scheduler|prop-types|loose-envify|object-assign)\//.test(normalizedId)) {
389
373
  return 'vendor-react';
390
374
  }
391
- // UI Libs comuns (opcional, pode ajustar conforme necessidade)
392
375
  if (id.includes('framer-motion') || id.includes('@radix-ui')) {
393
376
  return 'vendor-ui';
394
377
  }
395
- // Utils comuns
396
378
  if (id.includes('lodash') || id.includes('date-fns') || id.includes('axios')) {
397
379
  return 'vendor-utils';
398
380
  }
399
- // Resto das dependências
400
381
  return 'vendor';
401
382
  }
402
383
  }
403
384
  };
404
385
  const bundle = await rollup(inputOptions);
405
386
  await bundle.write(outputOptions);
406
- await bundle.close();
387
+ await bundle.close(); // Importante fechar para liberar memória
407
388
  }
408
389
  catch (error) {
409
390
  Console.error('An error occurred while building with chunks:', error);
@@ -421,9 +402,10 @@ async function build(entryPoint, outfile, isProduction = false) {
421
402
  const outputOptions = {
422
403
  file: outfile,
423
404
  format: 'iife',
424
- name: 'HwebApp',
405
+ name: 'Vattsjs',
425
406
  sourcemap: !isProduction,
426
- inlineDynamicImports: true
407
+ inlineDynamicImports: true,
408
+ compact: true // Ajuda na RAM
427
409
  };
428
410
  const bundle = await rollup(inputOptions);
429
411
  await bundle.write(outputOptions);
@@ -438,20 +420,22 @@ async function build(entryPoint, outfile, isProduction = false) {
438
420
  * Helper para lidar com notificações do Watcher
439
421
  */
440
422
  function handleWatcherEvents(watcher, hotReloadManager, resolveFirstBuild) {
441
- // Controla o estado do build por "geração" para evitar END atrasado
442
- // (ou múltiplos ciclos) emitirem sucesso após um erro.
443
423
  let currentBuildId = 0;
444
424
  let lastStartedBuildId = 0;
445
425
  const erroredBuildIds = new Set();
446
- // DEBUG: stack trace rate-limited
447
- let lastTraceAt = 0;
448
426
  watcher.on('event', event => {
449
427
  if (event.code === 'START') {
450
428
  currentBuildId += 1;
451
429
  lastStartedBuildId = currentBuildId;
430
+ // Dica pro V8 limpar lixo antes de começar um build pesado
431
+ if (global.gc) {
432
+ try {
433
+ global.gc();
434
+ }
435
+ catch (e) { }
436
+ }
452
437
  }
453
438
  if (event.code === 'ERROR') {
454
- // Marca erro para o build atualmente em andamento.
455
439
  erroredBuildIds.add(currentBuildId);
456
440
  const errDetails = {
457
441
  message: event.error?.message || 'Unknown build error',
@@ -461,7 +445,6 @@ function handleWatcherEvents(watcher, hotReloadManager, resolveFirstBuild) {
461
445
  loc: event.error?.loc,
462
446
  buildId: currentBuildId
463
447
  };
464
- // Notifica erro imediatamente
465
448
  if (hotReloadManager) {
466
449
  hotReloadManager.onBuildComplete(false, errDetails);
467
450
  }
@@ -470,21 +453,18 @@ function handleWatcherEvents(watcher, hotReloadManager, resolveFirstBuild) {
470
453
  if (resolveFirstBuild)
471
454
  resolveFirstBuild();
472
455
  }
473
- if (event.code === 'BUNDLE_END')
456
+ if (event.code === 'BUNDLE_END') {
457
+ // CRÍTICO: Fechar o bundle libera a memória dos módulos
474
458
  event.result.close();
459
+ }
475
460
  if (event.code === 'END') {
476
461
  const endBuildId = currentBuildId;
477
462
  const hadError = erroredBuildIds.has(endBuildId);
478
- // Só emite sucesso se:
479
- // 1) esse END é do build mais recentemente iniciado (evita END atrasado)
480
- // 2) esse build não teve ERROR
481
463
  if (endBuildId === lastStartedBuildId && !hadError) {
482
464
  if (hotReloadManager) {
483
465
  hotReloadManager.onBuildComplete(true, { buildId: endBuildId });
484
466
  }
485
467
  }
486
- // Limpa estados antigos pra não crescer sem limite.
487
- // (qualquer build mais antigo que o último START não faz mais sentido manter)
488
468
  for (const id of erroredBuildIds) {
489
469
  if (id < lastStartedBuildId)
490
470
  erroredBuildIds.delete(id);
@@ -502,11 +482,9 @@ function handleWatcherEvents(watcher, hotReloadManager, resolveFirstBuild) {
502
482
  async function watchWithChunks(entryPoint, outdir, hotReloadManager = null) {
503
483
  await cleanDirectoryExcept(outdir, 'temp');
504
484
  try {
505
- // DEV MODE: isProduction = false
506
485
  const inputOptions = createRollupConfig(entryPoint, outdir, false);
507
486
  const outputOptions = {
508
487
  dir: outdir,
509
- // Em DEV usamos ESM para suportar externals como react/react-dom sem output.globals
510
488
  format: 'es',
511
489
  entryFileNames: 'main.js',
512
490
  sourcemap: true
@@ -517,7 +495,9 @@ async function watchWithChunks(entryPoint, outdir, hotReloadManager = null) {
517
495
  watch: {
518
496
  exclude: 'node_modules/**',
519
497
  clearScreen: false,
520
- skipWrite: false
498
+ skipWrite: false,
499
+ // Atraso curto para evitar múltiplos rebuilds rápidos que comem CPU/RAM
500
+ buildDelay: 100
521
501
  }
522
502
  };
523
503
  const watcher = rollupWatch(watchOptions);
@@ -540,7 +520,6 @@ async function watch(entryPoint, outfile, hotReloadManager = null) {
540
520
  const inputOptions = createRollupConfig(entryPoint, outdir, false);
541
521
  const outputOptions = {
542
522
  file: outfile,
543
- // Em DEV usamos ESM para suportar externals como react/react-dom sem output.globals
544
523
  format: 'es',
545
524
  sourcemap: true
546
525
  };
@@ -549,7 +528,8 @@ async function watch(entryPoint, outfile, hotReloadManager = null) {
549
528
  output: outputOptions,
550
529
  watch: {
551
530
  exclude: 'node_modules/**',
552
- clearScreen: false
531
+ clearScreen: false,
532
+ buildDelay: 100
553
533
  }
554
534
  };
555
535
  const watcher = rollupWatch(watchOptions);
@@ -569,14 +549,21 @@ async function cleanDirectoryExcept(dirPath, excludeFolder) {
569
549
  return;
570
550
  const excludes = Array.isArray(excludeFolder) ? excludeFolder : [excludeFolder];
571
551
  const items = await readdir(dirPath);
572
- // Paraleliza a limpeza
573
- await Promise.all(items.map(async (item) => {
552
+ // OTIMIZAÇÃO: Loop sequencial ao invés de Promise.all.
553
+ // Promise.all é mais rápido, mas cria dezenas/centenas de Promises simultâneas na RAM.
554
+ // O loop sequencial é mais gentil com o Garbage Collector.
555
+ for (const item of items) {
574
556
  if (excludes.includes(item))
575
- return;
557
+ continue;
576
558
  const itemPath = path.join(dirPath, item);
577
- const info = await stat(itemPath);
578
- await rm(itemPath, { recursive: info.isDirectory(), force: true });
579
- }));
559
+ try {
560
+ const info = await stat(itemPath);
561
+ await rm(itemPath, { recursive: info.isDirectory(), force: true });
562
+ }
563
+ catch (e) {
564
+ // Ignora erro se arquivo sumir durante o loop
565
+ }
566
+ }
580
567
  }
581
568
  catch (e) {
582
569
  Console.warn(`Warning cleaning directory: ${e.message}`);
@@ -15,7 +15,6 @@
15
15
  * See the License for the specific language governing permissions and
16
16
  * limitations under the License.
17
17
  */
18
- // Sistema de roteamento do lado do cliente para hweb-sdk
19
18
  Object.defineProperty(exports, "__esModule", { value: true });
20
19
  exports.router = void 0;
21
20
  class Router {
@@ -201,7 +201,7 @@ function App({ componentMap, routes, initialComponentPath, initialParams, layout
201
201
  // para garantir que o ErrorModal apareça em qualquer estado.
202
202
  let resolvedContent;
203
203
  if (!CurrentPageComponent || initialComponentPath === '__404__') {
204
- const NotFoundComponent = window.__HWEB_NOT_FOUND__;
204
+ const NotFoundComponent = window.__VATTS_NOT_FOUND__;
205
205
  if (NotFoundComponent) {
206
206
  const NotFoundContent = (0, jsx_runtime_1.jsx)(NotFoundComponent, {});
207
207
  resolvedContent = layoutComponent
@@ -209,7 +209,7 @@ function App({ componentMap, routes, initialComponentPath, initialParams, layout
209
209
  : NotFoundContent;
210
210
  }
211
211
  else {
212
- const DefaultNotFound = window.__HWEB_DEFAULT_NOT_FOUND__;
212
+ const DefaultNotFound = window.__VATTS_DEFAULT_NOT_FOUND__;
213
213
  const NotFoundContent = (0, jsx_runtime_1.jsx)(DefaultNotFound, {});
214
214
  resolvedContent = layoutComponent
215
215
  ? react_1.default.createElement(layoutComponent, { children: NotFoundContent })
@@ -261,11 +261,11 @@ function initializeClient() {
261
261
  // Cria o mapa de componentes dinamicamente a partir dos módulos carregados
262
262
  const componentMap = {};
263
263
  // Registra todos os componentes que foram importados
264
- if (window.__HWEB_COMPONENTS__) {
265
- Object.assign(componentMap, window.__HWEB_COMPONENTS__);
264
+ if (window.__VATTS_COMPONENTS__) {
265
+ Object.assign(componentMap, window.__VATTS_COMPONENTS__);
266
266
  }
267
267
  else {
268
- console.warn('[Vatts] No components found in window.__HWEB_COMPONENTS__');
268
+ console.warn('[Vatts] No components found in window.__VATTS_COMPONENTS__');
269
269
  }
270
270
  const container = document.getElementById('root');
271
271
  if (!container) {
@@ -292,7 +292,7 @@ function initializeClient() {
292
292
  const root = (0, client_1.createRoot)(container);
293
293
  // Salva a referência globalmente
294
294
  window.__VATTS_ROOT__ = root;
295
- root.render((0, jsx_runtime_1.jsx)(App, { componentMap: componentMap, routes: initialData.routes, initialComponentPath: initialData.initialComponentPath, initialParams: initialData.initialParams, layoutComponent: window.__HWEB_LAYOUT__ }));
295
+ root.render((0, jsx_runtime_1.jsx)(App, { componentMap: componentMap, routes: initialData.routes, initialComponentPath: initialData.initialComponentPath, initialParams: initialData.initialParams, layoutComponent: window.__VATTS_LAYOUT__ }));
296
296
  }
297
297
  catch (error) {
298
298
  console.error('[Watts] Critical Error rendering application:', error);
@@ -316,7 +316,7 @@ if (document.readyState === 'loading') {
316
316
  else {
317
317
  // ESM Hoisting Fix:
318
318
  // Como este arquivo é importado pelo arquivo gerado automaticamente, ele executa
319
- // ANTES do corpo do arquivo gerado (onde window.__HWEB_COMPONENTS__ é definido).
319
+ // ANTES do corpo do arquivo gerado (onde window.__VATTS_COMPONENTS__ é definido).
320
320
  // Usamos setTimeout para garantir que a inicialização ocorra após as atribuições globais.
321
321
  setTimeout(initializeClient, 0);
322
322
  }
@@ -0,0 +1,5 @@
1
+ export declare const loadEnv: (options: {
2
+ dir: string;
3
+ dev: boolean;
4
+ envFiles?: string[];
5
+ }) => void;
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.loadEnv = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const console_1 = __importDefault(require("../api/console"));
10
+ function parse(src) {
11
+ const obj = {};
12
+ const lines = src.toString().split('\n');
13
+ for (const line of lines) {
14
+ const trimmedLine = line.trim();
15
+ if (trimmedLine.startsWith('#') || trimmedLine === '') {
16
+ continue;
17
+ }
18
+ const match = trimmedLine.match(/^([^=]+)=(.*)$/);
19
+ if (match) {
20
+ const key = match[1].trim();
21
+ let value = match[2].trim();
22
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
23
+ value = value.substring(1, value.length - 1);
24
+ }
25
+ obj[key] = value;
26
+ }
27
+ }
28
+ return obj;
29
+ }
30
+ function applyEnv(filePath) {
31
+ if (!fs_1.default.existsSync(filePath))
32
+ return;
33
+ try {
34
+ const fileContent = fs_1.default.readFileSync(filePath, 'utf-8');
35
+ const parsed = parse(fileContent);
36
+ for (const key in parsed) {
37
+ if (!Object.prototype.hasOwnProperty.call(process.env, key)) {
38
+ process.env[key] = parsed[key];
39
+ }
40
+ }
41
+ }
42
+ catch (e) {
43
+ console_1.default.error(`Error loading env file ${filePath}:`, e);
44
+ }
45
+ }
46
+ const loadEnv = (options) => {
47
+ const { dir, dev, envFiles = [] } = options;
48
+ const filesToLoad = [".env", ...envFiles].map((file) => path_1.default.join(dir, file));
49
+ filesToLoad.forEach(applyEnv);
50
+ if (dev) {
51
+ for (const file of filesToLoad) {
52
+ if (fs_1.default.existsSync(file)) {
53
+ let watchTimeout;
54
+ fs_1.default.watch(file, (eventType) => {
55
+ if (eventType === 'change') {
56
+ // Limpa o timeout anterior para evitar execuções múltiplas
57
+ clearTimeout(watchTimeout);
58
+ // Define um novo timeout de 100ms
59
+ watchTimeout = setTimeout(() => {
60
+ console_1.default.info(`Reloading environment variables from ${path_1.default.basename(file)}.`);
61
+ applyEnv(file);
62
+ }, 100);
63
+ }
64
+ });
65
+ }
66
+ }
67
+ }
68
+ };
69
+ exports.loadEnv = loadEnv;
package/dist/helpers.js CHANGED
@@ -132,6 +132,7 @@ async function loadVattsConfig(projectDir, phase) {
132
132
  individualRequestTimeout: 30000,
133
133
  maxUrlLength: 2048,
134
134
  accessLogging: true,
135
+ envFiles: [],
135
136
  };
136
137
  try {
137
138
  // Tenta primeiro .ts, depois .js
@@ -315,13 +316,15 @@ const parseBody = (req) => {
315
316
  /**
316
317
  * Inicializa servidor nativo do Vatts.js usando HTTP ou HTTPS
317
318
  */
318
- async function initNativeServer(hwebApp, options, port, hostname) {
319
+ async function initNativeServer(vattsApp, options, port, hostname) {
319
320
  const time = Date.now();
320
- await hwebApp.prepare();
321
321
  const projectDir = options.dir || process.cwd();
322
322
  const phase = options.dev ? 'development' : 'production';
323
323
  const vattsConfig = await loadVattsConfig(projectDir, phase);
324
- const handler = hwebApp.getRequestHandler();
324
+ // Passa envFiles da config para as opções do vatts
325
+ options.envFiles = vattsConfig.envFiles;
326
+ await vattsApp.prepare();
327
+ const handler = vattsApp.getRequestHandler();
325
328
  const msg = console_1.default.dynamicLine(`${console_1.Colors.Bright}Starting Vatts.js on port ${options.port}${console_1.Colors.Reset}`);
326
329
  // --- LÓGICA DO LISTENER (REUTILIZÁVEL) ---
327
330
  // Extraímos a lógica principal para uma variável
@@ -461,7 +464,7 @@ async function initNativeServer(hwebApp, options, port, hostname) {
461
464
  ca: options.ssl.ca ? fs_1.default.readFileSync(options.ssl.ca) : undefined
462
465
  };
463
466
  // 1. Cria o servidor HTTPS principal
464
- server = https_1.default.createServer(sslOptions, requestListener); // (any para contornar HWebIncomingMessage)
467
+ server = https_1.default.createServer(sslOptions, requestListener);
465
468
  // 2. Cria o servidor de REDIRECIONAMENTO (HTTP -> HTTPS)
466
469
  const httpRedirectPort = options.ssl.redirectPort;
467
470
  http_1.default.createServer((req, res) => {
@@ -490,7 +493,7 @@ async function initNativeServer(hwebApp, options, port, hostname) {
490
493
  else {
491
494
  // --- MODO HTTP (Original) ---
492
495
  // Cria o servidor HTTP nativo
493
- server = http_1.default.createServer(requestListener); // (any para contornar HWebIncomingMessage)
496
+ server = http_1.default.createServer(requestListener);
494
497
  }
495
498
  // Configurações de segurança do servidor (usa configuração personalizada)
496
499
  server.setTimeout(vattsConfig.serverTimeout || 35000); // Timeout geral do servidor
@@ -502,26 +505,26 @@ async function initNativeServer(hwebApp, options, port, hostname) {
502
505
  msg.end(`${console_1.Colors.Bright}Ready on port ${console_1.Colors.BgGreen} ${options.port} ${console_1.Colors.Reset}${console_1.Colors.Bright} in ${Date.now() - time}ms${console_1.Colors.Reset}\n`);
503
506
  });
504
507
  // Configura WebSocket para hot reload (Comum a ambos)
505
- hwebApp.setupWebSocket(server);
506
- hwebApp.executeInstrumentation();
508
+ vattsApp.setupWebSocket(server);
509
+ vattsApp.executeInstrumentation();
507
510
  return server;
508
511
  }
509
512
  // --- Função Principal ---
510
513
  function app(options = {}) {
511
514
  const framework = options.framework || 'native';
512
515
  index_1.FrameworkAdapterFactory.setFramework(framework);
513
- // Tipando a app principal do hweb
514
- const hwebApp = (0, index_1.default)(options);
516
+ // Tipando a app principal do vatts
517
+ const vattsApp = (0, index_1.default)(options);
515
518
  return {
516
- ...hwebApp,
519
+ ...vattsApp,
517
520
  /**
518
521
  * Integra com uma aplicação de qualquer framework (Express, Fastify, etc)
519
522
  * O 'serverApp: any' é mantido para flexibilidade, já que pode ser de tipos diferentes.
520
523
  */
521
524
  integrate: async (serverApp) => {
522
- await hwebApp.prepare();
523
- const handler = hwebApp.getRequestHandler();
524
- // O framework é setado nas opções do hweb, que deve
525
+ await vattsApp.prepare();
526
+ const handler = vattsApp.getRequestHandler();
527
+ // O framework é setado nas opções do vatts, que deve
525
528
  // retornar o handler correto em getRequestHandler()
526
529
  // A lógica de integração original parece correta.
527
530
  if (framework === 'express') {
@@ -536,7 +539,7 @@ function app(options = {}) {
536
539
  serverApp.use(express.json());
537
540
  serverApp.use(express.urlencoded({ extended: true }));
538
541
  serverApp.use(handler);
539
- hwebApp.setupWebSocket(serverApp);
542
+ vattsApp.setupWebSocket(serverApp);
540
543
  }
541
544
  else if (framework === 'fastify') {
542
545
  try {
@@ -554,14 +557,14 @@ function app(options = {}) {
554
557
  await serverApp.register(async (fastify) => {
555
558
  fastify.all('*', handler);
556
559
  });
557
- hwebApp.setupWebSocket(serverApp);
560
+ vattsApp.setupWebSocket(serverApp);
558
561
  }
559
562
  else {
560
563
  // Generic integration (assume Express-like)
561
564
  serverApp.use(handler);
562
- hwebApp.setupWebSocket(serverApp);
565
+ vattsApp.setupWebSocket(serverApp);
563
566
  }
564
- hwebApp.executeInstrumentation();
567
+ vattsApp.executeInstrumentation();
565
568
  return serverApp;
566
569
  },
567
570
  /**
@@ -602,7 +605,7 @@ ${console_1.Colors.Bright + console_1.Colors.FgRed} \\/ /~~\\ | | .__/
602
605
  if (framework !== 'native') {
603
606
  console_1.default.warn(`The "${framework}" framework was selected, but the init() method only works with the "native" framework. Starting native server...`);
604
607
  }
605
- return await initNativeServer(hwebApp, options, actualPort, actualHostname);
608
+ return await initNativeServer(vattsApp, options, actualPort, actualHostname);
606
609
  }
607
610
  };
608
611
  }
package/dist/index.d.ts CHANGED
@@ -9,7 +9,7 @@ export type { GenericRequest, GenericResponse, CookieOptions } from './types/fra
9
9
  export { app } from './helpers';
10
10
  export type { WebSocketContext, WebSocketHandler } from './types';
11
11
  export type { VattsConfig, VattsConfigFunction } from './types';
12
- export default function hweb(options: VattsOptions): {
12
+ export default function vatts(options: VattsOptions): {
13
13
  prepare: () => Promise<void>;
14
14
  executeInstrumentation: () => void;
15
15
  getRequestHandler: () => RequestHandler;
package/dist/index.js CHANGED
@@ -53,7 +53,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
53
53
  };
54
54
  Object.defineProperty(exports, "__esModule", { value: true });
55
55
  exports.app = exports.FrameworkAdapterFactory = exports.FastifyAdapter = exports.ExpressAdapter = exports.VattsResponse = exports.VattsRequest = void 0;
56
- exports.default = hweb;
56
+ exports.default = vatts;
57
57
  const path_1 = __importDefault(require("path"));
58
58
  const fs_1 = __importDefault(require("fs"));
59
59
  const express_1 = require("./adapters/express");
@@ -66,6 +66,7 @@ Object.defineProperty(exports, "VattsResponse", { enumerable: true, get: functio
66
66
  const hotReload_1 = require("./hotReload");
67
67
  const factory_1 = require("./adapters/factory");
68
68
  const console_1 = __importStar(require("./api/console"));
69
+ const env_1 = require("./env/env");
69
70
  // RPC
70
71
  const server_1 = require("./rpc/server");
71
72
  const types_1 = require("./rpc/types");
@@ -161,12 +162,12 @@ function createEntryFile(projectDir, routes) {
161
162
  .join('\n');
162
163
  // Registra o layout se existir
163
164
  const layoutRegistration = layout
164
- ? `window.__HWEB_LAYOUT__ = LayoutComponent.default || LayoutComponent;`
165
- : `window.__HWEB_LAYOUT__ = null;`;
165
+ ? `window.__VATTS_LAYOUT__ = LayoutComponent.default || LayoutComponent;`
166
+ : `window.__VATTS_LAYOUT__ = null;`;
166
167
  // Registra o notFound se existir
167
168
  const notFoundRegistration = notFound
168
- ? `window.__HWEB_NOT_FOUND__ = NotFoundComponent.default || NotFoundComponent;`
169
- : `window.__HWEB_NOT_FOUND__ = null;`;
169
+ ? `window.__VATTS_NOT_FOUND__ = NotFoundComponent.default || NotFoundComponent;`
170
+ : `window.__VATTS_NOT_FOUND__ = null;`;
170
171
  // Caminho correto para o entry.client
171
172
  // IMPORTANT: quando o pacote é instalado via npm, bundlers (Rollup/Vite/etc.) não transpilam TSX em node_modules.
172
173
  // Por isso, aqui a gente sempre aponta para os artefatos compilados em dist/.
@@ -176,14 +177,14 @@ function createEntryFile(projectDir, routes) {
176
177
  // Import do DefaultNotFound do SDK (compilado)
177
178
  const defaultNotFoundPath = path_1.default.join(sdkDir, 'dist', 'client', 'DefaultNotFound.js');
178
179
  const relativeDefaultNotFoundPath = path_1.default.relative(tempDir, defaultNotFoundPath).replace(/\\/g, '/');
179
- const entryContent = `// Arquivo gerado automaticamente pelo hweb
180
+ const entryContent = `// Arquivo gerado automaticamente pelo vatts
180
181
  ${imports}
181
182
  ${layoutImport}
182
183
  ${notFoundImport}
183
184
  import DefaultNotFound from '${relativeDefaultNotFoundPath}';
184
185
 
185
186
  // Registra os componentes para o cliente
186
- window.__HWEB_COMPONENTS__ = {
187
+ window.__VATTS_COMPONENTS__ = {
187
188
  ${componentRegistration}
188
189
  };
189
190
 
@@ -193,8 +194,8 @@ ${layoutRegistration}
193
194
  // Registra o notFound se existir
194
195
  ${notFoundRegistration}
195
196
 
196
- // Registra o DefaultNotFound do hweb
197
- window.__HWEB_DEFAULT_NOT_FOUND__ = DefaultNotFound;
197
+
198
+ window.__VATTS_DEFAULT_NOT_FOUND__ = DefaultNotFound;
198
199
 
199
200
  // Importa e executa o entry.client.tsx
200
201
  import '${relativeEntryPath}';
@@ -212,8 +213,9 @@ import '${relativeEntryPath}';
212
213
  throw e;
213
214
  }
214
215
  }
215
- function hweb(options) {
216
- const { dev = true, dir = process.cwd(), port = 3000 } = options;
216
+ function vatts(options) {
217
+ const { dev = true, dir = process.cwd(), port = 3000, envFiles } = options;
218
+ (0, env_1.loadEnv)({ dir, dev, envFiles });
217
219
  // @ts-ignore
218
220
  process.vatts = options;
219
221
  const userWebDir = path_1.default.join(dir, 'src', 'web');
package/dist/renderer.js CHANGED
@@ -175,7 +175,7 @@ async function render({ req, route, params, allRoutes }) {
175
175
  <html lang="${htmlLang}">
176
176
  <head>
177
177
  ${metaTags}
178
- <title>${metadata.title || 'App hweb'}</title>
178
+ <title>${metadata.title || 'Vatts.js'}</title>
179
179
  </head>
180
180
  <body>
181
181
  <div id="root"></div>
package/dist/types.d.ts CHANGED
@@ -26,6 +26,7 @@ export interface VattsOptions {
26
26
  cert: string;
27
27
  ca?: string;
28
28
  };
29
+ envFiles?: string[];
29
30
  }
30
31
  /**
31
32
  * Interface para as configurações avançadas do servidor Vatts.js.
@@ -137,6 +138,10 @@ export interface VattsConfig {
137
138
  * Exemplo: { 'X-Custom-Header': 'value', 'X-Powered-By': 'Vatts.js' }
138
139
  */
139
140
  customHeaders?: Record<string, string>;
141
+ /**
142
+ * Arquivos .env adicionais para carregar. O .env padrão é sempre carregado.
143
+ */
144
+ envFiles?: string[];
140
145
  }
141
146
  /**
142
147
  * Tipo da função de configuração que pode ser exportada no vatts.config.js
@@ -192,12 +197,10 @@ export type RequestHandler = (req: any, res: any) => Promise<void>;
192
197
  /**
193
198
  * Define o formato de uma função que manipula uma rota da API.
194
199
  */
195
- export type BackendHandler = (request: VattsRequest, // HWebRequest será importado onde necessário
196
- params: {
200
+ export type BackendHandler = (request: VattsRequest, params: {
197
201
  [key: string]: string;
198
202
  }) => Promise<VattsResponse> | VattsResponse;
199
- export type VattsMiddleware = (request: VattsRequest, // HWebRequest será importado onde necessário
200
- params: {
203
+ export type VattsMiddleware = (request: VattsRequest, params: {
201
204
  [key: string]: string;
202
205
  }, next: () => Promise<VattsResponse>) => Promise<VattsResponse> | VattsResponse;
203
206
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vatts",
3
- "version": "1.0.2-alpha.1",
3
+ "version": "1.0.2-alpha.3",
4
4
  "description": "Vatts.js is a high-level framework for building web applications with ease and speed. It provides a robust set of tools and features to streamline development and enhance productivity.",
5
5
  "types": "dist/index.d.ts",
6
6
  "author": "itsmuzin",