vatts 1.0.2-alpha.3 → 1.0.2-alpha.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.
- package/dist/bin/vatts.js +91 -100
- package/dist/builder.js +2 -1
- package/dist/client/clientRouter.d.ts +11 -54
- package/dist/client/clientRouter.js +39 -94
- package/dist/client/rpc.d.ts +1 -1
- package/dist/client/rpc.js +19 -2
- package/dist/index.js +41 -85
- package/dist/renderer.d.ts +3 -1
- package/dist/renderer.js +156 -138
- package/package.json +1 -1
package/dist/bin/vatts.js
CHANGED
|
@@ -22,14 +22,17 @@ require('ts-node').register();
|
|
|
22
22
|
const { registerLoaders } = require('../loaders');
|
|
23
23
|
registerLoaders();
|
|
24
24
|
const { program } = require('commander');
|
|
25
|
+
const fs = require('fs');
|
|
26
|
+
const path = require('path');
|
|
27
|
+
const { Writable } = require('stream');
|
|
28
|
+
// Importa o Console do framework
|
|
29
|
+
const ConsoleModule = require('../api/console');
|
|
30
|
+
const Console = ConsoleModule.default;
|
|
31
|
+
const { Levels, Colors } = ConsoleModule;
|
|
25
32
|
program
|
|
26
33
|
.version('1.0.0')
|
|
27
34
|
.description('CLI to manage the application.');
|
|
28
|
-
// ---
|
|
29
|
-
const fs = require('fs');
|
|
30
|
-
const path = require('path');
|
|
31
|
-
// 'program' já deve estar definido no seu arquivo
|
|
32
|
-
// const { program } = require('commander');
|
|
35
|
+
// --- Helpers ---
|
|
33
36
|
/**
|
|
34
37
|
* Função centralizada para iniciar a aplicação
|
|
35
38
|
* @param {object} options - Opções vindas do commander
|
|
@@ -45,84 +48,75 @@ function initializeApp(options, isDev) {
|
|
|
45
48
|
};
|
|
46
49
|
// 1. Verifica se a flag --ssl foi ativada
|
|
47
50
|
if (options.ssl) {
|
|
48
|
-
const C = require("../api/console");
|
|
49
|
-
const { Levels } = C;
|
|
50
|
-
const Console = C.default;
|
|
51
51
|
const sslDir = path.resolve(process.cwd(), 'certs');
|
|
52
|
-
const keyPath = path.join(sslDir, 'key.pem');
|
|
53
|
-
const certPath = path.join(sslDir, 'cert.pem');
|
|
54
|
-
// (Você pode mudar para 'cert.key' se preferir, apenas ajuste os nomes aqui)
|
|
52
|
+
const keyPath = path.join(sslDir, 'key.pem');
|
|
53
|
+
const certPath = path.join(sslDir, 'cert.pem');
|
|
55
54
|
// 2. Verifica se os arquivos existem
|
|
56
55
|
if (fs.existsSync(keyPath) && fs.existsSync(certPath)) {
|
|
57
56
|
appOptions.ssl = {
|
|
58
57
|
key: keyPath,
|
|
59
58
|
cert: certPath
|
|
60
59
|
};
|
|
61
|
-
// 3. Adiciona a porta de redirecionamento
|
|
60
|
+
// 3. Adiciona a porta de redirecionamento
|
|
62
61
|
appOptions.ssl.redirectPort = options.httpRedirectPort || 80;
|
|
63
62
|
}
|
|
64
63
|
else {
|
|
65
|
-
Console.
|
|
66
|
-
process.exit(1);
|
|
64
|
+
Console.error(`SSL Error: Ensure that './certs/key.pem' and './certs/cert.pem' exist.`);
|
|
65
|
+
process.exit(1);
|
|
67
66
|
}
|
|
68
67
|
}
|
|
69
68
|
// 4. Inicia o helper com as opções
|
|
70
|
-
const
|
|
71
|
-
const
|
|
72
|
-
|
|
69
|
+
const helperModule = require("../helpers");
|
|
70
|
+
const helper = helperModule.default(appOptions);
|
|
71
|
+
helper.init();
|
|
73
72
|
}
|
|
74
|
-
// --- Comando DEV ---
|
|
75
|
-
program
|
|
76
|
-
.command('dev')
|
|
77
|
-
.description('Starts the application in development mode.')
|
|
78
|
-
.option('-p, --port <number>', 'Specifies the port to run on', '3000')
|
|
79
|
-
.option('-H, --hostname <string>', 'Specifies the hostname to run on', '0.0.0.0')
|
|
80
|
-
.option('--ssl', 'Activates HTTPS/SSL mode (requires ./ssl/key.pem and ./ssl/cert.pem)')
|
|
81
|
-
.option('--http-redirect-port <number>', 'Port for HTTP->HTTPS redirection', '80')
|
|
82
|
-
.action((options) => {
|
|
83
|
-
initializeApp(options, true); // Chama a função com dev: true
|
|
84
|
-
});
|
|
85
|
-
// --- Comando START (Produção) ---
|
|
86
|
-
program
|
|
87
|
-
.command('start')
|
|
88
|
-
.description('Starts the application in production mode.')
|
|
89
|
-
.option('-p, --port <number>', 'Specifies the port to run on', '3000')
|
|
90
|
-
.option('-H, --hostname <string>', 'Specifies the hostname to run on', '0.0.0.0')
|
|
91
|
-
.option('--ssl', 'Activates HTTPS/SSL mode (requires ./ssl/key.pem and ./ssl/cert.pem)')
|
|
92
|
-
.option('--http-redirect-port <number>', 'Port for HTTP->HTTPS redirection', '80')
|
|
93
|
-
.action((options) => {
|
|
94
|
-
initializeApp(options, false); // Chama a função com dev: false
|
|
95
|
-
});
|
|
96
73
|
/**
|
|
97
74
|
* Função corrigida para copiar diretórios recursivamente.
|
|
98
|
-
* Ela agora verifica se um item é um arquivo ou um diretório.
|
|
99
75
|
*/
|
|
100
76
|
function copyDirRecursive(src, dest) {
|
|
101
77
|
try {
|
|
102
|
-
// Garante que o diretório de destino exista
|
|
103
78
|
fs.mkdirSync(dest, { recursive: true });
|
|
104
|
-
// Usamos { withFileTypes: true } para evitar uma chamada extra de fs.statSync
|
|
105
79
|
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
106
80
|
for (let entry of entries) {
|
|
107
81
|
const srcPath = path.join(src, entry.name);
|
|
108
82
|
const destPath = path.join(dest, entry.name);
|
|
109
83
|
if (entry.isDirectory()) {
|
|
110
|
-
// Se for um diretório, chama a si mesma (recursão)
|
|
111
84
|
copyDirRecursive(srcPath, destPath);
|
|
112
85
|
}
|
|
113
86
|
else {
|
|
114
|
-
// Se for um arquivo, apenas copia
|
|
115
87
|
fs.copyFileSync(srcPath, destPath);
|
|
116
88
|
}
|
|
117
89
|
}
|
|
118
90
|
}
|
|
119
91
|
catch (error) {
|
|
120
|
-
|
|
121
|
-
// Lança o erro para parar o processo de exportação se a cópia falhar
|
|
92
|
+
Console.error(`Error copying ${src} to ${dest}:`, error);
|
|
122
93
|
throw error;
|
|
123
94
|
}
|
|
124
95
|
}
|
|
125
|
-
// ---
|
|
96
|
+
// --- Comandos ---
|
|
97
|
+
// Comando DEV
|
|
98
|
+
program
|
|
99
|
+
.command('dev')
|
|
100
|
+
.description('Starts the application in development mode.')
|
|
101
|
+
.option('-p, --port <number>', 'Specifies the port to run on', '3000')
|
|
102
|
+
.option('-H, --hostname <string>', 'Specifies the hostname to run on', '0.0.0.0')
|
|
103
|
+
.option('--ssl', 'Activates HTTPS/SSL mode (requires ./ssl/key.pem and ./ssl/cert.pem)')
|
|
104
|
+
.option('--http-redirect-port <number>', 'Port for HTTP->HTTPS redirection', '80')
|
|
105
|
+
.action((options) => {
|
|
106
|
+
initializeApp(options, true);
|
|
107
|
+
});
|
|
108
|
+
// Comando START (Produção)
|
|
109
|
+
program
|
|
110
|
+
.command('start')
|
|
111
|
+
.description('Starts the application in production mode.')
|
|
112
|
+
.option('-p, --port <number>', 'Specifies the port to run on', '3000')
|
|
113
|
+
.option('-H, --hostname <string>', 'Specifies the hostname to run on', '0.0.0.0')
|
|
114
|
+
.option('--ssl', 'Activates HTTPS/SSL mode (requires ./ssl/key.pem and ./ssl/cert.pem)')
|
|
115
|
+
.option('--http-redirect-port <number>', 'Port for HTTP->HTTPS redirection', '80')
|
|
116
|
+
.action((options) => {
|
|
117
|
+
initializeApp(options, false);
|
|
118
|
+
});
|
|
119
|
+
// Comando EXPORT
|
|
126
120
|
program
|
|
127
121
|
.command('export')
|
|
128
122
|
.description('Exports the application as static HTML to the "exported" folder.')
|
|
@@ -132,94 +126,82 @@ program
|
|
|
132
126
|
.option('--output-h-data <file>', 'Write the data-h value from <script id="__vatts_data__" data-h="..."> into this file')
|
|
133
127
|
.action(async (options) => {
|
|
134
128
|
const projectDir = process.cwd();
|
|
135
|
-
// Resolve output
|
|
136
|
-
// - se vier absoluto, usa como está
|
|
137
|
-
// - se vier relativo, resolve a partir do projeto
|
|
129
|
+
// Resolve output
|
|
138
130
|
const outputInput = typeof options.output === 'string' && options.output.trim().length
|
|
139
131
|
? options.output.trim()
|
|
140
132
|
: 'exported';
|
|
141
133
|
const exportDir = path.isAbsolute(outputInput)
|
|
142
134
|
? path.resolve(outputInput)
|
|
143
135
|
: path.resolve(projectDir, outputInput);
|
|
144
|
-
// Proteções: nunca permitir apagar a raiz do drive (ex: D:\) ou o root do SO (ex: C:\)
|
|
145
136
|
const exportDirResolved = path.resolve(exportDir);
|
|
146
|
-
const exportDirRoot = path.parse(exportDirResolved).root;
|
|
137
|
+
const exportDirRoot = path.parse(exportDirResolved).root;
|
|
147
138
|
const projectDirResolved = path.resolve(projectDir);
|
|
148
139
|
if (exportDirResolved === exportDirRoot) {
|
|
149
|
-
|
|
140
|
+
Console.error(`Refusing to use output directory at drive root: ${exportDirResolved}`);
|
|
141
|
+
process.exit(1);
|
|
150
142
|
}
|
|
151
|
-
// Também evita `--output .` ou `--output ..` que cairia no diretório do projeto (perigoso)
|
|
152
|
-
// Regra: output precisa estar dentro do projeto, ou dentro de uma subpasta do projeto.
|
|
153
|
-
// (mantém comportamento esperado e evita deletar o repo inteiro por acidente)
|
|
154
143
|
const relExportToProject = path.relative(projectDirResolved, exportDirResolved);
|
|
155
144
|
if (relExportToProject === '' || relExportToProject === '.' || relExportToProject.startsWith('..')) {
|
|
156
|
-
|
|
145
|
+
Console.error(`Refusing to export to ${exportDirResolved}. Use a subfolder like "exported" or an explicit path inside the project.`);
|
|
146
|
+
process.exit(1);
|
|
157
147
|
}
|
|
158
|
-
// assetsDir
|
|
159
|
-
// Permite usar '.' (raiz do output)
|
|
148
|
+
// assetsDir
|
|
160
149
|
const assetsDirInputRaw = typeof options.assetsDir === 'string' ? options.assetsDir : '.vatts';
|
|
161
150
|
const assetsDirInput = assetsDirInputRaw.trim().length ? assetsDirInputRaw.trim() : '.';
|
|
162
151
|
const assetsDirResolved = path.resolve(exportDirResolved, assetsDirInput);
|
|
163
152
|
const relAssetsToExport = path.relative(exportDirResolved, assetsDirResolved);
|
|
164
153
|
if (relAssetsToExport.startsWith('..') || path.isAbsolute(relAssetsToExport)) {
|
|
165
|
-
|
|
154
|
+
Console.error(`Invalid --assets-dir: must be inside output directory. Received: ${assetsDirInputRaw}`);
|
|
155
|
+
process.exit(1);
|
|
166
156
|
}
|
|
167
|
-
// Normaliza a pasta para uso em URL no HTML
|
|
168
|
-
// Se assets-dir for '.', relAssetsToExport vira '' e assetsBaseHref vira './'
|
|
169
157
|
const assetsDirUrl = relAssetsToExport.split(path.sep).join('/').replace(/^\.?\/?/, '');
|
|
170
|
-
const assetsBaseHref = '
|
|
171
|
-
|
|
158
|
+
const assetsBaseHref = '/.' + (assetsDirUrl.length ? assetsDirUrl + '/' : '');
|
|
159
|
+
Console.info('Starting export process...');
|
|
172
160
|
try {
|
|
173
|
-
// 1.
|
|
161
|
+
// 1. Limpa pasta de exportação
|
|
174
162
|
if (fs.existsSync(exportDirResolved)) {
|
|
175
|
-
|
|
163
|
+
Console.info('Cleaning existing export folder...');
|
|
176
164
|
fs.rmSync(exportDirResolved, { recursive: true, force: true });
|
|
177
165
|
}
|
|
178
166
|
fs.mkdirSync(exportDirResolved, { recursive: true });
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
//
|
|
183
|
-
const
|
|
184
|
-
const app = teste.default({ dev: false, port: 3000, hostname: '0.0.0.0', framework: 'native' });
|
|
167
|
+
// 2. Build
|
|
168
|
+
Console.info('Building application...');
|
|
169
|
+
const helperModule = require("../helpers");
|
|
170
|
+
// Usando dev: false para produção
|
|
171
|
+
const app = helperModule.default({ dev: false, port: 3000, hostname: '0.0.0.0', framework: 'native' });
|
|
185
172
|
await app.prepare();
|
|
186
|
-
|
|
173
|
+
Console.success('Build complete.');
|
|
174
|
+
// 3. Copia JavaScript
|
|
187
175
|
const distDir = path.join(projectDirResolved, '.vatts');
|
|
188
176
|
if (fs.existsSync(distDir)) {
|
|
189
|
-
|
|
177
|
+
Console.info('Copying JavaScript files...');
|
|
190
178
|
const exportDistDir = assetsDirResolved;
|
|
191
179
|
copyDirRecursive(distDir, exportDistDir);
|
|
192
|
-
console.log(`✅ JavaScript files copied to: ${path.relative(exportDirResolved, exportDistDir) || '.'}\n`);
|
|
193
180
|
}
|
|
194
|
-
// 4. Copia
|
|
181
|
+
// 4. Copia Public
|
|
195
182
|
const publicDir = path.join(projectDirResolved, 'public');
|
|
196
183
|
if (fs.existsSync(publicDir)) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
copyDirRecursive(publicDir, exportPublicDir);
|
|
200
|
-
console.log('✅ Public files copied\n');
|
|
184
|
+
Console.info('Copying public files...');
|
|
185
|
+
copyDirRecursive(publicDir, exportDirResolved);
|
|
201
186
|
}
|
|
202
|
-
// 5. Gera
|
|
187
|
+
// 5. Gera index.html
|
|
203
188
|
const shouldExtractHData = typeof options.outputHData === 'string' && options.outputHData.trim().length > 0;
|
|
204
189
|
const shouldRenderHtml = Boolean(options.html) || shouldExtractHData;
|
|
205
190
|
if (shouldRenderHtml) {
|
|
206
191
|
const writeHtmlToDisk = Boolean(options.html);
|
|
207
192
|
if (writeHtmlToDisk) {
|
|
208
|
-
|
|
193
|
+
Console.info('Generating index.html...');
|
|
209
194
|
}
|
|
210
195
|
else {
|
|
211
|
-
|
|
196
|
+
Console.info('Rendering HTML for h-data extraction...');
|
|
212
197
|
}
|
|
213
|
-
|
|
214
|
-
const { render } = require('../renderer');
|
|
198
|
+
const { renderAsStream } = require('../renderer');
|
|
215
199
|
const { loadRoutes, loadLayout, loadNotFound } = require('../router');
|
|
216
|
-
// Carrega as rotas para gerar o HTML
|
|
217
200
|
const userWebDir = path.join(projectDirResolved, 'src', 'web');
|
|
218
201
|
const userWebRoutesDir = path.join(userWebDir, 'routes');
|
|
219
202
|
const routes = loadRoutes(userWebRoutesDir);
|
|
220
203
|
loadLayout(userWebDir);
|
|
221
204
|
loadNotFound(userWebDir);
|
|
222
|
-
// Gera HTML para a rota raiz
|
|
223
205
|
const rootRoute = routes.find(r => r.pattern === '/') || routes[0];
|
|
224
206
|
if (rootRoute) {
|
|
225
207
|
const mockReq = {
|
|
@@ -229,49 +211,58 @@ program
|
|
|
229
211
|
hwebDev: false,
|
|
230
212
|
hotReloadManager: null
|
|
231
213
|
};
|
|
232
|
-
|
|
214
|
+
let html = '';
|
|
215
|
+
let resolveStream;
|
|
216
|
+
const streamComplete = new Promise(r => resolveStream = r);
|
|
217
|
+
const mockRes = new Writable({
|
|
218
|
+
write(chunk, encoding, callback) {
|
|
219
|
+
html += chunk.toString();
|
|
220
|
+
callback();
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
mockRes.setHeader = () => { };
|
|
224
|
+
mockRes.statusCode = 200;
|
|
225
|
+
mockRes.on('finish', () => {
|
|
226
|
+
resolveStream();
|
|
227
|
+
});
|
|
228
|
+
await renderAsStream({
|
|
233
229
|
req: mockReq,
|
|
230
|
+
res: mockRes,
|
|
234
231
|
route: rootRoute,
|
|
235
232
|
params: {},
|
|
236
233
|
allRoutes: routes
|
|
237
234
|
});
|
|
235
|
+
await streamComplete;
|
|
238
236
|
if (shouldExtractHData) {
|
|
239
237
|
const m = html.match(/<script\b[^>]*\bid=["']__vatts_data__["'][^>]*\bdata-h=["']([^"']*)["'][^>]*>/i);
|
|
240
238
|
if (!m || typeof m[1] !== 'string') {
|
|
241
239
|
throw new Error('Could not find <script id="__vatts_data__" data-h="..."> in rendered HTML.');
|
|
242
240
|
}
|
|
243
241
|
const hDataValue = m[1];
|
|
244
|
-
// O path do arquivo é relativo ao projeto por padrão
|
|
245
242
|
const outputHDataPathInput = options.outputHData.trim();
|
|
246
243
|
const outputHDataPath = path.isAbsolute(outputHDataPathInput)
|
|
247
244
|
? path.resolve(outputHDataPathInput)
|
|
248
245
|
: path.resolve(projectDirResolved, outputHDataPathInput);
|
|
249
246
|
fs.mkdirSync(path.dirname(outputHDataPath), { recursive: true });
|
|
250
247
|
fs.writeFileSync(outputHDataPath, hDataValue, 'utf8');
|
|
251
|
-
|
|
248
|
+
Console.success(`h-data written to: ${path.relative(projectDirResolved, outputHDataPath)}`);
|
|
252
249
|
}
|
|
253
250
|
if (writeHtmlToDisk) {
|
|
254
251
|
const scriptReplaced = html.replace(/\/_vatts\//g, assetsBaseHref);
|
|
255
252
|
const indexPath = path.join(exportDirResolved, 'index.html');
|
|
256
253
|
fs.writeFileSync(indexPath, scriptReplaced, 'utf8');
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
else {
|
|
260
|
-
console.log('✅ HTML rendered (no index.html written)\n');
|
|
254
|
+
Console.success('index.html generated.');
|
|
261
255
|
}
|
|
262
256
|
}
|
|
263
257
|
}
|
|
264
258
|
else {
|
|
265
|
-
|
|
259
|
+
Console.info('Skipping index.html generation.');
|
|
266
260
|
}
|
|
267
|
-
|
|
268
|
-
console.log(`📂 Files exported to: ${exportDirResolved}\n`);
|
|
261
|
+
Console.success(`Export completed successfully to: ${path.relative(projectDirResolved, exportDirResolved)}`);
|
|
269
262
|
}
|
|
270
263
|
catch (error) {
|
|
271
|
-
|
|
272
|
-
console.error('❌ Error during export:', error);
|
|
264
|
+
Console.error('Error during export:', error);
|
|
273
265
|
process.exit(1);
|
|
274
266
|
}
|
|
275
267
|
});
|
|
276
|
-
// Faz o "parse" dos argumentos passados na linha de comando
|
|
277
268
|
program.parse(process.argv);
|
package/dist/builder.js
CHANGED
|
@@ -304,7 +304,8 @@ function createRollupConfig(entryPoint, outdir, isProduction) {
|
|
|
304
304
|
replace({
|
|
305
305
|
preventAssignment: true,
|
|
306
306
|
values: {
|
|
307
|
-
'process.env.NODE_ENV': JSON.stringify(isProduction ? 'production' : 'development')
|
|
307
|
+
'process.env.NODE_ENV': JSON.stringify(isProduction ? 'production' : 'development'),
|
|
308
|
+
'proccess.env.PORT': JSON.stringify(process.vatts.port || 3000)
|
|
308
309
|
}
|
|
309
310
|
}),
|
|
310
311
|
tsconfigPathsPlugin(process.cwd()),
|
|
@@ -1,58 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
afterNavigate?: (url: string) => void;
|
|
4
|
-
}
|
|
5
|
-
declare class Router {
|
|
6
|
-
private events;
|
|
1
|
+
type RouteListener = () => void;
|
|
2
|
+
export declare class Router {
|
|
7
3
|
private listeners;
|
|
8
|
-
|
|
9
|
-
* Navega para uma nova rota
|
|
10
|
-
*/
|
|
11
|
-
push(url: string): Promise<void>;
|
|
12
|
-
/**
|
|
13
|
-
* Substitui a entrada atual do histórico
|
|
14
|
-
*/
|
|
15
|
-
replace(url: string): Promise<void>;
|
|
16
|
-
/**
|
|
17
|
-
* Volta uma página no histórico
|
|
18
|
-
*/
|
|
19
|
-
back(): void;
|
|
20
|
-
/**
|
|
21
|
-
* Avança uma página no histórico
|
|
22
|
-
*/
|
|
23
|
-
forward(): void;
|
|
24
|
-
/**
|
|
25
|
-
* Recarrega a página atual (re-renderiza o componente)
|
|
26
|
-
*/
|
|
27
|
-
refresh(): void;
|
|
28
|
-
/**
|
|
29
|
-
* Obtém a URL atual
|
|
30
|
-
*/
|
|
4
|
+
constructor();
|
|
31
5
|
get pathname(): string;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
get url(): string;
|
|
40
|
-
/**
|
|
41
|
-
* Adiciona event listeners para eventos de roteamento
|
|
42
|
-
*/
|
|
43
|
-
on(events: RouterEvents): void;
|
|
44
|
-
/**
|
|
45
|
-
* Remove event listeners
|
|
46
|
-
*/
|
|
47
|
-
off(): void;
|
|
48
|
-
/**
|
|
49
|
-
* Adiciona um listener para mudanças de rota
|
|
50
|
-
*/
|
|
51
|
-
subscribe(listener: () => void): () => void;
|
|
52
|
-
/**
|
|
53
|
-
* Dispara evento de navegação para todos os listeners
|
|
54
|
-
*/
|
|
55
|
-
private triggerNavigation;
|
|
6
|
+
get search(): string;
|
|
7
|
+
get hash(): string;
|
|
8
|
+
push(path: string): void;
|
|
9
|
+
replace(path: string): void;
|
|
10
|
+
back(): void;
|
|
11
|
+
subscribe(listener: RouteListener): () => void;
|
|
12
|
+
private notify;
|
|
56
13
|
}
|
|
57
14
|
export declare const router: Router;
|
|
58
|
-
export
|
|
15
|
+
export {};
|
|
@@ -16,116 +16,61 @@
|
|
|
16
16
|
* limitations under the License.
|
|
17
17
|
*/
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
-
exports.router = void 0;
|
|
19
|
+
exports.router = exports.Router = void 0;
|
|
20
20
|
class Router {
|
|
21
21
|
constructor() {
|
|
22
|
-
this.events = {};
|
|
23
22
|
this.listeners = new Set();
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
// Callback antes de navegar
|
|
30
|
-
if (this.events.beforeNavigate) {
|
|
31
|
-
const shouldProceed = await this.events.beforeNavigate(url);
|
|
32
|
-
if (shouldProceed === false)
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
// Atualiza a URL na barra de endereço
|
|
36
|
-
window.history.pushState({ path: url }, '', url);
|
|
37
|
-
// Dispara evento para o roteador capturar de forma assíncrona
|
|
38
|
-
setTimeout(() => this.triggerNavigation(), 0);
|
|
39
|
-
// Callback após navegar
|
|
40
|
-
if (this.events.afterNavigate) {
|
|
41
|
-
this.events.afterNavigate(url);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Substitui a entrada atual do histórico
|
|
46
|
-
*/
|
|
47
|
-
async replace(url) {
|
|
48
|
-
// Callback antes de navegar
|
|
49
|
-
if (this.events.beforeNavigate) {
|
|
50
|
-
const shouldProceed = await this.events.beforeNavigate(url);
|
|
51
|
-
if (shouldProceed === false)
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
// Substitui a URL atual no histórico
|
|
55
|
-
window.history.replaceState({ path: url }, '', url);
|
|
56
|
-
// Dispara evento para o roteador capturar de forma assíncrona
|
|
57
|
-
setTimeout(() => this.triggerNavigation(), 0);
|
|
58
|
-
// Callback após navegar
|
|
59
|
-
if (this.events.afterNavigate) {
|
|
60
|
-
this.events.afterNavigate(url);
|
|
23
|
+
// Só adiciona listener no lado do cliente
|
|
24
|
+
if (typeof window !== 'undefined') {
|
|
25
|
+
window.addEventListener('popstate', () => {
|
|
26
|
+
this.notify();
|
|
27
|
+
});
|
|
61
28
|
}
|
|
62
29
|
}
|
|
63
|
-
/**
|
|
64
|
-
* Volta uma página no histórico
|
|
65
|
-
*/
|
|
66
|
-
back() {
|
|
67
|
-
window.history.back();
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Avança uma página no histórico
|
|
71
|
-
*/
|
|
72
|
-
forward() {
|
|
73
|
-
window.history.forward();
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Recarrega a página atual (re-renderiza o componente)
|
|
77
|
-
*/
|
|
78
|
-
refresh() {
|
|
79
|
-
setTimeout(() => this.triggerNavigation(), 0);
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Obtém a URL atual
|
|
83
|
-
*/
|
|
84
30
|
get pathname() {
|
|
31
|
+
if (typeof window === 'undefined') {
|
|
32
|
+
return '/'; // Retorno seguro para SSR
|
|
33
|
+
}
|
|
85
34
|
return window.location.pathname;
|
|
86
35
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return
|
|
36
|
+
get search() {
|
|
37
|
+
if (typeof window === 'undefined') {
|
|
38
|
+
return '';
|
|
39
|
+
}
|
|
40
|
+
return window.location.search;
|
|
41
|
+
}
|
|
42
|
+
get hash() {
|
|
43
|
+
if (typeof window === 'undefined') {
|
|
44
|
+
return '';
|
|
45
|
+
}
|
|
46
|
+
return window.location.hash;
|
|
92
47
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
48
|
+
push(path) {
|
|
49
|
+
if (typeof window !== 'undefined') {
|
|
50
|
+
window.history.pushState({}, '', path);
|
|
51
|
+
this.notify();
|
|
52
|
+
}
|
|
98
53
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
54
|
+
replace(path) {
|
|
55
|
+
if (typeof window !== 'undefined') {
|
|
56
|
+
window.history.replaceState({}, '', path);
|
|
57
|
+
this.notify();
|
|
58
|
+
}
|
|
104
59
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
this.events = {};
|
|
60
|
+
back() {
|
|
61
|
+
if (typeof window !== 'undefined') {
|
|
62
|
+
window.history.back();
|
|
63
|
+
}
|
|
110
64
|
}
|
|
111
|
-
/**
|
|
112
|
-
* Adiciona um listener para mudanças de rota
|
|
113
|
-
*/
|
|
114
65
|
subscribe(listener) {
|
|
115
66
|
this.listeners.add(listener);
|
|
116
|
-
return () =>
|
|
67
|
+
return () => {
|
|
68
|
+
this.listeners.delete(listener);
|
|
69
|
+
};
|
|
117
70
|
}
|
|
118
|
-
|
|
119
|
-
* Dispara evento de navegação para todos os listeners
|
|
120
|
-
*/
|
|
121
|
-
triggerNavigation() {
|
|
122
|
-
// Dispara o evento nativo para o roteador do hweb capturar
|
|
123
|
-
window.dispatchEvent(new PopStateEvent('popstate'));
|
|
124
|
-
// Notifica todos os listeners customizados
|
|
71
|
+
notify() {
|
|
125
72
|
this.listeners.forEach(listener => listener());
|
|
126
73
|
}
|
|
127
74
|
}
|
|
128
|
-
|
|
75
|
+
exports.Router = Router;
|
|
129
76
|
exports.router = new Router();
|
|
130
|
-
// Para compatibilidade, também exporta como default
|
|
131
|
-
exports.default = exports.router;
|
package/dist/client/rpc.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export type RpcClient<TApi extends Record<string, any> = Record<string, any>> =
|
|
|
17
17
|
* Typing:
|
|
18
18
|
* - Without a generic: `const api = importServer('...')` -> `api.anyFn<MyReturn>()`
|
|
19
19
|
* - With a generic: `importServer<typeof import('../../backend/helper')>('...')`
|
|
20
|
-
*
|
|
20
|
+
* gives argument + default return types, while still allowing overrides.
|
|
21
21
|
*
|
|
22
22
|
* Note: server functions can be defined as `(req, ...args)`; the first arg is injected
|
|
23
23
|
* by the server, so the client signature automatically drops that first parameter.
|
package/dist/client/rpc.js
CHANGED
|
@@ -28,6 +28,21 @@ function asErrorMessage(err) {
|
|
|
28
28
|
return 'Unknown error';
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
+
// Detecta se estamos rodando no Node.js
|
|
32
|
+
const isServer = typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
|
|
33
|
+
function getRpcEndpoint() {
|
|
34
|
+
if (isServer) {
|
|
35
|
+
// Tenta pegar a porta das variáveis de ambiente ou usa 3000 como fallback.
|
|
36
|
+
// Nota: Se você usar uma porta customizada no vatts.config.ts sem setar ENV,
|
|
37
|
+
// precisará garantir que process.env.PORT esteja alinhado.
|
|
38
|
+
const port = process.env.PORT || 3000;
|
|
39
|
+
// Em SSR, sempre usamos HTTP e Loopback IP (127.0.0.1) para garantir
|
|
40
|
+
// que a requisição chegue no próprio servidor localmente sem sair pra rede externa.
|
|
41
|
+
return `http://127.0.0.1:${port}${types_1.RPC_ENDPOINT}`;
|
|
42
|
+
}
|
|
43
|
+
// No cliente (browser), URL relativa funciona perfeitamente
|
|
44
|
+
return types_1.RPC_ENDPOINT;
|
|
45
|
+
}
|
|
31
46
|
/**
|
|
32
47
|
* `importServer("src/backend/index.ts")` returns a Proxy where every property is
|
|
33
48
|
* a function that performs a POST to `/api/rpc`.
|
|
@@ -35,7 +50,7 @@ function asErrorMessage(err) {
|
|
|
35
50
|
* Typing:
|
|
36
51
|
* - Without a generic: `const api = importServer('...')` -> `api.anyFn<MyReturn>()`
|
|
37
52
|
* - With a generic: `importServer<typeof import('../../backend/helper')>('...')`
|
|
38
|
-
*
|
|
53
|
+
* gives argument + default return types, while still allowing overrides.
|
|
39
54
|
*
|
|
40
55
|
* Note: server functions can be defined as `(req, ...args)`; the first arg is injected
|
|
41
56
|
* by the server, so the client signature automatically drops that first parameter.
|
|
@@ -71,9 +86,11 @@ function importServer(file) {
|
|
|
71
86
|
}
|
|
72
87
|
}
|
|
73
88
|
};
|
|
89
|
+
// Resolve a URL correta (Absoluta no server, Relativa no client)
|
|
90
|
+
const endpoint = getRpcEndpoint();
|
|
74
91
|
let res;
|
|
75
92
|
try {
|
|
76
|
-
res = await fetch(
|
|
93
|
+
res = await fetch(endpoint, {
|
|
77
94
|
method: 'POST',
|
|
78
95
|
headers: {
|
|
79
96
|
'Content-Type': 'application/json'
|