hightjs 0.3.2 → 0.3.4
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/adapters/factory.js +8 -8
- package/dist/adapters/native.js +3 -3
- package/dist/auth/client.js +5 -5
- package/dist/auth/components.js +2 -2
- package/dist/auth/core.js +2 -2
- package/dist/auth/react.js +4 -4
- package/dist/auth/routes.js +1 -1
- package/dist/bin/hightjs.js +29 -328
- package/dist/builder.js +47 -21
- package/dist/client/DefaultNotFound.js +1 -1
- package/dist/client/entry.client.js +3 -3
- package/dist/helpers.d.ts +1 -0
- package/dist/helpers.js +90 -28
- package/dist/hotReload.d.ts +3 -1
- package/dist/hotReload.js +43 -51
- package/dist/index.d.ts +1 -1
- package/dist/index.js +16 -30
- package/dist/router.js +133 -62
- package/dist/types.d.ts +42 -0
- package/docs/config.md +201 -0
- package/example/hightjs.config.ts +81 -0
- package/example/package-lock.json +633 -3054
- package/example/package.json +1 -1
- package/package.json +1 -1
- package/src/adapters/factory.ts +8 -8
- package/src/adapters/native.ts +3 -3
- package/src/auth/client.ts +5 -5
- package/src/auth/components.tsx +2 -2
- package/src/auth/core.ts +2 -2
- package/src/auth/react.tsx +4 -4
- package/src/auth/routes.ts +1 -1
- package/src/bin/hightjs.js +30 -391
- package/src/builder.js +47 -22
- package/src/client/DefaultNotFound.tsx +1 -1
- package/src/client/entry.client.tsx +3 -3
- package/src/helpers.ts +105 -29
- package/src/hotReload.ts +45 -55
- package/src/index.ts +20 -33
- package/src/router.ts +140 -63
- package/src/types.ts +52 -0
- package/example/.hweb/entry.client.js +0 -24
- package/example/hweb-dist/main-5KKAYNUU.js +0 -1137
package/dist/index.js
CHANGED
|
@@ -236,23 +236,20 @@ function hweb(options) {
|
|
|
236
236
|
await hotReloadManager.start();
|
|
237
237
|
// Adiciona callback para recarregar TUDO quando qualquer arquivo mudar
|
|
238
238
|
hotReloadManager.onBackendApiChange(() => {
|
|
239
|
-
console_1.default.info('🔄 Recarregando backend e dependências...');
|
|
240
239
|
(0, router_1.loadBackendRoutes)(userBackendRoutesDir);
|
|
241
240
|
(0, router_1.processWebSocketRoutes)(); // Processa rotas WS após recarregar backend
|
|
242
241
|
});
|
|
243
242
|
// Adiciona callback para regenerar entry file quando frontend mudar
|
|
244
243
|
hotReloadManager.onFrontendChange(() => {
|
|
245
|
-
console_1.default.info('🔄 Regenerando frontend...');
|
|
246
244
|
regenerateEntryFile();
|
|
247
245
|
});
|
|
248
246
|
}
|
|
249
247
|
const now = Date.now();
|
|
250
|
-
const timee = console_1.default.dynamicLine(` ${console_1.Colors.BgYellow} router ${console_1.Colors.Reset}
|
|
248
|
+
const timee = console_1.default.dynamicLine(` ${console_1.Colors.BgYellow} router ${console_1.Colors.Reset} Loading routes and components`);
|
|
251
249
|
const spinnerFrames1 = ['|', '/', '-', '\\'];
|
|
252
250
|
let frameIndex1 = 0;
|
|
253
251
|
const spinner1 = setInterval(() => {
|
|
254
|
-
console_1.
|
|
255
|
-
timee.update(` ${console_1.Colors.FgYellow}${spinnerFrames1[frameIndex1]}${console_1.Colors.Reset} Carregando rotas e componentes...`);
|
|
252
|
+
timee.update(` ${console_1.Colors.FgYellow}${spinnerFrames1[frameIndex1]}${console_1.Colors.Reset} Loading routes and components...`);
|
|
256
253
|
frameIndex1 = (frameIndex1 + 1) % spinnerFrames1.length;
|
|
257
254
|
}, 100); // muda a cada 100ms
|
|
258
255
|
// ORDEM IMPORTANTE: Carrega TUDO antes de criar o arquivo de entrada
|
|
@@ -267,14 +264,14 @@ function hweb(options) {
|
|
|
267
264
|
fs_1.default.mkdirSync(outDir, { recursive: true });
|
|
268
265
|
entryPoint = createEntryFile(dir, frontendRoutes);
|
|
269
266
|
clearInterval(spinner1);
|
|
270
|
-
timee.end(` ${console_1.Colors.BgGreen} router ${console_1.Colors.Reset}
|
|
267
|
+
timee.end(` ${console_1.Colors.BgGreen} router ${console_1.Colors.Reset} Routes and components loaded in ${Date.now() - now}ms`);
|
|
271
268
|
if (isProduction) {
|
|
272
|
-
const time = console_1.default.dynamicLine(` ${console_1.Colors.BgYellow} build ${console_1.Colors.Reset}
|
|
269
|
+
const time = console_1.default.dynamicLine(` ${console_1.Colors.BgYellow} build ${console_1.Colors.Reset} Starting client build`);
|
|
273
270
|
// Spinner
|
|
274
271
|
const spinnerFrames = ['|', '/', '-', '\\'];
|
|
275
272
|
let frameIndex = 0;
|
|
276
273
|
const spinner = setInterval(() => {
|
|
277
|
-
time.update(` ${console_1.Colors.FgYellow}${spinnerFrames[frameIndex]}${console_1.Colors.Reset}
|
|
274
|
+
time.update(` ${console_1.Colors.FgYellow}${spinnerFrames[frameIndex]}${console_1.Colors.Reset} Building...`);
|
|
278
275
|
frameIndex = (frameIndex + 1) % spinnerFrames.length;
|
|
279
276
|
}, 100); // muda a cada 100ms
|
|
280
277
|
const now = Date.now();
|
|
@@ -282,14 +279,14 @@ function hweb(options) {
|
|
|
282
279
|
const elapsed = Date.now() - now;
|
|
283
280
|
clearInterval(spinner); // para o spinner
|
|
284
281
|
time.update(""); // limpa a linha
|
|
285
|
-
time.end(` ${console_1.Colors.BgGreen} build ${console_1.Colors.Reset}
|
|
282
|
+
time.end(` ${console_1.Colors.BgGreen} build ${console_1.Colors.Reset} Client build completed in ${elapsed}ms`);
|
|
286
283
|
}
|
|
287
284
|
else {
|
|
288
|
-
const time = console_1.default.dynamicLine(` ${console_1.Colors.BgYellow} watcher ${console_1.Colors.Reset}
|
|
285
|
+
const time = console_1.default.dynamicLine(` ${console_1.Colors.BgYellow} watcher ${console_1.Colors.Reset} Starting client watch`);
|
|
289
286
|
(0, builder_1.watchWithChunks)(entryPoint, outDir, hotReloadManager).catch(err => {
|
|
290
|
-
console_1.default.error(`
|
|
287
|
+
console_1.default.error(`Error starting watch`, err);
|
|
291
288
|
});
|
|
292
|
-
time.end(` ${console_1.Colors.BgGreen} watcher ${console_1.Colors.Reset} Watch
|
|
289
|
+
time.end(` ${console_1.Colors.BgGreen} watcher ${console_1.Colors.Reset} Client Watch started`);
|
|
293
290
|
}
|
|
294
291
|
},
|
|
295
292
|
executeInstrumentation: () => {
|
|
@@ -303,7 +300,6 @@ function hweb(options) {
|
|
|
303
300
|
if (instrumentation.hotReloadListener && typeof instrumentation.hotReloadListener === 'function') {
|
|
304
301
|
if (hotReloadManager) {
|
|
305
302
|
hotReloadManager.setHotReloadListener(instrumentation.hotReloadListener);
|
|
306
|
-
console_1.default.info('✅ Hot reload listener registrado');
|
|
307
303
|
}
|
|
308
304
|
}
|
|
309
305
|
if (typeof instrumentation === 'function') {
|
|
@@ -313,7 +309,7 @@ function hweb(options) {
|
|
|
313
309
|
instrumentation.default();
|
|
314
310
|
}
|
|
315
311
|
else {
|
|
316
|
-
console_1.default.warn(`
|
|
312
|
+
console_1.default.warn(`The instrumentation file ${instrumentationFile} does not export a default function.`);
|
|
317
313
|
}
|
|
318
314
|
}
|
|
319
315
|
},
|
|
@@ -420,8 +416,8 @@ function hweb(options) {
|
|
|
420
416
|
}
|
|
421
417
|
}
|
|
422
418
|
catch (error) {
|
|
423
|
-
console_1.default.error(`
|
|
424
|
-
genericRes.status(500).text('
|
|
419
|
+
console_1.default.error(`API route error ${pathname}:`, error);
|
|
420
|
+
genericRes.status(500).text('Internal server error in API');
|
|
425
421
|
return;
|
|
426
422
|
}
|
|
427
423
|
}
|
|
@@ -446,8 +442,8 @@ function hweb(options) {
|
|
|
446
442
|
return;
|
|
447
443
|
}
|
|
448
444
|
catch (error) {
|
|
449
|
-
console_1.default.error(`
|
|
450
|
-
genericRes.status(404).text('
|
|
445
|
+
console_1.default.error(`Error rendering page 404:`, error);
|
|
446
|
+
genericRes.status(404).text('Page not found');
|
|
451
447
|
return;
|
|
452
448
|
}
|
|
453
449
|
}
|
|
@@ -461,8 +457,8 @@ function hweb(options) {
|
|
|
461
457
|
genericRes.status(200).header('Content-Type', 'text/html').send(html);
|
|
462
458
|
}
|
|
463
459
|
catch (error) {
|
|
464
|
-
console_1.default.error(`
|
|
465
|
-
genericRes.status(500).text('
|
|
460
|
+
console_1.default.error(`Error rendering page ${pathname}:`, error);
|
|
461
|
+
genericRes.status(500).text('Internal server error');
|
|
466
462
|
}
|
|
467
463
|
};
|
|
468
464
|
},
|
|
@@ -474,16 +470,6 @@ function hweb(options) {
|
|
|
474
470
|
// Usa o sistema coordenado de WebSocket upgrade que integra hot-reload e rotas de usuário
|
|
475
471
|
(0, router_1.setupWebSocketUpgrade)(actualServer, hotReloadManager);
|
|
476
472
|
},
|
|
477
|
-
build: async () => {
|
|
478
|
-
const msg = console_1.default.dynamicLine(` ${console_1.Colors.FgYellow}● ${console_1.Colors.Reset}Iniciando build do cliente para produção`);
|
|
479
|
-
const outDir = path_1.default.join(dir, 'hweb-dist');
|
|
480
|
-
fs_1.default.mkdirSync(outDir, { recursive: true });
|
|
481
|
-
const routes = (0, router_1.loadRoutes)(userWebRoutesDir);
|
|
482
|
-
const entryPoint = createEntryFile(dir, routes);
|
|
483
|
-
const outfile = path_1.default.join(outDir, 'main.js');
|
|
484
|
-
await (0, builder_1.build)(entryPoint, outfile, true); // Força produção no build manual
|
|
485
|
-
msg.end(` ${console_1.Colors.FgGreen}● ${console_1.Colors.Reset}Build do cliente concluído: ${outfile}`);
|
|
486
|
-
},
|
|
487
473
|
stop: () => {
|
|
488
474
|
if (hotReloadManager) {
|
|
489
475
|
hotReloadManager.stop();
|
package/dist/router.js
CHANGED
|
@@ -59,18 +59,15 @@ function clearRequireCache(filePath) {
|
|
|
59
59
|
try {
|
|
60
60
|
const resolvedPath = require.resolve(filePath);
|
|
61
61
|
delete require.cache[resolvedPath];
|
|
62
|
-
// Também limpa arquivos temporários relacionados
|
|
62
|
+
// Também limpa arquivos temporários relacionados (apenas se existir no cache)
|
|
63
63
|
const tempFile = filePath.replace(/\.(tsx|ts)$/, '.temp.$1');
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
delete require.cache[
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
// Arquivo temporário pode não existir
|
|
64
|
+
const tempResolvedPath = require.cache[require.resolve(tempFile)];
|
|
65
|
+
if (tempResolvedPath) {
|
|
66
|
+
delete require.cache[require.resolve(tempFile)];
|
|
70
67
|
}
|
|
71
68
|
}
|
|
72
|
-
catch
|
|
73
|
-
// Arquivo pode não estar no cache
|
|
69
|
+
catch {
|
|
70
|
+
// Arquivo pode não estar no cache ou não ser resolvível
|
|
74
71
|
}
|
|
75
72
|
}
|
|
76
73
|
/**
|
|
@@ -117,11 +114,9 @@ function loadLayout(webDir) {
|
|
|
117
114
|
const layoutFile = fs_1.default.existsSync(layoutPath) ? layoutPath :
|
|
118
115
|
fs_1.default.existsSync(layoutPathJs) ? layoutPathJs : null;
|
|
119
116
|
if (layoutFile) {
|
|
120
|
-
const componentPath = path_1.default.relative(process.cwd(), layoutFile).replace(/\\/g, '/');
|
|
121
117
|
const absolutePath = path_1.default.resolve(layoutFile);
|
|
118
|
+
const componentPath = path_1.default.relative(process.cwd(), layoutFile).replace(/\\/g, '/');
|
|
122
119
|
try {
|
|
123
|
-
// Limpa o cache antes de recarregar
|
|
124
|
-
clearRequireCache(absolutePath);
|
|
125
120
|
// HACK: Cria uma versão temporária do layout SEM imports de CSS para carregar no servidor
|
|
126
121
|
const layoutContent = fs_1.default.readFileSync(layoutFile, 'utf8');
|
|
127
122
|
const tempContent = layoutContent
|
|
@@ -130,8 +125,14 @@ function loadLayout(webDir) {
|
|
|
130
125
|
.replace(/import\s+['"][^'"]*\.sass['"];?/g, '// SASS import removido para servidor');
|
|
131
126
|
const tempFile = layoutFile.replace(/\.(tsx|ts)$/, '.temp.$1');
|
|
132
127
|
fs_1.default.writeFileSync(tempFile, tempContent);
|
|
133
|
-
//
|
|
134
|
-
|
|
128
|
+
// Otimização: limpa cache apenas se existir
|
|
129
|
+
try {
|
|
130
|
+
const resolvedPath = require.resolve(tempFile);
|
|
131
|
+
if (require.cache[resolvedPath]) {
|
|
132
|
+
delete require.cache[resolvedPath];
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch { }
|
|
135
136
|
const layoutModule = require(tempFile);
|
|
136
137
|
// Remove o arquivo temporário
|
|
137
138
|
fs_1.default.unlinkSync(tempFile);
|
|
@@ -142,7 +143,7 @@ function loadLayout(webDir) {
|
|
|
142
143
|
return layoutComponent;
|
|
143
144
|
}
|
|
144
145
|
catch (error) {
|
|
145
|
-
console_1.default.error(`
|
|
146
|
+
console_1.default.error(`Error loading layout ${layoutFile}:`, error);
|
|
146
147
|
layoutComponent = { componentPath };
|
|
147
148
|
return layoutComponent;
|
|
148
149
|
}
|
|
@@ -163,35 +164,53 @@ function getLayout() {
|
|
|
163
164
|
*/
|
|
164
165
|
function loadRoutes(routesDir) {
|
|
165
166
|
if (!fs_1.default.existsSync(routesDir)) {
|
|
166
|
-
console_1.default.warn(`
|
|
167
|
+
console_1.default.warn(`Frontend routes directory not found at ${routesDir}. No page will be loaded.`);
|
|
167
168
|
allRoutes = [];
|
|
168
169
|
return allRoutes;
|
|
169
170
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const
|
|
173
|
-
const
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
171
|
+
// Otimização: usa função recursiva manual para evitar overhead do recursive: true
|
|
172
|
+
const routeFiles = [];
|
|
173
|
+
const scanDirectory = (dir, baseDir = '') => {
|
|
174
|
+
const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
175
|
+
for (const entry of entries) {
|
|
176
|
+
const relativePath = baseDir ? path_1.default.join(baseDir, entry.name) : entry.name;
|
|
177
|
+
if (entry.isDirectory()) {
|
|
178
|
+
// Pula diretório backend inteiro
|
|
179
|
+
if (entry.name === 'backend')
|
|
180
|
+
continue;
|
|
181
|
+
scanDirectory(path_1.default.join(dir, entry.name), relativePath);
|
|
182
|
+
}
|
|
183
|
+
else if (entry.isFile()) {
|
|
184
|
+
// Filtra apenas arquivos .ts/.tsx
|
|
185
|
+
if (entry.name.endsWith('.ts') || entry.name.endsWith('.tsx')) {
|
|
186
|
+
routeFiles.push(relativePath);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
scanDirectory(routesDir);
|
|
177
192
|
const loaded = [];
|
|
193
|
+
const cwdPath = process.cwd();
|
|
194
|
+
// Otimização: processa arquivos em lote
|
|
178
195
|
for (const file of routeFiles) {
|
|
179
196
|
const filePath = path_1.default.join(routesDir, file);
|
|
180
197
|
const absolutePath = path_1.default.resolve(filePath);
|
|
181
|
-
// Usamos um caminho relativo ao CWD como um ID estável para o componente.
|
|
182
|
-
const componentPath = path_1.default.relative(process.cwd(), filePath).replace(/\\/g, '/');
|
|
183
198
|
try {
|
|
184
|
-
//
|
|
185
|
-
|
|
199
|
+
// Otimização: limpa cache apenas se já existir
|
|
200
|
+
const resolvedPath = require.resolve(filePath);
|
|
201
|
+
if (require.cache[resolvedPath]) {
|
|
202
|
+
delete require.cache[resolvedPath];
|
|
203
|
+
}
|
|
186
204
|
const routeModule = require(filePath);
|
|
187
|
-
if (routeModule.default
|
|
205
|
+
if (routeModule.default?.pattern && routeModule.default?.component) {
|
|
206
|
+
// Otimização: calcula componentPath apenas uma vez
|
|
207
|
+
const componentPath = path_1.default.relative(cwdPath, filePath).replace(/\\/g, '/');
|
|
188
208
|
loaded.push({ ...routeModule.default, componentPath });
|
|
189
|
-
// Registra o arquivo como carregado
|
|
190
209
|
loadedRouteFiles.add(absolutePath);
|
|
191
210
|
}
|
|
192
211
|
}
|
|
193
212
|
catch (error) {
|
|
194
|
-
console_1.default.error(`
|
|
213
|
+
console_1.default.error(`Error loading page route ${filePath}:`, error);
|
|
195
214
|
}
|
|
196
215
|
}
|
|
197
216
|
allRoutes = loaded;
|
|
@@ -268,7 +287,7 @@ function loadMiddlewareFromDirectory(dir) {
|
|
|
268
287
|
}
|
|
269
288
|
}
|
|
270
289
|
catch (error) {
|
|
271
|
-
console_1.default.error(`
|
|
290
|
+
console_1.default.error(`Error loading middleware ${middlewareFile}:`, error);
|
|
272
291
|
}
|
|
273
292
|
}
|
|
274
293
|
return middlewares;
|
|
@@ -301,32 +320,89 @@ function loadBackendRoutes(backendRoutesDir) {
|
|
|
301
320
|
}
|
|
302
321
|
// Limpa cache de middlewares para recarregar
|
|
303
322
|
loadedMiddlewares.clear();
|
|
304
|
-
|
|
305
|
-
const routeFiles =
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
323
|
+
// Otimização: usa função recursiva manual e coleta middlewares durante o scan
|
|
324
|
+
const routeFiles = [];
|
|
325
|
+
const middlewareFiles = new Map(); // dir -> filepath
|
|
326
|
+
const scanDirectory = (dir, baseDir = '') => {
|
|
327
|
+
const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
328
|
+
for (const entry of entries) {
|
|
329
|
+
const relativePath = baseDir ? path_1.default.join(baseDir, entry.name) : entry.name;
|
|
330
|
+
if (entry.isDirectory()) {
|
|
331
|
+
scanDirectory(path_1.default.join(dir, entry.name), relativePath);
|
|
332
|
+
}
|
|
333
|
+
else if (entry.isFile()) {
|
|
334
|
+
const isTypeScript = entry.name.endsWith('.ts') || entry.name.endsWith('.tsx');
|
|
335
|
+
if (!isTypeScript)
|
|
336
|
+
continue;
|
|
337
|
+
// Identifica middlewares durante o scan
|
|
338
|
+
if (entry.name.startsWith('middleware')) {
|
|
339
|
+
const dirPath = path_1.default.dirname(path_1.default.join(backendRoutesDir, relativePath));
|
|
340
|
+
middlewareFiles.set(dirPath, path_1.default.join(backendRoutesDir, relativePath));
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
routeFiles.push(relativePath);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
scanDirectory(backendRoutesDir);
|
|
349
|
+
// Otimização: pré-carrega todos os middlewares em um único passe
|
|
350
|
+
for (const [dirPath, middlewarePath] of middlewareFiles) {
|
|
351
|
+
try {
|
|
352
|
+
const resolvedPath = require.resolve(middlewarePath);
|
|
353
|
+
if (require.cache[resolvedPath]) {
|
|
354
|
+
delete require.cache[resolvedPath];
|
|
355
|
+
}
|
|
356
|
+
const middlewareModule = require(middlewarePath);
|
|
357
|
+
const middlewares = [];
|
|
358
|
+
if (typeof middlewareModule.default === 'function') {
|
|
359
|
+
middlewares.push(middlewareModule.default);
|
|
360
|
+
}
|
|
361
|
+
else if (Array.isArray(middlewareModule.default)) {
|
|
362
|
+
middlewares.push(...middlewareModule.default);
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
// Exports nomeados
|
|
366
|
+
for (const key in middlewareModule) {
|
|
367
|
+
if (key !== 'default' && typeof middlewareModule[key] === 'function') {
|
|
368
|
+
middlewares.push(middlewareModule[key]);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (middlewares.length > 0) {
|
|
373
|
+
loadedMiddlewares.set(dirPath, middlewares);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
catch (error) {
|
|
377
|
+
console_1.default.error(`Error loading middleware ${middlewarePath}:`, error);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
// Otimização: processa rotas com cache já limpo
|
|
310
381
|
const loaded = [];
|
|
311
382
|
for (const file of routeFiles) {
|
|
312
383
|
const filePath = path_1.default.join(backendRoutesDir, file);
|
|
313
384
|
try {
|
|
385
|
+
// Otimização: limpa cache apenas se existir
|
|
386
|
+
const resolvedPath = require.resolve(filePath);
|
|
387
|
+
if (require.cache[resolvedPath]) {
|
|
388
|
+
delete require.cache[resolvedPath];
|
|
389
|
+
}
|
|
314
390
|
const routeModule = require(filePath);
|
|
315
|
-
if (routeModule.default
|
|
391
|
+
if (routeModule.default?.pattern) {
|
|
316
392
|
const routeConfig = { ...routeModule.default };
|
|
317
|
-
// Se a rota NÃO tem
|
|
393
|
+
// Se a rota NÃO tem middleware definido, usa os da pasta
|
|
318
394
|
if (!routeConfig.hasOwnProperty('middleware')) {
|
|
319
|
-
const
|
|
320
|
-
|
|
395
|
+
const routeDir = path_1.default.dirname(path_1.default.resolve(filePath));
|
|
396
|
+
const folderMiddlewares = loadedMiddlewares.get(routeDir);
|
|
397
|
+
if (folderMiddlewares && folderMiddlewares.length > 0) {
|
|
321
398
|
routeConfig.middleware = folderMiddlewares;
|
|
322
399
|
}
|
|
323
400
|
}
|
|
324
|
-
// Se tem middleware definido (mesmo que seja []), usa só esses (não adiciona os da pasta)
|
|
325
401
|
loaded.push(routeConfig);
|
|
326
402
|
}
|
|
327
403
|
}
|
|
328
404
|
catch (error) {
|
|
329
|
-
console_1.default.error(`
|
|
405
|
+
console_1.default.error(`Error loading API route ${filePath}:`, error);
|
|
330
406
|
}
|
|
331
407
|
}
|
|
332
408
|
allBackendRoutes = loaded;
|
|
@@ -373,18 +449,24 @@ function loadNotFound(webDir) {
|
|
|
373
449
|
const notFoundFile = fs_1.default.existsSync(notFoundPath) ? notFoundPath :
|
|
374
450
|
fs_1.default.existsSync(notFoundPathJs) ? notFoundPathJs : null;
|
|
375
451
|
if (notFoundFile) {
|
|
376
|
-
const componentPath = path_1.default.relative(process.cwd(), notFoundFile).replace(/\\/g, '/');
|
|
377
452
|
const absolutePath = path_1.default.resolve(notFoundFile);
|
|
453
|
+
const componentPath = path_1.default.relative(process.cwd(), notFoundFile).replace(/\\/g, '/');
|
|
378
454
|
try {
|
|
379
|
-
//
|
|
380
|
-
|
|
455
|
+
// Otimização: limpa cache apenas se existir
|
|
456
|
+
try {
|
|
457
|
+
const resolvedPath = require.resolve(notFoundFile);
|
|
458
|
+
if (require.cache[resolvedPath]) {
|
|
459
|
+
delete require.cache[resolvedPath];
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
catch { }
|
|
381
463
|
// Registra o arquivo como carregado
|
|
382
464
|
loadedNotFoundFiles.add(absolutePath);
|
|
383
465
|
notFoundComponent = { componentPath };
|
|
384
466
|
return notFoundComponent;
|
|
385
467
|
}
|
|
386
468
|
catch (error) {
|
|
387
|
-
console_1.default.error(`
|
|
469
|
+
console_1.default.error(`Error loading notFound ${notFoundFile}:`, error);
|
|
388
470
|
notFoundComponent = { componentPath };
|
|
389
471
|
return notFoundComponent;
|
|
390
472
|
}
|
|
@@ -416,7 +498,6 @@ function processWebSocketRoutes() {
|
|
|
416
498
|
middleware: route.middleware
|
|
417
499
|
};
|
|
418
500
|
allWebSocketRoutes.push(wsRoute);
|
|
419
|
-
console_1.default.info(`WebSocket route registered: ${route.pattern}`);
|
|
420
501
|
}
|
|
421
502
|
}
|
|
422
503
|
}
|
|
@@ -453,7 +534,7 @@ function handleWebSocketConnection(ws, req, hwebReq) {
|
|
|
453
534
|
const pathname = url.pathname;
|
|
454
535
|
const matchedRoute = findMatchingWebSocketRoute(pathname);
|
|
455
536
|
if (!matchedRoute) {
|
|
456
|
-
ws.close(1000, '
|
|
537
|
+
ws.close(1000, 'Route not found');
|
|
457
538
|
return;
|
|
458
539
|
}
|
|
459
540
|
const params = extractWebSocketParams(pathname, matchedRoute.route.pattern);
|
|
@@ -488,8 +569,8 @@ function handleWebSocketConnection(ws, req, hwebReq) {
|
|
|
488
569
|
matchedRoute.route.handler(context);
|
|
489
570
|
}
|
|
490
571
|
catch (error) {
|
|
491
|
-
console.error('
|
|
492
|
-
ws.close(1011, '
|
|
572
|
+
console.error('Error in WebSocket handler:', error);
|
|
573
|
+
ws.close(1011, 'Internal server error');
|
|
493
574
|
}
|
|
494
575
|
}
|
|
495
576
|
/**
|
|
@@ -525,16 +606,11 @@ function setupWebSocketUpgrade(server, hotReloadManager) {
|
|
|
525
606
|
handleWebSocketUpgrade(request, socket, head, hotReloadManager);
|
|
526
607
|
});
|
|
527
608
|
}
|
|
528
|
-
else {
|
|
529
|
-
// Se já existe um listener (provavelmente do hot-reload),
|
|
530
|
-
// vamos interceptar e coordenar
|
|
531
|
-
console.log('🔧 Coordenando WebSocket upgrade com sistema existente');
|
|
532
|
-
}
|
|
533
609
|
}
|
|
534
610
|
function handleWebSocketUpgrade(request, socket, head, hotReloadManager) {
|
|
535
611
|
const adapter = factory_1.FrameworkAdapterFactory.getCurrentAdapter();
|
|
536
612
|
if (!adapter) {
|
|
537
|
-
console.error('❌ Framework adapter
|
|
613
|
+
console.error('❌ Framework adapter not detected. Unable to process WebSocket upgrade.');
|
|
538
614
|
socket.destroy();
|
|
539
615
|
return;
|
|
540
616
|
}
|
|
@@ -562,13 +638,10 @@ function handleWebSocketUpgrade(request, socket, head, hotReloadManager) {
|
|
|
562
638
|
});
|
|
563
639
|
wss.handleUpgrade(request, socket, head, (ws) => {
|
|
564
640
|
wsConnections.add(ws);
|
|
565
|
-
console.log(`✅ WebSocket conectado em ${pathname}`);
|
|
566
641
|
ws.on('close', () => {
|
|
567
642
|
wsConnections.delete(ws);
|
|
568
|
-
console.log(`❌ WebSocket desconectado de ${pathname}`);
|
|
569
643
|
});
|
|
570
644
|
ws.on('error', (error) => {
|
|
571
|
-
console.error(`💥 Erro WebSocket em ${pathname}:`, error);
|
|
572
645
|
wsConnections.delete(ws);
|
|
573
646
|
});
|
|
574
647
|
// Processa a conexão
|
|
@@ -576,7 +649,5 @@ function handleWebSocketUpgrade(request, socket, head, hotReloadManager) {
|
|
|
576
649
|
});
|
|
577
650
|
return;
|
|
578
651
|
}
|
|
579
|
-
// Nenhuma rota encontrada - rejeita conexão
|
|
580
|
-
console.log(`🚫 Nenhuma rota WebSocket encontrada para: ${pathname}`);
|
|
581
652
|
socket.destroy();
|
|
582
653
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -27,6 +27,48 @@ export interface HightJSOptions {
|
|
|
27
27
|
ca?: string;
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Interface para as configurações avançadas do servidor HightJS.
|
|
32
|
+
* Essas configurações podem ser definidas no arquivo hightjs.config.js
|
|
33
|
+
*/
|
|
34
|
+
export interface HightConfig {
|
|
35
|
+
/**
|
|
36
|
+
* Limita o número máximo de headers HTTP permitidos por requisição.
|
|
37
|
+
* Padrão: 100
|
|
38
|
+
*/
|
|
39
|
+
maxHeadersCount?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Timeout em milissegundos para receber os headers HTTP.
|
|
42
|
+
* Padrão: 60000 (60 segundos)
|
|
43
|
+
*/
|
|
44
|
+
headersTimeout?: number;
|
|
45
|
+
/**
|
|
46
|
+
* Timeout em milissegundos para uma requisição completa.
|
|
47
|
+
* Padrão: 30000 (30 segundos)
|
|
48
|
+
*/
|
|
49
|
+
requestTimeout?: number;
|
|
50
|
+
/**
|
|
51
|
+
* Timeout geral do servidor em milissegundos.
|
|
52
|
+
* Padrão: 35000 (35 segundos)
|
|
53
|
+
*/
|
|
54
|
+
serverTimeout?: number;
|
|
55
|
+
/**
|
|
56
|
+
* Timeout por requisição individual em milissegundos.
|
|
57
|
+
* Padrão: 30000 (30 segundos)
|
|
58
|
+
*/
|
|
59
|
+
individualRequestTimeout?: number;
|
|
60
|
+
/**
|
|
61
|
+
* Tamanho máximo permitido para a URL em caracteres.
|
|
62
|
+
* Padrão: 2048
|
|
63
|
+
*/
|
|
64
|
+
maxUrlLength?: number;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Tipo da função de configuração que pode ser exportada no hightjs.config.js
|
|
68
|
+
*/
|
|
69
|
+
export type HightConfigFunction = (phase: string, context: {
|
|
70
|
+
defaultConfig: HightConfig;
|
|
71
|
+
}) => HightConfig | Promise<HightConfig>;
|
|
30
72
|
export interface Metadata {
|
|
31
73
|
title?: string;
|
|
32
74
|
description?: string;
|