@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 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
- # Temporary Holding Version
1
+ # Local Meet SDK (`local-meet`)
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
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
+ [![npm version](https://img.shields.io/npm/v/local-meet.svg)](https://www.npmjs.com/package/local-meet)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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