hightjs 0.2.42 → 0.2.45

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 (76) hide show
  1. package/.idea/copilotDiffState.xml +67 -0
  2. package/README.md +26 -514
  3. package/dist/auth/core.js +3 -3
  4. package/dist/auth/index.d.ts +1 -1
  5. package/dist/auth/index.js +2 -1
  6. package/dist/auth/providers/google.d.ts +63 -0
  7. package/dist/auth/providers/google.js +186 -0
  8. package/dist/auth/providers.d.ts +1 -0
  9. package/dist/auth/providers.js +3 -1
  10. package/dist/auth/types.d.ts +6 -7
  11. package/dist/bin/hightjs.js +393 -0
  12. package/dist/client/entry.client.js +11 -1
  13. package/dist/hotReload.d.ts +8 -1
  14. package/dist/hotReload.js +304 -144
  15. package/dist/index.d.ts +2 -1
  16. package/dist/index.js +20 -33
  17. package/dist/renderer.js +1 -1
  18. package/dist/router.d.ts +24 -1
  19. package/dist/router.js +201 -2
  20. package/dist/types.d.ts +19 -1
  21. package/docs/README.md +59 -0
  22. package/docs/adapters.md +7 -0
  23. package/docs/arquivos-especiais.md +10 -0
  24. package/docs/autenticacao.md +212 -0
  25. package/docs/checklist.md +9 -0
  26. package/docs/cli.md +21 -0
  27. package/docs/estrutura.md +20 -0
  28. package/docs/faq.md +10 -0
  29. package/docs/hot-reload.md +5 -0
  30. package/docs/middlewares.md +73 -0
  31. package/docs/rotas-backend.md +45 -0
  32. package/docs/rotas-frontend.md +66 -0
  33. package/docs/seguranca.md +8 -0
  34. package/docs/websocket.md +45 -0
  35. package/package.json +1 -1
  36. package/src/auth/core.ts +3 -3
  37. package/src/auth/index.ts +2 -3
  38. package/src/auth/providers/google.ts +218 -0
  39. package/src/auth/providers.ts +1 -1
  40. package/src/auth/types.ts +3 -8
  41. package/src/bin/hightjs.js +475 -0
  42. package/src/client/entry.client.tsx +12 -1
  43. package/src/hotReload.ts +333 -147
  44. package/src/index.ts +58 -51
  45. package/src/renderer.tsx +1 -1
  46. package/src/router.ts +230 -3
  47. package/src/types.ts +24 -1
  48. package/dist/adapters/starters/express.d.ts +0 -0
  49. package/dist/adapters/starters/express.js +0 -1
  50. package/dist/adapters/starters/factory.d.ts +0 -0
  51. package/dist/adapters/starters/factory.js +0 -1
  52. package/dist/adapters/starters/fastify.d.ts +0 -0
  53. package/dist/adapters/starters/fastify.js +0 -1
  54. package/dist/adapters/starters/index.d.ts +0 -0
  55. package/dist/adapters/starters/index.js +0 -1
  56. package/dist/adapters/starters/native.d.ts +0 -0
  57. package/dist/adapters/starters/native.js +0 -1
  58. package/dist/auth/example.d.ts +0 -40
  59. package/dist/auth/example.js +0 -104
  60. package/dist/client/ErrorBoundary.d.ts +0 -16
  61. package/dist/client/ErrorBoundary.js +0 -181
  62. package/dist/client/routerContext.d.ts +0 -26
  63. package/dist/client/routerContext.js +0 -62
  64. package/dist/eslint/index.d.ts +0 -32
  65. package/dist/eslint/index.js +0 -15
  66. package/dist/eslint/use-client-rule.d.ts +0 -19
  67. package/dist/eslint/use-client-rule.js +0 -99
  68. package/dist/eslintSetup.d.ts +0 -0
  69. package/dist/eslintSetup.js +0 -1
  70. package/dist/example/src/web/routes/index.d.ts +0 -3
  71. package/dist/example/src/web/routes/index.js +0 -15
  72. package/dist/typescript/use-client-plugin.d.ts +0 -5
  73. package/dist/typescript/use-client-plugin.js +0 -113
  74. package/dist/validation.d.ts +0 -0
  75. package/dist/validation.js +0 -1
  76. package/src/auth/example.ts +0 -115
@@ -0,0 +1,67 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="CopilotDiffPersistence">
4
+ <option name="pendingDiffs">
5
+ <map>
6
+ <entry key="$PROJECT_DIR$/src/types/websocket.ts">
7
+ <value>
8
+ <PendingDiffInfo>
9
+ <option name="filePath" value="$PROJECT_DIR$/src/types/websocket.ts" />
10
+ <option name="updatedContent" value="import { IncomingMessage } from 'http';&#10;import { WebSocket } from 'ws';&#10;&#10;export interface WebSocketContext {&#10; ws: WebSocket;&#10; req: IncomingMessage;&#10; url: URL;&#10; params: Record&lt;string, string&gt;;&#10; query: Record&lt;string, string&gt;;&#10; send: (data: any) =&gt; void;&#10; close: (code?: number, reason?: string) =&gt; void;&#10; broadcast: (data: any, exclude?: WebSocket[]) =&gt; void;&#10;}&#10;&#10;export interface WebSocketHandler {&#10; (ctx: WebSocketContext): void | Promise&lt;void&gt;;&#10;}&#10;&#10;export interface WebSocketRoute {&#10; path: string;&#10; handler: WebSocketHandler;&#10; pathPattern?: RegExp;&#10; paramNames?: string[];&#10;}&#10;&#10;export interface WebSocketServer {&#10; route: (path: string, handler: WebSocketHandler) =&gt; void;&#10; start: (port?: number) =&gt; void;&#10; broadcast: (data: any) =&gt; void;&#10; getConnections: () =&gt; WebSocket[];&#10;}" />
11
+ </PendingDiffInfo>
12
+ </value>
13
+ </entry>
14
+ <entry key="$PROJECT_DIR$/src/websocket/adapter.ts">
15
+ <value>
16
+ <PendingDiffInfo>
17
+ <option name="filePath" value="$PROJECT_DIR$/src/websocket/adapter.ts" />
18
+ <option name="updatedContent" value="import { Server } from 'http';&#10;import { WebSocketServer as WSServer } from 'ws';&#10;import { HightWSServer } from './server';&#10;import { WebSocketServer, WebSocketHandler } from '../types/websocket';&#10;import Console, { Colors } from '../api/console';&#10;&#10;export class WebSocketAdapter {&#10; private wsServer: HightWSServer;&#10; private httpServer: Server | null = null;&#10; private wss: WSServer | null = null;&#10;&#10; constructor() {&#10; this.wsServer = new HightWSServer();&#10; }&#10;&#10; /**&#10; * Integra WebSocket com um servidor HTTP existente&#10; */&#10; attachToHttpServer(httpServer: Server): void {&#10; this.httpServer = httpServer;&#10; this.wss = new WSServer({ &#10; server: httpServer,&#10; perMessageDeflate: false &#10; });&#10;&#10; console.log(`${Colors.FgGreen}✓${Colors.Reset} WebSocket attached to HTTP server`);&#10;&#10; this.wss.on('connection', (ws, req) =&gt; {&#10; this.wsServer['handleConnection'](ws, req);&#10; });&#10;&#10; this.wss.on('error', (error) =&gt; {&#10; console.error(`${Colors.FgRed}[WebSocket Server Error]${Colors.Reset}`, error);&#10; });&#10; }&#10;&#10; /**&#10; * Adiciona uma rota WebSocket&#10; */&#10; route(path: string, handler: WebSocketHandler): void {&#10; this.wsServer.route(path, handler);&#10; }&#10;&#10; /**&#10; * Faz broadcast para todas as conexões&#10; */&#10; broadcast(data: any): void {&#10; this.wsServer.broadcast(data);&#10; }&#10;&#10; /**&#10; * Retorna todas as conexões ativas&#10; */&#10; getConnections() {&#10; return this.wsServer.getConnections();&#10; }&#10;&#10; /**&#10; * Inicia servidor WebSocket standalone&#10; */&#10; startStandalone(port: number = 8080): void {&#10; this.wsServer.start(port);&#10; }&#10;}" />
19
+ </PendingDiffInfo>
20
+ </value>
21
+ </entry>
22
+ <entry key="$PROJECT_DIR$/src/websocket/client.ts">
23
+ <value>
24
+ <PendingDiffInfo>
25
+ <option name="filePath" value="$PROJECT_DIR$/src/websocket/client.ts" />
26
+ <option name="originalContent" value="&#10;" />
27
+ <option name="updatedContent" value="import React from 'react';&#10;&#10;export class HightWSClient {&#10; private ws: WebSocket | null = null;&#10; private url: string;&#10; private reconnectAttempts: number = 0;&#10; private maxReconnectAttempts: number = 5;&#10; private reconnectDelay: number = 1000;&#10; private listeners: Map&lt;string, Function[]&gt; = new Map();&#10;&#10; constructor(url: string) {&#10; this.url = url;&#10; this.connect();&#10; }&#10;&#10; private connect(): void {&#10; try {&#10; this.ws = new WebSocket(this.url);&#10; &#10; this.ws.onopen = (event) =&gt; {&#10; console.log('[HightWS] Connected to WebSocket server');&#10; this.reconnectAttempts = 0;&#10; this.emit('open', event);&#10; };&#10;&#10; this.ws.onmessage = (event) =&gt; {&#10; try {&#10; const data = JSON.parse(event.data);&#10; this.emit('message', data);&#10; } catch (error) {&#10; // Se não for JSON, envia como string&#10; this.emit('message', event.data);&#10; }&#10; };&#10;&#10; this.ws.onclose = (event) =&gt; {&#10; console.log('[HightWS] Connection closed');&#10; this.emit('close', event);&#10; this.attemptReconnect();&#10; };&#10;&#10; this.ws.onerror = (event) =&gt; {&#10; console.error('[HightWS] WebSocket error:', event);&#10; this.emit('error', event);&#10; };&#10;&#10; } catch (error) {&#10; console.error('[HightWS] Failed to connect:', error);&#10; this.attemptReconnect();&#10; }&#10; }&#10;&#10; private attemptReconnect(): void {&#10; if (this.reconnectAttempts &lt; this.maxReconnectAttempts) {&#10; this.reconnectAttempts++;&#10; console.log(`[HightWS] Attempting to reconnect (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`);&#10; &#10; setTimeout(() =&gt; {&#10; this.connect();&#10; }, this.reconnectDelay * this.reconnectAttempts);&#10; } else {&#10; console.error('[HightWS] Max reconnection attempts reached');&#10; this.emit('maxReconnectAttemptsReached');&#10; }&#10; }&#10;&#10; public send(data: any): void {&#10; if (this.ws &amp;&amp; this.ws.readyState === WebSocket.OPEN) {&#10; const message = typeof data === 'string' ? data : JSON.stringify(data);&#10; this.ws.send(message);&#10; } else {&#10; console.warn('[HightWS] WebSocket is not connected');&#10; }&#10; }&#10;&#10; public on(event: string, callback: Function): void {&#10; if (!this.listeners.has(event)) {&#10; this.listeners.set(event, []);&#10; }&#10; this.listeners.get(event)!.push(callback);&#10; }&#10;&#10; public off(event: string, callback?: Function): void {&#10; if (!this.listeners.has(event)) return;&#10;&#10; if (callback) {&#10; const callbacks = this.listeners.get(event)!;&#10; const index = callbacks.indexOf(callback);&#10; if (index &gt; -1) {&#10; callbacks.splice(index, 1);&#10; }&#10; } else {&#10; this.listeners.delete(event);&#10; }&#10; }&#10;&#10; private emit(event: string, data?: any): void {&#10; if (this.listeners.has(event)) {&#10; this.listeners.get(event)!.forEach(callback =&gt; {&#10; try {&#10; callback(data);&#10; } catch (error) {&#10; console.error(`[HightWS] Error in event listener for ${event}:`, error);&#10; }&#10; });&#10; }&#10; }&#10;&#10; public close(): void {&#10; if (this.ws) {&#10; this.ws.close();&#10; this.ws = null;&#10; }&#10; }&#10;&#10; public get readyState(): number {&#10; return this.ws ? this.ws.readyState : WebSocket.CLOSED;&#10; }&#10;&#10; public get isConnected(): boolean {&#10; return this.ws !== null &amp;&amp; this.ws.readyState === WebSocket.OPEN;&#10; }&#10;}&#10;&#10;// Hook React para usar WebSocket&#10;export function useWebSocket(url: string) {&#10; const [client, setClient] = React.useState&lt;HightWSClient | null&gt;(null);&#10; const [isConnected, setIsConnected] = React.useState(false);&#10; const [lastMessage, setLastMessage] = React.useState&lt;any&gt;(null);&#10;&#10; React.useEffect(() =&gt; {&#10; const wsClient = new HightWSClient(url);&#10;&#10; wsClient.on('open', () =&gt; {&#10; setIsConnected(true);&#10; });&#10;&#10; wsClient.on('close', () =&gt; {&#10; setIsConnected(false);&#10; });&#10;&#10; wsClient.on('message', (data) =&gt; {&#10; setLastMessage(data);&#10; });&#10;&#10; setClient(wsClient);&#10;&#10; return () =&gt; {&#10; wsClient.close();&#10; };&#10; }, [url]);&#10;&#10; const sendMessage = React.useCallback((data: any) =&gt; {&#10; if (client) {&#10; client.send(data);&#10; }&#10; }, [client]);&#10;&#10; return {&#10; client,&#10; isConnected,&#10; lastMessage,&#10; sendMessage&#10; };&#10;}" />
28
+ </PendingDiffInfo>
29
+ </value>
30
+ </entry>
31
+ <entry key="$PROJECT_DIR$/src/websocket/example.ts">
32
+ <value>
33
+ <PendingDiffInfo>
34
+ <option name="filePath" value="$PROJECT_DIR$/src/websocket/example.ts" />
35
+ <option name="updatedContent" value="// Exemplo de uso do WebSocket no HightJS&#10;&#10;// ============================================&#10;// BACKEND - Como criar rotas WebSocket&#10;// ============================================&#10;&#10;// Em src/web/backend/websocket.ts (ou qualquer arquivo backend)&#10;import { ws } from 'hightjs/ws';&#10;&#10;// Rota simples de chat&#10;ws('/chat', (ctx) =&gt; {&#10; console.log('Nova conexão no chat:', ctx.req.socket.remoteAddress);&#10; &#10; // Envia mensagem de boas-vindas&#10; ctx.send({&#10; type: 'welcome',&#10; message: 'Bem-vindo ao chat!'&#10; });&#10;&#10; // Escuta mensagens do cliente&#10; ctx.ws.on('message', (data) =&gt; {&#10; try {&#10; const message = JSON.parse(data.toString());&#10; &#10; // Faz broadcast da mensagem para todos os outros clientes&#10; ctx.broadcast({&#10; type: 'message',&#10; user: message.user || 'Anônimo',&#10; text: message.text,&#10; timestamp: new Date().toISOString()&#10; }, [ctx.ws]); // Exclui o remetente&#10; &#10; } catch (error) {&#10; console.error('Erro ao processar mensagem:', error);&#10; }&#10; });&#10;&#10; // Quando a conexão fechar&#10; ctx.ws.on('close', () =&gt; {&#10; console.log('Cliente desconectado do chat');&#10; });&#10;});&#10;&#10;// Rota com parâmetros&#10;ws('/room/:roomId', (ctx) =&gt; {&#10; const { roomId } = ctx.params;&#10; console.log(`Nova conexão na sala: ${roomId}`);&#10; &#10; ctx.send({&#10; type: 'joined',&#10; room: roomId,&#10; message: `Você entrou na sala ${roomId}`&#10; });&#10;});&#10;&#10;// Rota para notificações em tempo real&#10;ws('/notifications/:userId', (ctx) =&gt; {&#10; const { userId } = ctx.params;&#10; &#10; // Pode usar query params também&#10; const token = ctx.query.token;&#10; &#10; if (!token) {&#10; ctx.close(1008, 'Token obrigatório');&#10; return;&#10; }&#10; &#10; console.log(`Usuário ${userId} conectado para notificações`);&#10; &#10; // Simula envio de notificação a cada 30 segundos&#10; const interval = setInterval(() =&gt; {&#10; ctx.send({&#10; type: 'notification',&#10; title: 'Nova notificação',&#10; body: 'Você tem uma nova mensagem',&#10; timestamp: new Date().toISOString()&#10; });&#10; }, 30000);&#10; &#10; ctx.ws.on('close', () =&gt; {&#10; clearInterval(interval);&#10; console.log(`Usuário ${userId} desconectado das notificações`);&#10; });&#10;});&#10;&#10;// ============================================&#10;// FRONTEND - Como usar WebSocket no React&#10;// ============================================&#10;&#10;// Em qualquer componente React&#10;import React from 'react';&#10;import { HightWSClient, useWebSocket } from 'hightjs/ws';&#10;&#10;// Método 1: Usando o hook useWebSocket (mais fácil)&#10;function ChatComponent() {&#10; const { client, isConnected, lastMessage, sendMessage } = useWebSocket('ws://localhost:8080/chat');&#10; const [messages, setMessages] = React.useState([]);&#10; const [newMessage, setNewMessage] = React.useState('');&#10;&#10; // Atualiza mensagens quando recebe uma nova&#10; React.useEffect(() =&gt; {&#10; if (lastMessage &amp;&amp; lastMessage.type === 'message') {&#10; setMessages(prev =&gt; [...prev, lastMessage]);&#10; }&#10; }, [lastMessage]);&#10;&#10; const handleSendMessage = () =&gt; {&#10; if (newMessage.trim() &amp;&amp; isConnected) {&#10; sendMessage({&#10; user: 'Seu Nome',&#10; text: newMessage&#10; });&#10; setNewMessage('');&#10; }&#10; };&#10;&#10; return (&#10; &lt;div&gt;&#10; &lt;div&gt;Status: {isConnected ? 'Conectado' : 'Desconectado'}&lt;/div&gt;&#10; &#10; &lt;div style={{ height: '300px', overflow: 'auto', border: '1px solid #ccc' }}&gt;&#10; {messages.map((msg, index) =&gt; (&#10; &lt;div key={index}&gt;&#10; &lt;strong&gt;{msg.user}:&lt;/strong&gt; {msg.text}&#10; &lt;small&gt; ({new Date(msg.timestamp).toLocaleTimeString()})&lt;/small&gt;&#10; &lt;/div&gt;&#10; ))}&#10; &lt;/div&gt;&#10; &#10; &lt;div&gt;&#10; &lt;input&#10; value={newMessage}&#10; onChange={(e) =&gt; setNewMessage(e.target.value)}&#10; onKeyPress={(e) =&gt; e.key === 'Enter' &amp;&amp; handleSendMessage()}&#10; placeholder=&quot;Digite sua mensagem...&quot;&#10; /&gt;&#10; &lt;button onClick={handleSendMessage} disabled={!isConnected}&gt;&#10; Enviar&#10; &lt;/button&gt;&#10; &lt;/div&gt;&#10; &lt;/div&gt;&#10; );&#10;}&#10;&#10;// Método 2: Usando a classe HightWSClient diretamente (mais controle)&#10;function NotificationsComponent() {&#10; const [client, setClient] = React.useState(null);&#10; const [notifications, setNotifications] = React.useState([]);&#10; const userId = '123'; // ID do usuário atual&#10;&#10; React.useEffect(() =&gt; {&#10; const wsClient = new HightWSClient(`ws://localhost:8080/notifications/${userId}?token=abc123`);&#10; &#10; wsClient.on('open', () =&gt; {&#10; console.log('Conectado às notificações');&#10; });&#10;&#10; wsClient.on('message', (data) =&gt; {&#10; if (data.type === 'notification') {&#10; setNotifications(prev =&gt; [data, ...prev.slice(0, 9)]); // Mantém só as 10 últimas&#10; &#10; // Pode mostrar notificação do browser também&#10; if (Notification.permission === 'granted') {&#10; new Notification(data.title, { body: data.body });&#10; }&#10; }&#10; });&#10;&#10; wsClient.on('close', () =&gt; {&#10; console.log('Desconectado das notificações');&#10; });&#10;&#10; setClient(wsClient);&#10;&#10; return () =&gt; {&#10; wsClient.close();&#10; };&#10; }, [userId]);&#10;&#10; return (&#10; &lt;div&gt;&#10; &lt;h3&gt;Notificações em tempo real&lt;/h3&gt;&#10; {notifications.map((notif, index) =&gt; (&#10; &lt;div key={index} style={{ padding: '10px', border: '1px solid #ddd', margin: '5px 0' }}&gt;&#10; &lt;strong&gt;{notif.title}&lt;/strong&gt;&#10; &lt;p&gt;{notif.body}&lt;/p&gt;&#10; &lt;small&gt;{new Date(notif.timestamp).toLocaleString()}&lt;/small&gt;&#10; &lt;/div&gt;&#10; ))}&#10; &lt;/div&gt;&#10; );&#10;}&#10;&#10;// ============================================&#10;// INICIANDO O SERVIDOR WEBSOCKET&#10;// ============================================&#10;&#10;// No seu arquivo principal de servidor (onde você inicia o HightJS)&#10;import { createWebSocketServer } from 'hightjs/ws';&#10;&#10;// Método 1: Servidor WebSocket independente&#10;const wsServer = createWebSocketServer();&#10;wsServer.start(8080); // Inicia na porta 8080&#10;&#10;// Método 2: Integrar com servidor HTTP existente (Express/Fastify)&#10;// Isso ainda precisa ser implementado nos adapters, mas a estrutura está pronta&#10;&#10;export default ChatComponent;" />
36
+ </PendingDiffInfo>
37
+ </value>
38
+ </entry>
39
+ <entry key="$PROJECT_DIR$/src/websocket/factory.ts">
40
+ <value>
41
+ <PendingDiffInfo>
42
+ <option name="filePath" value="$PROJECT_DIR$/src/websocket/factory.ts" />
43
+ <option name="updatedContent" value="import { HightWSServer } from './server';&#10;import { WebSocketServer } from '../types/websocket';&#10;&#10;/**&#10; * Cria uma nova instância do servidor WebSocket HightJS&#10; * @returns Uma nova instância do servidor WebSocket&#10; */&#10;export function createWebSocketServer(): WebSocketServer {&#10; return new HightWSServer();&#10;}&#10;&#10;/**&#10; * Instância global do servidor WebSocket (singleton)&#10; */&#10;let globalWSServer: WebSocketServer | null = null;&#10;&#10;/**&#10; * Retorna a instância global do servidor WebSocket ou cria uma nova&#10; * @returns A instância global do servidor WebSocket&#10; */&#10;export function getGlobalWebSocketServer(): WebSocketServer {&#10; if (!globalWSServer) {&#10; globalWSServer = new HightWSServer();&#10; }&#10; return globalWSServer;&#10;}&#10;&#10;/**&#10; * Helper para criar rotas WebSocket rapidamente&#10; * @param path Caminho da rota&#10; * @param handler Função handler da rota&#10; */&#10;export function ws(path: string, handler: (ctx: any) =&gt; void | Promise&lt;void&gt;) {&#10; const server = getGlobalWebSocketServer();&#10; server.route(path, handler);&#10; return server;&#10;}" />
44
+ </PendingDiffInfo>
45
+ </value>
46
+ </entry>
47
+ <entry key="$PROJECT_DIR$/src/websocket/index.ts">
48
+ <value>
49
+ <PendingDiffInfo>
50
+ <option name="filePath" value="$PROJECT_DIR$/src/websocket/index.ts" />
51
+ <option name="originalContent" value="&#10;&#10;" />
52
+ <option name="updatedContent" value="export { HightWSServer } from './server';&#10;export { WebSocketContext, WebSocketHandler, WebSocketRoute, WebSocketServer } from '../types/websocket';&#10;export { createWebSocketServer } from './factory';&#10;export { HightWSClient, useWebSocket } from './client';" />
53
+ </PendingDiffInfo>
54
+ </value>
55
+ </entry>
56
+ <entry key="$PROJECT_DIR$/src/websocket/server.ts">
57
+ <value>
58
+ <PendingDiffInfo>
59
+ <option name="filePath" value="$PROJECT_DIR$/src/websocket/server.ts" />
60
+ <option name="updatedContent" value="import { WebSocket, WebSocketServer as WSServer } from 'ws';&#10;import { IncomingMessage } from 'http';&#10;import { URL } from 'url';&#10;import { WebSocketContext, WebSocketHandler, WebSocketRoute, WebSocketServer } from '../types/websocket';&#10;import Console, { Colors } from '../api/console';&#10;&#10;export class HightWSServer implements WebSocketServer {&#10; private wss: WSServer | null = null;&#10; private routes: WebSocketRoute[] = [];&#10; private connections: Set&lt;WebSocket&gt; = new Set();&#10;&#10; constructor() {&#10; this.setupRoutePatterns();&#10; }&#10;&#10; route(path: string, handler: WebSocketHandler): void {&#10; const route: WebSocketRoute = {&#10; path,&#10; handler,&#10; ...this.createPathPattern(path)&#10; };&#10; this.routes.push(route);&#10; console.log(`${Colors.FgBlue}[WebSocket]${Colors.Reset} Route registered: ${Colors.FgYellow}${path}${Colors.Reset}`);&#10; }&#10;&#10; start(port: number = 8080): void {&#10; this.wss = new WSServer({ &#10; port,&#10; perMessageDeflate: false &#10; });&#10;&#10; console.log(`${Colors.FgGreen}✓${Colors.Reset} WebSocket server listening on port ${Colors.FgYellow}${port}${Colors.Reset}`);&#10;&#10; this.wss.on('connection', (ws: WebSocket, req: IncomingMessage) =&gt; {&#10; this.connections.add(ws);&#10; console.log(`${Colors.FgBlue}[WebSocket]${Colors.Reset} New connection from ${req.socket.remoteAddress}`);&#10;&#10; ws.on('close', () =&gt; {&#10; this.connections.delete(ws);&#10; console.log(`${Colors.FgBlue}[WebSocket]${Colors.Reset} Connection closed`);&#10; });&#10;&#10; ws.on('error', (error) =&gt; {&#10; console.error(`${Colors.FgRed}[WebSocket Error]${Colors.Reset}`, error);&#10; this.connections.delete(ws);&#10; });&#10;&#10; this.handleConnection(ws, req);&#10; });&#10;&#10; this.wss.on('error', (error) =&gt; {&#10; console.error(`${Colors.FgRed}[WebSocket Server Error]${Colors.Reset}`, error);&#10; });&#10; }&#10;&#10; broadcast(data: any): void {&#10; const message = typeof data === 'string' ? data : JSON.stringify(data);&#10; this.connections.forEach(ws =&gt; {&#10; if (ws.readyState === WebSocket.OPEN) {&#10; ws.send(message);&#10; }&#10; });&#10; }&#10;&#10; getConnections(): WebSocket[] {&#10; return Array.from(this.connections);&#10; }&#10;&#10; private handleConnection(ws: WebSocket, req: IncomingMessage): void {&#10; if (!req.url) return;&#10;&#10; const url = new URL(req.url, `http://${req.headers.host}`);&#10; const pathname = url.pathname;&#10;&#10; // Encontra a rota correspondente&#10; const matchedRoute = this.findMatchingRoute(pathname);&#10; if (!matchedRoute) {&#10; console.log(`${Colors.FgYellow}[WebSocket]${Colors.Reset} No route found for ${pathname}`);&#10; ws.close(1000, 'No route found');&#10; return;&#10; }&#10;&#10; // Extrai parâmetros da URL&#10; const params = this.extractParams(pathname, matchedRoute);&#10; const query = Object.fromEntries(url.searchParams.entries());&#10;&#10; // Cria o contexto&#10; const context: WebSocketContext = {&#10; ws,&#10; req,&#10; url,&#10; params,&#10; query,&#10; send: (data: any) =&gt; {&#10; if (ws.readyState === WebSocket.OPEN) {&#10; const message = typeof data === 'string' ? data : JSON.stringify(data);&#10; ws.send(message);&#10; }&#10; },&#10; close: (code?: number, reason?: string) =&gt; {&#10; ws.close(code || 1000, reason);&#10; },&#10; broadcast: (data: any, exclude?: WebSocket[]) =&gt; {&#10; const message = typeof data === 'string' ? data : JSON.stringify(data);&#10; const excludeSet = new Set(exclude || []);&#10; this.connections.forEach(connection =&gt; {&#10; if (connection.readyState === WebSocket.OPEN &amp;&amp; !excludeSet.has(connection)) {&#10; connection.send(message);&#10; }&#10; });&#10; }&#10; };&#10;&#10; try {&#10; matchedRoute.handler(context);&#10; } catch (error) {&#10; console.error(`${Colors.FgRed}[WebSocket Handler Error]${Colors.Reset}`, error);&#10; ws.close(1011, 'Internal server error');&#10; }&#10; }&#10;&#10; private createPathPattern(path: string): { pathPattern: RegExp; paramNames: string[] } {&#10; const paramNames: string[] = [];&#10; const pattern = path&#10; .replace(/\//g, '\\/')&#10; .replace(/:([^\/]+)/g, (_, paramName) =&gt; {&#10; paramNames.push(paramName);&#10; return '([^/]+)';&#10; })&#10; .replace(/\*/g, '.*');&#10;&#10; return {&#10; pathPattern: new RegExp(`^${pattern}$`),&#10; paramNames&#10; };&#10; }&#10;&#10; private findMatchingRoute(pathname: string): WebSocketRoute | null {&#10; return this.routes.find(route =&gt; {&#10; if (route.pathPattern) {&#10; return route.pathPattern.test(pathname);&#10; }&#10; return route.path === pathname;&#10; }) || null;&#10; }&#10;&#10; private extractParams(pathname: string, route: WebSocketRoute): Record&lt;string, string&gt; {&#10; const params: Record&lt;string, string&gt; = {};&#10; &#10; if (route.pathPattern &amp;&amp; route.paramNames) {&#10; const match = pathname.match(route.pathPattern);&#10; if (match) {&#10; route.paramNames.forEach((paramName, index) =&gt; {&#10; params[paramName] = match[index + 1];&#10; });&#10; }&#10; }&#10;&#10; return params;&#10; }&#10;&#10; private setupRoutePatterns(): void {&#10; // Reprocessa todos os patterns das rotas existentes&#10; this.routes.forEach(route =&gt; {&#10; const { pathPattern, paramNames } = this.createPathPattern(route.path);&#10; route.pathPattern = pathPattern;&#10; route.paramNames = paramNames;&#10; });&#10; }&#10;}" />
61
+ </PendingDiffInfo>
62
+ </value>
63
+ </entry>
64
+ </map>
65
+ </option>
66
+ </component>
67
+ </project>