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 +50 -0
- package/package.json +2 -2
- package/src/client.js +47 -0
- package/src/index.js +45 -1
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
|
|
4
|
-
"description": "
|
|
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;
|