nyte 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/LICENSE +13 -0
  2. package/README.md +59 -0
  3. package/dist/adapters/express.d.ts +7 -0
  4. package/dist/adapters/express.js +63 -0
  5. package/dist/adapters/factory.d.ts +23 -0
  6. package/dist/adapters/factory.js +121 -0
  7. package/dist/adapters/fastify.d.ts +25 -0
  8. package/dist/adapters/fastify.js +61 -0
  9. package/dist/adapters/native.d.ts +8 -0
  10. package/dist/adapters/native.js +200 -0
  11. package/dist/api/console.d.ts +81 -0
  12. package/dist/api/console.js +318 -0
  13. package/dist/api/http.d.ts +180 -0
  14. package/dist/api/http.js +469 -0
  15. package/dist/bin/nytejs.d.ts +2 -0
  16. package/dist/bin/nytejs.js +277 -0
  17. package/dist/builder.d.ts +32 -0
  18. package/dist/builder.js +634 -0
  19. package/dist/client/DefaultNotFound.d.ts +1 -0
  20. package/dist/client/DefaultNotFound.js +79 -0
  21. package/dist/client/client.d.ts +4 -0
  22. package/dist/client/client.js +27 -0
  23. package/dist/client/clientRouter.d.ts +58 -0
  24. package/dist/client/clientRouter.js +132 -0
  25. package/dist/client/entry.client.d.ts +1 -0
  26. package/dist/client/entry.client.js +455 -0
  27. package/dist/client/rpc.d.ts +8 -0
  28. package/dist/client/rpc.js +97 -0
  29. package/dist/components/Link.d.ts +7 -0
  30. package/dist/components/Link.js +13 -0
  31. package/dist/global/global.d.ts +117 -0
  32. package/dist/global/global.js +17 -0
  33. package/dist/helpers.d.ts +20 -0
  34. package/dist/helpers.js +604 -0
  35. package/dist/hotReload.d.ts +32 -0
  36. package/dist/hotReload.js +545 -0
  37. package/dist/index.d.ts +18 -0
  38. package/dist/index.js +515 -0
  39. package/dist/loaders.d.ts +1 -0
  40. package/dist/loaders.js +138 -0
  41. package/dist/renderer.d.ts +14 -0
  42. package/dist/renderer.js +380 -0
  43. package/dist/router.d.ts +101 -0
  44. package/dist/router.js +659 -0
  45. package/dist/rpc/server.d.ts +11 -0
  46. package/dist/rpc/server.js +166 -0
  47. package/dist/rpc/types.d.ts +22 -0
  48. package/dist/rpc/types.js +20 -0
  49. package/dist/types/framework.d.ts +37 -0
  50. package/dist/types/framework.js +2 -0
  51. package/dist/types.d.ts +218 -0
  52. package/dist/types.js +2 -0
  53. package/package.json +87 -0
  54. package/src/adapters/express.ts +87 -0
  55. package/src/adapters/factory.ts +112 -0
  56. package/src/adapters/fastify.ts +104 -0
  57. package/src/adapters/native.ts +245 -0
  58. package/src/api/console.ts +348 -0
  59. package/src/api/http.ts +535 -0
  60. package/src/bin/nytejs.js +331 -0
  61. package/src/builder.js +690 -0
  62. package/src/client/DefaultNotFound.tsx +119 -0
  63. package/src/client/client.ts +24 -0
  64. package/src/client/clientRouter.ts +153 -0
  65. package/src/client/entry.client.tsx +529 -0
  66. package/src/client/rpc.ts +101 -0
  67. package/src/components/Link.tsx +38 -0
  68. package/src/global/global.ts +171 -0
  69. package/src/helpers.ts +657 -0
  70. package/src/hotReload.ts +566 -0
  71. package/src/index.ts +582 -0
  72. package/src/loaders.js +160 -0
  73. package/src/renderer.tsx +421 -0
  74. package/src/router.ts +732 -0
  75. package/src/rpc/server.ts +190 -0
  76. package/src/rpc/types.ts +45 -0
  77. package/src/types/framework.ts +58 -0
  78. package/src/types.ts +288 -0
package/src/index.ts ADDED
@@ -0,0 +1,582 @@
1
+ /*
2
+ * This file is part of the Nyte.js Project.
3
+ * Copyright (c) 2026 itsmuzin
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ import path from 'path';
19
+ import fs from 'fs';
20
+ import {ExpressAdapter} from './adapters/express';
21
+ import {build, buildWithChunks, watch, watchWithChunks} from './builder';
22
+ import {
23
+ BackendHandler,
24
+ BackendRouteConfig,
25
+ NyteOptions,
26
+ NyteMiddleware,
27
+ RequestHandler,
28
+ RouteConfig
29
+ } from './types';
30
+ import {
31
+ findMatchingBackendRoute,
32
+ findMatchingRoute,
33
+ getLayout,
34
+ getNotFound,
35
+ loadBackendRoutes,
36
+ loadLayout,
37
+ loadNotFound,
38
+ loadRoutes,
39
+ processWebSocketRoutes,
40
+ setupWebSocketUpgrade
41
+ } from './router';
42
+ import {render} from './renderer';
43
+ import {NyteRequest, NyteResponse} from './api/http';
44
+ import {HotReloadManager} from './hotReload';
45
+ import {FrameworkAdapterFactory} from './adapters/factory';
46
+ import {GenericRequest, GenericResponse} from './types/framework';
47
+ import Console, {Colors} from "./api/console"
48
+
49
+ // RPC
50
+ import { executeRpc } from './rpc/server';
51
+ import { RPC_ENDPOINT } from './rpc/types';
52
+
53
+ // Exporta apenas os tipos e classes para o backend
54
+ export { NyteRequest, NyteResponse };
55
+ export type { BackendRouteConfig, BackendHandler };
56
+
57
+ // Exporta os adapters para uso manual se necessário
58
+ export { ExpressAdapter } from './adapters/express';
59
+ export { FastifyAdapter } from './adapters/fastify';
60
+ export { FrameworkAdapterFactory } from './adapters/factory';
61
+ export type { GenericRequest, GenericResponse, CookieOptions } from './types/framework';
62
+
63
+ // Exporta os helpers para facilitar integração
64
+ export { app } from './helpers';
65
+
66
+ // Exporta o sistema de WebSocket
67
+ export type { WebSocketContext, WebSocketHandler } from './types';
68
+
69
+ // Exporta os tipos de configuração
70
+ export type { NyteConfig, NyteConfigFunction } from './types';
71
+
72
+ // Função para verificar se o projeto é grande o suficiente para se beneficiar de chunks
73
+ function isLargeProject(projectDir: string): boolean {
74
+ try {
75
+ const srcDir = path.join(projectDir, 'src');
76
+ if (!fs.existsSync(srcDir)) return false;
77
+
78
+ let totalFiles = 0;
79
+ let totalSize = 0;
80
+
81
+ function scanDirectory(dir: string) {
82
+ const items = fs.readdirSync(dir, { withFileTypes: true });
83
+
84
+ for (const item of items) {
85
+ const fullPath = path.join(dir, item.name);
86
+
87
+ if (item.isDirectory() && item.name !== 'node_modules' && item.name !== '.git') {
88
+ scanDirectory(fullPath);
89
+ } else if (item.isFile() && /\.(tsx?|jsx?|css|scss|less)$/i.test(item.name)) {
90
+ totalFiles++;
91
+ totalSize += fs.statSync(fullPath).size;
92
+ }
93
+ }
94
+ }
95
+
96
+ scanDirectory(srcDir);
97
+
98
+ // Considera projeto grande se:
99
+ // - Mais de 20 arquivos de frontend/style
100
+ // - Ou tamanho total > 500KB
101
+ return totalFiles > 20 || totalSize > 500 * 1024;
102
+ } catch (error) {
103
+ // Em caso de erro, assume que não é um projeto grande
104
+ return false;
105
+ }
106
+ }
107
+
108
+ // Função para gerar o arquivo de entrada para o esbuild
109
+ function createEntryFile(projectDir: string, routes: (RouteConfig & { componentPath: string })[]): string {
110
+ try {
111
+ const tempDir = path.join(projectDir, '.nyte', 'temp');
112
+
113
+ fs.mkdirSync(tempDir, { recursive: true });
114
+
115
+ const entryFilePath = path.join(tempDir, 'entry.client.js');
116
+ // Verifica se há layout
117
+ const layout = getLayout();
118
+
119
+ // Verifica se há notFound personalizado
120
+ const notFound = getNotFound();
121
+
122
+ // Gera imports dinâmicos para cada componente
123
+ const imports = routes
124
+ .map((route, index) => {
125
+ const relativePath = path.relative(tempDir, route.componentPath).replace(/\\/g, '/');
126
+ return `import route${index} from '${relativePath}';`;
127
+ })
128
+ .join('\n');
129
+
130
+ // Import do layout se existir
131
+ const layoutImport = layout
132
+ ? `import LayoutComponent from '${path.relative(tempDir, layout.componentPath).replace(/\\/g, '/')}';`
133
+ : '';
134
+
135
+ // Import do notFound se existir
136
+ const notFoundImport = notFound
137
+ ? `import NotFoundComponent from '${path.relative(tempDir, notFound.componentPath).replace(/\\/g, '/')}';`
138
+ : '';
139
+
140
+ // Registra os componentes no window para o cliente acessar
141
+ const componentRegistration = routes
142
+ .map((route, index) => ` '${route.componentPath}': route${index}.component || route${index}.default?.component,`)
143
+ .join('\n');
144
+
145
+ // Registra o layout se existir
146
+ const layoutRegistration = layout
147
+ ? `window.__HWEB_LAYOUT__ = LayoutComponent.default || LayoutComponent;`
148
+ : `window.__HWEB_LAYOUT__ = null;`;
149
+
150
+ // Registra o notFound se existir
151
+ const notFoundRegistration = notFound
152
+ ? `window.__HWEB_NOT_FOUND__ = NotFoundComponent.default || NotFoundComponent;`
153
+ : `window.__HWEB_NOT_FOUND__ = null;`;
154
+
155
+ // Caminho correto para o entry.client.tsx
156
+ const sdkDir = path.dirname(__dirname); // Vai para a pasta pai de src (onde está o hweb-sdk)
157
+ const entryClientPath = path.join(sdkDir, 'src', 'client', 'entry.client.tsx');
158
+ const relativeEntryPath = path.relative(tempDir, entryClientPath).replace(/\\/g, '/');
159
+
160
+ // Import do DefaultNotFound do SDK
161
+ const defaultNotFoundPath = path.join(sdkDir, 'src', 'client', 'DefaultNotFound.tsx');
162
+ const relativeDefaultNotFoundPath = path.relative(tempDir, defaultNotFoundPath).replace(/\\/g, '/');
163
+
164
+ const entryContent = `// Arquivo gerado automaticamente pelo hweb
165
+ ${imports}
166
+ ${layoutImport}
167
+ ${notFoundImport}
168
+ import DefaultNotFound from '${relativeDefaultNotFoundPath}';
169
+
170
+ // Registra os componentes para o cliente
171
+ window.__HWEB_COMPONENTS__ = {
172
+ ${componentRegistration}
173
+ };
174
+
175
+ // Registra o layout se existir
176
+ ${layoutRegistration}
177
+
178
+ // Registra o notFound se existir
179
+ ${notFoundRegistration}
180
+
181
+ // Registra o DefaultNotFound do hweb
182
+ window.__HWEB_DEFAULT_NOT_FOUND__ = DefaultNotFound;
183
+
184
+ // Importa e executa o entry.client.tsx
185
+ import '${relativeEntryPath}';
186
+ `;
187
+
188
+ try {
189
+ fs.writeFileSync(entryFilePath, entryContent);
190
+ } catch (e) {
191
+ console.error("sdfijnsdfnijfsdijnfsdnijsdfnijfsdnijfsdnijfsdn", e)
192
+ }
193
+
194
+ return entryFilePath;
195
+ }catch (e){
196
+ Console.error("Error creating entry file:", e);
197
+ throw e;
198
+ }
199
+ }
200
+
201
+ export default function hweb(options: NyteOptions) {
202
+ const { dev = true, dir = process.cwd(), port = 3000 } = options;
203
+ // @ts-ignore
204
+ process.nyte = options;
205
+ const userWebDir = path.join(dir, 'src', 'web');
206
+ const userWebRoutesDir = path.join(userWebDir, 'routes');
207
+ const userBackendRoutesDir = path.join(dir, 'src', 'backend', 'routes');
208
+
209
+ /**
210
+ * Executa middlewares sequencialmente e depois o handler final
211
+ * @param middlewares Array de middlewares para executar
212
+ * @param finalHandler Handler final da rota
213
+ * @param request Requisição do Nyte.js
214
+ * @param params Parâmetros da rota
215
+ * @returns Resposta do middleware ou handler final
216
+ */
217
+ async function executeMiddlewareChain(
218
+ middlewares: NyteMiddleware[] | undefined,
219
+ finalHandler: BackendHandler,
220
+ request: NyteRequest,
221
+ params: { [key: string]: string }
222
+ ): Promise<NyteResponse> {
223
+ if (!middlewares || middlewares.length === 0) {
224
+ // Não há middlewares, executa diretamente o handler final
225
+ return await finalHandler(request, params);
226
+ }
227
+
228
+ let currentIndex = 0;
229
+
230
+ // Função next que será chamada pelos middlewares
231
+ const next = async (): Promise<NyteResponse> => {
232
+ if (currentIndex < middlewares.length) {
233
+ // Ainda há middlewares para executar
234
+ const currentMiddleware = middlewares[currentIndex];
235
+ currentIndex++;
236
+ return await currentMiddleware(request, params, next);
237
+ } else {
238
+ // Todos os middlewares foram executados, chama o handler final
239
+ return await finalHandler(request, params);
240
+ }
241
+ };
242
+
243
+ // Inicia a cadeia de execução
244
+ return await next();
245
+ }
246
+
247
+ let frontendRoutes: (RouteConfig & { componentPath: string })[] = [];
248
+ let hotReloadManager: HotReloadManager | null = null;
249
+ let entryPoint: string;
250
+ let outfile: string;
251
+
252
+ // Função para regenerar o entry file
253
+ const regenerateEntryFile = () => {
254
+ // Recarrega todas as rotas e componentes
255
+ frontendRoutes = loadRoutes(userWebRoutesDir);
256
+ loadLayout(userWebDir);
257
+ loadNotFound(userWebDir);
258
+
259
+ // Regenera o entry file
260
+ entryPoint = createEntryFile(dir, frontendRoutes);
261
+ };
262
+
263
+ return {
264
+ prepare: async () => {
265
+ const isProduction = !dev;
266
+
267
+ if (!isProduction) {
268
+ // Inicia hot reload apenas em desenvolvimento (com suporte ao main)
269
+ hotReloadManager = new HotReloadManager(dir);
270
+ await hotReloadManager.start();
271
+
272
+ // Adiciona callback para recarregar TUDO quando qualquer arquivo mudar
273
+ hotReloadManager.onBackendApiChange(() => {
274
+
275
+
276
+ loadBackendRoutes(userBackendRoutesDir);
277
+ processWebSocketRoutes(); // Processa rotas WS após recarregar backend
278
+ });
279
+
280
+ // Adiciona callback para regenerar entry file quando frontend mudar
281
+ hotReloadManager.onFrontendChange(() => {
282
+ regenerateEntryFile();
283
+ });
284
+ }
285
+ const now = Date.now();
286
+ const timee = Console.dynamicLine(`Loading routes and components`);
287
+ const spinnerFrames1 = ['|', '/', '-', '\\'];
288
+ let frameIndex1 = 0;
289
+
290
+ const spinner1 = setInterval(() => {
291
+ timee.update(` ${Colors.FgYellow}${spinnerFrames1[frameIndex1]}${Colors.Reset} Loading routes and components...`);
292
+ frameIndex1 = (frameIndex1 + 1) % spinnerFrames1.length;
293
+ }, 100); // muda a cada 100ms
294
+ // ORDEM IMPORTANTE: Carrega TUDO antes de criar o arquivo de entrada
295
+ frontendRoutes = loadRoutes(userWebRoutesDir);
296
+ loadBackendRoutes(userBackendRoutesDir);
297
+
298
+ // Processa rotas WebSocket após carregar backend
299
+ processWebSocketRoutes();
300
+
301
+ // Carrega layout.tsx ANTES de criar o entry file
302
+ const layout = loadLayout(userWebDir);
303
+
304
+ const notFound = loadNotFound(userWebDir);
305
+
306
+ const outDir = path.join(dir, '.nyte');
307
+ fs.mkdirSync(outDir, {recursive: true});
308
+
309
+ entryPoint = createEntryFile(dir, frontendRoutes);
310
+ clearInterval(spinner1)
311
+ timee.end(`Routes and components loaded in ${Date.now() - now}ms`);
312
+
313
+
314
+ if (isProduction) {
315
+ const time = Console.dynamicLine(`Starting client build`);
316
+
317
+ // Spinner
318
+ const spinnerFrames = ['|', '/', '-', '\\'];
319
+ let frameIndex = 0;
320
+
321
+ const spinner = setInterval(() => {
322
+ time.update(` ${Colors.FgYellow}${spinnerFrames[frameIndex]}${Colors.Reset} Building...`);
323
+ frameIndex = (frameIndex + 1) % spinnerFrames.length;
324
+ }, 100); // muda a cada 100ms
325
+
326
+ const now = Date.now();
327
+ await buildWithChunks(entryPoint, outDir, isProduction);
328
+ const elapsed = Date.now() - now;
329
+
330
+ clearInterval(spinner); // para o spinner
331
+ time.update(""); // limpa a linha
332
+ time.end(`Client build completed in ${elapsed}ms`);
333
+
334
+ // Notifica o hot reload manager que o build foi concluído
335
+ if (hotReloadManager) {
336
+ hotReloadManager.onBuildComplete(true);
337
+ }
338
+
339
+ } else {
340
+ const time = Console.dynamicLine(` ${Colors.BgYellow} watcher ${Colors.Reset} Starting client watch`);
341
+ watchWithChunks(entryPoint, outDir, hotReloadManager!).catch(err => {
342
+ Console.error(`Error starting watch`, err);
343
+ });
344
+ time.end(`Client Watch started`);
345
+ }
346
+
347
+ },
348
+
349
+ executeInstrumentation: () => {
350
+
351
+ // verificar se dir/src/instrumentation.(tsx/jsx/js/ts) existe com regex
352
+ const instrumentationFile = fs.readdirSync(path.join(dir, 'src')).find(file => /^nyteweb\.(tsx|jsx|js|ts)$/.test(file));
353
+ if (instrumentationFile) {
354
+ const instrumentationPath = path.join(dir, 'src', instrumentationFile);
355
+ // dar require, e executar a função principal do arquivo
356
+ const instrumentation = require(instrumentationPath);
357
+
358
+ // Registra o listener de hot reload se existir
359
+ if (instrumentation.hotReloadListener && typeof instrumentation.hotReloadListener === 'function') {
360
+ if (hotReloadManager) {
361
+ hotReloadManager.setHotReloadListener(instrumentation.hotReloadListener);
362
+ }
363
+ }
364
+
365
+ if (typeof instrumentation === 'function') {
366
+ instrumentation();
367
+ } else if (typeof instrumentation.default === 'function') {
368
+ instrumentation.default();
369
+ } else {
370
+ Console.warn(`The instrumentation file ${instrumentationFile} does not export a default function.`);
371
+ }
372
+ }
373
+ },
374
+ getRequestHandler: (): RequestHandler => {
375
+ return async (req: any, res: any) => {
376
+ // Detecta o framework e cria request/response genéricos
377
+ const adapter = FrameworkAdapterFactory.detectFramework(req, res);
378
+ const genericReq: GenericRequest = adapter.parseRequest(req);
379
+ const genericRes: GenericResponse = adapter.createResponse(res);
380
+
381
+ // Adiciona informações do hweb na requisição genérica
382
+ (genericReq as any).hwebDev = dev;
383
+ (genericReq as any).hotReloadManager = hotReloadManager;
384
+
385
+ const {hostname} = req.headers;
386
+ const method = (genericReq.method || 'GET').toUpperCase();
387
+ const pathname = new URL(genericReq.url, `http://${hostname}:${port}`).pathname;
388
+
389
+ // RPC endpoint (antes das rotas de backend)
390
+ if (pathname === RPC_ENDPOINT && method === 'POST') {
391
+ try {
392
+ const result = await executeRpc(
393
+ {
394
+ projectDir: dir,
395
+ request: genericReq
396
+ },
397
+ genericReq.body
398
+ );
399
+ genericRes.header('Content-Type', 'application/json');
400
+ genericRes.status(200).send(JSON.stringify(result));
401
+ return;
402
+ } catch (error) {
403
+ genericRes.header('Content-Type', 'application/json');
404
+ genericRes.status(200).send(JSON.stringify({ success: false, error: 'Internal RPC error' }));
405
+ return;
406
+ }
407
+ }
408
+
409
+ // 1. Verifica se é WebSocket upgrade para hot reload
410
+ if (pathname === '/hweb-hotreload/' && genericReq.headers.upgrade === 'websocket' && hotReloadManager) {
411
+ // Framework vai chamar o evento 'upgrade' do servidor HTTP
412
+ return;
413
+ }
414
+
415
+ // 2. Primeiro verifica se é um arquivo estático da pasta public
416
+ if (pathname !== '/' && !pathname.startsWith('/api/') && !pathname.startsWith('/.nyte')) {
417
+ const publicDir = path.join(dir, 'public');
418
+ const filePath = path.join(publicDir, pathname);
419
+
420
+ if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
421
+ const ext = path.extname(filePath).toLowerCase();
422
+ const contentTypes: Record<string, string> = {
423
+ '.html': 'text/html',
424
+ '.css': 'text/css',
425
+ '.js': 'application/javascript',
426
+ '.json': 'application/json',
427
+ '.png': 'image/png',
428
+ '.jpg': 'image/jpeg',
429
+ '.jpeg': 'image/jpeg',
430
+ '.gif': 'image/gif',
431
+ '.svg': 'image/svg+xml',
432
+ '.ico': 'image/x-icon',
433
+ '.webp': 'image/webp',
434
+ '.mp4': 'video/mp4',
435
+ '.webm': 'video/webm',
436
+ '.mp3': 'audio/mpeg',
437
+ '.wav': 'audio/wav',
438
+ '.pdf': 'application/pdf',
439
+ '.txt': 'text/plain',
440
+ '.xml': 'application/xml',
441
+ '.zip': 'application/zip'
442
+ };
443
+
444
+ genericRes.header('Content-Type', contentTypes[ext] || 'application/octet-stream');
445
+
446
+ // Para arquivos estáticos, usamos o método nativo do framework
447
+ if (adapter.type === 'express') {
448
+ (res as any).sendFile(filePath);
449
+ } else if (adapter.type === 'fastify') {
450
+ const fileContent = fs.readFileSync(filePath);
451
+ genericRes.send(fileContent);
452
+ } else if (adapter.type === 'native') {
453
+ const fileContent = fs.readFileSync(filePath);
454
+ genericRes.send(fileContent);
455
+ }
456
+ return;
457
+ }
458
+ }
459
+
460
+ // 3. Verifica se é um arquivo estático do .nyte
461
+ if (pathname.startsWith('/_nyte/')) {
462
+
463
+ const staticPath = path.join(dir, '.nyte');
464
+ const filePath = path.join(staticPath, pathname.replace('/_nyte/', ''));
465
+
466
+ if (fs.existsSync(filePath)) {
467
+
468
+ const ext = path.extname(filePath).toLowerCase();
469
+ const contentTypes: Record<string, string> = {
470
+ '.js': 'application/javascript',
471
+ '.css': 'text/css',
472
+ '.map': 'application/json'
473
+ };
474
+
475
+ genericRes.header('Content-Type', contentTypes[ext] || 'text/plain');
476
+
477
+ // Para arquivos estáticos, usamos o método nativo do framework
478
+ if (adapter.type === 'express') {
479
+ (res as any).sendFile(filePath);
480
+ } else if (adapter.type === 'fastify') {
481
+ const fileContent = fs.readFileSync(filePath);
482
+ genericRes.send(fileContent);
483
+ } else if (adapter.type === 'native') {
484
+ const fileContent = fs.readFileSync(filePath);
485
+ genericRes.send(fileContent);
486
+ }
487
+ return;
488
+ }
489
+ }
490
+
491
+ // 4. REMOVIDO: Verificação de arquivos React UMD - não precisamos mais
492
+ // O React agora será bundlado diretamente no main.js
493
+
494
+ // 5. Verifica se é uma rota de API (backend)
495
+ const backendMatch = findMatchingBackendRoute(pathname, method);
496
+ if (backendMatch) {
497
+ try {
498
+ const handler = backendMatch.route[method as keyof BackendRouteConfig] as BackendHandler;
499
+ if (handler) {
500
+ const hwebReq = new NyteRequest(genericReq);
501
+
502
+ // Executa middlewares e depois o handler final
503
+ const hwebRes = await executeMiddlewareChain(
504
+ backendMatch.route.middleware,
505
+ handler,
506
+ hwebReq,
507
+ backendMatch.params
508
+ );
509
+
510
+ // Aplica a resposta usando o adapter correto
511
+ hwebRes._applyTo(genericRes);
512
+ return;
513
+ }
514
+ } catch (error) {
515
+ Console.error(`API route error ${pathname}:`, error);
516
+ genericRes.status(500).text('Internal server error in API');
517
+ return;
518
+ }
519
+ }
520
+
521
+ // 6. Por último, tenta renderizar uma página (frontend) ou 404
522
+ const pageMatch = findMatchingRoute(pathname);
523
+
524
+ if (!pageMatch) {
525
+ // Em vez de enviar texto simples, renderiza a página 404 React
526
+ try {
527
+ // Cria uma "rota falsa" para a página 404
528
+ const notFoundRoute = {
529
+ pattern: '/__404__',
530
+ component: () => null, // Componente vazio, será tratado no cliente
531
+ componentPath: '__404__'
532
+ };
533
+
534
+ const html = await render({
535
+ req: genericReq,
536
+ route: notFoundRoute,
537
+ params: {},
538
+ allRoutes: frontendRoutes
539
+ });
540
+ genericRes.status(404).header('Content-Type', 'text/html').send(html);
541
+ return;
542
+ } catch (error) {
543
+ Console.error(`Error rendering page 404:`, error);
544
+ genericRes.status(404).text('Page not found');
545
+ return;
546
+ }
547
+ }
548
+
549
+ try {
550
+ const html = await render({
551
+ req: genericReq,
552
+ route: pageMatch.route,
553
+ params: pageMatch.params,
554
+ allRoutes: frontendRoutes
555
+ });
556
+ genericRes.status(200).header('Content-Type', 'text/html').send(html);
557
+ } catch (error) {
558
+ Console.error(`Error rendering page ${pathname}:`, error);
559
+ genericRes.status(500).text('Internal server error');
560
+ }
561
+ };
562
+ },
563
+
564
+ // Método para configurar WebSocket upgrade nos servidores Express e Fastify
565
+ setupWebSocket: (server: any) => {
566
+ // Detecta se é um servidor Express ou Fastify
567
+ const isExpressServer = FrameworkAdapterFactory.getCurrentAdapter() instanceof ExpressAdapter;
568
+ const actualServer = isExpressServer ? server : (server.server || server);
569
+
570
+ // Usa o sistema coordenado de WebSocket upgrade que integra hot-reload e rotas de usuário
571
+ setupWebSocketUpgrade(actualServer, hotReloadManager);
572
+ },
573
+
574
+
575
+ stop: () => {
576
+ if (hotReloadManager) {
577
+ hotReloadManager.stop();
578
+ }
579
+ }
580
+ };
581
+ }
582
+