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