hightjs 0.2.41 → 0.2.43

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/index.js CHANGED
@@ -60,7 +60,6 @@ Object.defineProperty(exports, "FrameworkAdapterFactory", { enumerable: true, ge
60
60
  // Exporta os helpers para facilitar integração
61
61
  var helpers_1 = require("./helpers");
62
62
  Object.defineProperty(exports, "app", { enumerable: true, get: function () { return helpers_1.app; } });
63
- // Exporta o sistema de autenticação
64
63
  // Função para verificar se o projeto é grande o suficiente para se beneficiar de chunks
65
64
  function isLargeProject(projectDir) {
66
65
  try {
@@ -210,25 +209,30 @@ function hweb(options) {
210
209
  // Regenera o entry file
211
210
  entryPoint = createEntryFile(dir, frontendRoutes);
212
211
  };
213
- const app = {
212
+ return {
214
213
  prepare: async () => {
215
214
  const isProduction = !dev;
216
215
  if (!isProduction) {
217
- // Inicia hot reload apenas em desenvolvimento (sem logs)
216
+ // Inicia hot reload apenas em desenvolvimento (com suporte ao main)
218
217
  hotReloadManager = new hotReload_1.HotReloadManager(dir);
219
218
  await hotReloadManager.start();
220
- // Adiciona callback para recarregar rotas de backend quando mudarem
219
+ // Adiciona callback para recarregar TUDO quando qualquer arquivo mudar
221
220
  hotReloadManager.onBackendApiChange(() => {
221
+ console_1.default.info('🔄 Recarregando backend e dependências...');
222
222
  (0, router_1.loadBackendRoutes)(userBackendRoutesDir);
223
+ (0, router_1.processWebSocketRoutes)(); // Processa rotas WS após recarregar backend
223
224
  });
224
225
  // Adiciona callback para regenerar entry file quando frontend mudar
225
226
  hotReloadManager.onFrontendChange(() => {
227
+ console_1.default.info('🔄 Regenerando frontend...');
226
228
  regenerateEntryFile();
227
229
  });
228
230
  }
229
231
  // ORDEM IMPORTANTE: Carrega TUDO antes de criar o arquivo de entrada
230
232
  frontendRoutes = (0, router_1.loadRoutes)(userWebRoutesDir);
231
233
  (0, router_1.loadBackendRoutes)(userBackendRoutesDir);
234
+ // Processa rotas WebSocket após carregar backend
235
+ (0, router_1.processWebSocketRoutes)();
232
236
  // Carrega layout.tsx ANTES de criar o entry file
233
237
  const layout = (0, router_1.loadLayout)(userWebDir);
234
238
  const notFound = (0, router_1.loadNotFound)(userWebDir);
@@ -269,6 +273,13 @@ function hweb(options) {
269
273
  const instrumentationPath = path_1.default.join(dir, 'src', instrumentationFile);
270
274
  // dar require, e executar a função principal do arquivo
271
275
  const instrumentation = require(instrumentationPath);
276
+ // Registra o listener de hot reload se existir
277
+ if (instrumentation.hotReloadListener && typeof instrumentation.hotReloadListener === 'function') {
278
+ if (hotReloadManager) {
279
+ hotReloadManager.setHotReloadListener(instrumentation.hotReloadListener);
280
+ console_1.default.info('✅ Hot reload listener registrado');
281
+ }
282
+ }
272
283
  if (typeof instrumentation === 'function') {
273
284
  instrumentation();
274
285
  }
@@ -431,34 +442,11 @@ function hweb(options) {
431
442
  },
432
443
  // Método para configurar WebSocket upgrade nos servidores Express e Fastify
433
444
  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
- }
445
+ // Detecta se é um servidor Express ou Fastify
446
+ const isExpressServer = factory_1.FrameworkAdapterFactory.getCurrentAdapter() instanceof express_1.ExpressAdapter;
447
+ const actualServer = isExpressServer ? server : (server.server || server);
448
+ // Usa o sistema coordenado de WebSocket upgrade que integra hot-reload e rotas de usuário
449
+ (0, router_1.setupWebSocketUpgrade)(actualServer, hotReloadManager);
462
450
  },
463
451
  build: async () => {
464
452
  const msg = console_1.default.dynamicLine(` ${console_1.Colors.FgYellow}● ${console_1.Colors.Reset}Iniciando build do cliente para produção`);
@@ -476,5 +464,4 @@ function hweb(options) {
476
464
  }
477
465
  }
478
466
  };
479
- return app;
480
467
  }
package/dist/renderer.js CHANGED
@@ -16,7 +16,7 @@ function encodeInitialData(data) {
16
16
  }
17
17
  function createDecodeScript() {
18
18
  return `
19
- const process = { env: { NODE_ENV: '${process.env.NODE_ENV || 'development'}' } };
19
+
20
20
  window.__HWEB_DECODE__ = function(encoded) { const base64 = encoded.replace('hweb_', '').replace('_config', ''); const jsonStr = atob(base64); return JSON.parse(jsonStr); };
21
21
  `;
22
22
  }
package/dist/router.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { RouteConfig, BackendRouteConfig } from './types';
1
+ import { RouteConfig, BackendRouteConfig, HightMiddleware, WebSocketHandler } from './types';
2
2
  /**
3
3
  * Limpa todo o cache de rotas carregadas
4
4
  */
@@ -76,3 +76,26 @@ export declare function loadNotFound(webDir: string): {
76
76
  export declare function getNotFound(): {
77
77
  componentPath: string;
78
78
  } | null;
79
+ /**
80
+ * Processa e registra rotas WebSocket encontradas nas rotas backend
81
+ */
82
+ export declare function processWebSocketRoutes(): void;
83
+ /**
84
+ * Encontra a rota WebSocket correspondente para uma URL
85
+ */
86
+ export declare function findMatchingWebSocketRoute(pathname: string): {
87
+ route: {
88
+ pattern: string;
89
+ handler: WebSocketHandler;
90
+ middleware?: HightMiddleware[];
91
+ };
92
+ params: {
93
+ [key: string]: string;
94
+ };
95
+ } | null;
96
+ /**
97
+ * Configura WebSocket upgrade no servidor HTTP existente
98
+ * @param server Servidor HTTP (Express, Fastify ou Native)
99
+ * @param hotReloadManager Instância do gerenciador de hot-reload para coordenação
100
+ */
101
+ export declare function setupWebSocketUpgrade(server: any, hotReloadManager?: any): void;
package/dist/router.js CHANGED
@@ -13,9 +13,16 @@ exports.loadBackendRoutes = loadBackendRoutes;
13
13
  exports.findMatchingBackendRoute = findMatchingBackendRoute;
14
14
  exports.loadNotFound = loadNotFound;
15
15
  exports.getNotFound = getNotFound;
16
+ exports.processWebSocketRoutes = processWebSocketRoutes;
17
+ exports.findMatchingWebSocketRoute = findMatchingWebSocketRoute;
18
+ exports.setupWebSocketUpgrade = setupWebSocketUpgrade;
16
19
  const fs_1 = __importDefault(require("fs"));
17
20
  const path_1 = __importDefault(require("path"));
21
+ const ws_1 = require("ws");
22
+ const url_1 = require("url");
18
23
  const console_1 = __importDefault(require("./api/console"));
24
+ const factory_1 = require("./adapters/factory");
25
+ const http_1 = require("./api/http");
19
26
  // --- Roteamento do Frontend ---
20
27
  // Guarda todas as rotas de PÁGINA (React) encontradas
21
28
  // A rota agora também armazena o caminho do arquivo para ser usado como um ID único no cliente
@@ -183,8 +190,18 @@ function findMatchingRoute(pathname) {
183
190
  for (const route of allRoutes) {
184
191
  if (!route.pattern)
185
192
  continue;
186
- // Converte o padrão da rota (ex: /users/[id]) em uma RegExp
187
- const regexPattern = route.pattern.replace(/\[(\w+)\]/g, '(?<$1>[^/]+)');
193
+ const regexPattern = route.pattern
194
+ // [[...param]] → opcional catch-all
195
+ .replace(/\[\[\.\.\.(\w+)\]\]/g, '(?<$1>.+)?')
196
+ // [...param] → obrigatório catch-all
197
+ .replace(/\[\.\.\.(\w+)\]/g, '(?<$1>.+)')
198
+ // /[[param]] → opcional com barra também opcional
199
+ .replace(/\/\[\[(\w+)\]\]/g, '(?:/(?<$1>[^/]+))?')
200
+ // [[param]] → segmento opcional (sem barra anterior)
201
+ .replace(/\[\[(\w+)\]\]/g, '(?<$1>[^/]+)?')
202
+ // [param] → segmento obrigatório
203
+ .replace(/\[(\w+)\]/g, '(?<$1>[^/]+)');
204
+ // permite / opcional no final
188
205
  const regex = new RegExp(`^${regexPattern}/?$`);
189
206
  const match = pathname.match(regex);
190
207
  if (match) {
@@ -365,3 +382,186 @@ function loadNotFound(webDir) {
365
382
  function getNotFound() {
366
383
  return notFoundComponent;
367
384
  }
385
+ // --- WebSocket Functions ---
386
+ // Guarda todas as rotas WebSocket encontradas
387
+ let allWebSocketRoutes = [];
388
+ // Conexões WebSocket ativas
389
+ let wsConnections = new Set();
390
+ /**
391
+ * Processa e registra rotas WebSocket encontradas nas rotas backend
392
+ */
393
+ function processWebSocketRoutes() {
394
+ allWebSocketRoutes = [];
395
+ for (const route of allBackendRoutes) {
396
+ if (route.WS) {
397
+ const wsRoute = {
398
+ pattern: route.pattern,
399
+ handler: route.WS,
400
+ middleware: route.middleware
401
+ };
402
+ allWebSocketRoutes.push(wsRoute);
403
+ console_1.default.info(`WebSocket route registered: ${route.pattern}`);
404
+ }
405
+ }
406
+ }
407
+ /**
408
+ * Encontra a rota WebSocket correspondente para uma URL
409
+ */
410
+ function findMatchingWebSocketRoute(pathname) {
411
+ for (const route of allWebSocketRoutes) {
412
+ if (!route.pattern)
413
+ continue;
414
+ const regexPattern = route.pattern
415
+ .replace(/\[\[\.\.\.(\w+)\]\]/g, '(?<$1>.+)?')
416
+ .replace(/\[\.\.\.(\w+)\]/g, '(?<$1>.+)')
417
+ .replace(/\[\[(\w+)\]\]/g, '(?<$1>[^/]+)?')
418
+ .replace(/\[(\w+)\]/g, '(?<$1>[^/]+)');
419
+ const regex = new RegExp(`^${regexPattern}/?$`);
420
+ const match = pathname.match(regex);
421
+ if (match) {
422
+ return {
423
+ route,
424
+ params: match.groups || {}
425
+ };
426
+ }
427
+ }
428
+ return null;
429
+ }
430
+ /**
431
+ * Trata uma nova conexão WebSocket
432
+ */
433
+ function handleWebSocketConnection(ws, req, hwebReq) {
434
+ if (!req.url)
435
+ return;
436
+ const url = new url_1.URL(req.url, `http://${req.headers.host}`);
437
+ const pathname = url.pathname;
438
+ const matchedRoute = findMatchingWebSocketRoute(pathname);
439
+ if (!matchedRoute) {
440
+ ws.close(1000, 'Rota não encontrada');
441
+ return;
442
+ }
443
+ const params = extractWebSocketParams(pathname, matchedRoute.route.pattern);
444
+ const query = Object.fromEntries(url.searchParams.entries());
445
+ const context = {
446
+ hightReq: hwebReq,
447
+ ws,
448
+ req,
449
+ url,
450
+ params,
451
+ query,
452
+ send: (data) => {
453
+ if (ws.readyState === ws_1.WebSocket.OPEN) {
454
+ const message = typeof data === 'string' ? data : JSON.stringify(data);
455
+ ws.send(message);
456
+ }
457
+ },
458
+ close: (code, reason) => {
459
+ ws.close(code || 1000, reason);
460
+ },
461
+ broadcast: (data, exclude) => {
462
+ const message = typeof data === 'string' ? data : JSON.stringify(data);
463
+ const excludeSet = new Set(exclude || []);
464
+ wsConnections.forEach(connection => {
465
+ if (connection.readyState === ws_1.WebSocket.OPEN && !excludeSet.has(connection)) {
466
+ connection.send(message);
467
+ }
468
+ });
469
+ }
470
+ };
471
+ try {
472
+ matchedRoute.route.handler(context);
473
+ }
474
+ catch (error) {
475
+ console.error('Erro no handler WebSocket:', error);
476
+ ws.close(1011, 'Erro interno do servidor');
477
+ }
478
+ }
479
+ /**
480
+ * Extrai parâmetros da URL para WebSocket
481
+ */
482
+ function extractWebSocketParams(pathname, pattern) {
483
+ const params = {};
484
+ const regexPattern = pattern
485
+ .replace(/\[\[\.\.\.(\w+)\]\]/g, '(?<$1>.+)?')
486
+ .replace(/\[\.\.\.(\w+)\]/g, '(?<$1>.+)')
487
+ .replace(/\[\[(\w+)\]\]/g, '(?<$1>[^/]+)?')
488
+ .replace(/\[(\w+)\]/g, '(?<$1>[^/]+)');
489
+ const regex = new RegExp(`^${regexPattern}/?$`);
490
+ const match = pathname.match(regex);
491
+ if (match && match.groups) {
492
+ Object.assign(params, match.groups);
493
+ }
494
+ return params;
495
+ }
496
+ /**
497
+ * Configura WebSocket upgrade no servidor HTTP existente
498
+ * @param server Servidor HTTP (Express, Fastify ou Native)
499
+ * @param hotReloadManager Instância do gerenciador de hot-reload para coordenação
500
+ */
501
+ function setupWebSocketUpgrade(server, hotReloadManager) {
502
+ // NÃO remove listeners existentes para preservar hot-reload
503
+ // Em vez disso, coordena com o sistema existente
504
+ // Verifica se já existe um listener de upgrade
505
+ const existingListeners = server.listeners('upgrade');
506
+ // Se não há listeners, ou se o hot-reload ainda não foi configurado, adiciona o nosso
507
+ if (existingListeners.length === 0) {
508
+ server.on('upgrade', (request, socket, head) => {
509
+ handleWebSocketUpgrade(request, socket, head, hotReloadManager);
510
+ });
511
+ }
512
+ else {
513
+ // Se já existe um listener (provavelmente do hot-reload),
514
+ // vamos interceptar e coordenar
515
+ console.log('🔧 Coordenando WebSocket upgrade com sistema existente');
516
+ }
517
+ }
518
+ function handleWebSocketUpgrade(request, socket, head, hotReloadManager) {
519
+ const adapter = factory_1.FrameworkAdapterFactory.getCurrentAdapter();
520
+ if (!adapter) {
521
+ console.error('❌ Framework adapter não detectado. Não é possível processar upgrade WebSocket.');
522
+ socket.destroy();
523
+ return;
524
+ }
525
+ const genericReq = adapter.parseRequest(request);
526
+ const hwebReq = new http_1.HightJSRequest(genericReq);
527
+ const { pathname } = new url_1.URL(request.url, `http://${request.headers.host}`);
528
+ // Prioridade 1: Hot reload (sistema interno)
529
+ if (pathname === '/hweb-hotreload/') {
530
+ if (hotReloadManager) {
531
+ hotReloadManager.handleUpgrade(request, socket, head);
532
+ }
533
+ else {
534
+ console.warn('⚠️ Hot-reload manager não disponível para:', pathname);
535
+ socket.destroy();
536
+ }
537
+ return;
538
+ }
539
+ // Prioridade 2: Rotas WebSocket do usuário
540
+ const matchedRoute = findMatchingWebSocketRoute(pathname);
541
+ if (matchedRoute) {
542
+ // Faz upgrade para WebSocket usando noServer
543
+ const wss = new ws_1.WebSocketServer({
544
+ noServer: true,
545
+ perMessageDeflate: false, // Melhor performance
546
+ maxPayload: 1024 * 1024 // Limite de 1MB
547
+ });
548
+ wss.handleUpgrade(request, socket, head, (ws) => {
549
+ wsConnections.add(ws);
550
+ console.log(`✅ WebSocket conectado em ${pathname}`);
551
+ ws.on('close', () => {
552
+ wsConnections.delete(ws);
553
+ console.log(`❌ WebSocket desconectado de ${pathname}`);
554
+ });
555
+ ws.on('error', (error) => {
556
+ console.error(`💥 Erro WebSocket em ${pathname}:`, error);
557
+ wsConnections.delete(ws);
558
+ });
559
+ // Processa a conexão
560
+ handleWebSocketConnection(ws, request, hwebReq);
561
+ });
562
+ return;
563
+ }
564
+ // Nenhuma rota encontrada - rejeita conexão
565
+ console.log(`🚫 Nenhuma rota WebSocket encontrada para: ${pathname}`);
566
+ socket.destroy();
567
+ }
File without changes
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1,27 @@
1
+ import { IncomingMessage } from 'http';
2
+ import { WebSocket } from 'ws';
3
+ export interface WebSocketContext {
4
+ ws: WebSocket;
5
+ req: IncomingMessage;
6
+ url: URL;
7
+ params: Record<string, string>;
8
+ query: Record<string, string>;
9
+ send: (data: any) => void;
10
+ close: (code?: number, reason?: string) => void;
11
+ broadcast: (data: any, exclude?: WebSocket[]) => void;
12
+ }
13
+ export interface WebSocketHandler {
14
+ (ctx: WebSocketContext): void | Promise<void>;
15
+ }
16
+ export interface WebSocketRoute {
17
+ path: string;
18
+ handler: WebSocketHandler;
19
+ pathPattern?: RegExp;
20
+ paramNames?: string[];
21
+ }
22
+ export interface WebSocketServer {
23
+ route: (path: string, handler: WebSocketHandler) => void;
24
+ start: (port?: number) => void;
25
+ broadcast: (data: any) => void;
26
+ getConnections: () => WebSocket[];
27
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/dist/types.d.ts CHANGED
@@ -1,6 +1,19 @@
1
1
  import type { ComponentType } from 'react';
2
2
  import type { GenericRequest } from './types/framework';
3
3
  import { HightJSRequest, HightJSResponse } from "./api/http";
4
+ import { WebSocket } from 'ws';
5
+ import { IncomingMessage } from 'http';
6
+ export interface WebSocketContext {
7
+ ws: WebSocket;
8
+ req: IncomingMessage;
9
+ hightReq: HightJSRequest;
10
+ url: URL;
11
+ params: Record<string, string>;
12
+ query: Record<string, string>;
13
+ send: (data: any) => void;
14
+ close: (code?: number, reason?: string) => void;
15
+ broadcast: (data: any, exclude?: WebSocket[]) => void;
16
+ }
4
17
  export interface HightJSOptions {
5
18
  dev?: boolean;
6
19
  hostname?: string;
@@ -31,7 +44,11 @@ params: {
31
44
  [key: string]: string;
32
45
  }, next: () => Promise<HightJSResponse>) => Promise<HightJSResponse> | HightJSResponse;
33
46
  /**
34
- * Define a estrutura de cada rota da API, com suporte para métodos HTTP.
47
+ * Define o formato de uma função que manipula uma rota WebSocket.
48
+ */
49
+ export type WebSocketHandler = (context: WebSocketContext) => Promise<void> | void;
50
+ /**
51
+ * Define a estrutura de cada rota da API, com suporte para métodos HTTP e WebSocket.
35
52
  */
36
53
  export interface BackendRouteConfig {
37
54
  pattern: string;
@@ -39,5 +56,6 @@ export interface BackendRouteConfig {
39
56
  POST?: BackendHandler;
40
57
  PUT?: BackendHandler;
41
58
  DELETE?: BackendHandler;
59
+ WS?: WebSocketHandler;
42
60
  middleware?: HightMiddleware[];
43
61
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hightjs",
3
- "version": "0.2.41",
3
+ "version": "0.2.43",
4
4
  "description": "HightJS is a high-level framework for building web applications with ease and speed. It provides a robust set of tools and features to streamline development and enhance productivity.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/auth/core.ts CHANGED
@@ -70,7 +70,7 @@ export class HWebAuth {
70
70
 
71
71
  // Callback de sessão se definido
72
72
  if (this.config.callbacks?.session) {
73
- sessionResult.session = await this.config.callbacks.session(sessionResult.session, user);
73
+ sessionResult.session = await this.config.callbacks.session({session: sessionResult.session, user, provider: providerId});
74
74
  }
75
75
 
76
76
  return sessionResult;
@@ -103,7 +103,7 @@ export class HWebAuth {
103
103
  .clearCookie('hweb-auth-token', {
104
104
  path: '/',
105
105
  httpOnly: true,
106
- secure: true,
106
+ secure: this.config.secureCookies || false,
107
107
  sameSite: 'strict'
108
108
  });
109
109
  }
@@ -168,7 +168,7 @@ export class HWebAuth {
168
168
  .json(data)
169
169
  .cookie('hweb-auth-token', token, {
170
170
  httpOnly: true,
171
- secure: true, // Always secure, even in development
171
+ secure: this.config.secureCookies || false, // Always secure, even in development
172
172
  sameSite: 'strict', // Prevent CSRF attacks
173
173
  maxAge: (this.config.session?.maxAge || 86400) * 1000,
174
174
  path: '/',
package/src/auth/index.ts CHANGED
@@ -5,6 +5,5 @@ export * from './core';
5
5
  export * from './routes';
6
6
  export * from './jwt';
7
7
 
8
- export { CredentialsProvider, DiscordProvider } from './providers';
9
- export { createAuthRoutes } from './routes';
10
-
8
+ export { CredentialsProvider, DiscordProvider, GoogleProvider } from './providers';
9
+ export { createAuthRoutes } from './routes';