stelar-time-real 1.1.0 → 1.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.
package/README.md CHANGED
@@ -15,6 +15,7 @@ Tu propio sistema de tiempo real personalizado. Una librería ligera y sin depen
15
15
  - 💓 **Heartbeat incluido** - Detecta desconexiones automáticamente
16
16
  - 🌐 **Namespaces** - Múltiples canales independientes (`/chat`, `/game`, etc.)
17
17
  - ⚡ **ACK ultra rápido** - Request-response con Promesas, sin overhead
18
+ - 📦 **Binarios** - Envía imágenes, archivos, audio, video sin overhead de base64
18
19
 
19
20
  ## Instalación
20
21
 
@@ -500,6 +501,49 @@ client.on('reconnecting', (attempt) => console.log(`Reintentando ${attempt}/5`))
500
501
  client.connect();
501
502
  ```
502
503
 
504
+ ### Enviar Archivos Binarios
505
+
506
+ ```javascript
507
+ // Servidor - recibir imagen
508
+ stelar.on('image', (ctx) => {
509
+ // ctx.buffer es un Uint8Array
510
+ console.log('Recibido:', ctx.buffer.byteLength, 'bytes');
511
+ // Guardar o procesar la imagen
512
+ saveImage(ctx.buffer);
513
+
514
+ // Responder al cliente
515
+ ctx.emit('imageSaved', { success: true });
516
+ });
517
+
518
+ // Cliente - enviar imagen
519
+ const input = document.querySelector('input[type="file"]');
520
+ input.addEventListener('change', async (e) => {
521
+ const file = e.target.files[0];
522
+ const buffer = await file.arrayBuffer();
523
+ client.emitBinary('image', buffer);
524
+ });
525
+
526
+ // Cliente - recibir imagen
527
+ client.on('image', (buffer) => {
528
+ const blob = new Blob([buffer], { type: 'image/png' });
529
+ const url = URL.createObjectURL(blob);
530
+ document.getElementById('img').src = url;
531
+ });
532
+ ```
533
+
534
+ ### Broadcast de Binarios
535
+
536
+ ```javascript
537
+ // Servidor - compartir archivo con todos
538
+ stelar.on('upload', (ctx) => {
539
+ ctx.broadcastBinary('file', ctx.buffer);
540
+ });
541
+
542
+ // Cliente - enviar archivo
543
+ const fileData = await file.arrayBuffer();
544
+ client.emitBinary('upload', fileData);
545
+ ```
546
+
503
547
  ---
504
548
 
505
549
  ## Diferencia con Socket.io
@@ -514,6 +558,12 @@ client.connect();
514
558
 
515
559
  ## Changelog
516
560
 
561
+ ### v1.2.0
562
+ - Soporte binarios: envía imágenes, archivos, audio, video sin base64
563
+ - `ctx.emitBinary('event', buffer)` y `client.emitBinary('event', buffer)`
564
+ - `client.sendFile(file)` y `client.sendImage(blob)`
565
+ - Eficiente: usa WebSocket binary diretamente, sin overhead
566
+
517
567
  ### v1.1.0
518
568
  - Namespaces: `StelarServer.of('/chat')` para múltiples canales
519
569
  - ACK personalizado: `ctx.ack('nombre', data)` y `client.request(event, data, 'ackName')`
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "stelar-time-real",
3
- "version": "1.1.0",
4
- "description": "Tu propio sistema de tiempo real personalizado - WebSocket ligero sin dependencias",
3
+ "version": "1.2.1",
4
+ "description": "WebSocket liviano y rápido sin dependecias. Alternativa a socket.io, namespaces, ACKs y archivos binarios.",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
7
7
  "keywords": [
package/src/client.js CHANGED
@@ -58,6 +58,27 @@ class StelarClient {
58
58
  return this;
59
59
  }
60
60
 
61
+ emitBinary(event, data) {
62
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
63
+ const header = JSON.stringify({ event });
64
+ const headerBytes = new TextEncoder().encode(header);
65
+ const combined = new Uint8Array(headerBytes.length + 1 + data.byteLength);
66
+ combined.set(headerBytes, 0);
67
+ combined[headerBytes.length] = 0;
68
+ combined.set(new Uint8Array(data), headerBytes.length + 1);
69
+ this.ws.send(combined);
70
+ }
71
+ return this;
72
+ }
73
+
74
+ sendFile(file) {
75
+ return this.emitBinary('file', file);
76
+ }
77
+
78
+ sendImage(blob) {
79
+ return this.emitBinary('image', blob);
80
+ }
81
+
61
82
  request(event, data, ackName) {
62
83
  return new Promise((resolve, reject) => {
63
84
  const timeout = setTimeout(() => {
@@ -143,8 +164,34 @@ class StelarClient {
143
164
  this._startHeartbeat();
144
165
  };
145
166
 
167
+ this.ws.binaryType = 'arraybuffer';
168
+
146
169
  this.ws.onmessage = (e) => {
147
170
  try {
171
+ if (e.data instanceof ArrayBuffer) {
172
+ const view = new Uint8Array(e.data);
173
+ let headerEnd = -1;
174
+ for (let i = 0; i < view.length; i++) {
175
+ if (view[i] === 0) {
176
+ headerEnd = i;
177
+ break;
178
+ }
179
+ }
180
+ if (headerEnd === -1) return;
181
+
182
+ const headerStr = new TextDecoder().decode(view.slice(0, headerEnd));
183
+ const header = JSON.parse(headerStr);
184
+ const buffer = view.slice(headerEnd + 1);
185
+
186
+ if (this.events[header.event]) {
187
+ this.events[header.event](buffer);
188
+ }
189
+ if (this._wildcardHandler) {
190
+ this._wildcardHandler({ event: header.event, data: buffer, buffer, isBinary: true });
191
+ }
192
+ return;
193
+ }
194
+
148
195
  const msg = JSON.parse(e.data);
149
196
  const { event, data, _isAck } = msg;
150
197
 
package/src/index.js CHANGED
@@ -57,6 +57,20 @@ class StelarServer {
57
57
  return this;
58
58
  }
59
59
 
60
+ broadcastBinary(event, buffer) {
61
+ const header = JSON.stringify({ event, _binary: true });
62
+ const headerBytes = new TextEncoder().encode(header);
63
+ const combined = new Uint8Array(headerBytes.length + 1 + buffer.byteLength);
64
+ combined.set(headerBytes, 0);
65
+ combined[headerBytes.length] = 0;
66
+ combined.set(new Uint8Array(buffer), headerBytes.length + 1);
67
+
68
+ this.clients.forEach((info, client) => {
69
+ client.send(combined);
70
+ });
71
+ return this;
72
+ }
73
+
60
74
  to(room, event, data) {
61
75
  this.clients.forEach((info, client) => {
62
76
  if (info.room === room) {
@@ -131,7 +145,9 @@ class StelarServer {
131
145
  req,
132
146
  emit: (evt, d) => client.send(JSON.stringify({ event: evt, data: d })),
133
147
  send: (respId, d) => client.send(JSON.stringify({ event: respId, data: d, _isAck: true })),
148
+ emitBinary: (evt, buffer) => client.send(buffer),
134
149
  broadcast: (evt, d) => this.broadcast(evt, d),
150
+ broadcastBinary: (evt, buffer) => this.broadcastBinary(evt, buffer),
135
151
  to: (room, evt, d) => this.to(room, evt, d),
136
152
  toId: (id, evt, d) => this.toId(id, evt, d),
137
153
  getClients: (room) => this.getClients(room),
@@ -159,7 +175,35 @@ class StelarServer {
159
175
  }
160
176
  });
161
177
 
162
- client.on('message', (raw) => {
178
+ client.on('message', (raw, isBinary) => {
179
+ if (isBinary) {
180
+ try {
181
+ const view = new Uint8Array(raw);
182
+ let headerEnd = -1;
183
+ for (let i = 0; i < view.length; i++) {
184
+ if (view[i] === 0) {
185
+ headerEnd = i;
186
+ break;
187
+ }
188
+ }
189
+ if (headerEnd === -1) return;
190
+
191
+ const headerStr = new TextDecoder().decode(view.slice(0, headerEnd));
192
+ const header = JSON.parse(headerStr);
193
+ const data = view.slice(headerEnd + 1);
194
+
195
+ const eventCtx = { ...ctx, data, buffer: data, isBinary: true };
196
+
197
+ if (this.events[header.event]) {
198
+ this.events[header.event](eventCtx);
199
+ }
200
+ if (this._wildcardHandler) {
201
+ this._wildcardHandler({ event: header.event, data: eventCtx });
202
+ }
203
+ } catch (e) {}
204
+ return;
205
+ }
206
+
163
207
  try {
164
208
  const msg = JSON.parse(raw);
165
209
  const { event, data } = msg;