@encarnage7/local-meet 0.0.0-stage → 1.0.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/LICENSE +21 -0
- package/README.md +403 -2
- package/dist/index.cjs +2558 -0
- package/dist/index.d.ts +223 -0
- package/dist/index.mjs +2560 -0
- package/dist/local-meet.min.js +2574 -0
- package/package.json +44 -4
- package/src/crypto.js +92 -0
- package/src/events.js +38 -0
- package/src/index.js +150 -0
- package/src/room.js +2272 -0
- package/src/types.d.ts +223 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Local Meet Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,404 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Local Meet SDK (`local-meet`)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Zero-trust, resilient WebRTC client library with P2P Mesh & Cloudflare Calls SFU, E2EE data channels, and HTTPS signaling fallback. Works everywhere like Axios.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/local-meet)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Características
|
|
11
|
+
|
|
12
|
+
- **Cero Dependencias en Runtime**: Ligera, rápida y empaquetada en ESM, CommonJS y Browser UMD.
|
|
13
|
+
- **Doble Arquitectura**: Funciona en modo **WebRTC P2P Mesh Ilimitado** (gratis, sin servidores de medios) y **Cloudflare Calls SFU** (1,000 GB/mes gratis).
|
|
14
|
+
- **Señalización Ultrarresistente por HTTPS**: Supera firewalls restrictivos, NAT simétrico y redes móviles 4G/5G gracias a su bus de respaldo HTTPS `/api/room-sync`.
|
|
15
|
+
- **Cifrado de Extremo a Extremo (E2EE)**: Mensajería de datos, chat y reacciones cifradas con AES-GCM-256 mediante Web Crypto API.
|
|
16
|
+
- **Pantalla Compartida Nativa**: Conexión directa y fluida sin pantallas negras.
|
|
17
|
+
- **Sala de Espera y Controles de Anfitrión**: Admitir, rechazar, silenciar micrófonos, expulsar y finalizar salas para todos.
|
|
18
|
+
- **Soporte TypeScript Completo**: Definiciones de tipos `.d.ts` incluidas.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Instalación
|
|
23
|
+
|
|
24
|
+
### Vía NPM (React, Next.js, Vue, Angular, Svelte, Vite)
|
|
25
|
+
```bash
|
|
26
|
+
npm install @encarnage7/local-meet
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Vía CDN (HTML directo sin bundler, igual que Axios)
|
|
30
|
+
```html
|
|
31
|
+
<script src="https://cdn.jsdelivr.net/npm/@encarnage7/local-meet@1.0.0/dist/local-meet.min.js"></script>
|
|
32
|
+
```
|
|
33
|
+
*(O a través de unpkg)*:
|
|
34
|
+
```html
|
|
35
|
+
<script src="https://unpkg.com/@encarnage7/local-meet@1.0.0/dist/local-meet.min.js"></script>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Inicio Rápido
|
|
41
|
+
|
|
42
|
+
### 1. Unirse como Invitado (3 líneas)
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
import { LocalMeet, RoomEvents } from "@encarnage7/local-meet";
|
|
46
|
+
|
|
47
|
+
// 1. Conectar a la sala
|
|
48
|
+
const room = await LocalMeet.join({
|
|
49
|
+
serverUrl: "https://tu-worker.workers.dev",
|
|
50
|
+
roomHash: "c4f2-9k81-m3n2",
|
|
51
|
+
guestName: "Carlos"
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// 2. Escuchar video de los participantes remotos
|
|
55
|
+
room.on(RoomEvents.TrackSubscribed, (track, publication, participant) => {
|
|
56
|
+
const mediaEl = track.attach();
|
|
57
|
+
document.getElementById("video-grid").appendChild(mediaEl);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// 3. Conectar y activar cámara + micrófono
|
|
61
|
+
await room.connect();
|
|
62
|
+
await room.enableCameraAndMicrophone();
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
### 2. Crear una Sala como Anfitrión
|
|
68
|
+
|
|
69
|
+
```javascript
|
|
70
|
+
import { LocalMeet, RoomEvents, EngineModes } from "@encarnage7/local-meet";
|
|
71
|
+
|
|
72
|
+
const room = await LocalMeet.createRoom({
|
|
73
|
+
serverUrl: "https://tu-worker.workers.dev",
|
|
74
|
+
hostName: "María (Anfitriona)",
|
|
75
|
+
title: "Reunión de Equipo",
|
|
76
|
+
engine: EngineModes.CloudflareCalls, // o EngineModes.P2PMesh
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
console.log("Comparte este hash con tus invitados:", room.roomHash);
|
|
80
|
+
|
|
81
|
+
// Escuchar cuando un invitado llega a la sala de espera
|
|
82
|
+
room.on(RoomEvents.ParticipantConnected, (participant) => {
|
|
83
|
+
console.log(`${participant.name} está en la sala de espera.`);
|
|
84
|
+
// Para admitirlo:
|
|
85
|
+
room.admitGuest(participant.identity);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
await room.connect();
|
|
89
|
+
await room.enableCameraAndMicrophone();
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
### 3. Ejemplo con Vue 3 (Composition API / `<script setup>`)
|
|
95
|
+
|
|
96
|
+
```vue
|
|
97
|
+
<script setup>
|
|
98
|
+
import { ref, onMounted, onUnmounted } from "vue";
|
|
99
|
+
import { LocalMeet, RoomEvents } from "@encarnage7/local-meet";
|
|
100
|
+
|
|
101
|
+
const props = defineProps({
|
|
102
|
+
roomHash: { type: String, required: true },
|
|
103
|
+
userName: { type: String, default: "Invitado" },
|
|
104
|
+
serverUrl: { type: String, default: "https://tu-worker.workers.dev" }
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const videoContainer = ref(null);
|
|
108
|
+
let room = null;
|
|
109
|
+
|
|
110
|
+
onMounted(async () => {
|
|
111
|
+
room = await LocalMeet.join({
|
|
112
|
+
serverUrl: props.serverUrl,
|
|
113
|
+
roomHash: props.roomHash,
|
|
114
|
+
guestName: props.userName,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
room.on(RoomEvents.TrackSubscribed, (track) => {
|
|
118
|
+
const el = track.attach();
|
|
119
|
+
videoContainer.value?.appendChild(el);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
room.on(RoomEvents.TrackUnsubscribed, (track) => {
|
|
123
|
+
track.detach().forEach((el) => el.remove());
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
await room.connect();
|
|
127
|
+
await room.enableCameraAndMicrophone();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
onUnmounted(() => {
|
|
131
|
+
room?.disconnect();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const toggleMic = () => room?.setMicrophoneEnabled(false);
|
|
135
|
+
const toggleScreen = () => room?.startScreenShare();
|
|
136
|
+
const leave = () => room?.disconnect();
|
|
137
|
+
</script>
|
|
138
|
+
|
|
139
|
+
<template>
|
|
140
|
+
<div class="meeting-room">
|
|
141
|
+
<div ref="videoContainer" class="video-grid"></div>
|
|
142
|
+
<div class="controls">
|
|
143
|
+
<button @click="toggleMic">Silenciar Micrófono</button>
|
|
144
|
+
<button @click="toggleScreen">Compartir Pantalla</button>
|
|
145
|
+
<button @click="leave">Salir de la Reunión</button>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
</template>
|
|
149
|
+
|
|
150
|
+
<style scoped>
|
|
151
|
+
.video-grid {
|
|
152
|
+
display: grid;
|
|
153
|
+
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
154
|
+
gap: 12px;
|
|
155
|
+
}
|
|
156
|
+
.controls {
|
|
157
|
+
margin-top: 16px;
|
|
158
|
+
display: flex;
|
|
159
|
+
gap: 8px;
|
|
160
|
+
}
|
|
161
|
+
</style>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
### 4. Ejemplo con Angular (Standalone Component / TypeScript)
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
import { Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
|
170
|
+
import { CommonModule } from '@angular/common';
|
|
171
|
+
import { LocalMeet, LocalMeetRoom, RoomEvents } from '@encarnage7/local-meet';
|
|
172
|
+
|
|
173
|
+
@Component({
|
|
174
|
+
selector: 'app-video-meeting',
|
|
175
|
+
standalone: true,
|
|
176
|
+
imports: [CommonModule],
|
|
177
|
+
template: `
|
|
178
|
+
<div class="meeting-room">
|
|
179
|
+
<div #videoContainer class="video-grid"></div>
|
|
180
|
+
<div class="controls">
|
|
181
|
+
<button (click)="toggleScreen()">Compartir Pantalla</button>
|
|
182
|
+
<button (click)="toggleMic()">Silenciar</button>
|
|
183
|
+
<button (click)="leave()">Salir</button>
|
|
184
|
+
</div>
|
|
185
|
+
</div>
|
|
186
|
+
`,
|
|
187
|
+
styles: [`
|
|
188
|
+
.video-grid {
|
|
189
|
+
display: grid;
|
|
190
|
+
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
191
|
+
gap: 12px;
|
|
192
|
+
}
|
|
193
|
+
.controls {
|
|
194
|
+
margin-top: 16px;
|
|
195
|
+
display: flex;
|
|
196
|
+
gap: 8px;
|
|
197
|
+
}
|
|
198
|
+
`]
|
|
199
|
+
})
|
|
200
|
+
export class VideoMeetingComponent implements OnInit, OnDestroy {
|
|
201
|
+
@Input() roomHash!: string;
|
|
202
|
+
@Input() userName: string = 'Invitado';
|
|
203
|
+
@Input() serverUrl: string = 'https://tu-worker.workers.dev';
|
|
204
|
+
|
|
205
|
+
@ViewChild('videoContainer', { static: true }) videoContainer!: ElementRef<HTMLDivElement>;
|
|
206
|
+
|
|
207
|
+
private room?: LocalMeetRoom;
|
|
208
|
+
|
|
209
|
+
async ngOnInit() {
|
|
210
|
+
this.room = await LocalMeet.join({
|
|
211
|
+
serverUrl: this.serverUrl,
|
|
212
|
+
roomHash: this.roomHash,
|
|
213
|
+
guestName: this.userName,
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
this.room.on(RoomEvents.TrackSubscribed, (track) => {
|
|
217
|
+
const el = track.attach();
|
|
218
|
+
this.videoContainer.nativeElement.appendChild(el);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
this.room.on(RoomEvents.TrackUnsubscribed, (track) => {
|
|
222
|
+
track.detach().forEach((el) => el.remove());
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
await this.room.connect();
|
|
226
|
+
await this.room.enableCameraAndMicrophone();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
ngOnDestroy() {
|
|
230
|
+
this.room?.disconnect();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
toggleMic() {
|
|
234
|
+
this.room?.setMicrophoneEnabled(false);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
toggleScreen() {
|
|
238
|
+
this.room?.startScreenShare();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
leave() {
|
|
242
|
+
this.room?.disconnect();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
### 5. Ejemplo con React / Next.js
|
|
250
|
+
|
|
251
|
+
```jsx
|
|
252
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
253
|
+
import { LocalMeet, RoomEvents } from "@encarnage7/local-meet";
|
|
254
|
+
|
|
255
|
+
export function VideoMeeting({ roomHash, userName }) {
|
|
256
|
+
const [room, setRoom] = useState(null);
|
|
257
|
+
const containerRef = useRef(null);
|
|
258
|
+
|
|
259
|
+
useEffect(() => {
|
|
260
|
+
let currentRoom = null;
|
|
261
|
+
|
|
262
|
+
async function init() {
|
|
263
|
+
currentRoom = await LocalMeet.join({
|
|
264
|
+
serverUrl: "https://tu-worker.workers.dev",
|
|
265
|
+
roomHash,
|
|
266
|
+
guestName: userName,
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
currentRoom.on(RoomEvents.TrackSubscribed, (track) => {
|
|
270
|
+
const el = track.attach();
|
|
271
|
+
containerRef.current?.appendChild(el);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
currentRoom.on(RoomEvents.TrackUnsubscribed, (track) => {
|
|
275
|
+
track.detach().forEach((el) => el.remove());
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
await currentRoom.connect();
|
|
279
|
+
await currentRoom.enableCameraAndMicrophone();
|
|
280
|
+
setRoom(currentRoom);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
init();
|
|
284
|
+
|
|
285
|
+
return () => {
|
|
286
|
+
currentRoom?.disconnect();
|
|
287
|
+
};
|
|
288
|
+
}, [roomHash, userName]);
|
|
289
|
+
|
|
290
|
+
return (
|
|
291
|
+
<div>
|
|
292
|
+
<div ref={containerRef} className="video-grid" />
|
|
293
|
+
<button onClick={() => room?.setMicrophoneEnabled(false)}>Silenciar</button>
|
|
294
|
+
<button onClick={() => room?.startScreenShare()}>Compartir Pantalla</button>
|
|
295
|
+
<button onClick={() => room?.disconnect()}>Salir</button>
|
|
296
|
+
</div>
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
### 4. Ejemplo en HTML puro (Vanilla JS con CDN)
|
|
304
|
+
|
|
305
|
+
```html
|
|
306
|
+
<!DOCTYPE html>
|
|
307
|
+
<html lang="es">
|
|
308
|
+
<head>
|
|
309
|
+
<meta charset="UTF-8" />
|
|
310
|
+
<title>Mi Videollamada</title>
|
|
311
|
+
<script src="https://cdn.jsdelivr.net/npm/local-meet@1.0.0/dist/local-meet.min.js"></script>
|
|
312
|
+
</head>
|
|
313
|
+
<body>
|
|
314
|
+
<div id="videos" style="display: flex; gap: 10px;"></div>
|
|
315
|
+
<button id="btn-share">Compartir Pantalla</button>
|
|
316
|
+
|
|
317
|
+
<script>
|
|
318
|
+
async function start() {
|
|
319
|
+
const room = await LocalMeet.join({
|
|
320
|
+
serverUrl: "https://tu-worker.workers.dev",
|
|
321
|
+
roomHash: "c4f2-9k81-m3n2",
|
|
322
|
+
guestName: "Invitado Web",
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
room.on(LocalMeet.RoomEvents.TrackSubscribed, (track) => {
|
|
326
|
+
document.getElementById("videos").appendChild(track.attach());
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
await room.connect();
|
|
330
|
+
await room.enableCameraAndMicrophone();
|
|
331
|
+
|
|
332
|
+
document.getElementById("btn-share").onclick = () => {
|
|
333
|
+
room.startScreenShare();
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
start();
|
|
338
|
+
</script>
|
|
339
|
+
</body>
|
|
340
|
+
</html>
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## Referencia de API
|
|
346
|
+
|
|
347
|
+
### `LocalMeet`
|
|
348
|
+
- `LocalMeet.createRoom(options)`: Petición a `/api/host-join` y retorno de instancia `LocalMeetRoom`.
|
|
349
|
+
- `LocalMeet.join(options)`: Petición a `/api/guest-join` y retorno de instancia `LocalMeetRoom`.
|
|
350
|
+
- `LocalMeet.RoomEvents`: Constantes de eventos.
|
|
351
|
+
- `LocalMeet.TrackSources`: `Camera`, `Microphone`, `ScreenShare`.
|
|
352
|
+
- `LocalMeet.EngineModes`: `CloudflareCalls`, `P2PMesh`, `LiveKit`.
|
|
353
|
+
|
|
354
|
+
### `LocalMeetRoom`
|
|
355
|
+
#### Métodos de Medios
|
|
356
|
+
- `await room.connect()`: Inicia señalización y conexión WebRTC.
|
|
357
|
+
- `await room.enableCameraAndMicrophone()`: Solicita permisos y publica audio y video.
|
|
358
|
+
- `await room.setCameraEnabled(boolean)`: Enciende o apaga la cámara.
|
|
359
|
+
- `await room.setMicrophoneEnabled(boolean)`: Enciende o silencia el micrófono.
|
|
360
|
+
- `await room.startScreenShare()`: Inicia compartir pantalla con selección nativa del navegador.
|
|
361
|
+
- `await room.stopScreenShare()`: Detiene la pantalla compartida.
|
|
362
|
+
- `room.disconnect()`: Finaliza y destruye todos los tracks y conexiones locales.
|
|
363
|
+
|
|
364
|
+
#### Métodos de Datos y Chat (E2EE)
|
|
365
|
+
- `await room.sendData(payload, { reliable: true })`: Envía texto, objetos o `Uint8Array` cifrados de extremo a extremo a todos los participantes.
|
|
366
|
+
|
|
367
|
+
#### Controles de Anfitrión (Solo Anfitriones)
|
|
368
|
+
- `await room.admitGuest(identity)`: Admite a un invitado de la sala de espera.
|
|
369
|
+
- `await room.rejectGuest(identity)`: Rechaza a un invitado.
|
|
370
|
+
- `await room.muteParticipant(identity, trackSid)`: Silencia el micrófono de un participante.
|
|
371
|
+
- `await room.kickParticipant(identity)`: Expulsa a un participante.
|
|
372
|
+
- `await room.endRoom()`: Cierra la reunión para todos los asistentes.
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
## ¿Cómo Publicar esta Librería en NPM?
|
|
377
|
+
|
|
378
|
+
Si deseas publicar tu paquete bajo tu cuenta de npm (ej: `mi-libreria` o `@mi-usuario/local-meet`):
|
|
379
|
+
|
|
380
|
+
1. **Editar el nombre en `packages/sdk/package.json`:**
|
|
381
|
+
```json
|
|
382
|
+
{
|
|
383
|
+
"name": "tu-nombre-o-organizacion/local-meet"
|
|
384
|
+
}
|
|
385
|
+
```
|
|
386
|
+
2. **Iniciar sesión en NPM desde tu terminal:**
|
|
387
|
+
```bash
|
|
388
|
+
npm login
|
|
389
|
+
```
|
|
390
|
+
3. **Publicar el SDK:**
|
|
391
|
+
```bash
|
|
392
|
+
cd packages/sdk
|
|
393
|
+
npm publish --access public
|
|
394
|
+
```
|
|
395
|
+
4. **¡Listo!** En minutos estará disponible en:
|
|
396
|
+
- `npm install tu-libreria`
|
|
397
|
+
- `https://cdn.jsdelivr.net/npm/tu-libreria/dist/local-meet.min.js`
|
|
398
|
+
- `https://unpkg.com/tu-libreria/dist/local-meet.min.js`
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
## Licencia
|
|
403
|
+
|
|
404
|
+
MIT © Local Meet Contributors
|