stelar-time-real 3.2.0 → 3.2.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 (2) hide show
  1. package/README.md +429 -429
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # stelar-time-real v3
2
2
 
3
- **Librería en tiempo real para producción.** Zero dependencias. Protocolo binario TCP custom + WebSocket manual (RFC 6455 implementado desde cero). Sin paquetes externos.
3
+ **Real-time library for production.** Zero dependencies. Custom binary TCP protocol + Manual WebSocket (RFC 6455 implemented from scratch). No external packages.
4
4
 
5
5
  ![npm](https://img.shields.io/npm/v/stelar-time-real)
6
6
  ![license](https://img.shields.io/npm/l/stelar-time-real)
@@ -9,88 +9,88 @@
9
9
 
10
10
  ---
11
11
 
12
- ## Que es stelar-time-real?
12
+ ## What is stelar-time-real?
13
13
 
14
- stelar-time-real es una librería de comunicación en tiempo real diseñada desde cero para producción. No envuelve ni depende de ninguna librería externa implementa su propio protocolo binario TCP y su propio WebSocket (RFC 6455) usando exclusivamente los módulos built-in de Node.js (`http`, `net`, `crypto`, `tls`).
14
+ stelar-time-real is a real-time communication library designed from scratch for production. It does not wrap nor depend on any external libraryit implements its own binary TCP protocol and its own WebSocket (RFC 6455) using exclusively Node.js built-in modules (`http`, `net`, `crypto`, `tls`).
15
15
 
16
- Esto significa control total: sin dependencias que se rompan, sin vulnerabilidades de terceros, sin bloat, y sin sorpresas. Cada byte que viaja por la red es controlado por la librería.
16
+ This means total control: no dependencies that break, no third-party vulnerabilities, no bloat, and no surprises. Every byte that travels across the network is controlled by the library.
17
17
 
18
- ### Para que sirve?
18
+ ### What is it for?
19
19
 
20
- - Chat en tiempo real (mensajería, notificaciones, typing indicators)
21
- - Aplicaciones colaborativas (editores, pizarras, documentos compartidos)
22
- - Streaming de datos binarios (imágenes, archivos, audio, video)
23
- - Microservicios internos con comunicación ultra rápida via TCP
24
- - Dashboards en vivo (métricas, monitoreo, trading)
25
- - Juegos multijugador en tiempo real
26
- - Redes sociales, plataformas tipo Discord
27
- - IoT y dispositivos conectados
20
+ - Real-time chat (messaging, notifications, typing indicators)
21
+ - Collaborative applications (editors, whiteboards, shared documents)
22
+ - Binary data streaming (images, files, audio, video)
23
+ - Internal microservices with ultra-fast communication via TCP
24
+ - Live dashboards (metrics, monitoring, trading)
25
+ - Real-time multiplayer games
26
+ - Social networks, Discord-like platforms
27
+ - IoT and connected devices
28
28
 
29
29
  ---
30
30
 
31
- ## Características Principales
31
+ ## Main Features
32
32
 
33
- ### Protocolo Dual
33
+ ### Dual Protocol
34
34
 
35
- El servidor soporta **dos protocolos simultáneamente** en puertos diferentes:
35
+ The server supports **two protocols simultaneously** on different ports:
36
36
 
37
- - **WebSocket** — Para navegadores y clientes web. Implementación manual completa de RFC 6455: handshake, framing, masking, close codes, validación de RSV bits, max frame size enforcement.
38
- - **TCP Custom** — Para comunicación entre servidores y microservicios Node.js. Protocolo binario propio con overhead mínimo (7 bytes de header). Latencia ultra baja.
37
+ - **WebSocket** — For browsers and web clients. Complete manual implementation of RFC 6455: handshake, framing, masking, close codes, RSV bits validation, max frame size enforcement.
38
+ - **Custom TCP** — For server-to-server and Node.js microservices communication. Custom binary protocol with minimal overhead (7-byte header). Ultra-low latency.
39
39
 
40
- Ambos protocolos comparten la misma API del servidor. Un cliente WebSocket y un cliente TCP pueden estar en el mismo room, recibir los mismos broadcasts, e interactuar como si fueran el mismo tipo de conexión.
40
+ Both protocols share the same server API. A WebSocket client and a TCP client can be in the same room, receive the same broadcasts, and interact as if they were the same type of connection.
41
41
 
42
- ### Zero Dependencias
42
+ ### Zero Dependencies
43
43
 
44
44
  ```
45
45
  dependencies: {}
46
46
  ```
47
47
 
48
- No hay `ws`, no hay `engine.io`, no hay nada. Solo Node.js puro. Esto significa:
48
+ No `ws`, no `engine.io`, nothing. Just pure Node.js. This means:
49
49
 
50
- - Sin vulnerabilidades en dependencias de terceros
51
- - Sin breaking changes por actualizaciones ajenas
52
- - Sin supply chain attacks
53
- - Tamaño mínimo en node_modules
54
- - Instalación instantánea
50
+ - No vulnerabilities in third-party dependencies
51
+ - No breaking changes from external updates
52
+ - No supply chain attacks
53
+ - Minimal size in node_modules
54
+ - Instant installation
55
55
 
56
- ### Preparada para Producción
56
+ ### Production-Ready
57
57
 
58
- Cada feature fue diseñada pensando en un entorno real con usuarios, ataques, y errores:
58
+ Every feature was designed with a real environment in mind — with users, attacks, and errors:
59
59
 
60
- | Feature | Descripción |
60
+ | Feature | Description |
61
61
  |---------|-------------|
62
- | **Rate Limiting** | Token bucket por cliente. Limita cuántos mensajes puede enviar cada cliente por ventana de tiempo. Previene spam y abuso. |
63
- | **Per-IP Throttling** | Limita conexiones simultáneas desde la misma dirección IP. Previene ataques de fuerza bruta y bots. |
64
- | **Max Connections** | Límite global de conexiones concurrentes. El servidor rechaza nuevas conexiones cuando se alcanza el límite. |
65
- | **Max Rooms** | Límite global de rooms y límite por cliente. Previene que un solo cliente cree miles de rooms y consuma memoria. |
66
- | **Graceful Shutdown** | Captura SIGINT/SIGTERM, deja de aceptar conexiones nuevas, espera a que las existentes se cierren (con timeout configurable), y limpia todos los recursos. |
67
- | **Health Check** | Endpoint HTTP `/health` con estadísticas del servidor en vivo. Compatible con Kubernetes, Docker, y load balancers. |
68
- | **Server Metrics** | Método `getStats()` con: conexiones activas, mensajes enviados/recibidos, rooms, uptime, uso de memoria, entradas del rate limiter. |
69
- | **TLS/SSL** | Soporte nativo para `wss://` y TCP sobre TLS. Configuración simple con key y cert. |
70
- | **Origin Checking** | Whitelist de orígenes permitidos para conexiones WebSocket. Previene CSRF y cross-origin abuse. |
71
- | **CORS** | Headers CORS automáticos en el health endpoint con soporte para OPTIONS preflight. |
72
- | **Input Validation** | Validación de nombres de eventos (strings no vacíos), tamaño máximo de payload, tamaño máximo de frames. |
73
- | **Backpressure Handling** | Manejo del evento `drain` del socket. No se pierden datos cuando el buffer de red está lleno. |
74
- | **Message Queue** | En el cliente: cola de mensajes cuando está desconectado. Se envían automáticamente al reconectar. Tamaño configurable, descarta los más viejos si se llena. |
75
- | **Exponential Backoff** | Reconexión inteligente con backoff exponencial y jitter. Evita thundering herd cuando el servidor se reinicia. |
76
- | **O(1) Client Lookup** | Búsqueda de cliente por ID en tiempo constante usando un Map indexado. Escalable a decenas de miles de clientes. |
77
- | **No Signal Handler Leaks** | Los handlers de SIGINT/SIGTERM se limpian correctamente al hacer `stop()`. Múltiples instancias no causan `MaxListenersExceeded`. |
78
- | **Timer unref** | Todos los timers internos usan `.unref()`. No impiden que el proceso de Node.js termine naturalmente. |
79
- | **Custom Rate Limiter** | Interfaz `IRateLimiter` para reemplazar el rate limiter built-in con tu propia implementación (Redis, MongoDB, etc). |
80
- | **Custom IP Tracker** | Interfaz `IIPTracker` para reemplazar el tracker de IP built-in con tu propia lógica. |
81
- | **Custom Client ID** | Función `generateClientId` para generar IDs con tu propio formato. |
82
- | **Event Rate Limits** | Rate limits por evento individual. Cada evento puede tener su propio límite de mensajes. |
83
- | **Per-Client Rate Limits** | Rate limits por cliente individual con `setClientRateLimit()`. Override del límite global para clientes específicos. |
84
- | **Hook System** | Callbacks para cada evento del servidor: rate limit excedido, max connections, payload too large, join/leave room, etc. |
85
- | **Custom Health Handler** | Función `customHealthHandler` para reemplazar el health check built-in con tu propia lógica. |
86
- | **Runtime Config** | Método `updateConfig()` para cambiar la configuración del servidor en caliente, sin reiniciar. |
87
- | **Client Hooks** | Hooks en el cliente: `onBeforeEmit`, `onMessage`, `onStateChange`, `onReconnectDelay`, `onMessageQueued`, `onQueueDrained`, `onError`. |
88
- | **Custom Reconnect** | Función `customReconnectDelay` o hook `onReconnectDelay` para controlar la lógica de reconexión del cliente. |
89
- | **Client Runtime Config** | Método `updateOptions()` para cambiar la configuración del cliente en caliente. |
62
+ | **Rate Limiting** | Token bucket per client. Limits how many messages each client can send per time window. Prevents spam and abuse. |
63
+ | **Per-IP Throttling** | Limits simultaneous connections from the same IP address. Prevents brute force attacks and bots. |
64
+ | **Max Connections** | Global limit of concurrent connections. The server rejects new connections when the limit is reached. |
65
+ | **Max Rooms** | Global limit of rooms and per-client limit. Prevents a single client from creating thousands of rooms and consuming memory. |
66
+ | **Graceful Shutdown** | Captures SIGINT/SIGTERM, stops accepting new connections, waits for existing ones to close (with configurable timeout), and cleans up all resources. |
67
+ | **Health Check** | HTTP `/health` endpoint with live server statistics. Compatible with Kubernetes, Docker, and load balancers. |
68
+ | **Server Metrics** | `getStats()` method with: active connections, messages sent/received, rooms, uptime, memory usage, rate limiter entries. |
69
+ | **TLS/SSL** | Native support for `wss://` and TCP over TLS. Simple configuration with key and cert. |
70
+ | **Origin Checking** | Whitelist of allowed origins for WebSocket connections. Prevents CSRF and cross-origin abuse. |
71
+ | **CORS** | Automatic CORS headers on the health endpoint with support for OPTIONS preflight. |
72
+ | **Input Validation** | Validation of event names (non-empty strings), max payload size, max frame size. |
73
+ | **Backpressure Handling** | Handles the socket's `drain` event. No data is lost when the network buffer is full. |
74
+ | **Message Queue** | On the client: message queue when disconnected. Sent automatically upon reconnection. Configurable size, discards oldest if full. |
75
+ | **Exponential Backoff** | Smart reconnection with exponential backoff and jitter. Prevents thundering herd when the server restarts. |
76
+ | **O(1) Client Lookup** | Client lookup by ID in constant time using an indexed Map. Scalable to tens of thousands of clients. |
77
+ | **No Signal Handler Leaks** | SIGINT/SIGTERM handlers are properly cleaned up when calling `stop()`. Multiple instances don't cause `MaxListenersExceeded`. |
78
+ | **Timer unref** | All internal timers use `.unref()`. They don't prevent the Node.js process from terminating naturally. |
79
+ | **Custom Rate Limiter** | `IRateLimiter` interface to replace the built-in rate limiter with your own implementation (Redis, MongoDB, etc). |
80
+ | **Custom IP Tracker** | `IIPTracker` interface to replace the built-in IP tracker with your own logic. |
81
+ | **Custom Client ID** | `generateClientId` function to generate IDs with your own format. |
82
+ | **Event Rate Limits** | Rate limits per individual event. Each event can have its own message limit. |
83
+ | **Per-Client Rate Limits** | Rate limits per individual client with `setClientRateLimit()`. Override the global limit for specific clients. |
84
+ | **Hook System** | Callbacks for every server event: rate limit exceeded, max connections, payload too large, join/leave room, etc. |
85
+ | **Custom Health Handler** | `customHealthHandler` function to replace the built-in health check with your own logic. |
86
+ | **Runtime Config** | `updateConfig()` method to change server configuration on the fly, without restarting. |
87
+ | **Client Hooks** | Hooks on the client: `onBeforeEmit`, `onMessage`, `onStateChange`, `onReconnectDelay`, `onMessageQueued`, `onQueueDrained`, `onError`. |
88
+ | **Custom Reconnect** | `customReconnectDelay` function or `onReconnectDelay` hook to control client reconnection logic. |
89
+ | **Client Runtime Config** | `updateOptions()` method to change client configuration on the fly. |
90
90
 
91
91
  ---
92
92
 
93
- ## Instalación
93
+ ## Installation
94
94
 
95
95
  ```bash
96
96
  npm install stelar-time-real
@@ -100,7 +100,7 @@ npm install stelar-time-real
100
100
 
101
101
  ## Quick Start
102
102
 
103
- ### Servidor basico
103
+ ### Basic Server
104
104
 
105
105
  ```javascript
106
106
  import express from 'express';
@@ -112,8 +112,8 @@ const server = app.listen(3000);
112
112
  const stelar = new StelarServer({ server });
113
113
 
114
114
  stelar.onConnection((client) => {
115
- console.log('Conectado:', client.id);
116
- client.emit('welcome', { message: 'Bienvenido al servidor!' });
115
+ console.log('Connected:', client.id);
116
+ client.emit('welcome', { message: 'Welcome to the server!' });
117
117
  });
118
118
 
119
119
  stelar.on('chat', (ctx) => {
@@ -123,7 +123,7 @@ stelar.on('chat', (ctx) => {
123
123
  await stelar.start();
124
124
  ```
125
125
 
126
- ### Servidor con configuracion de produccion
126
+ ### Server with Production Configuration
127
127
 
128
128
  ```javascript
129
129
  import express from 'express';
@@ -145,25 +145,25 @@ const stelar = new StelarServer({
145
145
  heartbeatTimeout: 60000,
146
146
  gracefulShutdown: true,
147
147
  shutdownTimeout: 10000,
148
- allowedOrigins: ['https://midominio.com'],
148
+ allowedOrigins: ['https://mydomain.com'],
149
149
  logger: 'info',
150
150
  });
151
151
 
152
- // Middleware de autenticación
152
+ // Authentication middleware
153
153
  stelar.use((ctx, next) => {
154
154
  const token = ctx.req?.headers?.authorization;
155
- if (!token) return ctx.ack('error', { message: 'Token requerido' });
155
+ if (!token) return ctx.ack('error', { message: 'Token required' });
156
156
  next();
157
157
  });
158
158
 
159
159
  stelar.onConnection((client) => {
160
- console.log(`[${client.protocol}] Cliente conectado: ${client.id} desde ${client.remoteAddress}`);
160
+ console.log(`[${client.protocol}] Client connected: ${client.id} from ${client.remoteAddress}`);
161
161
  client.setMetadata('role', 'user');
162
162
  client.emit('welcome', { id: client.id });
163
163
  });
164
164
 
165
165
  stelar.onDisconnect((client) => {
166
- console.log('Cliente desconectado:', client.id);
166
+ console.log('Client disconnected:', client.id);
167
167
  });
168
168
 
169
169
  stelar.on('chat', (ctx) => {
@@ -171,14 +171,14 @@ stelar.on('chat', (ctx) => {
171
171
  });
172
172
 
173
173
  stelar.onAck('getUser', (ctx) => {
174
- return { id: ctx.data.id, name: 'Juan', role: ctx.getMetadata('role') };
174
+ return { id: ctx.data.id, name: 'John', role: ctx.getMetadata('role') };
175
175
  });
176
176
 
177
177
  await stelar.start();
178
- console.log('Servidor listo en puerto', stelar.getPort());
178
+ console.log('Server ready on port', stelar.getPort());
179
179
  ```
180
180
 
181
- ### Cliente (Navegador o Node.js)
181
+ ### Client (Browser or Node.js)
182
182
 
183
183
  ```javascript
184
184
  import { StelarClient } from 'stelar-time-real';
@@ -192,29 +192,29 @@ const client = new StelarClient('localhost:3000', {
192
192
  messageQueueSize: 100,
193
193
  });
194
194
 
195
- client.on('connect', () => console.log('Conectado!'));
196
- client.on('disconnect', () => console.log('Desconectado'));
197
- client.on('welcome', (data) => console.log('Bienvenido:', data));
195
+ client.on('connect', () => console.log('Connected!'));
196
+ client.on('disconnect', () => console.log('Disconnected'));
197
+ client.on('welcome', (data) => console.log('Welcome:', data));
198
198
 
199
199
  client.connect();
200
200
 
201
- // Enviar mensaje
202
- client.emit('chat', { message: 'Hola a todos!' });
201
+ // Send message
202
+ client.emit('chat', { message: 'Hello everyone!' });
203
203
 
204
- // Request-response con Promise
204
+ // Request-response with Promise
205
205
  const user = await client.request('getUser', { id: 1 }, 'getUser');
206
- console.log(user); // { id: 1, name: 'Juan', role: 'user' }
206
+ console.log(user); // { id: 1, name: 'John', role: 'user' }
207
207
 
208
- // Unirse a rooms
208
+ // Join rooms
209
209
  client.joinRoom('general');
210
210
  client.joinRoom('random');
211
211
 
212
- // Enviar binario
213
- const buffer = Buffer.from('datos binarios');
212
+ // Send binary
213
+ const buffer = Buffer.from('binary data');
214
214
  client.emitBinary('file', buffer);
215
215
  ```
216
216
 
217
- ### Cliente con modo TCP (Node.js unicamentemaxima eficiencia)
217
+ ### Client with TCP Mode (Node.js onlymaximum efficiency)
218
218
 
219
219
  ```javascript
220
220
  const client = new StelarClient('localhost:3001', {
@@ -222,30 +222,30 @@ const client = new StelarClient('localhost:3001', {
222
222
  reconnection: true,
223
223
  });
224
224
 
225
- client.on('connect', () => console.log('TCP conectado!'));
225
+ client.on('connect', () => console.log('TCP connected!'));
226
226
  client.connect();
227
227
  ```
228
228
 
229
- El modo TCP usa el protocolo binario custom en vez de WebSocket. Menos overhead, menor latencia, ideal para comunicación entre servidores.
229
+ TCP mode uses the custom binary protocol instead of WebSocket. Less overhead, lower latency, ideal for server-to-server communication.
230
230
 
231
- ### Cliente con TLS/WSS (conexiones seguras)
231
+ ### Client with TLS/WSS (secure connections)
232
232
 
233
233
  ```javascript
234
- // WSS — WebSocket seguro
235
- const client = new StelarClient('wss://secure.midominio.com', {
234
+ // WSS — Secure WebSocket
235
+ const client = new StelarClient('wss://secure.mydomain.com', {
236
236
  tls: true,
237
237
  rejectUnauthorized: true,
238
238
  });
239
239
 
240
240
  // TCP + TLS
241
- const client = new StelarClient('secure.midominio.com:3001', {
241
+ const client = new StelarClient('secure.mydomain.com:3001', {
242
242
  mode: 'tcp',
243
243
  tls: true,
244
244
  rejectUnauthorized: true,
245
245
  });
246
246
  ```
247
247
 
248
- ### Servidor con TLS
248
+ ### Server with TLS
249
249
 
250
250
  ```javascript
251
251
  import { readFileSync } from 'fs';
@@ -262,45 +262,45 @@ const stelar = new StelarServer({
262
262
 
263
263
  ---
264
264
 
265
- ## Arquitectura
265
+ ## Architecture
266
266
 
267
- ### Protocolo Dual
267
+ ### Dual Protocol
268
268
 
269
269
  ```
270
- Servidor stelar-time-real
270
+ stelar-time-real Server
271
271
  ┌──────────────────────────┐
272
272
  │ │
273
- Navegadores ──────► Puerto 3000 (WebSocket) │
273
+ Browsers ──────► Port 3000 (WebSocket) │
274
274
  (ws://) │ │ │
275
- Misma lógica
276
- Node.js ──────► Puerto 3001 (TCP Custom) │
277
- (modo tcp) │ │ │
275
+ Same logic
276
+ Node.js ──────► │ Port 3001 (Custom TCP) │
277
+ (tcp mode) │ │ │
278
278
  │ │
279
279
  └──────────────────────────┘
280
280
  ```
281
281
 
282
- Ambos protocolos comparten:
283
- - Mismos event handlers
284
- - Mismos rooms
285
- - Mismo sistema de broadcast
286
- - Mismo sistema de ACK
287
- - Mismo middleware
288
- - Mismas métricas
282
+ Both protocols share:
283
+ - Same event handlers
284
+ - Same rooms
285
+ - Same broadcast system
286
+ - Same ACK system
287
+ - Same middleware
288
+ - Same metrics
289
289
 
290
- Un cliente WebSocket y un cliente TCP pueden estar en el mismo room y comunicarse sin problemas.
290
+ A WebSocket client and a TCP client can be in the same room and communicate without issues.
291
291
 
292
292
  ### WebSocket Mode vs TCP Mode
293
293
 
294
- | Aspecto | WebSocket | TCP Custom |
294
+ | Aspect | WebSocket | Custom TCP |
295
295
  |---------|-----------|------------|
296
- | Navegador | Si | No |
297
- | Node.js | Si | Si |
298
- | Overhead por frame | 2-14 bytes (RFC 6455) | 7 bytes (header custom) |
299
- | Latencia | Baja | Ultra baja |
300
- | TLS | wss:// | TLS nativo |
301
- | Caso de uso | Frontend, apps web | Microservicios, backend |
296
+ | Browser | Yes | No |
297
+ | Node.js | Yes | Yes |
298
+ | Overhead per frame | 2-14 bytes (RFC 6455) | 7 bytes (custom header) |
299
+ | Latency | Low | Ultra low |
300
+ | TLS | wss:// | Native TLS |
301
+ | Use case | Frontend, web apps | Microservices, backend |
302
302
 
303
- ### Formato del Protocolo Binario (TCP)
303
+ ### Binary Protocol Format (TCP)
304
304
 
305
305
  ```
306
306
  ┌──────────────┬──────────┬───────────────┬──────────────┬──────────────┐
@@ -309,267 +309,267 @@ Un cliente WebSocket y un cliente TCP pueden estar en el mismo room y comunicars
309
309
  └──────────────┴──────────┴───────────────┴──────────────┴──────────────┘
310
310
  ```
311
311
 
312
- **11 tipos de frame:**
312
+ **11 frame types:**
313
313
 
314
- | Tipo | Código | Descripción |
314
+ | Type | Code | Description |
315
315
  |------|--------|-------------|
316
- | JSON | 0 | Evento con payload JSON |
317
- | Binary | 1 | Datos binarios puros |
318
- | Ping | 2 | Heartbeat del cliente |
319
- | Pong | 3 | Respuesta del servidor |
320
- | ACK Request | 4 | Petición que espera respuesta |
321
- | ACK Response | 5 | Respuesta a una petición ACK |
322
- | Connect | 6 | Frame de conexión inicial |
323
- | Disconnect | 7 | Frame de desconexión |
324
- | Join Room | 8 | Unirse a un room |
325
- | Leave Room | 9 | Salir de un room |
326
- | Error | 10 | Frame de error |
327
-
328
- ### WebSocket Manual (RFC 6455)
329
-
330
- stelar-time-real implementa WebSocket desde cero usando solo `http` y `crypto` de Node.js. No usa la librería `ws` ni ninguna otra.
331
-
332
- La implementación incluye:
333
- - **Handshake** — Calcula el Sec-WebSocket-Accept con SHA-1 según RFC 6455
334
- - **Framing** — Parseo y creación de frames (text, binary, ping, pong, close)
335
- - **Masking** — Aplica/desaplica XOR mask (requerido clienteservidor)
336
- - **Fragmentación** — Manejo de frames fragmentados
337
- - **Close codes** — Todos los códigos de cierre soportados
338
- - **Validación** — RSV bits, opcode validation, max frame size
339
- - **PING/PONG** — El servidor responde PONG a PING correctamente
316
+ | JSON | 0 | Event with JSON payload |
317
+ | Binary | 1 | Pure binary data |
318
+ | Ping | 2 | Client heartbeat |
319
+ | Pong | 3 | Server response |
320
+ | ACK Request | 4 | Request expecting response |
321
+ | ACK Response | 5 | Response to an ACK request |
322
+ | Connect | 6 | Initial connection frame |
323
+ | Disconnect | 7 | Disconnection frame |
324
+ | Join Room | 8 | Join a room |
325
+ | Leave Room | 9 | Leave a room |
326
+ | Error | 10 | Error frame |
327
+
328
+ ### Manual WebSocket (RFC 6455)
329
+
330
+ stelar-time-real implements WebSocket from scratch using only Node.js `http` and `crypto`. It doesn't use the `ws` library or any other.
331
+
332
+ The implementation includes:
333
+ - **Handshake** — Calculates Sec-WebSocket-Accept with SHA-1 per RFC 6455
334
+ - **Framing** — Frame parsing and creation (text, binary, ping, pong, close)
335
+ - **Masking** — Applies/removes XOR mask (required clientserver)
336
+ - **Fragmentation** — Fragmented frame handling
337
+ - **Close codes** — All close codes supported
338
+ - **Validation** — RSV bits, opcode validation, max frame size
339
+ - **PING/PONG** — Server responds PONG to PING correctly
340
340
 
341
341
  ---
342
342
 
343
- ## API Completa
343
+ ## Complete API
344
344
 
345
- ### StelarServer — Opciones
345
+ ### StelarServer — Options
346
346
 
347
347
  ```javascript
348
348
  new StelarServer({
349
- // Conexión
350
- port: 3000, // Puerto HTTP/WebSocket
351
- server: httpServer, // Servidor HTTP existente (alternativa a port)
352
- namespace: '/', // Path del namespace
353
- tcpPort: 3001, // Puerto TCP (false = deshabilitado)
354
-
355
- // Límites
356
- maxConnections: 10000, // Máximo de conexiones concurrentes
357
- maxConnectionsPerIP: 50, // Máximo de conexiones por dirección IP
358
- maxRooms: 10000, // Máximo de rooms globales
359
- maxRoomsPerClient: 50, // Máximo de rooms por cliente
360
- maxPayloadSize: 10 * 1024 * 1024, // Tamaño máximo de payload (10MB)
361
- maxFrameSize: 10 * 1024 * 1024, // Tamaño máximo de frame WebSocket (10MB)
349
+ // Connection
350
+ port: 3000, // HTTP/WebSocket port
351
+ server: httpServer, // Existing HTTP server (alternative to port)
352
+ namespace: '/', // Namespace path
353
+ tcpPort: 3001, // TCP port (false = disabled)
354
+
355
+ // Limits
356
+ maxConnections: 10000, // Maximum concurrent connections
357
+ maxConnectionsPerIP: 50, // Maximum connections per IP address
358
+ maxRooms: 10000, // Maximum global rooms
359
+ maxRoomsPerClient: 50, // Maximum rooms per client
360
+ maxPayloadSize: 10 * 1024 * 1024, // Maximum payload size (10MB)
361
+ maxFrameSize: 10 * 1024 * 1024, // Maximum WebSocket frame size (10MB)
362
362
 
363
363
  // Rate Limiting
364
364
  rateLimit: {
365
- maxPoints: 100, // Máximo de puntos (mensajes) por ventana
366
- windowMs: 1000, // Ventana de tiempo en milisegundos
365
+ maxPoints: 100, // Maximum points (messages) per window
366
+ windowMs: 1000, // Time window in milliseconds
367
367
  },
368
368
 
369
369
  // Timeouts
370
- heartbeatInterval: 30000, // Intervalo de ping (30s)
371
- heartbeatTimeout: 60000, // Timeout antes de desconectar (60s)
372
- connectTimeout: 10000, // Timeout de conexión inicial (10s)
370
+ heartbeatInterval: 30000, // Ping interval (30s)
371
+ heartbeatTimeout: 60000, // Timeout before disconnecting (60s)
372
+ connectTimeout: 10000, // Initial connection timeout (10s)
373
373
 
374
- // Producción
375
- healthEndpoint: '/health', // URL del health check (false = deshabilitado)
376
- gracefulShutdown: true, // Capturar SIGINT/SIGTERM
377
- shutdownTimeout: 10000, // Tiempo máximo de espera al cerrar (10s)
378
- allowedOrigins: ['https://midominio.com'], // Orígenes permitidos (null = todos)
379
- tls: { key, cert }, // Opciones TLS para wss:// y TCP TLS
374
+ // Production
375
+ healthEndpoint: '/health', // Health check URL (false = disabled)
376
+ gracefulShutdown: true, // Capture SIGINT/SIGTERM
377
+ shutdownTimeout: 10000, // Maximum wait time when closing (10s)
378
+ allowedOrigins: ['https://mydomain.com'], // Allowed origins (null = all)
379
+ tls: { key, cert }, // TLS options for wss:// and TCP TLS
380
380
 
381
381
  // Logging
382
- logger: 'info', // Nivel: 'debug'|'info'|'warn'|'error'|'silent'
383
- // También acepta instancia de Logger o false
382
+ logger: 'info', // Level: 'debug'|'info'|'warn'|'error'|'silent'
383
+ // Also accepts Logger instance or false
384
384
  });
385
385
  ```
386
386
 
387
- ### StelarServer — Métodos
387
+ ### StelarServer — Methods
388
388
 
389
- #### Eventos
389
+ #### Events
390
390
 
391
- | Método | Descripción |
391
+ | Method | Description |
392
392
  |--------|-------------|
393
- | `.on(event, handler)` | Escuchar eventos de clientes |
394
- | `.onAll(handler)` | Escuchar todos los eventos |
395
- | `.onConnection(handler)` | Cliente conectado |
396
- | `.onDisconnect(handler)` | Cliente desconectado |
397
- | `.onAck(name, handler)` | Registrar handler ACK (retorna valor al cliente) |
393
+ | `.on(event, handler)` | Listen to client events |
394
+ | `.onAll(handler)` | Listen to all events |
395
+ | `.onConnection(handler)` | Client connected |
396
+ | `.onDisconnect(handler)` | Client disconnected |
397
+ | `.onAck(name, handler)` | Register ACK handler (returns value to client) |
398
398
 
399
- #### Envío de mensajes
399
+ #### Message Sending
400
400
 
401
- | Método | Descripción |
401
+ | Method | Description |
402
402
  |--------|-------------|
403
- | `.broadcast(event, data, excludeId?)` | Enviar a todos los clientes (opcionalmente excluir uno) |
404
- | `.to(room, event, data, excludeId?)` | Enviar a un room (opcionalmente excluir) |
405
- | `.toId(id, event, data)` | Enviar a un cliente específico búsqueda O(1) |
406
- | `.broadcastBinary(event, buffer)` | Broadcast de datos binarios |
403
+ | `.broadcast(event, data, excludeId?)` | Send to all clients (optionally exclude one) |
404
+ | `.to(room, event, data, excludeId?)` | Send to a room (optionally exclude) |
405
+ | `.toId(id, event, data)` | Send to a specific client — O(1) lookup |
406
+ | `.broadcastBinary(event, buffer)` | Broadcast binary data |
407
407
 
408
- #### Información
408
+ #### Information
409
409
 
410
- | Método | Descripción |
410
+ | Method | Description |
411
411
  |--------|-------------|
412
- | `.getClients(room?)` | Lista de clientes con sus rooms |
413
- | `.getRoomMembers(room)` | IDs de clientes en un room |
414
- | `.getRooms()` | Lista de rooms activos |
415
- | `.getStats()` | Estadísticas del servidor |
416
- | `.getPort()` | Puerto en el que corre el servidor |
412
+ | `.getClients(room?)` | Client list with their rooms |
413
+ | `.getRoomMembers(room)` | Client IDs in a room |
414
+ | `.getRooms()` | List of active rooms |
415
+ | `.getStats()` | Server statistics |
416
+ | `.getPort()` | Port the server is running on |
417
417
 
418
418
  #### Lifecycle
419
419
 
420
- | Método | Descripción |
420
+ | Method | Description |
421
421
  |--------|-------------|
422
- | `.use(middleware)` | Agregar middleware de conexión |
423
- | `.start(callback?)` | Iniciar servidor, retorna `Promise<number>` con el puerto |
424
- | `.stop()` | Detener servidor, cerrar conexiones, limpiar handlers |
422
+ | `.use(middleware)` | Add connection middleware |
423
+ | `.start(callback?)` | Start server, returns `Promise<number>` with the port |
424
+ | `.stop()` | Stop server, close connections, clean up handlers |
425
425
 
426
- ### StelarContext (ctx) — Dentro de los handlers
426
+ ### StelarContext (ctx) — Inside handlers
427
427
 
428
- Cada handler de evento recibe un contexto (`ctx`) con toda la información y acciones disponibles:
428
+ Every event handler receives a context (`ctx`) with all available information and actions:
429
429
 
430
430
  ```javascript
431
431
  stelar.on('message', (ctx) => {
432
- // Información del cliente
433
- ctx.id // ID único del cliente
434
- ctx.socket // net.Socket crudo
435
- ctx.req // HTTP request (null para TCP)
436
- ctx.data // Datos recibidos
437
- ctx.clientInfo // Info del cliente
438
- ctx.clientInfo.rooms // Set de rooms del cliente
439
- ctx.clientInfo.metadata // Map de metadata custom
440
- ctx.clientInfo.remoteAddress // Dirección IP del cliente
441
- ctx.clientInfo.protocol // 'ws' o 'tcp'
442
-
443
- // AccionesEnviar mensajes
444
- ctx.emit('event', data) // Enviar a este cliente
445
- ctx.send('response', data) // Responder a ACK
446
- ctx.emitBinary('event', buffer) // Enviar binario
447
- ctx.broadcast('event', data) // Enviar a todos (excluyéndose)
448
- ctx.broadcastBinary('event', buf) // Broadcast binario
449
- ctx.to('room', 'event', data) // Enviar a un room
450
- ctx.toId('id', 'event', data) // Enviar a cliente específico (O(1))
451
-
452
- // Acciones — Rooms
453
- ctx.joinRoom('room') // Unirse a un room
454
- ctx.leaveRoom('room') // Salir de un room
455
- ctx.getClients('room') // Listar clientes del room
456
-
457
- // Acciones — Metadata
458
- ctx.setMetadata('role', 'admin') // Guardar dato custom
459
- ctx.getMetadata('role') // Leer dato custom
460
-
461
- // Acciones — ACK
462
- ctx.ack('myAck', data) // Responder a una petición ACK
432
+ // Client information
433
+ ctx.id // Unique client ID
434
+ ctx.socket // Raw net.Socket
435
+ ctx.req // HTTP request (null for TCP)
436
+ ctx.data // Received data
437
+ ctx.clientInfo // Client info
438
+ ctx.clientInfo.rooms // Client's room Set
439
+ ctx.clientInfo.metadata // Custom metadata Map
440
+ ctx.clientInfo.remoteAddress // Client's IP address
441
+ ctx.clientInfo.protocol // 'ws' or 'tcp'
442
+
443
+ // ActionsSend messages
444
+ ctx.emit('event', data) // Send to this client
445
+ ctx.send('response', data) // Respond to ACK
446
+ ctx.emitBinary('event', buffer) // Send binary
447
+ ctx.broadcast('event', data) // Send to all (excluding self)
448
+ ctx.broadcastBinary('event', buf) // Binary broadcast
449
+ ctx.to('room', 'event', data) // Send to a room
450
+ ctx.toId('id', 'event', data) // Send to specific client (O(1))
451
+
452
+ // Actions — Rooms
453
+ ctx.joinRoom('room') // Join a room
454
+ ctx.leaveRoom('room') // Leave a room
455
+ ctx.getClients('room') // List room clients
456
+
457
+ // Actions — Metadata
458
+ ctx.setMetadata('role', 'admin') // Store custom data
459
+ ctx.getMetadata('role') // Read custom data
460
+
461
+ // Actions — ACK
462
+ ctx.ack('myAck', data) // Respond to an ACK request
463
463
  });
464
464
  ```
465
465
 
466
- ### StelarClient — Opciones
466
+ ### StelarClient — Options
467
467
 
468
468
  ```javascript
469
469
  new StelarClient(urlOrPort, {
470
- // Conexión
471
- reconnection: true, // Auto reconectar
472
- reconnectionAttempts: 10, // Máximo de intentos
473
- reconnectionDelay: 1000, // Delay base (ms)
474
- maxReconnectionDelay: 30000, // Delay máximo (ms)
475
- heartbeatInterval: 30000, // Intervalo de heartbeat
476
-
477
- // Protocolo
478
- mode: 'ws', // 'ws' o 'tcp'
470
+ // Connection
471
+ reconnection: true, // Auto reconnect
472
+ reconnectionAttempts: 10, // Maximum attempts
473
+ reconnectionDelay: 1000, // Base delay (ms)
474
+ maxReconnectionDelay: 30000, // Maximum delay (ms)
475
+ heartbeatInterval: 30000, // Heartbeat interval
476
+
477
+ // Protocol
478
+ mode: 'ws', // 'ws' or 'tcp'
479
479
  maxPayloadSize: 10 * 1024 * 1024,
480
480
  maxFrameSize: 10 * 1024 * 1024,
481
481
 
482
482
  // ACK
483
- ackTimeout: 5000, // Timeout de ACK (ms)
483
+ ackTimeout: 5000, // ACK timeout (ms)
484
484
 
485
- // Cola de mensajes
486
- messageQueueSize: 100, // Mensajes en cola cuando está desconectado
485
+ // Message queue
486
+ messageQueueSize: 100, // Queued messages when disconnected
487
487
 
488
- // Seguridad
489
- tls: false, // Habilitar TLS para wss:// o TCP TLS
490
- rejectUnauthorized: true, // Validar certificado TLS
488
+ // Security
489
+ tls: false, // Enable TLS for wss:// or TCP TLS
490
+ rejectUnauthorized: true, // Validate TLS certificate
491
491
 
492
- // Headers custom
493
- headers: {}, // Headers para el handshake WebSocket
492
+ // Custom headers
493
+ headers: {}, // Headers for WebSocket handshake
494
494
 
495
495
  // Logging
496
- logger: 'warn', // Nivel de log
496
+ logger: 'warn', // Log level
497
497
  });
498
498
  ```
499
499
 
500
- ### StelarClient — Métodos
500
+ ### StelarClient — Methods
501
501
 
502
- #### Eventos
502
+ #### Events
503
503
 
504
- | Método | Descripción |
504
+ | Method | Description |
505
505
  |--------|-------------|
506
- | `.on(event, handler)` | Escuchar evento |
507
- | `.off(event, handler)` | Remover listener |
508
- | `.once(event, handler)` | Escuchar una sola vez |
509
- | `.onAll(handler)` | Escuchar todos los eventos |
510
- | `.onAck(name, handler)` | Escuchar respuestas ACK |
506
+ | `.on(event, handler)` | Listen to event |
507
+ | `.off(event, handler)` | Remove listener |
508
+ | `.once(event, handler)` | Listen once |
509
+ | `.onAll(handler)` | Listen to all events |
510
+ | `.onAck(name, handler)` | Listen to ACK responses |
511
511
 
512
- #### Envío
512
+ #### Sending
513
513
 
514
- | Método | Descripción |
514
+ | Method | Description |
515
515
  |--------|-------------|
516
- | `.emit(event, data, opts?)` | Enviar evento (`opts.ack` para ACK) |
517
- | `.emitBinary(event, data)` | Enviar datos binarios |
518
- | `.sendFile(file)` | Enviar archivo |
519
- | `.sendImage(blob)` | Enviar imagen |
520
- | `.request(event, data, ackName)` | Request-response con Promise |
516
+ | `.emit(event, data, opts?)` | Send event (`opts.ack` for ACK) |
517
+ | `.emitBinary(event, data)` | Send binary data |
518
+ | `.sendFile(file)` | Send file |
519
+ | `.sendImage(blob)` | Send image |
520
+ | `.request(event, data, ackName)` | Request-response with Promise |
521
521
 
522
522
  #### Rooms
523
523
 
524
- | Método | Descripción |
524
+ | Method | Description |
525
525
  |--------|-------------|
526
- | `.joinRoom(room)` | Unirse a un room |
527
- | `.leaveRoom(room)` | Salir de un room |
526
+ | `.joinRoom(room)` | Join a room |
527
+ | `.leaveRoom(room)` | Leave a room |
528
528
 
529
529
  #### Lifecycle
530
530
 
531
- | Método | Descripción |
531
+ | Method | Description |
532
532
  |--------|-------------|
533
- | `.connect(callback?)` | Conectar al servidor |
534
- | `.disconnect()` | Desconectar y limpiar todos los recursos |
533
+ | `.connect(callback?)` | Connect to server |
534
+ | `.disconnect()` | Disconnect and clean up all resources |
535
535
 
536
- #### Estado y métricas
536
+ #### State and Metrics
537
537
 
538
- | Método | Descripción |
538
+ | Method | Description |
539
539
  |--------|-------------|
540
- | `.isConnected()` | Está conectado? |
541
- | `.getState()` | Estado: `'disconnected'` \| `'connecting'` \| `'connected'` \| `'reconnecting'` |
542
- | `.getId()` | ID asignado por el servidor |
543
- | `.getUrl()` | URL del servidor |
544
- | `.setUrl(url)` | Cambiar URL antes de conectar |
545
- | `.getMessagesSent()` | Total de mensajes enviados |
546
- | `.getMessagesReceived()` | Total de mensajes recibidos |
547
- | `.getLastError()` | Último error |
548
- | `.getConnectTime()` | Timestamp de la última conexión exitosa |
549
- | `.getQueueSize()` | Mensajes pendientes en la cola |
550
- | `.removeAllListeners(event?)` | Limpiar listeners |
551
-
552
- ### Eventos del Cliente
540
+ | `.isConnected()` | Is connected? |
541
+ | `.getState()` | State: `'disconnected'` \| `'connecting'` \| `'connected'` \| `'reconnecting'` |
542
+ | `.getId()` | ID assigned by the server |
543
+ | `.getUrl()` | Server URL |
544
+ | `.setUrl(url)` | Change URL before connecting |
545
+ | `.getMessagesSent()` | Total messages sent |
546
+ | `.getMessagesReceived()` | Total messages received |
547
+ | `.getLastError()` | Last error |
548
+ | `.getConnectTime()` | Timestamp of last successful connection |
549
+ | `.getQueueSize()` | Pending messages in queue |
550
+ | `.removeAllListeners(event?)` | Clear listeners |
551
+
552
+ ### Client Events
553
553
 
554
554
  ```javascript
555
555
  client.on('connect', () => {
556
- // Conexión establecida
556
+ // Connection established
557
557
  });
558
558
 
559
559
  client.on('disconnect', (info) => {
560
- // info = { code, reason } para WebSocket
560
+ // info = { code, reason } for WebSocket
561
561
  });
562
562
 
563
563
  client.on('reconnecting', (attempt) => {
564
- // Intento número `attempt` de reconexión
564
+ // Reconnection attempt number `attempt`
565
565
  });
566
566
 
567
567
  client.on('reconnect_failed', () => {
568
- // Se agotaron los intentos de reconexión
568
+ // Reconnection attempts exhausted
569
569
  });
570
570
 
571
571
  client.on('error', (err) => {
572
- // Error de conexión o protocolo
572
+ // Connection or protocol error
573
573
  });
574
574
  ```
575
575
 
@@ -577,13 +577,13 @@ client.on('error', (err) => {
577
577
 
578
578
  ## Health Check
579
579
 
580
- El endpoint de health check está diseñado para integrarse con orquestadores como Kubernetes, Docker Swarm, o cualquier load balancer.
580
+ The health check endpoint is designed to integrate with orchestrators like Kubernetes, Docker Swarm, or any load balancer.
581
581
 
582
582
  ```bash
583
583
  curl http://localhost:3000/health
584
584
  ```
585
585
 
586
- Respuesta:
586
+ Response:
587
587
 
588
588
  ```json
589
589
  {
@@ -608,27 +608,27 @@ Respuesta:
608
608
  }
609
609
  ```
610
610
 
611
- CORS es automático en el health endpoint. Si `allowedOrigins` está configurado, se agrega el header `Access-Control-Allow-Origin` para los orígenes coincidentes. Las peticiones OPTIONS preflight retornan 204.
611
+ CORS is automatic on the health endpoint. If `allowedOrigins` is configured, the `Access-Control-Allow-Origin` header is added for matching origins. OPTIONS preflight requests return 204.
612
612
 
613
613
  ---
614
614
 
615
615
  ## Middleware
616
616
 
617
- El sistema de middleware permite validar conexiones antes de que un cliente sea aceptado:
617
+ The middleware system allows validating connections before a client is accepted:
618
618
 
619
619
  ```javascript
620
- // Autenticación con token
620
+ // Token authentication
621
621
  stelar.use((ctx, next) => {
622
622
  const token = ctx.req?.headers?.authorization;
623
623
  if (!token) {
624
- return ctx.ack('error', { message: 'Token requerido' });
624
+ return ctx.ack('error', { message: 'Token required' });
625
625
  }
626
- // Validar token...
626
+ // Validate token...
627
627
  ctx.setMetadata('userId', getUserIdFromToken(token));
628
628
  next();
629
629
  });
630
630
 
631
- // Rate limiting custom
631
+ // Custom rate limiting
632
632
  stelar.use((ctx, next) => {
633
633
  const ip = ctx.req?.headers?.['x-forwarded-for'] || ctx.socket.remoteAddress;
634
634
  if (isBlocked(ip)) {
@@ -639,21 +639,21 @@ stelar.use((ctx, next) => {
639
639
 
640
640
  // Logging
641
641
  stelar.use((ctx, next) => {
642
- console.log(`Nueva conexión desde ${ctx.clientInfo.remoteAddress}`);
642
+ console.log(`New connection from ${ctx.clientInfo.remoteAddress}`);
643
643
  next();
644
644
  });
645
645
  ```
646
646
 
647
- Múltiples middlewares se ejecutan en orden. Si un middleware no llama a `next()`, la conexión se rechaza.
647
+ Multiple middlewares execute in order. If a middleware doesn't call `next()`, the connection is rejected.
648
648
 
649
649
  ---
650
650
 
651
651
  ## Rooms
652
652
 
653
- Los rooms son canales de comunicación. Un cliente puede estar en múltiples rooms simultáneamente:
653
+ Rooms are communication channels. A client can be in multiple rooms simultaneously:
654
654
 
655
655
  ```javascript
656
- // Servidor
656
+ // Server
657
657
  stelar.on('joinChannel', (ctx) => {
658
658
  ctx.joinRoom(ctx.data.channel);
659
659
  ctx.to(ctx.data.channel, 'userJoined', { userId: ctx.id });
@@ -666,71 +666,71 @@ stelar.on('channelMessage', (ctx) => {
666
666
  }
667
667
  });
668
668
 
669
- // Cliente
669
+ // Client
670
670
  client.joinRoom('general');
671
671
  client.joinRoom('random');
672
672
  client.joinRoom('project-alpha');
673
673
  ```
674
674
 
675
- Los rooms se limpian automáticamente cuando el último cliente sale o se desconecta. No hay que liberar recursos manualmente.
675
+ Rooms are automatically cleaned up when the last client leaves or disconnects. No manual resource release needed.
676
676
 
677
677
  ---
678
678
 
679
679
  ## ACK (Request-Response)
680
680
 
681
- El sistema de ACK permite comunicación request-response confiable sobre el protocolo en tiempo real:
681
+ The ACK system enables reliable request-response communication over the real-time protocol:
682
682
 
683
683
  ```javascript
684
- // ServidorRegistrar handler ACK
684
+ // ServerRegister ACK handler
685
685
  stelar.onAck('getUsers', (ctx) => {
686
- return { users: ['Juan', 'Maria', 'Pedro'] };
686
+ return { users: ['John', 'Mary', 'Peter'] };
687
687
  });
688
688
 
689
689
  stelar.onAck('validateToken', (ctx) => {
690
690
  const valid = validateToken(ctx.data.token);
691
- if (!valid) throw new Error('Token inválido');
691
+ if (!valid) throw new Error('Invalid token');
692
692
  return { userId: 123 };
693
693
  });
694
694
 
695
- // ClienteEnviar petición y esperar respuesta
695
+ // ClientSend request and wait for response
696
696
  const users = await client.request('getUsers', {}, 'getUsers');
697
- console.log(users); // { users: ['Juan', 'Maria', 'Pedro'] }
697
+ console.log(users); // { users: ['John', 'Mary', 'Peter'] }
698
698
 
699
699
  try {
700
700
  const result = await client.request('validateToken', { token: 'abc' }, 'validateToken');
701
701
  } catch (err) {
702
- console.log('Token inválido');
702
+ console.log('Invalid token');
703
703
  }
704
704
  ```
705
705
 
706
- Las peticiones ACK tienen timeout configurable (`ackTimeout`). Si el servidor no responde en ese tiempo, la Promise se rechaza.
706
+ ACK requests have configurable timeout (`ackTimeout`). If the server doesn't respond within that time, the Promise is rejected.
707
707
 
708
708
  ---
709
709
 
710
- ## Datos Binarios
710
+ ## Binary Data
711
711
 
712
- Enviar archivos, imágenes, audio, o cualquier dato binario sin overhead de base64:
712
+ Send files, images, audio, or any binary data without base64 overhead:
713
713
 
714
714
  ```javascript
715
- // ServidorRecibir y reenviar binario
715
+ // ServerReceive and forward binary
716
716
  stelar.on('file', (ctx) => {
717
- ctx.broadcastBinary('file', ctx.data); // ctx.data es un Buffer
717
+ ctx.broadcastBinary('file', ctx.data); // ctx.data is a Buffer
718
718
  });
719
719
 
720
- // ClienteEnviar binario
720
+ // ClientSend binary
721
721
  const imageBuffer = await fs.readFile('photo.png');
722
722
  client.emitBinary('file', imageBuffer);
723
723
 
724
- // ClienteRecibir binario
724
+ // ClientReceive binary
725
725
  client.on('file', (buffer) => {
726
- console.log('Archivo recibido:', buffer.length, 'bytes');
726
+ console.log('File received:', buffer.length, 'bytes');
727
727
  fs.writeFile('received.png', buffer);
728
728
  });
729
729
  ```
730
730
 
731
731
  ---
732
732
 
733
- ## Métricas del Servidor
733
+ ## Server Metrics
734
734
 
735
735
  ```javascript
736
736
  const stats = stelar.getStats();
@@ -754,23 +754,23 @@ console.log(stats);
754
754
 
755
755
  ---
756
756
 
757
- ## Métricas del Cliente
757
+ ## Client Metrics
758
758
 
759
759
  ```javascript
760
- console.log('Mensajes enviados:', client.getMessagesSent());
761
- console.log('Mensajes recibidos:', client.getMessagesReceived());
762
- console.log('Hora de conexión:', client.getConnectTime());
763
- console.log('Último error:', client.getLastError());
764
- console.log('Mensajes en cola:', client.getQueueSize());
765
- console.log('Estado:', client.getState());
766
- console.log('Conectado?', client.isConnected());
760
+ console.log('Messages sent:', client.getMessagesSent());
761
+ console.log('Messages received:', client.getMessagesReceived());
762
+ console.log('Connection time:', client.getConnectTime());
763
+ console.log('Last error:', client.getLastError());
764
+ console.log('Messages in queue:', client.getQueueSize());
765
+ console.log('State:', client.getState());
766
+ console.log('Connected?', client.isConnected());
767
767
  ```
768
768
 
769
769
  ---
770
770
 
771
- ## Escalabilidad Horizontal
771
+ ## Horizontal Scalability
772
772
 
773
- stelar-time-real funciona en un solo servidor por instancia. Para escalar a múltiples instancias, usa Redis Pub/Sub como puente:
773
+ stelar-time-real runs on a single server per instance. To scale to multiple instances, use Redis Pub/Sub as a bridge:
774
774
 
775
775
  ```javascript
776
776
  import { StelarServer } from 'stelar-time-real';
@@ -779,7 +779,7 @@ import Redis from 'redis';
779
779
  const redis = Redis.createClient();
780
780
  const stelar = new StelarServer({ port: 3000, tcpPort: 3001 });
781
781
 
782
- // Cuando un broadcast se hace en esta instancia, publicar en Redis
782
+ // When a broadcast happens on this instance, publish to Redis
783
783
  stelar.onAll((ctx) => {
784
784
  redis.publish('stelar:events', JSON.stringify({
785
785
  event: ctx.eventName,
@@ -788,7 +788,7 @@ stelar.onAll((ctx) => {
788
788
  }));
789
789
  });
790
790
 
791
- // Cuando otra instancia publica, emitir localmente
791
+ // When another instance publishes, emit locally
792
792
  redis.subscribe('stelar:events', (message) => {
793
793
  const { event, data, excludeId } = JSON.parse(message);
794
794
  stelar.broadcast(event, data, excludeId);
@@ -799,31 +799,31 @@ redis.subscribe('stelar:events', (message) => {
799
799
 
800
800
  ## Performance
801
801
 
802
- Mediciones con stress test (50 WebSocket + 20 TCP clientes):
802
+ Measurements with stress test (50 WebSocket + 20 TCP clients):
803
803
 
804
- | Métrica | Valor |
804
+ | Metric | Value |
805
805
  |---------|-------|
806
- | Conexiones simultáneas | 70 |
807
- | RAM por cliente | ~58 KB |
806
+ | Simultaneous connections | 70 |
807
+ | RAM per client | ~58 KB |
808
808
  | Throughput | 3,425 msg/sec |
809
- | Heap estable | ~10 MB |
810
- | Memory leaks | No detectados |
809
+ | Stable heap | ~10 MB |
810
+ | Memory leaks | None detected |
811
811
  | MaxListeners warnings | 0 |
812
812
 
813
- La librería usa ~58KB por cliente conectado. Un servidor con 1GB de RAM puede manejar aproximadamente 17,000 conexiones simultáneas.
813
+ The library uses ~58KB per connected client. A server with 1GB of RAM can handle approximately 17,000 simultaneous connections.
814
814
 
815
815
  ---
816
816
 
817
- ## Estructura del Proyecto
817
+ ## Project Structure
818
818
 
819
819
  ```
820
820
  stelar-time-real/
821
821
  ├── src/
822
- │ ├── index.ts # Servidor (StelarServer, RateLimiter, IPConnectionTracker)
823
- │ ├── client.ts # Cliente (StelarClient, MessageQueue)
824
- │ ├── protocol.ts # Protocolo binario TCP (encode/decode, FrameParser)
825
- │ ├── websocket.ts # WebSocket manual RFC 6455 (WSFrameParser, framing)
826
- │ └── logger.ts # Logger con niveles
822
+ │ ├── index.ts # Server (StelarServer, RateLimiter, IPConnectionTracker)
823
+ │ ├── client.ts # Client (StelarClient, MessageQueue)
824
+ │ ├── protocol.ts # Binary TCP protocol (encode/decode, FrameParser)
825
+ │ ├── websocket.ts # Manual WebSocket RFC 6455 (WSFrameParser, framing)
826
+ │ └── logger.ts # Logger with levels
827
827
  ├── package.json
828
828
  ├── tsconfig.json
829
829
  └── README.md
@@ -833,7 +833,7 @@ stelar-time-real/
833
833
 
834
834
  ## TypeScript
835
835
 
836
- stelar-time-real está escrita en TypeScript e incluye definiciones de tipos (.d.ts). No necesitas instalar @types separados:
836
+ stelar-time-real is written in TypeScript and includes type definitions (.d.ts). You don't need to install separate @types:
837
837
 
838
838
  ```typescript
839
839
  import { StelarServer, StelarClient, StelarStats } from 'stelar-time-real';
@@ -847,31 +847,31 @@ const stats: StelarStats = server.getStats();
847
847
  ## Tests
848
848
 
849
849
  ```bash
850
- # Tests de producción (54 assertions, 16 suites)
850
+ # Production tests (54 assertions, 16 suites)
851
851
  node test-production.mjs
852
852
 
853
- # Stress test (70 clientes, throughput, memoria)
853
+ # Stress test (70 clients, throughput, memory)
854
854
  node test-stress.mjs
855
855
  ```
856
856
 
857
- Cobertura: server start/stop, health check, CORS, WS connect/emit/broadcast, TCP connect/emit/reply, rooms, ACK, max connections, rate limiting, server stats, max rooms, O(1) lookup, client metrics, binary data, origin checking, middleware.
857
+ Coverage: server start/stop, health check, CORS, WS connect/emit/broadcast, TCP connect/emit/reply, rooms, ACK, max connections, rate limiting, server stats, max rooms, O(1) lookup, client metrics, binary data, origin checking, middleware.
858
858
 
859
859
  ---
860
860
 
861
- ## Configuracion Extensible
861
+ ## Extensible Configuration
862
862
 
863
- stelar-time-real v3.2 te da control total sobre cada aspecto del servidor y el cliente. Puedes reemplazar componentes enteros, agregar hooks para personalizar el comportamiento, y cambiar la configuración en runtime.
863
+ stelar-time-real v3.2 gives you total control over every aspect of the server and client. You can replace entire components, add hooks to customize behavior, and change configuration at runtime.
864
864
 
865
865
  ### Custom Rate Limiter
866
866
 
867
- Reemplaza el rate limiter built-in (token bucket) con tu propia implementación. Ideal para usar Redis, MongoDB, o cualquier otro store:
867
+ Replace the built-in rate limiter (token bucket) with your own implementation. Ideal for using Redis, MongoDB, or any other store:
868
868
 
869
869
  ```javascript
870
870
  import { StelarServer, IRateLimiter } from 'stelar-time-real';
871
871
 
872
- // Tu propio rate limiter con Redis
872
+ // Your own rate limiter with Redis
873
873
  class RedisRateLimiter implements IRateLimiter {
874
- private redis; // tu conexión Redis
874
+ private redis; // your Redis connection
875
875
 
876
876
  constructor(redisClient) {
877
877
  this.redis = redisClient;
@@ -881,9 +881,9 @@ class RedisRateLimiter implements IRateLimiter {
881
881
  const key = `ratelimit:${id}`;
882
882
  const current = await this.redis.incr(key);
883
883
  if (current === 1) {
884
- await this.redis.expire(key, 1); // 1 segundo ventana
884
+ await this.redis.expire(key, 1); // 1 second window
885
885
  }
886
- return current <= 100; // 100 por segundo
886
+ return current <= 100; // 100 per second
887
887
  }
888
888
 
889
889
  async reset(id) {
@@ -891,11 +891,11 @@ class RedisRateLimiter implements IRateLimiter {
891
891
  }
892
892
 
893
893
  async cleanup() {
894
- // Redis maneja la expiración automáticamente
894
+ // Redis handles expiration automatically
895
895
  }
896
896
 
897
897
  async size() {
898
- return 0; // No aplicable con Redis
898
+ return 0; // Not applicable with Redis
899
899
  }
900
900
  }
901
901
 
@@ -907,7 +907,7 @@ const stelar = new StelarServer({
907
907
 
908
908
  ### Custom IP Tracker
909
909
 
910
- Reemplaza el per-IP connection tracker con tu propia lógica. Útil para usar una base de datos de IPs bloqueadas o lógica de whitelist:
910
+ Replace the per-IP connection tracker with your own logic. Useful for using a database of blocked IPs or whitelist logic:
911
911
 
912
912
  ```javascript
913
913
  class CustomIPTracker implements IIPTracker {
@@ -916,15 +916,15 @@ class CustomIPTracker implements IIPTracker {
916
916
  private counts = new Map<string, number>();
917
917
 
918
918
  check(ip) {
919
- if (this.blockedIPs.has(ip)) return false; // IP bloqueada
920
- if (this.vipIPs.has(ip)) return true; // VIP sin límite
921
- return (this.counts.get(ip) || 0) < 20; // 20 para normales
919
+ if (this.blockedIPs.has(ip)) return false; // Blocked IP
920
+ if (this.vipIPs.has(ip)) return true; // VIP no limit
921
+ return (this.counts.get(ip) || 0) < 20; // 20 for normal
922
922
  }
923
923
 
924
924
  add(ip) { this.counts.set(ip, (this.counts.get(ip) || 0) + 1); }
925
925
  remove(ip) { /* ... */ }
926
926
  getCount(ip) { return this.counts.get(ip) || 0; }
927
- cleanup() { /* limpiar entradas expiradas */ }
927
+ cleanup() { /* clean expired entries */ }
928
928
  }
929
929
 
930
930
  const stelar = new StelarServer({
@@ -935,7 +935,7 @@ const stelar = new StelarServer({
935
935
 
936
936
  ### Custom Client ID Generator
937
937
 
938
- Genera IDs de cliente con tu propio formato. Por defecto usa UUID v4:
938
+ Generate client IDs with your own format. By default uses UUID v4:
939
939
 
940
940
  ```javascript
941
941
  const stelar = new StelarServer({
@@ -948,7 +948,7 @@ const stelar = new StelarServer({
948
948
 
949
949
  ### Event-Specific Rate Limits
950
950
 
951
- Cada evento puede tener su propio rate limit, independiente del global:
951
+ Each event can have its own rate limit, independent from the global one:
952
952
 
953
953
  ```javascript
954
954
  const stelar = new StelarServer({
@@ -956,112 +956,112 @@ const stelar = new StelarServer({
956
956
  rateLimit: { maxPoints: 100, windowMs: 1000 }, // Global: 100 msg/sec
957
957
  eventRateLimits: {
958
958
  'chat': { maxPoints: 5, windowMs: 1000 }, // Chat: 5 msg/sec
959
- 'file-upload': { maxPoints: 2, windowMs: 10000 }, // Archivos: 2 cada 10s
959
+ 'file-upload': { maxPoints: 2, windowMs: 10000 }, // Files: 2 every 10s
960
960
  'typing': { maxPoints: 10, windowMs: 1000 }, // Typing: 10 msg/sec
961
- 'location': { maxPoints: 1, windowMs: 5000 }, // Ubicación: 1 cada 5s
961
+ 'location': { maxPoints: 1, windowMs: 5000 }, // Location: 1 every 5s
962
962
  },
963
963
  });
964
964
 
965
- // También puedes agregar/remover en runtime:
965
+ // You can also add/remove at runtime:
966
966
  stelar.setEventRateLimit('voice', { maxPoints: 50, windowMs: 1000 });
967
967
  stelar.removeEventRateLimit('voice');
968
968
  ```
969
969
 
970
970
  ### Per-Client Rate Limits
971
971
 
972
- Dale a clientes específicos rate limits diferentes. Útil para usuarios premium vs gratuitos:
972
+ Give specific clients different rate limits. Useful for premium vs free users:
973
973
 
974
974
  ```javascript
975
975
  stelar.onConnection((ctx) => {
976
976
  const role = ctx.getMetadata('role');
977
977
 
978
- // Usuario premium: 500 msg/sec
978
+ // Premium user: 500 msg/sec
979
979
  if (role === 'premium') {
980
980
  stelar.setClientRateLimit(ctx.id, { maxPoints: 500, windowMs: 1000 });
981
981
  }
982
- // Usuario bot verificado: 1000 msg/sec
982
+ // Verified bot: 1000 msg/sec
983
983
  else if (role === 'bot') {
984
984
  stelar.setClientRateLimit(ctx.id, { maxPoints: 1000, windowMs: 1000 });
985
985
  }
986
- // Usuario normal: usa el rate limit global (100 msg/sec)
986
+ // Normal user: uses global rate limit (100 msg/sec)
987
987
  });
988
988
 
989
- // Remover override (vuelve al global):
989
+ // Remove override (reverts to global):
990
990
  stelar.removeClientRateLimit(clientId);
991
991
  ```
992
992
 
993
- La prioridad de rate limiting es: **per-client override > event-specific > global > custom rate limiter**.
993
+ Rate limiting priority is: **per-client override > event-specific > global > custom rate limiter**.
994
994
 
995
- ### Hook System (Servidor)
995
+ ### Hook System (Server)
996
996
 
997
- Hooks te permiten personalizar lo que pasa cuando el servidor detecta un evento. Cada hook puede retornar `false` para cancelar la acción por defecto:
997
+ Hooks let you customize what happens when the server detects an event. Each hook can return `false` to cancel the default action:
998
998
 
999
999
  ```javascript
1000
1000
  const stelar = new StelarServer({
1001
1001
  port: 3000,
1002
1002
  hooks: {
1003
- // Cuando un cliente excede el rate limit
1004
- // Return false para NO desconectar (ej: solo warn)
1003
+ // When a client exceeds the rate limit
1004
+ // Return false to NOT disconnect (e.g.: just warn)
1005
1005
  onRateLimitExceeded: ({ clientId, event, protocol }) => {
1006
- console.warn(`Rate limit: ${clientId} en evento ${event}`);
1007
- // return false; // Descomenta para NO desconectar al cliente
1006
+ console.warn(`Rate limit: ${clientId} on event ${event}`);
1007
+ // return false; // Uncomment to NOT disconnect the client
1008
1008
  },
1009
1009
 
1010
- // Cuando se alcanza el máximo de conexiones
1010
+ // When maximum connections is reached
1011
1011
  onMaxConnectionsReached: ({ activeConnections, max, ip }) => {
1012
- console.error(`Servidor lleno: ${activeConnections}/${max} desde ${ip}`);
1013
- // Enviar alerta a Slack, etc.
1012
+ console.error(`Server full: ${activeConnections}/${max} from ${ip}`);
1013
+ // Send alert to Slack, etc.
1014
1014
  },
1015
1015
 
1016
- // Cuando un cliente intenta unirse a un room
1017
- // Return false para RECHAZAR el join
1016
+ // When a client tries to join a room
1017
+ // Return false to REJECT the join
1018
1018
  onClientJoinRoom: ({ clientId, room, metadata }) => {
1019
1019
  const role = metadata.get('role');
1020
1020
  if (room.startsWith('admin-') && role !== 'admin') {
1021
- return false; // Rechazar: solo admins
1021
+ return false; // Reject: admins only
1022
1022
  }
1023
1023
  },
1024
1024
 
1025
- // Cuando un cliente sale de un room
1026
- // Return false para RECHAZAR el leave
1025
+ // When a client leaves a room
1026
+ // Return false to REJECT the leave
1027
1027
  onClientLeaveRoom: ({ clientId, room }) => {
1028
- // Lógica custom...
1028
+ // Custom logic...
1029
1029
  },
1030
1030
 
1031
- // Cuando se alcanza el máximo de rooms global
1031
+ // When global maximum rooms is reached
1032
1032
  onMaxRoomsReached: ({ clientId, room, totalRooms, max }) => {
1033
1033
  console.warn(`Max rooms: ${totalRooms}/${max}`);
1034
1034
  },
1035
1035
 
1036
- // Cuando un cliente excede rooms por cliente
1036
+ // When a client exceeds rooms per client
1037
1037
  onMaxRoomsPerClientReached: ({ clientId, room, currentRooms, max }) => {
1038
- console.warn(`Cliente ${clientId}: ${currentRooms}/${max} rooms`);
1038
+ console.warn(`Client ${clientId}: ${currentRooms}/${max} rooms`);
1039
1039
  },
1040
1040
 
1041
- // Cuando un payload es demasiado grande
1041
+ // When a payload is too large
1042
1042
  onPayloadTooLarge: ({ clientId, event, size, max }) => {
1043
- console.warn(`Payload grande: ${size} bytes de ${clientId}`);
1043
+ console.warn(`Large payload: ${size} bytes from ${clientId}`);
1044
1044
  },
1045
1045
 
1046
- // Cuando se recibe un mensaje inválido
1046
+ // When an invalid message is received
1047
1047
  onInvalidMessage: ({ clientId, reason, protocol }) => {
1048
- console.warn(`Mensaje inválido de ${clientId}: ${reason}`);
1048
+ console.warn(`Invalid message from ${clientId}: ${reason}`);
1049
1049
  },
1050
1050
 
1051
- // Antes de un broadcast
1052
- // Return false para CANCELAR el broadcast
1051
+ // Before a broadcast
1052
+ // Return false to CANCEL the broadcast
1053
1053
  onBeforeBroadcast: ({ event, data, excludeId }) => {
1054
- if (event === 'spam') return false; // Cancelar broadcast de spam
1054
+ if (event === 'spam') return false; // Cancel spam broadcast
1055
1055
  },
1056
1056
 
1057
- // Cuando un cliente se conecta
1057
+ // When a client connects
1058
1058
  onClientConnect: ({ clientId, ip, protocol, metadata }) => {
1059
- console.log(`Conectado: ${clientId} via ${protocol} desde ${ip}`);
1059
+ console.log(`Connected: ${clientId} via ${protocol} from ${ip}`);
1060
1060
  },
1061
1061
 
1062
- // Cuando un cliente se desconecta
1062
+ // When a client disconnects
1063
1063
  onClientDisconnect: ({ clientId, ip, protocol, rooms }) => {
1064
- console.log(`Desconectado: ${clientId} estaba en ${rooms.size} rooms`);
1064
+ console.log(`Disconnected: ${clientId} was in ${rooms.size} rooms`);
1065
1065
  },
1066
1066
  },
1067
1067
  });
@@ -1069,13 +1069,13 @@ const stelar = new StelarServer({
1069
1069
 
1070
1070
  ### Custom Health Check
1071
1071
 
1072
- Reemplaza el health check built-in con tu propio handler. Útil para agregar checks de base de datos, disk space, etc:
1072
+ Replace the built-in health check with your own handler. Useful for adding database checks, disk space, etc:
1073
1073
 
1074
1074
  ```javascript
1075
1075
  const stelar = new StelarServer({
1076
1076
  port: 3000,
1077
1077
  customHealthHandler: (req, res, stats) => {
1078
- // stats contiene todas las estadísticas del servidor
1078
+ // stats contains all server statistics
1079
1079
 
1080
1080
  const dbConnected = await checkDatabase();
1081
1081
  const diskSpace = checkDiskSpace();
@@ -1096,13 +1096,13 @@ const stelar = new StelarServer({
1096
1096
 
1097
1097
  ### Runtime Configuration
1098
1098
 
1099
- Cambia la configuración del servidor sin reiniciar:
1099
+ Change server configuration without restarting:
1100
1100
 
1101
1101
  ```javascript
1102
1102
  const stelar = new StelarServer({ port: 3000, maxConnections: 100 });
1103
1103
  await stelar.start();
1104
1104
 
1105
- // Más tarde... necesitas más capacidad
1105
+ // Later... you need more capacity
1106
1106
  stelar.updateConfig({
1107
1107
  maxConnections: 500,
1108
1108
  maxRooms: 5000,
@@ -1110,17 +1110,17 @@ stelar.updateConfig({
1110
1110
  allowedOrigins: ['https://app.com', 'https://admin.app.com'],
1111
1111
  });
1112
1112
 
1113
- // Cambiar hooks en runtime
1113
+ // Change hooks at runtime
1114
1114
  stelar.updateConfig({
1115
1115
  hooks: {
1116
1116
  onRateLimitExceeded: ({ clientId }) => {
1117
- banUser(clientId); // Ban automático en vez de desconectar
1118
- return false; // No desconectar, ya lo baneaste
1117
+ banUser(clientId); // Auto-ban instead of disconnecting
1118
+ return false; // Don't disconnect, you already banned them
1119
1119
  },
1120
1120
  },
1121
1121
  });
1122
1122
 
1123
- // Ver configuración actual
1123
+ // View current configuration
1124
1124
  const config = stelar.getConfig();
1125
1125
  console.log(config);
1126
1126
  // {
@@ -1135,49 +1135,49 @@ console.log(config);
1135
1135
 
1136
1136
  ### Client Hooks
1137
1137
 
1138
- Personaliza el comportamiento del cliente con hooks:
1138
+ Customize client behavior with hooks:
1139
1139
 
1140
1140
  ```javascript
1141
1141
  const client = new StelarClient('localhost:3000', {
1142
1142
  hooks: {
1143
- // Antes de enviar un mensaje — return false para cancelar
1143
+ // Before sending a message — return false to cancel
1144
1144
  onBeforeEmit: ({ event, data }) => {
1145
- if (event === 'debug') return false; // No enviar debug en producción
1146
- console.log(`Enviando: ${event}`);
1145
+ if (event === 'debug') return false; // Don't send debug in production
1146
+ console.log(`Sending: ${event}`);
1147
1147
  },
1148
1148
 
1149
- // Cuando se recibe cualquier mensaje
1149
+ // When any message is received
1150
1150
  onMessage: ({ event, data, isBinary }) => {
1151
1151
  metrics.increment('messages.received');
1152
1152
  if (isBinary) metrics.increment('binary.received');
1153
1153
  },
1154
1154
 
1155
- // Cuando cambia el estado de conexión
1155
+ // When connection state changes
1156
1156
  onStateChange: ({ from, to }) => {
1157
- console.log(`Estado: ${from} -> ${to}`);
1157
+ console.log(`State: ${from} -> ${to}`);
1158
1158
  if (to === 'reconnecting') showReconnectingUI();
1159
1159
  if (to === 'connected') hideReconnectingUI();
1160
1160
  },
1161
1161
 
1162
- // Personalizar el delay de reconexión
1162
+ // Customize reconnection delay
1163
1163
  onReconnectDelay: ({ attempt, defaultDelay }) => {
1164
- // Horario laboral: reconexión rápida
1164
+ // Business hours: fast reconnection
1165
1165
  const hour = new Date().getHours();
1166
1166
  if (hour >= 9 && hour <= 18) return 500;
1167
- return defaultDelay; // Fuera de horario: delay normal
1167
+ return defaultDelay; // Off-hours: normal delay
1168
1168
  },
1169
1169
 
1170
- // Cuando un mensaje se encola (desconectado)
1170
+ // When a message is queued (disconnected)
1171
1171
  onMessageQueued: ({ event, queueSize }) => {
1172
- console.log(`Mensaje encolado: ${event} (cola: ${queueSize})`);
1172
+ console.log(`Message queued: ${event} (queue: ${queueSize})`);
1173
1173
  },
1174
1174
 
1175
- // Cuando se drena la cola después de reconectar
1175
+ // When queue is drained after reconnecting
1176
1176
  onQueueDrained: ({ count }) => {
1177
- console.log(`${count} mensajes enviados después de reconectar`);
1177
+ console.log(`${count} messages sent after reconnecting`);
1178
1178
  },
1179
1179
 
1180
- // Cuando ocurre un error
1180
+ // When an error occurs
1181
1181
  onError: ({ error, context }) => {
1182
1182
  errorReporter.report(error, { context });
1183
1183
  },
@@ -1187,24 +1187,24 @@ const client = new StelarClient('localhost:3000', {
1187
1187
 
1188
1188
  ### Custom Reconnect Delay
1189
1189
 
1190
- Controla exactamente cuánto esperar antes de cada reintento de reconexión:
1190
+ Control exactly how long to wait before each reconnection attempt:
1191
1191
 
1192
1192
  ```javascript
1193
- // Opción 1: Función custom
1193
+ // Option 1: Custom function
1194
1194
  const client = new StelarClient('localhost:3000', {
1195
1195
  customReconnectDelay: (attempt, baseDelay, maxDelay) => {
1196
- // Retry rápido los primeros 3 intentos, luego lento
1196
+ // Fast retry for first 3 attempts, then slow
1197
1197
  if (attempt <= 3) return 200;
1198
1198
  if (attempt <= 10) return 2000;
1199
- return 30000; // 30s para intentos posteriores
1199
+ return 30000; // 30s for later attempts
1200
1200
  },
1201
1201
  });
1202
1202
 
1203
- // Opción 2: Via hook (puedes cambiar en runtime)
1203
+ // Option 2: Via hook (can change at runtime)
1204
1204
  const client = new StelarClient('localhost:3000', {
1205
1205
  hooks: {
1206
1206
  onReconnectDelay: ({ attempt, defaultDelay }) => {
1207
- return Math.min(100 * attempt, 10000); // Lineal en vez de exponencial
1207
+ return Math.min(100 * attempt, 10000); // Linear instead of exponential
1208
1208
  },
1209
1209
  },
1210
1210
  });
@@ -1212,25 +1212,25 @@ const client = new StelarClient('localhost:3000', {
1212
1212
 
1213
1213
  ### Client Runtime Configuration
1214
1214
 
1215
- Cambia la configuración del cliente sin reconectar:
1215
+ Change client configuration without reconnecting:
1216
1216
 
1217
1217
  ```javascript
1218
1218
  const client = new StelarClient('localhost:3000');
1219
1219
  client.connect();
1220
1220
 
1221
- // Más tarde... necesitas ajustar timeouts
1221
+ // Later... adjust timeouts
1222
1222
  client.updateOptions({
1223
1223
  heartbeatInterval: 15000,
1224
1224
  ackTimeout: 10000,
1225
1225
  maxPayloadSize: 50 * 1024 * 1024, // 50MB
1226
1226
  hooks: {
1227
1227
  onBeforeEmit: ({ event }) => {
1228
- if (event === 'log') return false; // Ya no enviar logs
1228
+ if (event === 'log') return false; // No longer send logs
1229
1229
  },
1230
1230
  },
1231
1231
  });
1232
1232
 
1233
- // Ver configuración actual
1233
+ // View current configuration
1234
1234
  const opts = client.getOptions();
1235
1235
  console.log(opts);
1236
1236
  ```