vatts 1.3.1-alpha.2 → 1.4.1-test.1

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.
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Interface para as opções do servidor Proxy HTTP/3.
3
+ */
4
+ export interface ProxyOptions {
5
+ /** Porta HTTP para redirecionamento (ex: ":80") */
6
+ httpPort: string;
7
+ /** Porta HTTPS/HTTP3 (ex: ":443") */
8
+ httpsPort: string;
9
+ /** URL do servidor backend (ex: "http://localhost:3000") */
10
+ backendUrl: string;
11
+ /** Caminho para o arquivo de certificado SSL (.pem) */
12
+ certPath: string;
13
+ /** Caminho para o arquivo de chave privada SSL (.pem) */
14
+ keyPath: string;
15
+ /** (Opcional) Sobrescreve o caminho da biblioteca manualmente */
16
+ customLibPath?: string;
17
+ }
18
+ export declare class NativeProxy {
19
+ private static instance;
20
+ /**
21
+ * Detecta a plataforma e arquitetura para montar o nome do arquivo.
22
+ * Padrão esperado: vatts-proxy-{os}-{arch}.node
23
+ * Ex: vatts-proxy-win-x64.node
24
+ */
25
+ static getLibPath(): string;
26
+ /**
27
+ * Carrega a biblioteca nativa usando Koffi.
28
+ */
29
+ private static loadLibrary;
30
+ /**
31
+ * Inicia o servidor Proxy.
32
+ * Nota: A execução no Go é assíncrona (non-blocking), então esta função retorna
33
+ * imediatamente após iniciar as threads do servidor, a menos que haja um erro inicial.
34
+ */
35
+ static start(options: ProxyOptions): void;
36
+ }
37
+ /**
38
+ * API Pública
39
+ */
40
+ export declare const startProxy: (options: ProxyOptions) => void;
41
+ export default startProxy;
@@ -0,0 +1,110 @@
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.startProxy = exports.NativeProxy = void 0;
7
+ const koffi_1 = __importDefault(require("koffi"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const os_1 = __importDefault(require("os"));
11
+ class NativeProxy {
12
+ static instance = null;
13
+ /**
14
+ * Detecta a plataforma e arquitetura para montar o nome do arquivo.
15
+ * Padrão esperado: vatts-proxy-{os}-{arch}.node
16
+ * Ex: vatts-proxy-win-x64.node
17
+ */
18
+ static getLibPath() {
19
+ const platform = os_1.default.platform();
20
+ const arch = os_1.default.arch();
21
+ let osName = '';
22
+ let ext = '.node'; // Mantendo sua convenção de extensão
23
+ // Mapeamento de SO
24
+ switch (platform) {
25
+ case 'win32':
26
+ osName = 'win';
27
+ break;
28
+ case 'linux':
29
+ osName = 'linux';
30
+ break;
31
+ case 'darwin':
32
+ osName = 'darwin';
33
+ break;
34
+ default:
35
+ throw new Error(`Sistema operacional não suportado: ${platform}`);
36
+ }
37
+ // Mapeamento de Arquitetura
38
+ let archName = '';
39
+ switch (arch) {
40
+ case 'x64':
41
+ archName = 'x64';
42
+ break;
43
+ case 'arm64':
44
+ archName = 'arm64';
45
+ break;
46
+ default:
47
+ throw new Error(`Arquitetura não suportada: ${arch}`);
48
+ }
49
+ const filename = `core-${osName}-${archName}${ext}`;
50
+ // Ajuste o caminho relativo conforme a estrutura do seu projeto
51
+ // Assumindo que estará na pasta '../proxy' relativo a este arquivo
52
+ return path_1.default.resolve(__dirname, '..', 'core-go', filename);
53
+ }
54
+ /**
55
+ * Carrega a biblioteca nativa usando Koffi.
56
+ */
57
+ static loadLibrary(customPath) {
58
+ if (this.instance)
59
+ return this.instance;
60
+ const libPath = customPath || this.getLibPath();
61
+ if (!fs_1.default.existsSync(libPath)) {
62
+ throw new Error(`Biblioteca nativa não encontrada: ${libPath}.\n`);
63
+ }
64
+ try {
65
+ const lib = koffi_1.default.load(libPath);
66
+ // Mapeia a função Go: StartServer
67
+ // Argumentos: 5 strings
68
+ // Retorno: string (erro) ou null (sucesso)
69
+ this.instance = lib.func('StartServer', 'str', ['str', 'str', 'str', 'str', 'str']);
70
+ return this.instance;
71
+ }
72
+ catch (error) {
73
+ throw new Error(`Falha ao carregar a biblioteca nativa de proxy em ${libPath}: ${error}`);
74
+ }
75
+ }
76
+ /**
77
+ * Inicia o servidor Proxy.
78
+ * Nota: A execução no Go é assíncrona (non-blocking), então esta função retorna
79
+ * imediatamente após iniciar as threads do servidor, a menos que haja um erro inicial.
80
+ */
81
+ static start(options) {
82
+ const { httpPort, httpsPort, backendUrl, certPath, keyPath, customLibPath } = options;
83
+ const startServer = this.loadLibrary(customLibPath);
84
+ // Resolve caminhos absolutos para evitar erros no Go
85
+ const absCert = path_1.default.resolve(certPath);
86
+ const absKey = path_1.default.resolve(keyPath);
87
+ // Validação básica de existência dos arquivos de certificado antes de chamar o Go
88
+ if (!fs_1.default.existsSync(absCert)) {
89
+ throw new Error(`Arquivo de certificado não encontrado: ${absCert}`);
90
+ }
91
+ if (!fs_1.default.existsSync(absKey)) {
92
+ throw new Error(`Arquivo de chave não encontrado: ${absKey}`);
93
+ }
94
+ // Chama a função nativa
95
+ const errorMsg = startServer(httpPort, httpsPort, backendUrl, absCert, absKey);
96
+ if (errorMsg) {
97
+ throw new Error(`[Vatts Proxy Error] ${errorMsg}`);
98
+ }
99
+ }
100
+ }
101
+ exports.NativeProxy = NativeProxy;
102
+ /**
103
+ * API Pública
104
+ */
105
+ const startProxy = (options) => {
106
+ return NativeProxy.start(options);
107
+ };
108
+ exports.startProxy = startProxy;
109
+ // Export padrão
110
+ exports.default = exports.startProxy;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Interface para as opções do otimizador.
3
+ */
4
+ export interface OptimizerOptions {
5
+ /** Diretório onde estão os arquivos a serem otimizados (ex: .vatts/exported) */
6
+ targetDir: string;
7
+ /** (Opcional) Diretório de saída. Padrão: 'optimized' dentro do target */
8
+ outputDir?: string;
9
+ /** (Opcional) Lista de arquivos ou pastas a serem ignorados */
10
+ ignoredPatterns?: string[];
11
+ /** (Opcional) Sobrescreve o caminho da biblioteca manualmemte */
12
+ customLibPath?: string;
13
+ }
14
+ export declare class NativeOptimizer {
15
+ private static instance;
16
+ /**
17
+ * Detecta a plataforma e arquitetura para montar o nome do arquivo.
18
+ * Padrão esperado: optimizer-{os}-{arch}.{ext}
19
+ * Ex: optimizer-win-64.dll, optimizer-linux-arm64.so
20
+ */
21
+ static getLibPath(): string;
22
+ /**
23
+ * Carrega a biblioteca nativa usando Koffi.
24
+ */
25
+ private static loadLibrary;
26
+ /**
27
+ * Executa a otimização.
28
+ */
29
+ static run(options: OptimizerOptions): void;
30
+ }
31
+ /**
32
+ * API Pública
33
+ */
34
+ export declare const runOptimizer: (options: OptimizerOptions) => void;
35
+ export default runOptimizer;
@@ -0,0 +1,104 @@
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.runOptimizer = exports.NativeOptimizer = void 0;
7
+ const koffi_1 = __importDefault(require("koffi"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const os_1 = __importDefault(require("os"));
11
+ class NativeOptimizer {
12
+ static instance = null;
13
+ /**
14
+ * Detecta a plataforma e arquitetura para montar o nome do arquivo.
15
+ * Padrão esperado: optimizer-{os}-{arch}.{ext}
16
+ * Ex: optimizer-win-64.dll, optimizer-linux-arm64.so
17
+ */
18
+ static getLibPath() {
19
+ const platform = os_1.default.platform(); // 'win32', 'linux', 'darwin'
20
+ const arch = os_1.default.arch(); // 'x64', 'arm64'
21
+ let osName = '';
22
+ let ext = '.node';
23
+ // Mapeamento de SO
24
+ switch (platform) {
25
+ case 'win32':
26
+ osName = 'win';
27
+ break;
28
+ case 'linux':
29
+ osName = 'linux';
30
+ break;
31
+ case 'darwin':
32
+ osName = 'darwin'; // ou 'darwin', ajuste conforme seu build
33
+ break;
34
+ default:
35
+ throw new Error(`Sistema operacional não suportado: ${platform}`);
36
+ }
37
+ // Mapeamento de Arquitetura (user pediu '64' para x64)
38
+ let archName = '';
39
+ switch (arch) {
40
+ case 'x64':
41
+ archName = 'x64';
42
+ break;
43
+ case 'arm64':
44
+ archName = 'arm64';
45
+ break;
46
+ default:
47
+ throw new Error(`Arquitetura não suportada: ${arch}`);
48
+ }
49
+ const filename = `core-${osName}-${archName}${ext}`;
50
+ // Caminho relativo: sobe um nível (fora do dist/src) e entra em builds
51
+ // Ajuste o '..' conforme a estrutura final da sua pasta de compilação
52
+ return path_1.default.resolve(__dirname, '..', 'core-go', filename);
53
+ }
54
+ /**
55
+ * Carrega a biblioteca nativa usando Koffi.
56
+ */
57
+ static loadLibrary(customPath) {
58
+ if (this.instance)
59
+ return this.instance;
60
+ const libPath = customPath || this.getLibPath();
61
+ if (!fs_1.default.existsSync(libPath)) {
62
+ throw new Error(`Biblioteca nativa não encontrada: ${libPath}.\n`);
63
+ }
64
+ try {
65
+ const lib = koffi_1.default.load(libPath);
66
+ // Mapeia a função Go: func Optimize(...) *C.char
67
+ // Em Koffi, 'str' como retorno significa char* (string C)
68
+ // Se o Go retornar nil, o Koffi converte para null no JS
69
+ this.instance = lib.func('Optimize', 'str', ['str', 'str', 'str']);
70
+ return this.instance;
71
+ }
72
+ catch (error) {
73
+ throw new Error(`Falha ao carregar a biblioteca nativa em ${libPath}: ${error}`);
74
+ }
75
+ }
76
+ /**
77
+ * Executa a otimização.
78
+ */
79
+ static run(options) {
80
+ const { targetDir, outputDir = '', ignoredPatterns = [], customLibPath } = options;
81
+ const optimize = this.loadLibrary(customLibPath);
82
+ const absTarget = path_1.default.resolve(targetDir);
83
+ const absOutput = outputDir ? path_1.default.resolve(outputDir) : '';
84
+ const ignoredStr = ignoredPatterns.join(',');
85
+ // Validação básica
86
+ if (!fs_1.default.existsSync(absTarget)) {
87
+ return;
88
+ }
89
+ const errorMsg = optimize(absTarget, absOutput, ignoredStr);
90
+ if (errorMsg) {
91
+ throw new Error(errorMsg);
92
+ }
93
+ }
94
+ }
95
+ exports.NativeOptimizer = NativeOptimizer;
96
+ /**
97
+ * API Pública
98
+ */
99
+ const runOptimizer = (options) => {
100
+ return NativeOptimizer.run(options);
101
+ };
102
+ exports.runOptimizer = runOptimizer;
103
+ // Export padrão também para facilitar imports
104
+ exports.default = exports.runOptimizer;
package/dist/bin/vatts.js CHANGED
@@ -94,6 +94,7 @@ program
94
94
  .option('--no-html', 'Do not generate index.html (assets only)')
95
95
  .option('--output-h-data <file>', 'Write the data-h value into this file')
96
96
  .action(async (options) => {
97
+ process.env.NODE_ENV = 'production';
97
98
  const projectDir = process.cwd();
98
99
  const outputInput = (typeof options.output === 'string' && options.output.trim()) || 'exported';
99
100
  const exportDirResolved = path.resolve(projectDir, outputInput);
package/dist/builder.js CHANGED
@@ -1,4 +1,12 @@
1
1
  "use strict";
2
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
3
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
4
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
5
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
6
+ });
7
+ }
8
+ return path;
9
+ };
2
10
  /*
3
11
  * This file is part of the Vatts.js Project.
4
12
  * Copyright (c) 2026 mfraz
@@ -15,19 +23,12 @@
15
23
  * See the License for the specific language governing permissions and
16
24
  * limitations under the License.
17
25
  */
18
- var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
19
- if (typeof path === "string" && /^\.\.?\//.test(path)) {
20
- return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
21
- return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
22
- });
23
- }
24
- return path;
25
- };
26
26
  const { rollup, watch: rollupWatch } = require('rollup');
27
27
  const path = require('path');
28
28
  const Console = require("./api/console").default;
29
29
  const fs = require('fs');
30
- const { readdir, stat, rm } = require("node:fs/promises");
30
+ const crypto = require('crypto');
31
+ const { readdir, stat, rm, rename } = require("node:fs/promises");
31
32
  const { loadTsConfigPaths, resolveTsConfigAlias } = require('./tsconfigPaths');
32
33
  // --- Optimization Plugins ---
33
34
  let terser, replace;
@@ -137,12 +138,9 @@ const customPostCssPlugin = (isProduction, isWatch = false) => {
137
138
  };
138
139
  return {
139
140
  name: 'custom-postcss-plugin',
140
- // O load hook anterior estava causando conflitos com o watch interno do Rollup.
141
- // Removemos ele e usamos addWatchFile no transform.
142
141
  async transform(code, id) {
143
142
  if (!id.endsWith('.css'))
144
143
  return null;
145
- // Garante que o Rollup vigie este arquivo explicitamente
146
144
  if (isWatch) {
147
145
  this.addWatchFile(id);
148
146
  }
@@ -157,13 +155,13 @@ const customPostCssPlugin = (isProduction, isWatch = false) => {
157
155
  Console.warn(`PostCSS process error:`, e.message);
158
156
  }
159
157
  }
160
- const cleanName = path.basename(id).split('?')[0];
158
+ // --- FIX: Evitar colisão de nomes usando Hash do ID ---
159
+ const hash = crypto.createHash('md5').update(id).digest('hex').slice(0, 8);
160
+ const baseName = path.basename(id).split('?')[0];
161
+ const uniqueName = `${hash}-${baseName}`;
161
162
  // Sanitiza o nome para usar como ID no DOM
162
- const safeId = cleanName.replace(/[^a-zA-Z0-9-_]/g, '_');
163
- const referenceId = this.emitFile({ type: 'asset', name: cleanName, source: processedCss });
164
- // Lógica melhorada para injetar o CSS:
165
- // 1. Usa um ID único para evitar tags <link> duplicadas.
166
- // 2. Adiciona um timestamp (?t=...) no href se estiver em dev para quebrar o cache do navegador.
163
+ const safeId = uniqueName.replace(/[^a-zA-Z0-9-_]/g, '_');
164
+ const referenceId = this.emitFile({ type: 'asset', name: uniqueName, source: processedCss });
167
165
  return {
168
166
  code: `
169
167
  const cssUrl = String(import.meta.ROLLUP_FILE_URL_${referenceId});
@@ -178,7 +176,6 @@ const customPostCssPlugin = (isProduction, isWatch = false) => {
178
176
  document.head.appendChild(link);
179
177
  }
180
178
 
181
- // Em dev, força o reload do CSS adicionando timestamp
182
179
  const timestamp = ${isWatch ? 'Date.now()' : 'null'};
183
180
  link.href = timestamp ? (cssUrl + '?t=' + timestamp) : cssUrl;
184
181
  }
@@ -218,6 +215,10 @@ const smartAssetPlugin = (isProduction) => {
218
215
  }
219
216
  let buffer = await fs.promises.readFile(cleanId);
220
217
  const size = buffer.length;
218
+ // --- FIX: Evitar colisão de nomes em assets estáticos ---
219
+ const hash = crypto.createHash('md5').update(cleanId).digest('hex').slice(0, 8);
220
+ const baseName = path.basename(cleanId);
221
+ const uniqueName = `${hash}-${baseName}`;
221
222
  if (type === 'svg') {
222
223
  if (size < INLINE_LIMIT) {
223
224
  const content = buffer.toString('utf8');
@@ -228,7 +229,7 @@ const smartAssetPlugin = (isProduction) => {
228
229
  `;
229
230
  }
230
231
  else {
231
- const referenceId = this.emitFile({ type: 'asset', name: path.basename(cleanId), source: buffer });
232
+ const referenceId = this.emitFile({ type: 'asset', name: uniqueName, source: buffer });
232
233
  const content = buffer.toString('utf8');
233
234
  return `
234
235
  export default String(import.meta.ROLLUP_FILE_URL_${referenceId});
@@ -241,7 +242,7 @@ const smartAssetPlugin = (isProduction) => {
241
242
  return `export default "data:${type};base64,${base64}";`;
242
243
  }
243
244
  else {
244
- const referenceId = this.emitFile({ type: 'asset', name: path.basename(cleanId), source: buffer });
245
+ const referenceId = this.emitFile({ type: 'asset', name: uniqueName, source: buffer });
245
246
  return `export default String(import.meta.ROLLUP_FILE_URL_${referenceId});`;
246
247
  }
247
248
  }
@@ -379,12 +380,10 @@ async function buildWithChunks(entryPoint, outdir, isProduction = false) {
379
380
  manualChunks(id) {
380
381
  if (id.includes('node_modules')) {
381
382
  const normalizedId = id.replace(/\\/g, '/');
382
- // --- VUE SPLITTING AVANÇADO ---
383
383
  if (/\/node_modules\/vue-router\//.test(normalizedId))
384
384
  return 'vendor-vue-router';
385
385
  if (/\/node_modules\/(pinia|vuex)\//.test(normalizedId))
386
386
  return 'vendor-vue-store';
387
- // Separa os modulos internos do Vue para evitar um chunk gigante
388
387
  if (/\/node_modules\/(vue|@vue)\//.test(normalizedId)) {
389
388
  if (normalizedId.includes('/runtime-core'))
390
389
  return 'vendor-vue-runtime-core';
@@ -398,8 +397,6 @@ async function buildWithChunks(entryPoint, outdir, isProduction = false) {
398
397
  return 'vendor-vue-compiler';
399
398
  return 'vendor-vue-core';
400
399
  }
401
- // --- REACT SPLITTING AVANÇADO ---
402
- // Separa DOM de Core e Scheduler
403
400
  if (/\/node_modules\/react-dom\//.test(normalizedId))
404
401
  return 'vendor-react-dom';
405
402
  if (/\/node_modules\/scheduler\//.test(normalizedId))
@@ -408,7 +405,6 @@ async function buildWithChunks(entryPoint, outdir, isProduction = false) {
408
405
  return 'vendor-react-router';
409
406
  if (/\/node_modules\/react\//.test(normalizedId))
410
407
  return 'vendor-react-core';
411
- // --- UI LIBS (Granular) ---
412
408
  if (id.includes('framer-motion'))
413
409
  return 'vendor-framer';
414
410
  if (id.includes('@radix-ui'))
@@ -417,15 +413,12 @@ async function buildWithChunks(entryPoint, outdir, isProduction = false) {
417
413
  return 'vendor-headless';
418
414
  if (id.includes('@heroicons'))
419
415
  return 'vendor-icons';
420
- // --- UTILS ---
421
416
  if (id.includes('lodash'))
422
417
  return 'vendor-lodash';
423
418
  if (id.includes('date-fns') || id.includes('moment'))
424
419
  return 'vendor-date';
425
420
  if (id.includes('axios'))
426
421
  return 'vendor-axios';
427
- // Resto cai em vendor-libs genérico para não criar 1 arquivo por pacote,
428
- // mas já tiramos o peso pesado acima.
429
422
  return 'vendor-libs';
430
423
  }
431
424
  }
@@ -433,6 +426,51 @@ async function buildWithChunks(entryPoint, outdir, isProduction = false) {
433
426
  const bundle = await rollup(inputOptions);
434
427
  await bundle.write(outputOptions);
435
428
  await bundle.close();
429
+ // --- Native Optimization Integration ---
430
+ if (isProduction) {
431
+ try {
432
+ // Dynamically require to avoid issues if file doesn't exist in dev/non-opt environments
433
+ // Assuming ./api/optimizer matches the transpiled location of src/optimizer.ts
434
+ const { runOptimizer } = require('./api/optimizer');
435
+ const optimizedDir = path.join(outdir, 'optimized');
436
+ // 1. Run Optimizer
437
+ runOptimizer({
438
+ targetDir: outdir,
439
+ outputDir: optimizedDir,
440
+ // Ignore already optimized assets or node_modules if present
441
+ ignoredPatterns: ['assets']
442
+ });
443
+ // 2. Cleanup: Remove original main*.js and chunks folder from root
444
+ const rootFiles = await readdir(outdir);
445
+ for (const file of rootFiles) {
446
+ // Match main.js, main.hash.js, etc and the chunks directory
447
+ if ((file.startsWith('main') && file.endsWith('.js')) || file === 'chunks') {
448
+ await rm(path.join(outdir, file), { recursive: true, force: true });
449
+ }
450
+ }
451
+ // 3. Move optimized files back to root
452
+ if (fs.existsSync(optimizedDir)) {
453
+ const optFiles = await readdir(optimizedDir);
454
+ for (const file of optFiles) {
455
+ const srcPath = path.join(optimizedDir, file);
456
+ const destPath = path.join(outdir, file);
457
+ // Force remove destination if it exists (e.g. if assets folder exists and we are moving optimized/assets)
458
+ // This prevents EEXIST errors on rename
459
+ await rm(destPath, { recursive: true, force: true });
460
+ await rename(srcPath, destPath);
461
+ }
462
+ // Remove the now empty optimized directory
463
+ await rm(optimizedDir, { recursive: true, force: true });
464
+ }
465
+ Console.log("✅ Build successfully optimized with native optimizer.");
466
+ }
467
+ catch (err) {
468
+ Console.error('Native optimization failed:', err);
469
+ // Not exiting process here to allow build to "succeed" even if optimization fails,
470
+ // though files might be in a weird state.
471
+ // If critical, uncomment: process.exit(1);
472
+ }
473
+ }
436
474
  }
437
475
  catch (error) {
438
476
  Console.error('An error occurred while building with chunks:', error);
@@ -541,7 +579,6 @@ async function watchWithChunks(entryPoint, outdir, hotReloadManager = null) {
541
579
  dir: outdir,
542
580
  format: 'es',
543
581
  entryFileNames: 'main.js',
544
- // CHANGE: Remove hash in watch mode to prevent file accumulation in assets folder
545
582
  assetFileNames: 'assets/[name][extname]',
546
583
  sourcemap: true,
547
584
  intro: processPolyfill
@@ -573,7 +610,6 @@ async function watch(entryPoint, outfile, hotReloadManager = null) {
573
610
  const outputOptions = {
574
611
  file: outfile,
575
612
  format: 'es',
576
- // CHANGE: Remove hash in watch mode to prevent file accumulation
577
613
  assetFileNames: 'assets/[name][extname]',
578
614
  sourcemap: true,
579
615
  intro: processPolyfill
Binary file
Binary file
package/dist/helpers.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import http, { Server } from 'http';
3
3
  import type { VattsOptions, VattsConfig } from './types';
4
- import http2 from 'http2';
5
4
  /**
6
5
  * Carrega o arquivo de configuração vatts.config.ts ou vatts.config.js do projeto
7
6
  * @param projectDir Diretório raiz do projeto
@@ -20,7 +19,7 @@ export declare function app(options?: VattsOptions): {
20
19
  /**
21
20
  * Inicia um servidor Vatts.js fechado (o usuário não tem acesso ao framework)
22
21
  */
23
- init: () => Promise<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse> | http2.Http2SecureServer<typeof http.IncomingMessage, typeof http.ServerResponse, typeof http2.Http2ServerRequest, typeof http2.Http2ServerResponse>>;
22
+ init: () => Promise<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>>;
24
23
  prepare: () => Promise<void>;
25
24
  getRequestHandler: () => (req: any, res: any, next?: any) => Promise<void> | void;
26
25
  setupWebSocket: (server: Server | any) => void;
package/dist/helpers.js CHANGED
@@ -65,8 +65,8 @@ const path_1 = __importDefault(require("path"));
65
65
  // Helpers para integração com diferentes frameworks
66
66
  const index_js_1 = __importStar(require("./index.js")); // Importando o tipo
67
67
  const console_1 = __importStar(require("./api/console"));
68
- const http2_1 = __importDefault(require("http2")); // <-- ADICIONADO: Import do HTTP/2
69
68
  const fs_1 = __importDefault(require("fs"));
69
+ const http3_1 = __importDefault(require("./api/http3"));
70
70
  // Registra loaders customizados para importar arquivos não-JS
71
71
  const { registerLoaders } = require('./loaders');
72
72
  registerLoaders({ projectDir: process.cwd() });
@@ -325,7 +325,7 @@ function setConfig(newConfig) {
325
325
  /**
326
326
  * Inicializa servidor nativo do Vatts.js usando HTTP ou HTTPS
327
327
  */
328
- async function initNativeServer(vattsApp, options, port, hostname) {
328
+ async function initNativeServer(vattsApp, options, hostname) {
329
329
  const time = Date.now();
330
330
  const projectDir = options.dir || process.cwd();
331
331
  const phase = options.dev ? 'development' : 'production';
@@ -425,7 +425,7 @@ async function initNativeServer(vattsApp, options, port, hostname) {
425
425
  // Parse do body com proteções
426
426
  req.body = await parseBody(req); // Assumindo que parseBody existe
427
427
  // Adiciona host se não existir (necessário para `new URL`)
428
- req.headers.host = req.headers.host || `localhost:${port}`;
428
+ req.headers.host = req.headers.host || `localhost:${exports.config?.port}`;
429
429
  await handler(req, res);
430
430
  }
431
431
  catch (error) {
@@ -465,51 +465,12 @@ async function initNativeServer(vattsApp, options, port, hostname) {
465
465
  }
466
466
  };
467
467
  // --- FIM DO LISTENER ---
468
- let server; // <-- ADICIONADO: Suporte a Http2SecureServer
469
- const isSSL = exports.config.ssl && exports.config.ssl.key && exports.config.ssl.cert;
470
- if (isSSL && exports.config.ssl) {
471
- const sslOptions = {
472
- key: fs_1.default.readFileSync(exports.config.ssl.key, 'utf8'),
473
- cert: fs_1.default.readFileSync(exports.config.ssl.cert, 'utf8'),
474
- ca: exports.config.ssl.ca ? fs_1.default.readFileSync(exports.config.ssl.ca, 'utf8') : undefined,
475
- allowHTTP1: true // <-- IMPORTANTE: Garante compatibilidade com clientes HTTP/1.1
476
- };
477
- // 1. Cria o servidor HTTPS com HTTP/2
478
- // Substituído https.createServer por http2.createSecureServer
479
- server = http2_1.default.createSecureServer(sslOptions, requestListener);
480
- // 2. Cria o servidor de REDIRECIONAMENTO (HTTP -> HTTPS)
481
- const httpRedirectPort = exports.config.ssl.redirectPort;
482
- http_1.default.createServer((req, res) => {
483
- // Evita host header injection/open redirect: prefere hostname configurado
484
- const rawHost = String(req.headers['host'] || '').trim();
485
- const configuredHost = (options.hostname || hostname || '').trim();
486
- let hostToUse = configuredHost;
487
- // Se não tiver hostname configurado, tenta usar Host do request com validação básica
488
- if (!hostToUse) {
489
- const hostWithoutPort = rawHost.split(':')[0];
490
- // allowlist: hostname simples (sem espaços, sem barras), e sem caracteres perigosos
491
- const isValidHost = /^[a-zA-Z0-9.-]+$/.test(hostWithoutPort) && !hostWithoutPort.includes('..');
492
- hostToUse = isValidHost ? hostWithoutPort : 'localhost';
493
- }
494
- // Monta a URL de redirecionamento
495
- let redirectUrl = `https://${hostToUse}`;
496
- // Adiciona a porta HTTPS apenas se não for a padrão (443)
497
- if (port !== 443) {
498
- redirectUrl += `:${port}`;
499
- }
500
- redirectUrl += req.url || '/';
501
- res.writeHead(301, { 'Location': redirectUrl });
502
- res.end();
503
- }).listen(httpRedirectPort, hostname, () => { });
504
- }
505
- else {
506
- // --- MODO HTTP (Original) ---
507
- // Cria o servidor HTTP nativo
508
- server = http_1.default.createServer(requestListener);
509
- }
510
- // Configurações de segurança do servidor (usa configuração personalizada)
511
- server.setTimeout(vattsConfig.serverTimeout || 35000); // Timeout geral do servidor
512
- // @ts-ignore (Propriedades específicas do HTTP/1, HTTP/2 gerencia isso de forma diferente, mas mantemos para o server HTTP padrão)
468
+ // Sempre criamos um servidor HTTP padrão do Node.js.
469
+ // Se estivermos em modo SSL (HTTP/3), este servidor será apenas o "backend" local.
470
+ const server = http_1.default.createServer(requestListener);
471
+ // Configurações de timeout (aplicam-se ao backend)
472
+ server.setTimeout(vattsConfig.serverTimeout || 35000);
473
+ // @ts-ignore
513
474
  if (server.maxHeadersCount)
514
475
  server.maxHeadersCount = vattsConfig.maxHeadersCount || 100;
515
476
  // @ts-ignore
@@ -518,11 +479,49 @@ async function initNativeServer(vattsApp, options, port, hostname) {
518
479
  // @ts-ignore
519
480
  if (server.requestTimeout)
520
481
  server.requestTimeout = vattsConfig.requestTimeout || 30000;
521
- server.listen(port, hostname, () => {
522
- sendBox({ ...options });
523
- msg.end(`${console_1.Colors.Bright}Ready on port ${console_1.Colors.BgGreen} ${exports.config?.port} ${console_1.Colors.Reset}${console_1.Colors.Bright} in ${Date.now() - time}ms${console_1.Colors.Reset}\n`);
524
- });
525
- // Configura WebSocket para hot reload (Comum a ambos)
482
+ const isSSL = exports.config.ssl && exports.config.ssl.key && exports.config.ssl.cert;
483
+ if (isSSL) {
484
+ // --- MODO SSL (HTTP/3 Proxy Nativo) ---
485
+ // 1. Definição da Porta do Backend (Onde o Node.js vai rodar escondido)
486
+ // Se a porta pública for 3000, usamos 3001 para o backend para não dar conflito de porta presa.
487
+ const backendPort = exports.config.ssl?.backendPort;
488
+ // 2. Portas Públicas
489
+ const publicPort = exports.config.port || 443;
490
+ const redirectPort = exports.config.ssl?.redirectPort || 80;
491
+ // 3. Inicia o Node.js em localhost (Backend)
492
+ server.listen(backendPort, '127.0.0.1', () => {
493
+ try {
494
+ // 4. Inicia o Proxy Go (HTTP/3 + Fallback H2/H1)
495
+ // Isso não bloqueia o Node, roda em background via CGO
496
+ (0, http3_1.default)({
497
+ httpPort: `:${redirectPort}`, // Porta para redirecionar HTTP -> HTTPS
498
+ httpsPort: `:${publicPort}`, // Porta Principal (UDP/TCP)
499
+ backendUrl: `http://127.0.0.1:${backendPort}`, // Para onde enviar o tráfego
500
+ certPath: exports.config?.ssl?.cert,
501
+ keyPath: exports.config?.ssl?.key
502
+ });
503
+ // Atualiza o box de info para mostrar a porta pública correta
504
+ sendBox({ ...options });
505
+ msg.end(`${console_1.Colors.Bright}Ready on port ${console_1.Colors.BgGreen} ${publicPort} (HTTP/3 Enabled) ${console_1.Colors.Reset}\n` +
506
+ `${console_1.Colors.Dim} ↳ Backend active on 127.0.0.1:${backendPort}${console_1.Colors.Reset}\n` +
507
+ `${console_1.Colors.Bright} in ${Date.now() - time}ms${console_1.Colors.Reset}\n`);
508
+ }
509
+ catch (err) {
510
+ console_1.default.error(`${console_1.Colors.FgRed}[Critical] Failed to start Native HTTP/3 Proxy:${console_1.Colors.Reset} ${err.message}`);
511
+ // Opcional: Fallback ou exit. Geralmente se o SSL falhar, melhor parar.
512
+ process.exit(1);
513
+ }
514
+ });
515
+ }
516
+ else {
517
+ // --- MODO HTTP CLÁSSICO (Sem SSL) ---
518
+ // Roda direto na porta configurada, exposto para o mundo
519
+ server.listen(exports.config?.port, hostname, () => {
520
+ sendBox({ ...options });
521
+ msg.end(`${console_1.Colors.Bright}Ready on port ${console_1.Colors.BgGreen} ${exports.config?.port} ${console_1.Colors.Reset}${console_1.Colors.Bright} in ${Date.now() - time}ms${console_1.Colors.Reset}\n`);
522
+ });
523
+ }
524
+ // Configura WebSocket (Funciona através do Proxy pois ele suporta upgrade de conexão)
526
525
  vattsApp.setupWebSocket(server);
527
526
  vattsApp.executeInstrumentation();
528
527
  return server;
@@ -618,12 +617,11 @@ ${console_1.Colors.Bright + console_1.Colors.FgCyan} \\ / /\\ | | /__\
618
617
  ${console_1.Colors.Bright + console_1.Colors.FgCyan} \\/ /~~\\ | | .__/ .${console_1.Colors.FgWhite} \\__/ .__/ ${message}
619
618
 
620
619
  `);
621
- const actualPort = exports.config?.port || 3000;
622
620
  const actualHostname = options.hostname || "0.0.0.0";
623
621
  if (framework !== 'native') {
624
622
  console_1.default.warn(`The "${framework}" framework was selected, but the init() method only works with the "native" framework. Starting native server...`);
625
623
  }
626
- return await initNativeServer(vattsApp, options, actualPort, actualHostname);
624
+ return await initNativeServer(vattsApp, options, actualHostname);
627
625
  }
628
626
  };
629
627
  }
package/dist/index.js CHANGED
@@ -60,7 +60,6 @@ const crypto_1 = __importDefault(require("crypto")); // Adicionado para gerar ha
60
60
  const express_1 = require("./adapters/express");
61
61
  const builder_1 = require("./builder");
62
62
  const router_1 = require("./router");
63
- // import { renderAsStream } from './renderer'; // REMOVIDO: Carregamento dinâmico agora
64
63
  const http_1 = require("./api/http");
65
64
  Object.defineProperty(exports, "VattsRequest", { enumerable: true, get: function () { return http_1.VattsRequest; } });
66
65
  Object.defineProperty(exports, "VattsResponse", { enumerable: true, get: function () { return http_1.VattsResponse; } });
@@ -236,33 +235,6 @@ Object.defineProperty(exports, "FrameworkAdapterFactory", { enumerable: true, ge
236
235
  var helpers_2 = require("./helpers");
237
236
  Object.defineProperty(exports, "app", { enumerable: true, get: function () { return helpers_2.app; } });
238
237
  // Função para verificar se o projeto é grande o suficiente para se beneficiar de chunks
239
- function isLargeProject(projectDir) {
240
- try {
241
- const srcDir = path_1.default.join(projectDir, 'src');
242
- if (!fs_1.default.existsSync(srcDir))
243
- return false;
244
- let totalFiles = 0;
245
- let totalSize = 0;
246
- function scanDirectory(dir) {
247
- const items = fs_1.default.readdirSync(dir, { withFileTypes: true });
248
- for (const item of items) {
249
- const fullPath = path_1.default.join(dir, item.name);
250
- if (item.isDirectory() && item.name !== 'node_modules' && item.name !== '.git') {
251
- scanDirectory(fullPath);
252
- }
253
- else if (item.isFile() && /\.(tsx?|jsx?|css|scss|less)$/i.test(item.name)) {
254
- totalFiles++;
255
- totalSize += fs_1.default.statSync(fullPath).size;
256
- }
257
- }
258
- }
259
- scanDirectory(srcDir);
260
- return totalFiles > 20 || totalSize > 500 * 1024;
261
- }
262
- catch (error) {
263
- return false;
264
- }
265
- }
266
238
  // Função para gerar o arquivo de entrada para o esbuild
267
239
  function createEntryFile(projectDir, routes, framework) {
268
240
  try {
@@ -556,11 +528,24 @@ function vatts(options) {
556
528
  const staticPath = path_1.default.join(dir, '.vatts');
557
529
  const requestPath = pathname.replace('/_vatts/', '');
558
530
  if (!isSuspiciousPathname(requestPath)) {
559
- const filePath = resolveWithin(staticPath, requestPath);
531
+ let filePath = resolveWithin(staticPath, requestPath);
532
+ let isGzipped = false;
533
+ // --- LÓGICA GZIP (Compatibilidade com Otimizador Go) ---
534
+ // Se o arquivo solicitado termina em .js e NÃO existe fisicamente (foi deletado pelo otimizador),
535
+ // tentamos encontrar a versão .js.gz
536
+ if (filePath && !fs_1.default.existsSync(filePath) && filePath.endsWith('.js')) {
537
+ const gzipPath = filePath + '.gz';
538
+ if (fs_1.default.existsSync(gzipPath)) {
539
+ filePath = gzipPath;
540
+ isGzipped = true;
541
+ }
542
+ }
560
543
  // Verifica se existe E se é arquivo
561
544
  if (filePath && fs_1.default.existsSync(filePath) && fs_1.default.statSync(filePath).isFile()) {
562
545
  const stats = fs_1.default.statSync(filePath);
563
- const ext = path_1.default.extname(filePath).toLowerCase();
546
+ // Se for GZIP, usamos '.js' para definir o Content-Type correto (application/javascript),
547
+ // caso contrário, pegamos a extensão real do arquivo.
548
+ const ext = isGzipped ? '.js' : path_1.default.extname(filePath).toLowerCase();
564
549
  const contentTypes = {
565
550
  '.js': 'application/javascript',
566
551
  '.css': 'text/css',
@@ -578,14 +563,17 @@ function vatts(options) {
578
563
  genericRes.header('Expires', '0');
579
564
  }
580
565
  else {
581
- // Em produção, mantemos o cache agressivo (assumindo hash nos arquivos exceto entry points)
582
- // Dica: Se o main.js de prod também não tiver hash, use 'no-cache' nele também
566
+ // Em produção, mantemos o cache agressivo
583
567
  genericRes.header('Cache-Control', 'public, max-age=31536000, immutable');
584
568
  }
569
+ // --- HEADER VITAL PARA GZIP ---
570
+ if (isGzipped) {
571
+ genericRes.header('Content-Encoding', 'gzip');
572
+ }
585
573
  const lastModified = stats.mtime.toUTCString();
586
574
  genericRes.header('Last-Modified', lastModified);
587
575
  genericRes.header('Content-Type', contentTypes[ext] || 'text/plain');
588
- // Lógica 304 (Mantida igual, pois ajuda na performance mesmo em dev se o arquivo n mudou nada)
576
+ // Lógica 304
589
577
  const ifModifiedSince = req.headers['if-modified-since'];
590
578
  if (ifModifiedSince) {
591
579
  const requestDate = new Date(ifModifiedSince).getTime();
@@ -612,8 +600,7 @@ function vatts(options) {
612
600
  return; // Encerra aqui com sucesso
613
601
  }
614
602
  else {
615
- // CORREÇÃO 2: Se o arquivo não existe, retornamos 404 AQUI.
616
- // Isso impede que o código continue e caia na rota de renderização do HTML (SPA Fallback)
603
+ // CORREÇÃO 2: 404 para assets não encontrados
617
604
  if (adapter.type === 'express') {
618
605
  res.status(404).send('Vatts Asset Not Found');
619
606
  }
@@ -151,8 +151,6 @@ function obfuscateData(data) {
151
151
  const hash = Buffer.from(Date.now().toString()).toString('base64').substring(0, 8);
152
152
  return `${hash}.${base64}`;
153
153
  }
154
- // Retorna as URLs dos scripts e CSS
155
- // Procura em .vatts/ e .vatts/assets/
156
154
  function getBuildAssets(req) {
157
155
  const projectDir = process.cwd();
158
156
  const distDir = path_1.default.join(projectDir, '.vatts');
@@ -173,8 +171,9 @@ function getBuildAssets(req) {
173
171
  const fullPath = path_1.default.join(directory, file);
174
172
  const stat = fs_1.default.statSync(fullPath);
175
173
  if (stat.isFile()) {
176
- if (file.endsWith('.js')) {
177
- scripts.push(`${urlPrefix}/${file}`);
174
+ // MODIFICADO: Aceita .js OU .js.gz
175
+ if (file.endsWith('.js') || file.endsWith('.js.gz')) {
176
+ scripts.push(`${urlPrefix}/${file.replace(".gz", '')}`);
178
177
  }
179
178
  else if (file.endsWith('.css')) {
180
179
  styles.push(`${urlPrefix}/${file}`);
@@ -189,8 +188,9 @@ function getBuildAssets(req) {
189
188
  const manifest = JSON.parse(fs_1.default.readFileSync(manifestPath, 'utf8'));
190
189
  const manifestFiles = Object.values(manifest);
191
190
  scripts = manifestFiles
192
- .filter((f) => f.endsWith('.js'))
193
- .map((f) => `/_vatts/${f}`);
191
+ // MODIFICADO: Filtra .js E .js.gz no manifesto
192
+ .filter((f) => f.endsWith('.js') || f.endsWith('.js.gz'))
193
+ .map((f) => `/_vatts/${f.replace(".gz", "")}`);
194
194
  styles = manifestFiles
195
195
  .filter((f) => f.endsWith('.css'))
196
196
  .map((f) => `/_vatts/${f}`);
package/dist/types.d.ts CHANGED
@@ -27,6 +27,7 @@ export interface VattsOptions {
27
27
  export interface VattsConfig {
28
28
  port: number;
29
29
  ssl?: {
30
+ backendPort: number;
30
31
  redirectPort: number;
31
32
  key: string;
32
33
  cert: string;
@@ -101,9 +101,9 @@ function extractComponentPreloads(componentPath) {
101
101
  tags.add(`<link rel="preload" as="style" href="${publicUrl}">`);
102
102
  tags.add(`<link rel="stylesheet" href="${publicUrl}">`);
103
103
  }
104
- else if (['.js'].includes(ext)) {
104
+ else if (['.js', '.js.gz'].includes(ext)) {
105
105
  // Adicionado suporte para JS
106
- tags.add(`<link rel="preload" as="script" href="${publicUrl}">`);
106
+ tags.add(`<link rel="preload" as="script" href="${publicUrl.replace(".gz", '')}">`);
107
107
  }
108
108
  else if (['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.avif'].includes(ext)) {
109
109
  tags.add(`<link rel="preload" as="image" href="${publicUrl}">`);
@@ -331,8 +331,8 @@ function getBuildAssets(req) {
331
331
  return;
332
332
  const fullPath = path_1.default.join(directory, file);
333
333
  if (fs_1.default.statSync(fullPath).isFile()) {
334
- if (file.endsWith('.js'))
335
- scripts.push(`${urlPrefix}/${file}`);
334
+ if (file.endsWith('.js') || file.endsWith(".js.gz"))
335
+ scripts.push(`${urlPrefix}/${file.replace(".gz", '')}`);
336
336
  else if (file.endsWith('.css'))
337
337
  styles.push(`${urlPrefix}/${file}`);
338
338
  }
@@ -343,7 +343,7 @@ function getBuildAssets(req) {
343
343
  if (fs_1.default.existsSync(manifestPath)) {
344
344
  const manifest = JSON.parse(fs_1.default.readFileSync(manifestPath, 'utf8'));
345
345
  const manifestFiles = Object.values(manifest);
346
- scripts = manifestFiles.filter((f) => f.endsWith('.js')).map((f) => `/_vatts/${f}`);
346
+ scripts = manifestFiles.filter((f) => f.endsWith('.js') || f.endsWith(".js.gz")).map((f) => `/_vatts/${f.replace('.gz', '')}`);
347
347
  styles = manifestFiles.filter((f) => f.endsWith('.css')).map((f) => `/_vatts/${f}`);
348
348
  }
349
349
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vatts",
3
- "version": "1.3.1-alpha.2",
3
+ "version": "1.4.1-test.1",
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": "mfraz",
@@ -110,6 +110,7 @@
110
110
  "chokidar": "^3.6.0",
111
111
  "commander": "^14.0.2",
112
112
  "esbuild": "^0.27.2",
113
+ "koffi": "^2.15.1",
113
114
  "rollup": "^4.56.0",
114
115
  "rollup-plugin-esbuild": "^6.2.1",
115
116
  "rollup-plugin-vue": "^6.0.0",
@@ -136,6 +137,6 @@
136
137
  "vue-tsc": "^3.2.3"
137
138
  },
138
139
  "scripts": {
139
- "build": "tsc && copyfiles -u 1 \"src/**/*.{vue,d.ts}\" dist"
140
+ "build": "tsc && copyfiles -u 1 \"src/**/*.{vue,d.ts}\" dist && copyfiles -f \"../../services/core-go/binaries/*.node\" dist/core-go"
140
141
  }
141
142
  }