hightjs 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/.idea/HightJS.iml +9 -0
  2. package/.idea/copilot.data.migration.agent.xml +6 -0
  3. package/.idea/copilot.data.migration.ask.xml +6 -0
  4. package/.idea/copilot.data.migration.ask2agent.xml +6 -0
  5. package/.idea/copilot.data.migration.edit.xml +6 -0
  6. package/.idea/inspectionProfiles/Project_Default.xml +13 -0
  7. package/.idea/libraries/test_package.xml +9 -0
  8. package/.idea/libraries/ts_commonjs_default_export.xml +9 -0
  9. package/.idea/misc.xml +7 -0
  10. package/.idea/modules.xml +8 -0
  11. package/.idea/vcs.xml +6 -0
  12. package/LICENSE +13 -0
  13. package/README.md +508 -0
  14. package/dist/adapters/express.d.ts +7 -0
  15. package/dist/adapters/express.js +63 -0
  16. package/dist/adapters/factory.d.ts +23 -0
  17. package/dist/adapters/factory.js +122 -0
  18. package/dist/adapters/fastify.d.ts +25 -0
  19. package/dist/adapters/fastify.js +61 -0
  20. package/dist/adapters/native.d.ts +8 -0
  21. package/dist/adapters/native.js +203 -0
  22. package/dist/adapters/starters/express.d.ts +0 -0
  23. package/dist/adapters/starters/express.js +1 -0
  24. package/dist/adapters/starters/factory.d.ts +0 -0
  25. package/dist/adapters/starters/factory.js +1 -0
  26. package/dist/adapters/starters/fastify.d.ts +0 -0
  27. package/dist/adapters/starters/fastify.js +1 -0
  28. package/dist/adapters/starters/index.d.ts +0 -0
  29. package/dist/adapters/starters/index.js +1 -0
  30. package/dist/adapters/starters/native.d.ts +0 -0
  31. package/dist/adapters/starters/native.js +1 -0
  32. package/dist/api/console.d.ts +92 -0
  33. package/dist/api/console.js +276 -0
  34. package/dist/api/http.d.ts +180 -0
  35. package/dist/api/http.js +467 -0
  36. package/dist/auth/client.d.ts +14 -0
  37. package/dist/auth/client.js +68 -0
  38. package/dist/auth/components.d.ts +29 -0
  39. package/dist/auth/components.js +84 -0
  40. package/dist/auth/core.d.ts +38 -0
  41. package/dist/auth/core.js +124 -0
  42. package/dist/auth/index.d.ts +7 -0
  43. package/dist/auth/index.js +27 -0
  44. package/dist/auth/jwt.d.ts +41 -0
  45. package/dist/auth/jwt.js +169 -0
  46. package/dist/auth/providers.d.ts +5 -0
  47. package/dist/auth/providers.js +14 -0
  48. package/dist/auth/react/index.d.ts +6 -0
  49. package/dist/auth/react/index.js +32 -0
  50. package/dist/auth/react.d.ts +22 -0
  51. package/dist/auth/react.js +175 -0
  52. package/dist/auth/routes.d.ts +16 -0
  53. package/dist/auth/routes.js +104 -0
  54. package/dist/auth/types.d.ts +62 -0
  55. package/dist/auth/types.js +2 -0
  56. package/dist/bin/hightjs.d.ts +2 -0
  57. package/dist/bin/hightjs.js +35 -0
  58. package/dist/builder.d.ts +32 -0
  59. package/dist/builder.js +341 -0
  60. package/dist/client/DefaultNotFound.d.ts +1 -0
  61. package/dist/client/DefaultNotFound.js +53 -0
  62. package/dist/client/ErrorBoundary.d.ts +16 -0
  63. package/dist/client/ErrorBoundary.js +181 -0
  64. package/dist/client/clientRouter.d.ts +58 -0
  65. package/dist/client/clientRouter.js +116 -0
  66. package/dist/client/entry.client.d.ts +1 -0
  67. package/dist/client/entry.client.js +271 -0
  68. package/dist/client/routerContext.d.ts +26 -0
  69. package/dist/client/routerContext.js +62 -0
  70. package/dist/client.d.ts +3 -0
  71. package/dist/client.js +8 -0
  72. package/dist/components/Link.d.ts +7 -0
  73. package/dist/components/Link.js +13 -0
  74. package/dist/eslint/index.d.ts +32 -0
  75. package/dist/eslint/index.js +15 -0
  76. package/dist/eslint/use-client-rule.d.ts +19 -0
  77. package/dist/eslint/use-client-rule.js +99 -0
  78. package/dist/eslintSetup.d.ts +0 -0
  79. package/dist/eslintSetup.js +1 -0
  80. package/dist/example/src/web/routes/index.d.ts +3 -0
  81. package/dist/example/src/web/routes/index.js +15 -0
  82. package/dist/helpers.d.ts +18 -0
  83. package/dist/helpers.js +318 -0
  84. package/dist/hotReload.d.ts +23 -0
  85. package/dist/hotReload.js +292 -0
  86. package/dist/index.d.ts +17 -0
  87. package/dist/index.js +480 -0
  88. package/dist/renderer.d.ts +14 -0
  89. package/dist/renderer.js +106 -0
  90. package/dist/router.d.ts +78 -0
  91. package/dist/router.js +359 -0
  92. package/dist/types/framework.d.ts +37 -0
  93. package/dist/types/framework.js +2 -0
  94. package/dist/types.d.ts +43 -0
  95. package/dist/types.js +2 -0
  96. package/dist/typescript/use-client-plugin.d.ts +5 -0
  97. package/dist/typescript/use-client-plugin.js +113 -0
  98. package/dist/validation.d.ts +0 -0
  99. package/dist/validation.js +1 -0
  100. package/package.json +72 -0
  101. package/src/adapters/express.ts +70 -0
  102. package/src/adapters/factory.ts +96 -0
  103. package/src/adapters/fastify.ts +88 -0
  104. package/src/adapters/native.ts +223 -0
  105. package/src/api/console.ts +285 -0
  106. package/src/api/http.ts +515 -0
  107. package/src/auth/client.ts +74 -0
  108. package/src/auth/components.tsx +109 -0
  109. package/src/auth/core.ts +143 -0
  110. package/src/auth/index.ts +9 -0
  111. package/src/auth/jwt.ts +194 -0
  112. package/src/auth/providers.ts +13 -0
  113. package/src/auth/react/index.ts +9 -0
  114. package/src/auth/react.tsx +209 -0
  115. package/src/auth/routes.ts +133 -0
  116. package/src/auth/types.ts +73 -0
  117. package/src/bin/hightjs.js +40 -0
  118. package/src/builder.js +362 -0
  119. package/src/client/DefaultNotFound.tsx +68 -0
  120. package/src/client/clientRouter.ts +137 -0
  121. package/src/client/entry.client.tsx +302 -0
  122. package/src/client.ts +8 -0
  123. package/src/components/Link.tsx +22 -0
  124. package/src/helpers.ts +316 -0
  125. package/src/hotReload.ts +289 -0
  126. package/src/index.ts +514 -0
  127. package/src/renderer.tsx +122 -0
  128. package/src/router.ts +400 -0
  129. package/src/types/framework.ts +42 -0
  130. package/src/types.ts +54 -0
  131. package/tsconfig.json +17 -0
package/dist/index.js ADDED
@@ -0,0 +1,480 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.app = exports.FrameworkAdapterFactory = exports.FastifyAdapter = exports.ExpressAdapter = exports.HightJSResponse = exports.HightJSRequest = void 0;
40
+ exports.default = hweb;
41
+ const path_1 = __importDefault(require("path"));
42
+ const fs_1 = __importDefault(require("fs"));
43
+ const express_1 = require("./adapters/express");
44
+ const builder_1 = require("./builder");
45
+ const router_1 = require("./router");
46
+ const renderer_1 = require("./renderer");
47
+ const http_1 = require("./api/http");
48
+ Object.defineProperty(exports, "HightJSRequest", { enumerable: true, get: function () { return http_1.HightJSRequest; } });
49
+ Object.defineProperty(exports, "HightJSResponse", { enumerable: true, get: function () { return http_1.HightJSResponse; } });
50
+ const hotReload_1 = require("./hotReload");
51
+ const factory_1 = require("./adapters/factory");
52
+ const console_1 = __importStar(require("./api/console"));
53
+ // Exporta os adapters para uso manual se necessário
54
+ var express_2 = require("./adapters/express");
55
+ Object.defineProperty(exports, "ExpressAdapter", { enumerable: true, get: function () { return express_2.ExpressAdapter; } });
56
+ var fastify_1 = require("./adapters/fastify");
57
+ Object.defineProperty(exports, "FastifyAdapter", { enumerable: true, get: function () { return fastify_1.FastifyAdapter; } });
58
+ var factory_2 = require("./adapters/factory");
59
+ Object.defineProperty(exports, "FrameworkAdapterFactory", { enumerable: true, get: function () { return factory_2.FrameworkAdapterFactory; } });
60
+ // Exporta os helpers para facilitar integração
61
+ var helpers_1 = require("./helpers");
62
+ Object.defineProperty(exports, "app", { enumerable: true, get: function () { return helpers_1.app; } });
63
+ // Exporta o sistema de autenticação
64
+ // Função para verificar se o projeto é grande o suficiente para se beneficiar de chunks
65
+ function isLargeProject(projectDir) {
66
+ try {
67
+ const srcDir = path_1.default.join(projectDir, 'src');
68
+ if (!fs_1.default.existsSync(srcDir))
69
+ return false;
70
+ let totalFiles = 0;
71
+ let totalSize = 0;
72
+ function scanDirectory(dir) {
73
+ const items = fs_1.default.readdirSync(dir, { withFileTypes: true });
74
+ for (const item of items) {
75
+ const fullPath = path_1.default.join(dir, item.name);
76
+ if (item.isDirectory() && item.name !== 'node_modules' && item.name !== '.git') {
77
+ scanDirectory(fullPath);
78
+ }
79
+ else if (item.isFile() && /\.(tsx?|jsx?|css|scss|less)$/i.test(item.name)) {
80
+ totalFiles++;
81
+ totalSize += fs_1.default.statSync(fullPath).size;
82
+ }
83
+ }
84
+ }
85
+ scanDirectory(srcDir);
86
+ // Considera projeto grande se:
87
+ // - Mais de 20 arquivos de frontend/style
88
+ // - Ou tamanho total > 500KB
89
+ return totalFiles > 20 || totalSize > 500 * 1024;
90
+ }
91
+ catch (error) {
92
+ // Em caso de erro, assume que não é um projeto grande
93
+ return false;
94
+ }
95
+ }
96
+ // Função para gerar o arquivo de entrada para o esbuild
97
+ function createEntryFile(projectDir, routes) {
98
+ const tempDir = path_1.default.join(projectDir, '.hweb');
99
+ fs_1.default.mkdirSync(tempDir, { recursive: true });
100
+ const entryFilePath = path_1.default.join(tempDir, 'entry.client.js');
101
+ // Verifica se há layout
102
+ const layout = (0, router_1.getLayout)();
103
+ // Verifica se há notFound personalizado
104
+ const notFound = (0, router_1.getNotFound)();
105
+ // Gera imports dinâmicos para cada componente
106
+ const imports = routes
107
+ .map((route, index) => {
108
+ const relativePath = path_1.default.relative(tempDir, route.componentPath).replace(/\\/g, '/');
109
+ return `import route${index} from '${relativePath}';`;
110
+ })
111
+ .join('\n');
112
+ // Import do layout se existir
113
+ const layoutImport = layout
114
+ ? `import LayoutComponent from '${path_1.default.relative(tempDir, layout.componentPath).replace(/\\/g, '/')}';`
115
+ : '';
116
+ // Import do notFound se existir
117
+ const notFoundImport = notFound
118
+ ? `import NotFoundComponent from '${path_1.default.relative(tempDir, notFound.componentPath).replace(/\\/g, '/')}';`
119
+ : '';
120
+ // Registra os componentes no window para o cliente acessar
121
+ const componentRegistration = routes
122
+ .map((route, index) => ` '${route.componentPath}': route${index}.component || route${index}.default?.component,`)
123
+ .join('\n');
124
+ // Registra o layout se existir
125
+ const layoutRegistration = layout
126
+ ? `window.__HWEB_LAYOUT__ = LayoutComponent.default || LayoutComponent;`
127
+ : `window.__HWEB_LAYOUT__ = null;`;
128
+ // Registra o notFound se existir
129
+ const notFoundRegistration = notFound
130
+ ? `window.__HWEB_NOT_FOUND__ = NotFoundComponent.default || NotFoundComponent;`
131
+ : `window.__HWEB_NOT_FOUND__ = null;`;
132
+ // Caminho correto para o entry.client.tsx
133
+ const sdkDir = path_1.default.dirname(__dirname); // Vai para a pasta pai de src (onde está o hweb-sdk)
134
+ const entryClientPath = path_1.default.join(sdkDir, 'src', 'client', 'entry.client.tsx');
135
+ const relativeEntryPath = path_1.default.relative(tempDir, entryClientPath).replace(/\\/g, '/');
136
+ // Import do DefaultNotFound do SDK
137
+ const defaultNotFoundPath = path_1.default.join(sdkDir, 'src', 'client', 'DefaultNotFound.tsx');
138
+ const relativeDefaultNotFoundPath = path_1.default.relative(tempDir, defaultNotFoundPath).replace(/\\/g, '/');
139
+ const entryContent = `// Arquivo gerado automaticamente pelo hweb
140
+ ${imports}
141
+ ${layoutImport}
142
+ ${notFoundImport}
143
+ import DefaultNotFound from '${relativeDefaultNotFoundPath}';
144
+
145
+ // Registra os componentes para o cliente
146
+ window.__HWEB_COMPONENTS__ = {
147
+ ${componentRegistration}
148
+ };
149
+
150
+ // Registra o layout se existir
151
+ ${layoutRegistration}
152
+
153
+ // Registra o notFound se existir
154
+ ${notFoundRegistration}
155
+
156
+ // Registra o DefaultNotFound do hweb
157
+ window.__HWEB_DEFAULT_NOT_FOUND__ = DefaultNotFound;
158
+
159
+ // Importa e executa o entry.client.tsx
160
+ import '${relativeEntryPath}';
161
+ `;
162
+ fs_1.default.writeFileSync(entryFilePath, entryContent);
163
+ return entryFilePath;
164
+ }
165
+ function hweb(options) {
166
+ const { dev = true, dir = process.cwd(), port = 3000 } = options;
167
+ const userWebDir = path_1.default.join(dir, 'src', 'web');
168
+ const userWebRoutesDir = path_1.default.join(userWebDir, 'routes');
169
+ const userBackendRoutesDir = path_1.default.join(userWebDir, 'backend', 'routes');
170
+ /**
171
+ * Executa middlewares sequencialmente e depois o handler final
172
+ * @param middlewares Array de middlewares para executar
173
+ * @param finalHandler Handler final da rota
174
+ * @param request Requisição do HightJS
175
+ * @param params Parâmetros da rota
176
+ * @returns Resposta do middleware ou handler final
177
+ */
178
+ async function executeMiddlewareChain(middlewares, finalHandler, request, params) {
179
+ if (!middlewares || middlewares.length === 0) {
180
+ // Não há middlewares, executa diretamente o handler final
181
+ return await finalHandler(request, params);
182
+ }
183
+ let currentIndex = 0;
184
+ // Função next que será chamada pelos middlewares
185
+ const next = async () => {
186
+ if (currentIndex < middlewares.length) {
187
+ // Ainda há middlewares para executar
188
+ const currentMiddleware = middlewares[currentIndex];
189
+ currentIndex++;
190
+ return await currentMiddleware(request, params, next);
191
+ }
192
+ else {
193
+ // Todos os middlewares foram executados, chama o handler final
194
+ return await finalHandler(request, params);
195
+ }
196
+ };
197
+ // Inicia a cadeia de execução
198
+ return await next();
199
+ }
200
+ let frontendRoutes = [];
201
+ let hotReloadManager = null;
202
+ let entryPoint;
203
+ let outfile;
204
+ // Função para regenerar o entry file
205
+ const regenerateEntryFile = () => {
206
+ // Recarrega todas as rotas e componentes
207
+ frontendRoutes = (0, router_1.loadRoutes)(userWebRoutesDir);
208
+ (0, router_1.loadLayout)(userWebDir);
209
+ (0, router_1.loadNotFound)(userWebDir);
210
+ // Regenera o entry file
211
+ entryPoint = createEntryFile(dir, frontendRoutes);
212
+ };
213
+ const app = {
214
+ prepare: async () => {
215
+ const isProduction = !dev;
216
+ if (!isProduction) {
217
+ // Inicia hot reload apenas em desenvolvimento (sem logs)
218
+ hotReloadManager = new hotReload_1.HotReloadManager(dir);
219
+ await hotReloadManager.start();
220
+ // Adiciona callback para recarregar rotas de backend quando mudarem
221
+ hotReloadManager.onBackendApiChange(() => {
222
+ (0, router_1.loadBackendRoutes)(userBackendRoutesDir);
223
+ });
224
+ // Adiciona callback para regenerar entry file quando frontend mudar
225
+ hotReloadManager.onFrontendChange(() => {
226
+ regenerateEntryFile();
227
+ });
228
+ }
229
+ // ORDEM IMPORTANTE: Carrega TUDO antes de criar o arquivo de entrada
230
+ frontendRoutes = (0, router_1.loadRoutes)(userWebRoutesDir);
231
+ (0, router_1.loadBackendRoutes)(userBackendRoutesDir);
232
+ // Carrega layout.tsx ANTES de criar o entry file
233
+ const layout = (0, router_1.loadLayout)(userWebDir);
234
+ const notFound = (0, router_1.loadNotFound)(userWebDir);
235
+ const outDir = path_1.default.join(dir, 'hweb-dist');
236
+ fs_1.default.mkdirSync(outDir, { recursive: true });
237
+ entryPoint = createEntryFile(dir, frontendRoutes);
238
+ // Usa chunks quando há muitas rotas ou o projeto é grande
239
+ const shouldUseChunks = frontendRoutes.length > 5 || isLargeProject(dir);
240
+ if (shouldUseChunks) {
241
+ const outDir = path_1.default.join(dir, 'hweb-dist');
242
+ if (isProduction) {
243
+ await (0, builder_1.buildWithChunks)(entryPoint, outDir, isProduction);
244
+ console_1.default.info(`✅ Build com chunks finalizado! ${frontendRoutes.length} rotas processadas.`);
245
+ }
246
+ else {
247
+ (0, builder_1.watchWithChunks)(entryPoint, outDir, hotReloadManager).catch(err => {
248
+ console_1.default.error(`Erro ao iniciar o watch com chunks`, err);
249
+ });
250
+ console_1.default.info(`🚀 Modo watch com chunks ativo para ${frontendRoutes.length} rotas.`);
251
+ }
252
+ }
253
+ else {
254
+ outfile = path_1.default.join(dir, 'hweb-dist', 'main.js');
255
+ if (isProduction) {
256
+ await (0, builder_1.build)(entryPoint, outfile, isProduction);
257
+ }
258
+ else {
259
+ (0, builder_1.watch)(entryPoint, outfile, hotReloadManager).catch(err => {
260
+ console_1.default.error(`Erro ao iniciar o watch`, err);
261
+ });
262
+ }
263
+ }
264
+ },
265
+ executeInstrumentation: () => {
266
+ // verificar se dir/src/instrumentation.(tsx/jsx/js/ts) existe com regex
267
+ const instrumentationFile = fs_1.default.readdirSync(path_1.default.join(dir, 'src')).find(file => /^hightweb\.(tsx|jsx|js|ts)$/.test(file));
268
+ if (instrumentationFile) {
269
+ const instrumentationPath = path_1.default.join(dir, 'src', instrumentationFile);
270
+ // dar require, e executar a função principal do arquivo
271
+ const instrumentation = require(instrumentationPath);
272
+ if (typeof instrumentation === 'function') {
273
+ instrumentation();
274
+ }
275
+ else if (typeof instrumentation.default === 'function') {
276
+ instrumentation.default();
277
+ }
278
+ else {
279
+ console_1.default.warn(`O arquivo de instrumentação ${instrumentationFile} não exporta uma função padrão.`);
280
+ }
281
+ }
282
+ },
283
+ getRequestHandler: () => {
284
+ return async (req, res) => {
285
+ // Detecta automaticamente o framework e cria o adapter apropriado
286
+ const adapter = factory_1.FrameworkAdapterFactory.detectFramework(req, res);
287
+ const genericReq = adapter.parseRequest(req);
288
+ const genericRes = adapter.createResponse(res);
289
+ // Adiciona informações do hweb na requisição genérica
290
+ genericReq.hwebDev = dev;
291
+ genericReq.hotReloadManager = hotReloadManager;
292
+ const { pathname } = new URL(genericReq.url, `http://${genericReq.headers.host || 'localhost'}`);
293
+ const method = genericReq.method.toUpperCase();
294
+ // 1. Verifica se é WebSocket upgrade para hot reload
295
+ if (pathname === '/hweb-hotreload/' && genericReq.headers.upgrade === 'websocket' && hotReloadManager) {
296
+ // Framework vai chamar o evento 'upgrade' do servidor HTTP
297
+ return;
298
+ }
299
+ // 2. Primeiro verifica se é um arquivo estático da pasta public
300
+ if (pathname !== '/' && !pathname.startsWith('/api/') && !pathname.startsWith('/hweb-')) {
301
+ const publicDir = path_1.default.join(dir, 'public');
302
+ const filePath = path_1.default.join(publicDir, pathname);
303
+ if (fs_1.default.existsSync(filePath) && fs_1.default.statSync(filePath).isFile()) {
304
+ const ext = path_1.default.extname(filePath).toLowerCase();
305
+ const contentTypes = {
306
+ '.html': 'text/html',
307
+ '.css': 'text/css',
308
+ '.js': 'application/javascript',
309
+ '.json': 'application/json',
310
+ '.png': 'image/png',
311
+ '.jpg': 'image/jpeg',
312
+ '.jpeg': 'image/jpeg',
313
+ '.gif': 'image/gif',
314
+ '.svg': 'image/svg+xml',
315
+ '.ico': 'image/x-icon',
316
+ '.webp': 'image/webp',
317
+ '.mp4': 'video/mp4',
318
+ '.webm': 'video/webm',
319
+ '.mp3': 'audio/mpeg',
320
+ '.wav': 'audio/wav',
321
+ '.pdf': 'application/pdf',
322
+ '.txt': 'text/plain',
323
+ '.xml': 'application/xml',
324
+ '.zip': 'application/zip'
325
+ };
326
+ genericRes.header('Content-Type', contentTypes[ext] || 'application/octet-stream');
327
+ // Para arquivos estáticos, usamos o método nativo do framework
328
+ if (adapter.type === 'express') {
329
+ res.sendFile(filePath);
330
+ }
331
+ else if (adapter.type === 'fastify') {
332
+ const fileContent = fs_1.default.readFileSync(filePath);
333
+ genericRes.send(fileContent);
334
+ }
335
+ else if (adapter.type === 'native') {
336
+ const fileContent = fs_1.default.readFileSync(filePath);
337
+ genericRes.send(fileContent);
338
+ }
339
+ return;
340
+ }
341
+ }
342
+ // 3. Verifica se é um arquivo estático do hweb-dist
343
+ if (pathname.startsWith('/hweb-dist/')) {
344
+ const staticPath = path_1.default.join(dir, 'hweb-dist');
345
+ const filePath = path_1.default.join(staticPath, pathname.replace('/hweb-dist/', ''));
346
+ if (fs_1.default.existsSync(filePath)) {
347
+ const ext = path_1.default.extname(filePath).toLowerCase();
348
+ const contentTypes = {
349
+ '.js': 'application/javascript',
350
+ '.css': 'text/css',
351
+ '.map': 'application/json'
352
+ };
353
+ genericRes.header('Content-Type', contentTypes[ext] || 'text/plain');
354
+ // Para arquivos estáticos, usamos o método nativo do framework
355
+ if (adapter.type === 'express') {
356
+ res.sendFile(filePath);
357
+ }
358
+ else if (adapter.type === 'fastify') {
359
+ const fileContent = fs_1.default.readFileSync(filePath);
360
+ genericRes.send(fileContent);
361
+ }
362
+ else if (adapter.type === 'native') {
363
+ const fileContent = fs_1.default.readFileSync(filePath);
364
+ genericRes.send(fileContent);
365
+ }
366
+ return;
367
+ }
368
+ }
369
+ // 4. REMOVIDO: Verificação de arquivos React UMD - não precisamos mais
370
+ // O React agora será bundlado diretamente no main.js
371
+ // 5. Verifica se é uma rota de API (backend)
372
+ const backendMatch = (0, router_1.findMatchingBackendRoute)(pathname, method);
373
+ if (backendMatch) {
374
+ try {
375
+ const handler = backendMatch.route[method];
376
+ if (handler) {
377
+ const hwebReq = new http_1.HightJSRequest(genericReq);
378
+ // Executa middlewares e depois o handler final
379
+ const hwebRes = await executeMiddlewareChain(backendMatch.route.middleware, handler, hwebReq, backendMatch.params);
380
+ // Aplica a resposta usando o adapter correto
381
+ hwebRes._applyTo(genericRes);
382
+ return;
383
+ }
384
+ }
385
+ catch (error) {
386
+ console_1.default.error(`Erro na rota de API ${pathname}:`, error);
387
+ genericRes.status(500).text('Erro interno do servidor na API');
388
+ return;
389
+ }
390
+ }
391
+ // 6. Por último, tenta renderizar uma página (frontend) ou 404
392
+ const pageMatch = (0, router_1.findMatchingRoute)(pathname);
393
+ if (!pageMatch) {
394
+ // Em vez de enviar texto simples, renderiza a página 404 React
395
+ try {
396
+ // Cria uma "rota falsa" para a página 404
397
+ const notFoundRoute = {
398
+ pattern: '/__404__',
399
+ component: () => null, // Componente vazio, será tratado no cliente
400
+ componentPath: '__404__'
401
+ };
402
+ const html = await (0, renderer_1.render)({
403
+ req: genericReq,
404
+ route: notFoundRoute,
405
+ params: {},
406
+ allRoutes: frontendRoutes
407
+ });
408
+ genericRes.status(404).header('Content-Type', 'text/html').send(html);
409
+ return;
410
+ }
411
+ catch (error) {
412
+ console_1.default.error(`Erro ao renderizar página 404:`, error);
413
+ genericRes.status(404).text('Página não encontrada');
414
+ return;
415
+ }
416
+ }
417
+ try {
418
+ const html = await (0, renderer_1.render)({
419
+ req: genericReq,
420
+ route: pageMatch.route,
421
+ params: pageMatch.params,
422
+ allRoutes: frontendRoutes
423
+ });
424
+ genericRes.status(200).header('Content-Type', 'text/html').send(html);
425
+ }
426
+ catch (error) {
427
+ console_1.default.error(`Erro ao renderizar a página ${pathname}:`, error);
428
+ genericRes.status(500).text('Erro interno do servidor');
429
+ }
430
+ };
431
+ },
432
+ // Método para configurar WebSocket upgrade nos servidores Express e Fastify
433
+ setupWebSocket: (server) => {
434
+ if (hotReloadManager) {
435
+ // Detecta se é um servidor Express ou Fastify
436
+ const isExpressServer = factory_1.FrameworkAdapterFactory.getCurrentAdapter() instanceof express_1.ExpressAdapter;
437
+ if (isExpressServer) {
438
+ server.on('upgrade', (request, socket, head) => {
439
+ const { pathname } = new URL(request.url, `http://${request.headers.host}`);
440
+ if (pathname === '/hweb-hotreload/') {
441
+ hotReloadManager.handleUpgrade(request, socket, head);
442
+ }
443
+ else {
444
+ socket.destroy();
445
+ }
446
+ });
447
+ }
448
+ else {
449
+ // Fastify usa um approach diferente para WebSockets
450
+ const actualServer = server.server || server;
451
+ actualServer.on('upgrade', (request, socket, head) => {
452
+ const { pathname } = new URL(request.url, `http://${request.headers.host}`);
453
+ if (pathname === '/hweb-hotreload/') {
454
+ hotReloadManager.handleUpgrade(request, socket, head);
455
+ }
456
+ else {
457
+ socket.destroy();
458
+ }
459
+ });
460
+ }
461
+ }
462
+ },
463
+ build: async () => {
464
+ const msg = console_1.default.dynamicLine(` ${console_1.Colors.FgYellow}● ${console_1.Colors.Reset}Iniciando build do cliente para produção`);
465
+ const outDir = path_1.default.join(dir, 'hweb-dist');
466
+ fs_1.default.mkdirSync(outDir, { recursive: true });
467
+ const routes = (0, router_1.loadRoutes)(userWebRoutesDir);
468
+ const entryPoint = createEntryFile(dir, routes);
469
+ const outfile = path_1.default.join(outDir, 'main.js');
470
+ await (0, builder_1.build)(entryPoint, outfile, true); // Força produção no build manual
471
+ msg.end(` ${console_1.Colors.FgGreen}● ${console_1.Colors.Reset}Build do cliente concluído: ${outfile}`);
472
+ },
473
+ stop: () => {
474
+ if (hotReloadManager) {
475
+ hotReloadManager.stop();
476
+ }
477
+ }
478
+ };
479
+ return app;
480
+ }
@@ -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 {};
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.render = render;
7
+ const router_1 = require("./router");
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ // Funções para codificar/decodificar dados (disfarça o JSON no HTML)
11
+ function encodeInitialData(data) {
12
+ // Converte para JSON, depois para base64, e adiciona um prefixo fake
13
+ const jsonStr = JSON.stringify(data);
14
+ const base64 = Buffer.from(jsonStr).toString('base64');
15
+ return `hweb_${base64}_config`;
16
+ }
17
+ function createDecodeScript() {
18
+ return `
19
+ const process = { env: { NODE_ENV: '${process.env.NODE_ENV || 'development'}' } };
20
+ window.__HWEB_DECODE__ = function(encoded) { const base64 = encoded.replace('hweb_', '').replace('_config', ''); const jsonStr = atob(base64); return JSON.parse(jsonStr); };
21
+ `;
22
+ }
23
+ async function render({ req, route, params, allRoutes }) {
24
+ const { generateMetadata } = route;
25
+ // Pega a opção dev e hot reload manager do req
26
+ const isProduction = !req.hwebDev;
27
+ const hotReloadManager = req.hotReloadManager;
28
+ // Pega o layout se existir
29
+ const layout = (0, router_1.getLayout)();
30
+ let metadata = { title: 'App hweb' };
31
+ // Primeiro usa o metadata do layout se existir
32
+ if (layout && layout.metadata) {
33
+ metadata = { ...metadata, ...layout.metadata };
34
+ }
35
+ // Depois sobrescreve com metadata específico da rota se existir
36
+ if (generateMetadata) {
37
+ const routeMetadata = await Promise.resolve(generateMetadata(params, req));
38
+ metadata = { ...metadata, ...routeMetadata };
39
+ }
40
+ // Prepara os dados para injetar na janela do navegador
41
+ const initialData = {
42
+ routes: allRoutes.map(r => ({ pattern: r.pattern, componentPath: r.componentPath })),
43
+ initialComponentPath: route.componentPath,
44
+ initialParams: params,
45
+ };
46
+ // Codifica os dados para disfarçar
47
+ const encodedData = encodeInitialData(initialData);
48
+ // Script de hot reload apenas em desenvolvimento
49
+ const hotReloadScript = !isProduction && hotReloadManager
50
+ ? hotReloadManager.getClientScript()
51
+ : '';
52
+ const favicon = metadata.favicon ? `<link rel="icon" href="${metadata.favicon}">` : '';
53
+ // Determina quais arquivos JavaScript carregar
54
+ const jsFiles = getJavaScriptFiles(req);
55
+ // HTML base sem SSR - apenas o container e scripts para client-side rendering
56
+ return `<!DOCTYPE html><html lang="pt-BR"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>${metadata.title || 'App hweb'}</title>${metadata.description ? `<meta name="description" content="${metadata.description}">` : ''}${favicon}</head><body><div id="root"></div><script>${createDecodeScript()}window.__HWEB_INITIAL_DATA__ = window.__HWEB_DECODE__('${encodedData}');</script>${jsFiles}${hotReloadScript}</body></html>`;
57
+ }
58
+ // Função para determinar quais arquivos JavaScript carregar
59
+ function getJavaScriptFiles(req) {
60
+ const projectDir = process.cwd();
61
+ const distDir = path_1.default.join(projectDir, 'hweb-dist');
62
+ try {
63
+ // Verifica se existe um manifesto de chunks (gerado pelo ESBuild com splitting)
64
+ const manifestPath = path_1.default.join(distDir, 'manifest.json');
65
+ if (fs_1.default.existsSync(manifestPath)) {
66
+ // Modo chunks - carrega todos os arquivos necessários
67
+ const manifest = JSON.parse(fs_1.default.readFileSync(manifestPath, 'utf8'));
68
+ const scripts = Object.values(manifest)
69
+ .filter((file) => file.endsWith('.js'))
70
+ .map((file) => `<script src="/hweb-dist/${file}"></script>`)
71
+ .join('');
72
+ return scripts;
73
+ }
74
+ else {
75
+ // Verifica se existem múltiplos arquivos JS (chunks sem manifesto)
76
+ const jsFiles = fs_1.default.readdirSync(distDir)
77
+ .filter(file => file.endsWith('.js') && !file.endsWith('.map'))
78
+ .sort((a, b) => {
79
+ // Ordena para carregar arquivos principais primeiro
80
+ if (a.includes('main'))
81
+ return -1;
82
+ if (b.includes('main'))
83
+ return 1;
84
+ if (a.includes('vendor') || a.includes('react'))
85
+ return -1;
86
+ if (b.includes('vendor') || b.includes('react'))
87
+ return 1;
88
+ return a.localeCompare(b);
89
+ });
90
+ if (jsFiles.length > 1) {
91
+ // Modo chunks sem manifesto
92
+ return jsFiles
93
+ .map(file => `<script src="/hweb-dist/${file}"></script>`)
94
+ .join('');
95
+ }
96
+ else {
97
+ // Modo tradicional - único arquivo
98
+ return '<script src="/hweb-dist/main.js"></script>';
99
+ }
100
+ }
101
+ }
102
+ catch (error) {
103
+ // Fallback para o modo tradicional
104
+ return '<script src="/hweb-dist/main.js"></script>';
105
+ }
106
+ }