stinky-moq-js 0.1.0
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 +446 -0
- package/dist/broadcast.d.ts +21 -0
- package/dist/broadcast.d.ts.map +1 -0
- package/dist/event-emitter.d.ts +8 -0
- package/dist/event-emitter.d.ts.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +438 -0
- package/dist/index.mjs.map +1 -0
- package/dist/session.d.ts +70 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/subscription.d.ts +26 -0
- package/dist/subscription.d.ts.map +1 -0
- package/dist/types.d.ts +99 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +71 -0
package/README.md
ADDED
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
# Stinky MoQ JS
|
|
2
|
+
|
|
3
|
+
A TypeScript library for Media over Quic (MoQ) with session management, automatic reconnection, and simple API for browser applications. Built on top of [@kixelated/moq](https://github.com/kixelated/moq-js) using the moq-lite-draft-04 specification.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔄 **Infinite Reconnection**: Persistent reconnection with configurable delays
|
|
8
|
+
- 🔍 **Broadcast Discovery**: Automatic discovery of available broadcasts via announcements
|
|
9
|
+
- 📡 **Session Management**: Handle MoQ relay connections with namespace support
|
|
10
|
+
- 📤 **Broadcasting**: Send binary data to MoQ tracks with status reporting
|
|
11
|
+
- 📥 **Subscriptions**: Receive binary data from MoQ tracks with automatic retry
|
|
12
|
+
- 🎯 **Type Safe**: Full TypeScript support with comprehensive type definitions
|
|
13
|
+
- 🌐 **WebTransport Only**: Uses modern WebTransport for optimal performance
|
|
14
|
+
- 🔁 **Bidirectional Communication**: Full pub/sub support for two-way data exchange
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install stinky-moq-js
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { MoQSession, SessionState } from 'stinky-moq-js';
|
|
26
|
+
|
|
27
|
+
// Create a session
|
|
28
|
+
const session = new MoQSession({
|
|
29
|
+
relayUrl: 'wss://relay.quic.video',
|
|
30
|
+
namespace: 'my-app',
|
|
31
|
+
reconnection: {
|
|
32
|
+
delay: 1000 // Reconnect delay in ms
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Listen to session events
|
|
37
|
+
session.on('stateChange', (status) => {
|
|
38
|
+
console.log('Session state:', status.state);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
session.on('error', (error) => {
|
|
42
|
+
console.error('Session error:', error);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Listen for broadcast discoveries
|
|
46
|
+
session.on('broadcastAnnounced', (announcement) => {
|
|
47
|
+
console.log('Found broadcast:', announcement.path);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Connect to the relay
|
|
51
|
+
await session.connect();
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Working with Broadcasts
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
// Define what you want to broadcast
|
|
58
|
+
const broadcasts = [
|
|
59
|
+
{ trackName: 'my-video-track', priority: 1 },
|
|
60
|
+
{ trackName: 'my-audio-track', priority: 2 }
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
// Apply the configuration
|
|
64
|
+
session.updateBroadcasts(broadcasts);
|
|
65
|
+
|
|
66
|
+
// Listen to broadcast events through the session
|
|
67
|
+
session.on('broadcastStateChange', (trackName, status) => {
|
|
68
|
+
console.log(`Broadcast ${trackName} state:`, status.state);
|
|
69
|
+
console.log('Bytes sent:', status.bytesSent);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
session.on('broadcastError', (trackName, error) => {
|
|
73
|
+
console.error(`Broadcast error on ${trackName}:`, error.message);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Send binary data through the session
|
|
77
|
+
const data = new Uint8Array([1, 2, 3, 4, 5]);
|
|
78
|
+
await session.send('my-video-track', data);
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Working with Subscriptions
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
// Define what you want to subscribe to
|
|
85
|
+
const subscriptions = [
|
|
86
|
+
{ trackName: 'remote-video-track', priority: 1 },
|
|
87
|
+
{ trackName: 'remote-audio-track', priority: 2, retry: { delay: 2000 } }
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
// Apply the configuration
|
|
91
|
+
session.updateSubscriptions(subscriptions);
|
|
92
|
+
|
|
93
|
+
// Listen to incoming data through the session
|
|
94
|
+
session.on('data', (trackName, data) => {
|
|
95
|
+
console.log('Received data from:', trackName);
|
|
96
|
+
console.log('Data length:', data.length);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Listen to subscription status through the session
|
|
100
|
+
session.on('subscriptionStateChange', (trackName, status) => {
|
|
101
|
+
console.log(`Subscription ${trackName} state:`, status.state);
|
|
102
|
+
console.log('Bytes received:', status.bytesReceived);
|
|
103
|
+
console.log('Retry attempts:', status.retryAttempts);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
session.on('subscriptionError', (trackName, error) => {
|
|
107
|
+
console.error(`Subscription error on ${trackName}:`, error.message);
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Configuration
|
|
112
|
+
|
|
113
|
+
### Session Configuration
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
interface MoQSessionConfig {
|
|
117
|
+
relayUrl: string; // URL of the MoQ relay server
|
|
118
|
+
namespace: string; // MoQ namespace (broadcast name)
|
|
119
|
+
reconnection?: {
|
|
120
|
+
delay?: number; // Delay between attempts in ms (default: 1000)
|
|
121
|
+
};
|
|
122
|
+
connectionTimeout?: number; // Connection timeout in ms (default: 10000)
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Broadcast Configuration
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
interface BroadcastConfig {
|
|
130
|
+
trackName: string; // Track name for the broadcast
|
|
131
|
+
priority?: number; // Priority for the track (default: 0)
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Subscription Configuration
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
interface SubscriptionConfig {
|
|
139
|
+
trackName: string; // Track name to subscribe to
|
|
140
|
+
priority?: number; // Priority for the subscription (default: 0)
|
|
141
|
+
retry?: {
|
|
142
|
+
delay?: number; // Delay between retries in ms (default: 2000)
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Status Monitoring
|
|
148
|
+
|
|
149
|
+
### Session Status
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
const status = session.status;
|
|
153
|
+
console.log({
|
|
154
|
+
state: status.state, // SessionState enum
|
|
155
|
+
reconnectAttempts: status.reconnectAttempts,
|
|
156
|
+
lastError: status.lastError,
|
|
157
|
+
connectedAt: status.connectedAt
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Broadcast Status
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
const status = session.getBroadcastStatus('my-track');
|
|
165
|
+
if (status) {
|
|
166
|
+
console.log({
|
|
167
|
+
state: status.state, // BroadcastState enum
|
|
168
|
+
trackName: status.trackName,
|
|
169
|
+
bytesSent: status.bytesSent,
|
|
170
|
+
lastError: status.lastError
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Subscription Status
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
const status = session.getSubscriptionStatus('remote-track');
|
|
179
|
+
if (status) {
|
|
180
|
+
console.log({
|
|
181
|
+
state: status.state, // SubscriptionState enum
|
|
182
|
+
trackName: status.trackName,
|
|
183
|
+
bytesReceived: status.bytesReceived,
|
|
184
|
+
retryAttempts: status.retryAttempts,
|
|
185
|
+
lastError: status.lastError
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## States
|
|
191
|
+
|
|
192
|
+
### Session States
|
|
193
|
+
- `DISCONNECTED`: Not connected to relay
|
|
194
|
+
- `CONNECTING`: Attempting to connect
|
|
195
|
+
- `CONNECTED`: Successfully connected
|
|
196
|
+
- `RECONNECTING`: Attempting to reconnect after disconnect
|
|
197
|
+
- `FAILED`: Connection failed completely
|
|
198
|
+
|
|
199
|
+
### Broadcast States
|
|
200
|
+
- `IDLE`: Not broadcasting
|
|
201
|
+
- `BROADCASTING`: Actively broadcasting
|
|
202
|
+
- `ERROR`: Error occurred
|
|
203
|
+
|
|
204
|
+
### Subscription States
|
|
205
|
+
- `PENDING`: Waiting to subscribe
|
|
206
|
+
- `SUBSCRIBED`: Successfully subscribed
|
|
207
|
+
- `RETRYING`: Retrying after failure
|
|
208
|
+
- `FAILED`: Subscription failed completely
|
|
209
|
+
|
|
210
|
+
## Error Handling
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
// Session errors
|
|
214
|
+
session.on('error', (error) => {
|
|
215
|
+
console.error('Session error:', error.message);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// Broadcast errors (per track)
|
|
219
|
+
session.on('broadcastError', (trackName, error) => {
|
|
220
|
+
console.error(`Broadcast error on ${trackName}:`, error.message);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Subscription errors (per track)
|
|
224
|
+
session.on('subscriptionError', (trackName, error) => {
|
|
225
|
+
console.error(`Subscription error on ${trackName}:`, error.message);
|
|
226
|
+
});
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Cleanup
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
// Remove all broadcasts and subscriptions
|
|
233
|
+
session.updateBroadcasts([]); // Empty array removes all
|
|
234
|
+
session.updateSubscriptions([]);
|
|
235
|
+
|
|
236
|
+
// Or disconnect to cleanup everything
|
|
237
|
+
await session.disconnect();
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Broadcast Discovery
|
|
241
|
+
|
|
242
|
+
The library automatically discovers available broadcasts via MoQ announcements:
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
// Listen for broadcast announcements
|
|
246
|
+
session.on('broadcastAnnounced', (announcement) => {
|
|
247
|
+
console.log('Found broadcast:', announcement.path);
|
|
248
|
+
console.log('Is active:', announcement.active);
|
|
249
|
+
|
|
250
|
+
// Subscribe to discovered broadcast
|
|
251
|
+
if (announcement.active) {
|
|
252
|
+
session.updateSubscriptions([
|
|
253
|
+
{ trackName: announcement.path }
|
|
254
|
+
]);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Dynamic Track Management
|
|
260
|
+
|
|
261
|
+
The library provides a declarative API for managing broadcasts and subscriptions. Simply define the desired state and the library handles all add/remove/update operations automatically:
|
|
262
|
+
|
|
263
|
+
### Managing Broadcasts Dynamically
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
// Define the desired state of broadcasts
|
|
267
|
+
const desiredBroadcasts = [
|
|
268
|
+
{ trackName: 'video', priority: 1 },
|
|
269
|
+
{ trackName: 'audio', priority: 2 },
|
|
270
|
+
{ trackName: 'metadata', priority: 0 }
|
|
271
|
+
];
|
|
272
|
+
|
|
273
|
+
// Apply the desired state - library handles add/remove/update automatically
|
|
274
|
+
session.updateBroadcasts(desiredBroadcasts);
|
|
275
|
+
|
|
276
|
+
// Later, change the desired state
|
|
277
|
+
const newBroadcasts = [
|
|
278
|
+
{ trackName: 'video', priority: 1 }, // Kept
|
|
279
|
+
{ trackName: 'chat', priority: 0 } // Audio and metadata removed, chat added
|
|
280
|
+
];
|
|
281
|
+
|
|
282
|
+
session.updateBroadcasts(newBroadcasts);
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Managing Subscriptions Dynamically
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
// Define what you want to subscribe to
|
|
289
|
+
const desiredSubscriptions = [
|
|
290
|
+
{ trackName: 'remote-video', priority: 1 },
|
|
291
|
+
{ trackName: 'remote-audio', priority: 2, retry: { delay: 1000 } },
|
|
292
|
+
{ trackName: 'remote-chat', priority: 0 }
|
|
293
|
+
];
|
|
294
|
+
|
|
295
|
+
// Apply the desired state
|
|
296
|
+
session.updateSubscriptions(desiredSubscriptions);
|
|
297
|
+
|
|
298
|
+
// Update subscriptions based on user preferences
|
|
299
|
+
const userPreferences = {
|
|
300
|
+
videoEnabled: true,
|
|
301
|
+
audioEnabled: false,
|
|
302
|
+
chatEnabled: true
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
const activeSubscriptions = [
|
|
306
|
+
...(userPreferences.videoEnabled ? [{ trackName: 'remote-video', priority: 1 }] : []),
|
|
307
|
+
...(userPreferences.audioEnabled ? [{ trackName: 'remote-audio', priority: 2 }] : []),
|
|
308
|
+
...(userPreferences.chatEnabled ? [{ trackName: 'remote-chat', priority: 0 }] : [])
|
|
309
|
+
];
|
|
310
|
+
|
|
311
|
+
session.updateSubscriptions(activeSubscriptions);
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Benefits of Declarative API
|
|
315
|
+
|
|
316
|
+
- **Cleaner State Management**: Just declare what you want, not how to get there
|
|
317
|
+
- **Automatic Cleanup**: Tracks not in the desired state are automatically removed
|
|
318
|
+
- **Idempotent**: Calling `updateBroadcasts`/`updateSubscriptions` multiple times with the same config is safe
|
|
319
|
+
- **Less Error-Prone**: No need to manually track what's been added/removed
|
|
320
|
+
- **Reactive Updates**: Easy to update based on user preferences or external state changes
|
|
321
|
+
|
|
322
|
+
## Advanced Usage
|
|
323
|
+
|
|
324
|
+
### Managing Multiple Tracks
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
// Define multiple tracks at once
|
|
328
|
+
const broadcasts = [
|
|
329
|
+
{ trackName: 'video', priority: 1 },
|
|
330
|
+
{ trackName: 'audio', priority: 2 },
|
|
331
|
+
{ trackName: 'screen', priority: 3 }
|
|
332
|
+
];
|
|
333
|
+
|
|
334
|
+
const subscriptions = [
|
|
335
|
+
{ trackName: 'remote-video', priority: 1 },
|
|
336
|
+
{ trackName: 'remote-audio', priority: 2 },
|
|
337
|
+
{ trackName: 'remote-screen', priority: 3 }
|
|
338
|
+
];
|
|
339
|
+
|
|
340
|
+
session.updateBroadcasts(broadcasts);
|
|
341
|
+
session.updateSubscriptions(subscriptions);
|
|
342
|
+
|
|
343
|
+
// Get all active track names
|
|
344
|
+
const allBroadcasts = session.getAllBroadcastTrackNames();
|
|
345
|
+
const allSubscriptions = session.getAllSubscriptionTrackNames();
|
|
346
|
+
console.log('Broadcasting:', allBroadcasts);
|
|
347
|
+
console.log('Subscribed to:', allSubscriptions);
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### Infinite Retry Logic
|
|
351
|
+
|
|
352
|
+
Both reconnection and subscription retries run infinitely with configurable delays:
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
// Session with custom reconnection delay
|
|
356
|
+
const session = new MoQSession({
|
|
357
|
+
relayUrl: 'wss://relay.quic.video',
|
|
358
|
+
namespace: 'persistent-app',
|
|
359
|
+
reconnection: {
|
|
360
|
+
delay: 5000 // Wait 5 seconds between reconnection attempts
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
// Subscription with custom retry delay
|
|
365
|
+
session.updateSubscriptions([
|
|
366
|
+
{
|
|
367
|
+
trackName: 'important-track',
|
|
368
|
+
retry: {
|
|
369
|
+
delay: 3000 // Wait 3 seconds between retry attempts
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
]);
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Bidirectional Communication
|
|
376
|
+
|
|
377
|
+
Set up full bidirectional communication between two applications:
|
|
378
|
+
|
|
379
|
+
```typescript
|
|
380
|
+
// App A: Publisher and Subscriber
|
|
381
|
+
const sessionA = new MoQSession({
|
|
382
|
+
relayUrl: 'wss://relay.quic.video',
|
|
383
|
+
namespace: 'app-a'
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
await sessionA.connect();
|
|
387
|
+
|
|
388
|
+
// Set up tracks
|
|
389
|
+
sessionA.updateBroadcasts([{ trackName: 'data-from-a' }]);
|
|
390
|
+
sessionA.updateSubscriptions([{ trackName: 'data-from-b' }]);
|
|
391
|
+
|
|
392
|
+
// Listen for data from App B
|
|
393
|
+
sessionA.on('data', (trackName, data) => {
|
|
394
|
+
if (trackName === 'data-from-b') {
|
|
395
|
+
console.log('App A received data from App B:', data);
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
// App B: Subscriber and Publisher
|
|
400
|
+
const sessionB = new MoQSession({
|
|
401
|
+
relayUrl: 'wss://relay.quic.video',
|
|
402
|
+
namespace: 'app-b'
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
await sessionB.connect();
|
|
406
|
+
|
|
407
|
+
// Set up tracks
|
|
408
|
+
sessionB.updateSubscriptions([{ trackName: 'data-from-a' }]);
|
|
409
|
+
sessionB.updateBroadcasts([{ trackName: 'data-from-b' }]);
|
|
410
|
+
|
|
411
|
+
// Listen for data from App A
|
|
412
|
+
sessionB.on('data', (trackName, data) => {
|
|
413
|
+
if (trackName === 'data-from-a') {
|
|
414
|
+
console.log('App B received data from App A:', data);
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// Send data from App A to App B
|
|
419
|
+
await sessionA.send('data-from-a', new TextEncoder().encode('Hello from A'));
|
|
420
|
+
|
|
421
|
+
// Send data from App B to App A
|
|
422
|
+
await sessionB.send('data-from-b', new TextEncoder().encode('Hello from B'));
|
|
423
|
+
```
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
## Dependencies
|
|
427
|
+
|
|
428
|
+
This library is built on top of:
|
|
429
|
+
- [@kixelated/moq](https://github.com/kixelated/moq-js) v0.9.1 - Core MoQ implementation
|
|
430
|
+
- Uses the `moq-lite-draft-04` specification (not the standard IETF MoQ draft)
|
|
431
|
+
- WebTransport only (no WebSocket fallback)
|
|
432
|
+
|
|
433
|
+
## Browser Support
|
|
434
|
+
|
|
435
|
+
Requires modern browsers with WebTransport support:
|
|
436
|
+
- Chrome 97+
|
|
437
|
+
- Edge 97+
|
|
438
|
+
- Safari 15.4+ (with WebTransport enabled)
|
|
439
|
+
|
|
440
|
+
## License
|
|
441
|
+
|
|
442
|
+
MIT
|
|
443
|
+
|
|
444
|
+
## Contributing
|
|
445
|
+
|
|
446
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Connection } from '@kixelated/moq';
|
|
2
|
+
import { EventEmitter } from './event-emitter';
|
|
3
|
+
import { BroadcastConfig, BroadcastStatus, BroadcastEvents } from './types';
|
|
4
|
+
export declare class MoQBroadcast extends EventEmitter<BroadcastEvents> {
|
|
5
|
+
private _config;
|
|
6
|
+
private state;
|
|
7
|
+
private bytesSent;
|
|
8
|
+
private lastError?;
|
|
9
|
+
private track?;
|
|
10
|
+
private broadcast?;
|
|
11
|
+
constructor(config: BroadcastConfig);
|
|
12
|
+
get status(): BroadcastStatus;
|
|
13
|
+
get config(): BroadcastConfig;
|
|
14
|
+
start(connection: Connection.Established, namespace: string): Promise<void>;
|
|
15
|
+
private handleTrackRequests;
|
|
16
|
+
send(data: Uint8Array): Promise<void>;
|
|
17
|
+
stop(): Promise<void>;
|
|
18
|
+
private setState;
|
|
19
|
+
private handleError;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=broadcast.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"broadcast.d.ts","sourceRoot":"","sources":["../src/broadcast.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,UAAU,EAAQ,MAAM,gBAAgB,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EACL,eAAe,EAEf,eAAe,EACf,eAAe,EAChB,MAAM,SAAS,CAAC;AAEjB,qBAAa,YAAa,SAAQ,YAAY,CAAC,eAAe,CAAC;IAC7D,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,KAAK,CAAuC;IACpD,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,SAAS,CAAC,CAAQ;IAC1B,OAAO,CAAC,KAAK,CAAC,CAAQ;IACtB,OAAO,CAAC,SAAS,CAAC,CAAY;gBAElB,MAAM,EAAE,eAAe;IAKnC,IAAI,MAAM,IAAI,eAAe,CAO5B;IAED,IAAI,MAAM,IAAI,eAAe,CAE5B;IAEK,KAAK,CAAC,UAAU,EAAE,UAAU,CAAC,WAAW,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAoBnE,mBAAmB;IAmB3B,IAAI,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAcrC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAsB3B,OAAO,CAAC,QAAQ;IAOhB,OAAO,CAAC,WAAW;CAKpB"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare class EventEmitter<T extends Record<string, (...args: any[]) => void>> {
|
|
2
|
+
private listeners;
|
|
3
|
+
on<K extends keyof T>(event: K, listener: T[K]): void;
|
|
4
|
+
off<K extends keyof T>(event: K, listener: T[K]): void;
|
|
5
|
+
emit<K extends keyof T>(event: K, ...args: Parameters<T[K]>): void;
|
|
6
|
+
removeAllListeners<K extends keyof T>(event?: K): void;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=event-emitter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-emitter.d.ts","sourceRoot":"","sources":["../src/event-emitter.ts"],"names":[],"mappings":"AAEA,qBAAa,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;IAC1E,OAAO,CAAC,SAAS,CAAuC;IAExD,EAAE,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI;IAOrD,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI;IAOtD,IAAI,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI;IAalE,kBAAkB,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC,GAAG,IAAI;CAOvD"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const m=require("@kixelated/moq");class p{constructor(){this.listeners=new Map}on(t,s){this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(s)}off(t,s){const e=this.listeners.get(t);e&&e.delete(s)}emit(t,...s){const e=this.listeners.get(t);e&&e.forEach(c=>{try{c(...s)}catch(n){console.error("Error in event listener:",n)}})}removeAllListeners(t){t?this.listeners.delete(t):this.listeners.clear()}}var o=(r=>(r.DISCONNECTED="disconnected",r.CONNECTING="connecting",r.CONNECTED="connected",r.RECONNECTING="reconnecting",r.FAILED="failed",r))(o||{}),d=(r=>(r.IDLE="idle",r.BROADCASTING="broadcasting",r.ERROR="error",r))(d||{}),l=(r=>(r.PENDING="pending",r.SUBSCRIBED="subscribed",r.RETRYING="retrying",r.FAILED="failed",r))(l||{});class b extends p{constructor(t){super(),this.state=d.IDLE,this.bytesSent=0,this._config=t}get status(){return{state:this.state,trackName:this._config.trackName,bytesSent:this.bytesSent,lastError:this.lastError}}get config(){return{...this._config}}async start(t,s){try{this.setState(d.BROADCASTING),this.broadcast=new m.Broadcast;const e=m.Path.from(s);t.publish(e,this.broadcast),this.handleTrackRequests()}catch(e){throw this.handleError(e),e}}async handleTrackRequests(){if(this.broadcast)try{for(;;){const t=await this.broadcast.requested();if(!t)break;t.track.name===this._config.trackName&&(this.track=t.track)}}catch(t){console.warn("Error handling track requests:",t),this.handleError(t)}}async send(t){if(this.state!==d.BROADCASTING||!this.track)throw new Error("Broadcast is not active");try{this.track.writeFrame(t),this.bytesSent+=t.length}catch(s){throw this.handleError(s),s}}async stop(){if(this.track){try{this.track.close()}catch(t){console.warn("Error closing track:",t)}this.track=void 0}if(this.broadcast){try{this.broadcast.close()}catch(t){console.warn("Error closing broadcast:",t)}this.broadcast=void 0}this.setState(d.IDLE)}setState(t){this.state!==t&&(this.state=t,this.emit("stateChange",this.status))}handleError(t){this.lastError=t,this.setState(d.ERROR),this.emit("error",t)}}class f extends p{constructor(t){super(),this.state=l.PENDING,this.bytesReceived=0,this.retryAttempts=0,this._config=t}get status(){return{state:this.state,trackName:this._config.trackName,bytesReceived:this.bytesReceived,retryAttempts:this.retryAttempts,lastError:this.lastError}}get config(){return{...this._config}}async start(t,s){this.connection=t,this.namespace=s;try{await this.subscribe(t,s)}catch(e){this.handleError(e),this.scheduleRetry(t,s)}}async subscribe(t,s){this.setState(l.PENDING);const e=m.Path.from(s);this.broadcast=t.consume(e),this.subscription=this.broadcast.subscribe(this._config.trackName,this._config.priority||0),this.setState(l.SUBSCRIBED),this.retryAttempts=0,this.readFrames()}async readFrames(){if(this.subscription)try{for(;;){const t=await this.subscription.nextGroup();if(!t)break;for(;;){const s=await t.readFrame();if(!s)break;this.bytesReceived+=s.length;const e={trackName:this._config.trackName,data:s,timestamp:Date.now()};this.emit("data",e)}}}catch(t){this.handleError(t),this.connection&&this.namespace&&this.scheduleRetry(this.connection,this.namespace)}}scheduleRetry(t,s){var c;const e=((c=this._config.retry)==null?void 0:c.delay)||2e3;this.setState(l.RETRYING),this.retryAttempts++,this.retryTimer=setTimeout(async()=>{try{await this.subscribe(t,s)}catch(n){this.handleError(n),this.scheduleRetry(t,s)}},e)}async stop(){if(this.retryTimer&&(clearTimeout(this.retryTimer),this.retryTimer=void 0),this.subscription){try{this.subscription.close()}catch(t){console.warn("Error closing subscription:",t)}this.subscription=void 0}if(this.broadcast){try{this.broadcast.close()}catch(t){console.warn("Error closing broadcast:",t)}this.broadcast=void 0}this.setState(l.PENDING)}setState(t){this.state!==t&&(this.state=t,this.emit("stateChange",this.status))}handleError(t){this.lastError=t,this.emit("error",t)}}class y extends p{constructor(t){var s;super(),this.state=o.DISCONNECTED,this.reconnectAttempts=0,this.broadcasts=new Map,this.subscriptions=new Map,this.announcedBroadcasts=new Set,console.log("AJAOFJODJFADJFJDASOFJADOSFJ"),this.config={...t,reconnection:{delay:Math.max(((s=t.reconnection)==null?void 0:s.delay)||1e3,1e3)},connectionTimeout:t.connectionTimeout||1e4}}get status(){return{state:this.state,reconnectAttempts:this.reconnectAttempts,lastError:this.lastError,connectedAt:this.connectedAt}}async connect(){if(!(this.state===o.CONNECTING||this.state===o.CONNECTED)){this.setState(o.CONNECTING);try{this.connection=await m.Connection.connect(new URL(this.config.relayUrl),{websocket:{enabled:!1}}),this.setState(o.CONNECTED),this.connectedAt=new Date,this.reconnectAttempts=0,this.connection.closed.then(()=>{this.state===o.CONNECTED&&this.handleDisconnection()}),this.startAnnouncementDiscovery(),await this.restartBroadcasts(),await this.restartSubscriptions()}catch(t){throw this.handleError(t),this.scheduleReconnect(),t}}}async disconnect(){if(this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0),await this.stopAllBroadcasts(),await this.stopAllSubscriptions(),this.connection){try{this.connection.close()}catch(t){console.warn("Error closing connection:",t)}this.connection=void 0}this.setState(o.DISCONNECTED),this.connectedAt=void 0}getAllBroadcastTrackNames(){return Array.from(this.broadcasts.keys())}getAllSubscriptionTrackNames(){return Array.from(this.subscriptions.keys())}updateBroadcasts(t){const s=t.map(i=>i.trackName),e=new Set(s);if(s.length!==e.size){const i=s.filter((h,a)=>s.indexOf(h)!==a);throw new Error(`Duplicate broadcast track name: ${i[0]}`)}const c=new Set(t.map(i=>i.trackName)),n=new Set(this.broadcasts.keys());for(const i of n)c.has(i)||this._removeBroadcast(i);for(const i of t){const h=this.broadcasts.get(i.trackName);h?h.config.priority!==i.priority&&(this._removeBroadcast(i.trackName),this._createBroadcast(i)):this._createBroadcast(i)}}updateSubscriptions(t){var i,h;const s=t.map(a=>a.trackName),e=new Set(s);if(s.length!==e.size){const a=s.filter((u,E)=>s.indexOf(u)!==E);throw new Error(`Duplicate subscription track name: ${a[0]}`)}const c=new Set(t.map(a=>a.trackName)),n=new Set(this.subscriptions.keys());for(const a of n)c.has(a)||this._removeSubscription(a);for(const a of t){const u=this.subscriptions.get(a.trackName);u?(u.config.priority!==a.priority||((i=u.config.retry)==null?void 0:i.delay)!==((h=a.retry)==null?void 0:h.delay))&&(this._removeSubscription(a.trackName),this._createSubscription(a)):this._createSubscription(a)}}getAnnouncedBroadcasts(){return Array.from(this.announcedBroadcasts)}async send(t,s){const e=this.broadcasts.get(t);if(!e)throw new Error(`No broadcast found for track: ${t}`);await e.send(s)}getBroadcastStatus(t){const s=this.broadcasts.get(t);return s==null?void 0:s.status}getSubscriptionStatus(t){const s=this.subscriptions.get(t);return s==null?void 0:s.status}_createBroadcast(t){const s=new b(t);return this.broadcasts.set(t.trackName,s),s.on("stateChange",e=>{this.emit("broadcastStateChange",t.trackName,e)}),s.on("error",e=>{this.emit("broadcastError",t.trackName,e)}),this.state===o.CONNECTED&&this.connection&&s.start(this.connection,this.config.namespace).catch(e=>{console.error("Failed to start broadcast:",e)}),s}_createSubscription(t){const s=new f(t);return this.subscriptions.set(t.trackName,s),s.on("data",e=>{this.emit("data",e.trackName,e.data)}),s.on("stateChange",e=>{this.emit("subscriptionStateChange",t.trackName,e)}),s.on("error",e=>{this.emit("subscriptionError",t.trackName,e)}),this.state===o.CONNECTED&&this.connection&&s.start(this.connection,this.config.namespace).catch(e=>{console.error("Failed to start subscription:",e)}),s}_removeBroadcast(t){const s=this.broadcasts.get(t);s&&(s.stop().catch(e=>{console.warn("Error stopping broadcast:",e)}),this.broadcasts.delete(t))}_removeSubscription(t){const s=this.subscriptions.get(t);s&&(s.stop().catch(e=>{console.warn("Error stopping subscription:",e)}),this.subscriptions.delete(t))}async startAnnouncementDiscovery(){if(this.connection)try{const t=m.Path.from(this.config.namespace),s=this.connection.announced(t);this.processAnnouncements(s)}catch(t){console.error("Failed to start announcement discovery:",t)}}async processAnnouncements(t){try{for(;;){const s=await t.next();if(!s)break;s.active?(this.announcedBroadcasts.add(s.path),this.emit("broadcastAnnounced",{path:s.path,active:!0})):(this.announcedBroadcasts.delete(s.path),this.emit("broadcastAnnounced",{path:s.path,active:!1}))}}catch(s){console.error("Error processing announcements:",s)}}async restartBroadcasts(){if(!this.connection)return;const t=Array.from(this.broadcasts.values()).map(s=>s.start(this.connection,this.config.namespace).catch(e=>{console.error("Failed to restart broadcast:",e)}));await Promise.allSettled(t)}async restartSubscriptions(){if(!this.connection)return;const t=Array.from(this.subscriptions.values()).map(s=>s.start(this.connection,this.config.namespace).catch(e=>{console.error("Failed to restart subscription:",e)}));await Promise.allSettled(t)}async stopAllBroadcasts(){const t=Array.from(this.broadcasts.values()).map(s=>s.stop().catch(e=>{console.warn("Error stopping broadcast:",e)}));await Promise.allSettled(t)}async stopAllSubscriptions(){const t=Array.from(this.subscriptions.values()).map(s=>s.stop().catch(e=>{console.warn("Error stopping subscription:",e)}));await Promise.allSettled(t)}handleDisconnection(){this.setState(o.DISCONNECTED),this.scheduleReconnect()}scheduleReconnect(){var s;const t=((s=this.config.reconnection)==null?void 0:s.delay)||1e3;this.setState(o.RECONNECTING),this.reconnectAttempts++,this.reconnectTimer=setTimeout(()=>{this.connect().catch(e=>{console.error("Reconnection failed:",e)})},t)}setState(t){this.state!==t&&(this.state=t,this.emit("stateChange",this.status))}handleError(t){this.lastError=t,this.emit("error",t)}}exports.BroadcastState=d;exports.MoQBroadcast=b;exports.MoQSession=y;exports.MoQSubscription=f;exports.SessionState=o;exports.SubscriptionState=l;exports.default=y;
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/event-emitter.ts","../src/types.ts","../src/broadcast.ts","../src/subscription.ts","../src/session.ts"],"sourcesContent":["// Simple event emitter for type-safe events\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport class EventEmitter<T extends Record<string, (...args: any[]) => void>> {\n private listeners = new Map<keyof T, Set<T[keyof T]>>();\n\n on<K extends keyof T>(event: K, listener: T[K]): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(listener);\n }\n\n off<K extends keyof T>(event: K, listener: T[K]): void {\n const eventListeners = this.listeners.get(event);\n if (eventListeners) {\n eventListeners.delete(listener);\n }\n }\n\n emit<K extends keyof T>(event: K, ...args: Parameters<T[K]>): void {\n const eventListeners = this.listeners.get(event);\n if (eventListeners) {\n eventListeners.forEach(listener => {\n try {\n listener(...args);\n } catch (error) {\n console.error('Error in event listener:', error);\n }\n });\n }\n }\n\n removeAllListeners<K extends keyof T>(event?: K): void {\n if (event) {\n this.listeners.delete(event);\n } else {\n this.listeners.clear();\n }\n }\n}","// Types for the library\nexport interface MoQSessionConfig {\n /** URL of the MoQ relay server */\n relayUrl: string;\n /** MoQ namespace (broadcast name) */\n namespace: string;\n /** Reconnection configuration */\n reconnection?: {\n /** Delay between reconnection attempts in ms (minimum: 1000) */\n delay?: number;\n };\n /** Connection timeout in ms (default: 10000) */\n connectionTimeout?: number;\n}\n\nexport interface BroadcastConfig {\n /** Track name for the broadcast */\n trackName: string;\n /** Priority for the track (default: 0) */\n priority?: number;\n}\n\nexport interface SubscriptionConfig {\n /** Track name to subscribe to */\n trackName: string;\n /** Priority for the subscription (default: 0) */\n priority?: number;\n /** Retry configuration for failed subscriptions */\n retry?: {\n /** Delay between retry attempts in ms (default: 2000) */\n delay?: number;\n };\n}\n\nexport enum SessionState {\n DISCONNECTED = 'disconnected',\n CONNECTING = 'connecting',\n CONNECTED = 'connected',\n RECONNECTING = 'reconnecting',\n FAILED = 'failed'\n}\n\nexport enum BroadcastState {\n IDLE = 'idle',\n BROADCASTING = 'broadcasting',\n ERROR = 'error'\n}\n\nexport enum SubscriptionState {\n PENDING = 'pending',\n SUBSCRIBED = 'subscribed',\n RETRYING = 'retrying',\n FAILED = 'failed'\n}\n\nexport interface SessionStatus {\n state: SessionState;\n reconnectAttempts: number;\n lastError?: Error;\n connectedAt?: Date;\n}\n\nexport interface BroadcastStatus {\n state: BroadcastState;\n trackName: string;\n bytesSent: number;\n lastError?: Error;\n}\n\nexport interface SubscriptionStatus {\n state: SubscriptionState;\n trackName: string;\n bytesReceived: number;\n retryAttempts: number;\n lastError?: Error;\n}\n\nexport interface DataReceived {\n trackName: string;\n data: Uint8Array;\n timestamp: number;\n}\n\n// Event types\nexport interface BroadcastAnnouncement {\n path: string;\n active: boolean;\n}\n\nexport interface SessionEvents {\n stateChange: (status: SessionStatus) => void;\n error: (error: Error) => void;\n broadcastAnnounced: (announcement: BroadcastAnnouncement) => void;\n data: (trackName: string, data: Uint8Array) => void;\n broadcastStateChange: (trackName: string, status: BroadcastStatus) => void;\n subscriptionStateChange: (trackName: string, status: SubscriptionStatus) => void;\n broadcastError: (trackName: string, error: Error) => void;\n subscriptionError: (trackName: string, error: Error) => void;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n [key: string]: (...args: any[]) => void;\n}\n\nexport interface BroadcastEvents {\n stateChange: (status: BroadcastStatus) => void;\n error: (error: Error) => void;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n [key: string]: (...args: any[]) => void;\n}\n\nexport interface SubscriptionEvents {\n stateChange: (status: SubscriptionStatus) => void;\n data: (data: DataReceived) => void;\n error: (error: Error) => void;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n [key: string]: (...args: any[]) => void;\n}","import { Broadcast, Track, Connection, Path } from '@kixelated/moq';\nimport { EventEmitter } from './event-emitter';\nimport {\n BroadcastConfig,\n BroadcastState,\n BroadcastStatus,\n BroadcastEvents\n} from './types';\n\nexport class MoQBroadcast extends EventEmitter<BroadcastEvents> {\n private _config: BroadcastConfig;\n private state: BroadcastState = BroadcastState.IDLE;\n private bytesSent = 0;\n private lastError?: Error;\n private track?: Track;\n private broadcast?: Broadcast;\n\n constructor(config: BroadcastConfig) {\n super();\n this._config = config;\n }\n\n get status(): BroadcastStatus {\n return {\n state: this.state,\n trackName: this._config.trackName,\n bytesSent: this.bytesSent,\n lastError: this.lastError\n };\n }\n\n get config(): BroadcastConfig {\n return { ...this._config };\n }\n\n async start(connection: Connection.Established, namespace: string): Promise<void> {\n try {\n this.setState(BroadcastState.BROADCASTING);\n \n // Create broadcast for publishing\n this.broadcast = new Broadcast();\n \n // Publish the broadcast to the connection using namespace as broadcast ID\n const broadcastPath = Path.from(namespace);\n connection.publish(broadcastPath, this.broadcast);\n \n // Handle incoming track requests\n this.handleTrackRequests();\n \n } catch (error) {\n this.handleError(error as Error);\n throw error;\n }\n }\n\n private async handleTrackRequests(): Promise<void> {\n if (!this.broadcast) return;\n \n try {\n for (;;) {\n const request = await this.broadcast.requested();\n if (!request) break; // Broadcast is closed\n \n // Check if this is the track we want to serve\n if (request.track.name === this._config.trackName) {\n this.track = request.track;\n }\n }\n } catch (error) {\n console.warn('Error handling track requests:', error);\n this.handleError(error as Error);\n }\n }\n\n async send(data: Uint8Array): Promise<void> {\n if (this.state !== BroadcastState.BROADCASTING || !this.track) {\n throw new Error('Broadcast is not active');\n }\n\n try {\n this.track.writeFrame(data);\n this.bytesSent += data.length;\n } catch (error) {\n this.handleError(error as Error);\n throw error;\n }\n }\n\n async stop(): Promise<void> {\n if (this.track) {\n try {\n this.track.close();\n } catch (error) {\n console.warn('Error closing track:', error);\n }\n this.track = undefined;\n }\n \n if (this.broadcast) {\n try {\n this.broadcast.close();\n } catch (error) {\n console.warn('Error closing broadcast:', error);\n }\n this.broadcast = undefined;\n }\n \n this.setState(BroadcastState.IDLE);\n }\n\n private setState(newState: BroadcastState): void {\n if (this.state !== newState) {\n this.state = newState;\n this.emit('stateChange', this.status);\n }\n }\n\n private handleError(error: Error): void {\n this.lastError = error;\n this.setState(BroadcastState.ERROR);\n this.emit('error', error);\n }\n}","import { Broadcast, Track, Connection, Path } from '@kixelated/moq';\nimport { EventEmitter } from './event-emitter';\nimport {\n SubscriptionConfig,\n SubscriptionState,\n SubscriptionStatus,\n SubscriptionEvents,\n DataReceived\n} from './types';\n\nexport class MoQSubscription extends EventEmitter<SubscriptionEvents> {\n private _config: SubscriptionConfig;\n private state: SubscriptionState = SubscriptionState.PENDING;\n private bytesReceived = 0;\n private retryAttempts = 0;\n private lastError?: Error;\n private subscription?: Track;\n private broadcast?: Broadcast;\n private retryTimer?: ReturnType<typeof setTimeout>;\n private connection?: Connection.Established;\n private namespace?: string;\n\n constructor(config: SubscriptionConfig) {\n super();\n this._config = config;\n }\n\n get status(): SubscriptionStatus {\n return {\n state: this.state,\n trackName: this._config.trackName,\n bytesReceived: this.bytesReceived,\n retryAttempts: this.retryAttempts,\n lastError: this.lastError\n };\n }\n\n get config(): SubscriptionConfig {\n return { ...this._config };\n }\n\n async start(connection: Connection.Established, namespace: string): Promise<void> {\n this.connection = connection;\n this.namespace = namespace;\n try {\n await this.subscribe(connection, namespace);\n } catch (error) {\n this.handleError(error as Error);\n this.scheduleRetry(connection, namespace);\n }\n }\n\n private async subscribe(connection: Connection.Established, namespace: string): Promise<void> {\n this.setState(SubscriptionState.PENDING);\n \n const broadcastPath = Path.from(namespace);\n this.broadcast = connection.consume(broadcastPath);\n \n // Subscribe to the specific track within the broadcast\n this.subscription = this.broadcast.subscribe(this._config.trackName, this._config.priority || 0);\n this.setState(SubscriptionState.SUBSCRIBED);\n this.retryAttempts = 0;\n \n // Set up data reception\n this.readFrames();\n }\n\n private async readFrames(): Promise<void> {\n if (!this.subscription) return;\n \n try {\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const group = await this.subscription.nextGroup();\n if (!group) break; // Track is closed\n \n // Read all frames from the group\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const frame = await group.readFrame();\n if (!frame) break; // Group is finished\n \n this.bytesReceived += frame.length;\n const receivedData: DataReceived = {\n trackName: this._config.trackName,\n data: frame,\n timestamp: Date.now()\n };\n this.emit('data', receivedData);\n }\n }\n } catch (error) {\n this.handleError(error as Error);\n // Trigger retry when stream gets reset/closed\n if (this.connection && this.namespace) {\n this.scheduleRetry(this.connection, this.namespace);\n }\n }\n } private scheduleRetry(connection: Connection.Established, namespace: string): void {\n const delay = this._config.retry?.delay || 2000;\n\n this.setState(SubscriptionState.RETRYING);\n this.retryAttempts++;\n\n this.retryTimer = setTimeout(async () => {\n try {\n await this.subscribe(connection, namespace);\n } catch (error) {\n this.handleError(error as Error);\n this.scheduleRetry(connection, namespace);\n }\n }, delay);\n }\n\n async stop(): Promise<void> {\n if (this.retryTimer) {\n clearTimeout(this.retryTimer);\n this.retryTimer = undefined;\n }\n\n if (this.subscription) {\n try {\n this.subscription.close();\n } catch (error) {\n console.warn('Error closing subscription:', error);\n }\n this.subscription = undefined;\n }\n\n if (this.broadcast) {\n try {\n this.broadcast.close();\n } catch (error) {\n console.warn('Error closing broadcast:', error);\n }\n this.broadcast = undefined;\n }\n\n this.setState(SubscriptionState.PENDING);\n }\n\n private setState(newState: SubscriptionState): void {\n if (this.state !== newState) {\n this.state = newState;\n this.emit('stateChange', this.status);\n }\n }\n\n private handleError(error: Error): void {\n this.lastError = error;\n this.emit('error', error);\n }\n}","import { Connection, Path } from '@kixelated/moq';\nimport { EventEmitter } from './event-emitter';\nimport { MoQBroadcast } from './broadcast';\nimport { MoQSubscription } from './subscription';\nimport {\n MoQSessionConfig,\n SessionState,\n SessionStatus,\n SessionEvents,\n BroadcastConfig,\n SubscriptionConfig\n} from './types';\n\nexport class MoQSession extends EventEmitter<SessionEvents> {\n private config: MoQSessionConfig;\n private state: SessionState = SessionState.DISCONNECTED;\n private connection?: Connection.Established;\n private reconnectAttempts = 0;\n private lastError?: Error;\n private connectedAt?: Date;\n private reconnectTimer?: ReturnType<typeof setTimeout>;\n private broadcasts = new Map<string, MoQBroadcast>();\n private subscriptions = new Map<string, MoQSubscription>();\n private announcedBroadcasts = new Set<string>();\n\n constructor(config: MoQSessionConfig) {\n super();\n console.log('AJAOFJODJFADJFJDASOFJADOSFJ')\n this.config = {\n ...config,\n reconnection: {\n delay: Math.max(config.reconnection?.delay || 1000, 1000), // Enforce minimum 1 second\n },\n connectionTimeout: config.connectionTimeout || 10000\n };\n }\n\n get status(): SessionStatus {\n return {\n state: this.state,\n reconnectAttempts: this.reconnectAttempts,\n lastError: this.lastError,\n connectedAt: this.connectedAt\n };\n }\n\n async connect(): Promise<void> {\n if (this.state === SessionState.CONNECTING || this.state === SessionState.CONNECTED) {\n return;\n }\n\n this.setState(SessionState.CONNECTING);\n\n try {\n // Connect using the @kixelated/moq library with WebSocket fallback disabled\n this.connection = await Connection.connect(new URL(this.config.relayUrl), {\n websocket: {\n enabled: false // Disable WebSocket fallback\n }\n });\n\n this.setState(SessionState.CONNECTED);\n this.connectedAt = new Date();\n this.reconnectAttempts = 0;\n\n // Set up connection close handling\n this.connection.closed.then(() => {\n if (this.state === SessionState.CONNECTED) {\n this.handleDisconnection();\n }\n });\n\n // Start discovering announced broadcasts\n this.startAnnouncementDiscovery();\n\n // Restart all broadcasts and subscriptions\n await this.restartBroadcasts();\n await this.restartSubscriptions();\n\n } catch (error) {\n this.handleError(error as Error);\n this.scheduleReconnect();\n throw error;\n }\n }\n\n async disconnect(): Promise<void> {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = undefined;\n }\n\n // Stop all broadcasts and subscriptions\n await this.stopAllBroadcasts();\n await this.stopAllSubscriptions();\n\n if (this.connection) {\n try {\n this.connection.close();\n } catch (error) {\n console.warn('Error closing connection:', error);\n }\n this.connection = undefined;\n }\n\n this.setState(SessionState.DISCONNECTED);\n this.connectedAt = undefined;\n }\n\n /**\n * Get all active broadcast track names\n */\n getAllBroadcastTrackNames(): string[] {\n return Array.from(this.broadcasts.keys());\n }\n\n /**\n * Get all active subscription track names\n */\n getAllSubscriptionTrackNames(): string[] {\n return Array.from(this.subscriptions.keys());\n }\n\n /**\n * Declaratively update broadcasts - add, remove, or update broadcasts to match the desired state.\n * \n * @param configs Array of broadcast configurations representing the desired state\n */\n updateBroadcasts(configs: BroadcastConfig[]): void {\n // Check for duplicate track names in the input\n const trackNames = configs.map(config => config.trackName);\n const uniqueTrackNames = new Set(trackNames);\n if (trackNames.length !== uniqueTrackNames.size) {\n const duplicates = trackNames.filter((name, index) => trackNames.indexOf(name) !== index);\n throw new Error(`Duplicate broadcast track name: ${duplicates[0]}`);\n }\n\n const desiredTracks = new Set(configs.map(config => config.trackName));\n const currentTracks = new Set(this.broadcasts.keys());\n\n // Remove broadcasts that are no longer needed\n for (const trackName of currentTracks) {\n if (!desiredTracks.has(trackName)) {\n this._removeBroadcast(trackName);\n }\n }\n\n // Add new broadcasts or update existing ones\n for (const config of configs) {\n const existing = this.broadcasts.get(config.trackName);\n if (!existing) {\n // Create new broadcast\n this._createBroadcast(config);\n } else {\n // Update existing broadcast config if needed\n if (existing.config.priority !== config.priority) {\n // For now, we recreate if priority changed\n // In future, we could support dynamic priority updates\n this._removeBroadcast(config.trackName);\n this._createBroadcast(config);\n }\n }\n }\n }\n\n /**\n * Declaratively update subscriptions - add, remove, or update subscriptions to match the desired state.\n * \n * @param configs Array of subscription configurations representing the desired state\n */\n updateSubscriptions(configs: SubscriptionConfig[]): void {\n // Check for duplicate track names in the input\n const trackNames = configs.map(config => config.trackName);\n const uniqueTrackNames = new Set(trackNames);\n if (trackNames.length !== uniqueTrackNames.size) {\n const duplicates = trackNames.filter((name, index) => trackNames.indexOf(name) !== index);\n throw new Error(`Duplicate subscription track name: ${duplicates[0]}`);\n }\n\n const desiredTracks = new Set(configs.map(config => config.trackName));\n const currentTracks = new Set(this.subscriptions.keys());\n\n // Remove subscriptions that are no longer needed\n for (const trackName of currentTracks) {\n if (!desiredTracks.has(trackName)) {\n this._removeSubscription(trackName);\n }\n }\n\n // Add new subscriptions or update existing ones\n for (const config of configs) {\n const existing = this.subscriptions.get(config.trackName);\n if (!existing) {\n // Create new subscription\n this._createSubscription(config);\n } else {\n // Update existing subscription config if needed\n if (existing.config.priority !== config.priority || \n existing.config.retry?.delay !== config.retry?.delay) {\n // For now, we recreate if config changed\n // In future, we could support dynamic config updates\n this._removeSubscription(config.trackName);\n this._createSubscription(config);\n }\n }\n }\n }\n\n getAnnouncedBroadcasts(): string[] {\n return Array.from(this.announcedBroadcasts);\n }\n\n /**\n * Send data to a broadcast track\n * @param trackName Name of the track to send data to\n * @param data Binary data to send\n */\n async send(trackName: string, data: Uint8Array): Promise<void> {\n const broadcast = this.broadcasts.get(trackName);\n if (!broadcast) {\n throw new Error(`No broadcast found for track: ${trackName}`);\n }\n await broadcast.send(data);\n }\n\n /**\n * Get the status of a specific broadcast track\n * @param trackName Name of the track\n */\n getBroadcastStatus(trackName: string) {\n const broadcast = this.broadcasts.get(trackName);\n return broadcast?.status;\n }\n\n /**\n * Get the status of a specific subscription track\n * @param trackName Name of the track\n */\n getSubscriptionStatus(trackName: string) {\n const subscription = this.subscriptions.get(trackName);\n return subscription?.status;\n }\n\n private _createBroadcast(config: BroadcastConfig): MoQBroadcast {\n const broadcast = new MoQBroadcast(config);\n this.broadcasts.set(config.trackName, broadcast);\n\n // Forward broadcast events to session\n broadcast.on('stateChange', (status) => {\n this.emit('broadcastStateChange', config.trackName, status);\n });\n\n broadcast.on('error', (error) => {\n this.emit('broadcastError', config.trackName, error);\n });\n\n // Start broadcast if we're connected\n if (this.state === SessionState.CONNECTED && this.connection) {\n broadcast.start(this.connection, this.config.namespace).catch(error => {\n console.error('Failed to start broadcast:', error);\n });\n }\n\n return broadcast;\n }\n\n private _createSubscription(config: SubscriptionConfig): MoQSubscription {\n const subscription = new MoQSubscription(config);\n this.subscriptions.set(config.trackName, subscription);\n\n // Forward subscription events to session\n subscription.on('data', (received) => {\n this.emit('data', received.trackName, received.data);\n });\n\n subscription.on('stateChange', (status) => {\n this.emit('subscriptionStateChange', config.trackName, status);\n });\n\n subscription.on('error', (error) => {\n this.emit('subscriptionError', config.trackName, error);\n });\n\n // Start subscription if we're connected\n if (this.state === SessionState.CONNECTED && this.connection) {\n subscription.start(this.connection, this.config.namespace).catch(error => {\n console.error('Failed to start subscription:', error);\n });\n }\n\n return subscription;\n }\n\n private _removeBroadcast(trackName: string): void {\n const broadcast = this.broadcasts.get(trackName);\n if (broadcast) {\n broadcast.stop().catch(error => {\n console.warn('Error stopping broadcast:', error);\n });\n this.broadcasts.delete(trackName);\n }\n }\n\n private _removeSubscription(trackName: string): void {\n const subscription = this.subscriptions.get(trackName);\n if (subscription) {\n subscription.stop().catch(error => {\n console.warn('Error stopping subscription:', error);\n });\n this.subscriptions.delete(trackName);\n }\n }\n\n private async startAnnouncementDiscovery(): Promise<void> {\n if (!this.connection) return;\n\n try {\n // Listen for announcements with our namespace as prefix\n const namespacePath = Path.from(this.config.namespace);\n const announced = this.connection.announced(namespacePath);\n \n // Process announcements in the background\n this.processAnnouncements(announced);\n \n } catch (error) {\n console.error('Failed to start announcement discovery:', error);\n }\n }\n\n private async processAnnouncements(announced: ReturnType<Connection.Established['announced']>): Promise<void> {\n try {\n for (;;) {\n const announcement = await announced.next();\n if (!announcement) break; // Announcements closed\n \n if (announcement.active) {\n this.announcedBroadcasts.add(announcement.path);\n this.emit('broadcastAnnounced', {\n path: announcement.path,\n active: true\n });\n } else {\n this.announcedBroadcasts.delete(announcement.path);\n this.emit('broadcastAnnounced', {\n path: announcement.path,\n active: false\n });\n }\n }\n } catch (error) {\n console.error('Error processing announcements:', error);\n }\n }\n\n private async restartBroadcasts(): Promise<void> {\n if (!this.connection) return;\n\n const promises = Array.from(this.broadcasts.values()).map(broadcast => \n broadcast.start(this.connection!, this.config.namespace).catch(error => {\n console.error('Failed to restart broadcast:', error);\n })\n );\n\n await Promise.allSettled(promises);\n }\n\n private async restartSubscriptions(): Promise<void> {\n if (!this.connection) return;\n\n const promises = Array.from(this.subscriptions.values()).map(subscription => \n subscription.start(this.connection!, this.config.namespace).catch(error => {\n console.error('Failed to restart subscription:', error);\n })\n );\n\n await Promise.allSettled(promises);\n }\n\n private async stopAllBroadcasts(): Promise<void> {\n const promises = Array.from(this.broadcasts.values()).map(broadcast => \n broadcast.stop().catch(error => {\n console.warn('Error stopping broadcast:', error);\n })\n );\n\n await Promise.allSettled(promises);\n }\n\n private async stopAllSubscriptions(): Promise<void> {\n const promises = Array.from(this.subscriptions.values()).map(subscription => \n subscription.stop().catch(error => {\n console.warn('Error stopping subscription:', error);\n })\n );\n\n await Promise.allSettled(promises);\n }\n\n private handleDisconnection(): void {\n this.setState(SessionState.DISCONNECTED);\n this.scheduleReconnect();\n }\n\n private scheduleReconnect(): void {\n const delay = this.config.reconnection?.delay || 1000;\n\n this.setState(SessionState.RECONNECTING);\n this.reconnectAttempts++;\n\n this.reconnectTimer = setTimeout(() => {\n this.connect().catch(error => {\n console.error('Reconnection failed:', error);\n });\n }, delay);\n }\n\n private setState(newState: SessionState): void {\n if (this.state !== newState) {\n this.state = newState;\n this.emit('stateChange', this.status);\n }\n }\n\n private handleError(error: Error): void {\n this.lastError = error;\n this.emit('error', error);\n }\n}"],"names":["EventEmitter","event","listener","eventListeners","args","error","SessionState","BroadcastState","SubscriptionState","MoQBroadcast","config","connection","namespace","Broadcast","broadcastPath","Path","request","data","newState","MoQSubscription","group","frame","receivedData","delay","_a","MoQSession","Connection","configs","trackNames","uniqueTrackNames","duplicates","name","index","desiredTracks","currentTracks","trackName","existing","_b","broadcast","subscription","status","received","namespacePath","announced","announcement","promises"],"mappings":"8IAEO,MAAMA,CAAiE,CAAvE,aAAA,CACL,KAAQ,cAAgB,GAA8B,CAEtD,GAAsBC,EAAUC,EAAsB,CAC/C,KAAK,UAAU,IAAID,CAAK,GAC3B,KAAK,UAAU,IAAIA,EAAO,IAAI,GAAK,EAErC,KAAK,UAAU,IAAIA,CAAK,EAAG,IAAIC,CAAQ,CACzC,CAEA,IAAuBD,EAAUC,EAAsB,CACrD,MAAMC,EAAiB,KAAK,UAAU,IAAIF,CAAK,EAC3CE,GACFA,EAAe,OAAOD,CAAQ,CAElC,CAEA,KAAwBD,KAAaG,EAA8B,CACjE,MAAMD,EAAiB,KAAK,UAAU,IAAIF,CAAK,EAC3CE,GACFA,EAAe,QAAQD,GAAY,CACjC,GAAI,CACFA,EAAS,GAAGE,CAAI,CAClB,OAASC,EAAO,CACd,QAAQ,MAAM,2BAA4BA,CAAK,CACjD,CACF,CAAC,CAEL,CAEA,mBAAsCJ,EAAiB,CACjDA,EACF,KAAK,UAAU,OAAOA,CAAK,EAE3B,KAAK,UAAU,MAAA,CAEnB,CACF,CCLO,IAAKK,GAAAA,IACVA,EAAA,aAAe,eACfA,EAAA,WAAa,aACbA,EAAA,UAAY,YACZA,EAAA,aAAe,eACfA,EAAA,OAAS,SALCA,IAAAA,GAAA,CAAA,CAAA,EAQAC,GAAAA,IACVA,EAAA,KAAO,OACPA,EAAA,aAAe,eACfA,EAAA,MAAQ,QAHEA,IAAAA,GAAA,CAAA,CAAA,EAMAC,GAAAA,IACVA,EAAA,QAAU,UACVA,EAAA,WAAa,aACbA,EAAA,SAAW,WACXA,EAAA,OAAS,SAJCA,IAAAA,GAAA,CAAA,CAAA,ECvCL,MAAMC,UAAqBT,CAA8B,CAQ9D,YAAYU,EAAyB,CACnC,MAAA,EAPF,KAAQ,MAAwBH,EAAe,KAC/C,KAAQ,UAAY,EAOlB,KAAK,QAAUG,CACjB,CAEA,IAAI,QAA0B,CAC5B,MAAO,CACL,MAAO,KAAK,MACZ,UAAW,KAAK,QAAQ,UACxB,UAAW,KAAK,UAChB,UAAW,KAAK,SAAA,CAEpB,CAEA,IAAI,QAA0B,CAC5B,MAAO,CAAE,GAAG,KAAK,OAAA,CACnB,CAEA,MAAM,MAAMC,EAAoCC,EAAkC,CAChF,GAAI,CACF,KAAK,SAASL,EAAe,YAAY,EAGzC,KAAK,UAAY,IAAIM,YAGrB,MAAMC,EAAgBC,EAAAA,KAAK,KAAKH,CAAS,EACzCD,EAAW,QAAQG,EAAe,KAAK,SAAS,EAGhD,KAAK,oBAAA,CAEP,OAAST,EAAO,CACd,WAAK,YAAYA,CAAc,EACzBA,CACR,CACF,CAEA,MAAc,qBAAqC,CACjD,GAAK,KAAK,UAEV,GAAI,CACF,OAAS,CACP,MAAMW,EAAU,MAAM,KAAK,UAAU,UAAA,EACrC,GAAI,CAACA,EAAS,MAGVA,EAAQ,MAAM,OAAS,KAAK,QAAQ,YACtC,KAAK,MAAQA,EAAQ,MAEzB,CACF,OAASX,EAAO,CACd,QAAQ,KAAK,iCAAkCA,CAAK,EACpD,KAAK,YAAYA,CAAc,CACjC,CACF,CAEA,MAAM,KAAKY,EAAiC,CAC1C,GAAI,KAAK,QAAUV,EAAe,cAAgB,CAAC,KAAK,MACtD,MAAM,IAAI,MAAM,yBAAyB,EAG3C,GAAI,CACF,KAAK,MAAM,WAAWU,CAAI,EAC1B,KAAK,WAAaA,EAAK,MACzB,OAASZ,EAAO,CACd,WAAK,YAAYA,CAAc,EACzBA,CACR,CACF,CAEA,MAAM,MAAsB,CAC1B,GAAI,KAAK,MAAO,CACd,GAAI,CACF,KAAK,MAAM,MAAA,CACb,OAASA,EAAO,CACd,QAAQ,KAAK,uBAAwBA,CAAK,CAC5C,CACA,KAAK,MAAQ,MACf,CAEA,GAAI,KAAK,UAAW,CAClB,GAAI,CACF,KAAK,UAAU,MAAA,CACjB,OAASA,EAAO,CACd,QAAQ,KAAK,2BAA4BA,CAAK,CAChD,CACA,KAAK,UAAY,MACnB,CAEA,KAAK,SAASE,EAAe,IAAI,CACnC,CAEQ,SAASW,EAAgC,CAC3C,KAAK,QAAUA,IACjB,KAAK,MAAQA,EACb,KAAK,KAAK,cAAe,KAAK,MAAM,EAExC,CAEQ,YAAYb,EAAoB,CACtC,KAAK,UAAYA,EACjB,KAAK,SAASE,EAAe,KAAK,EAClC,KAAK,KAAK,QAASF,CAAK,CAC1B,CACF,CChHO,MAAMc,UAAwBnB,CAAiC,CAYpE,YAAYU,EAA4B,CACtC,MAAA,EAXF,KAAQ,MAA2BF,EAAkB,QACrD,KAAQ,cAAgB,EACxB,KAAQ,cAAgB,EAUtB,KAAK,QAAUE,CACjB,CAEA,IAAI,QAA6B,CAC/B,MAAO,CACL,MAAO,KAAK,MACZ,UAAW,KAAK,QAAQ,UACxB,cAAe,KAAK,cACpB,cAAe,KAAK,cACpB,UAAW,KAAK,SAAA,CAEpB,CAEA,IAAI,QAA6B,CAC/B,MAAO,CAAE,GAAG,KAAK,OAAA,CACnB,CAEA,MAAM,MAAMC,EAAoCC,EAAkC,CAChF,KAAK,WAAaD,EAClB,KAAK,UAAYC,EACjB,GAAI,CACF,MAAM,KAAK,UAAUD,EAAYC,CAAS,CAC5C,OAASP,EAAO,CACd,KAAK,YAAYA,CAAc,EAC/B,KAAK,cAAcM,EAAYC,CAAS,CAC1C,CACF,CAEA,MAAc,UAAUD,EAAoCC,EAAkC,CAC5F,KAAK,SAASJ,EAAkB,OAAO,EAEvC,MAAMM,EAAgBC,EAAAA,KAAK,KAAKH,CAAS,EACzC,KAAK,UAAYD,EAAW,QAAQG,CAAa,EAGjD,KAAK,aAAe,KAAK,UAAU,UAAU,KAAK,QAAQ,UAAW,KAAK,QAAQ,UAAY,CAAC,EAC/F,KAAK,SAASN,EAAkB,UAAU,EAC1C,KAAK,cAAgB,EAGrB,KAAK,WAAA,CACP,CAEA,MAAc,YAA4B,CACxC,GAAK,KAAK,aAEV,GAAI,CAEF,OAAa,CACX,MAAMY,EAAQ,MAAM,KAAK,aAAa,UAAA,EACtC,GAAI,CAACA,EAAO,MAIZ,OAAa,CACX,MAAMC,EAAQ,MAAMD,EAAM,UAAA,EAC1B,GAAI,CAACC,EAAO,MAEZ,KAAK,eAAiBA,EAAM,OAC5B,MAAMC,EAA6B,CACjC,UAAW,KAAK,QAAQ,UACxB,KAAMD,EACN,UAAW,KAAK,IAAA,CAAI,EAEtB,KAAK,KAAK,OAAQC,CAAY,CAChC,CACF,CACA,OAASjB,EAAO,CAChB,KAAK,YAAYA,CAAc,EAE3B,KAAK,YAAc,KAAK,WAC1B,KAAK,cAAc,KAAK,WAAY,KAAK,SAAS,CAEtD,CACF,CAAW,cAAcM,EAAoCC,EAAyB,OACpF,MAAMW,IAAQC,EAAA,KAAK,QAAQ,QAAb,YAAAA,EAAoB,QAAS,IAE3C,KAAK,SAAShB,EAAkB,QAAQ,EACxC,KAAK,gBAEL,KAAK,WAAa,WAAW,SAAY,CACvC,GAAI,CACF,MAAM,KAAK,UAAUG,EAAYC,CAAS,CAC5C,OAASP,EAAO,CACd,KAAK,YAAYA,CAAc,EAC/B,KAAK,cAAcM,EAAYC,CAAS,CAC1C,CACF,EAAGW,CAAK,CACV,CAEA,MAAM,MAAsB,CAM1B,GALI,KAAK,aACP,aAAa,KAAK,UAAU,EAC5B,KAAK,WAAa,QAGhB,KAAK,aAAc,CACrB,GAAI,CACF,KAAK,aAAa,MAAA,CACpB,OAASlB,EAAO,CACd,QAAQ,KAAK,8BAA+BA,CAAK,CACnD,CACA,KAAK,aAAe,MACtB,CAEA,GAAI,KAAK,UAAW,CAClB,GAAI,CACF,KAAK,UAAU,MAAA,CACjB,OAASA,EAAO,CACd,QAAQ,KAAK,2BAA4BA,CAAK,CAChD,CACA,KAAK,UAAY,MACnB,CAEA,KAAK,SAASG,EAAkB,OAAO,CACzC,CAEQ,SAASU,EAAmC,CAC9C,KAAK,QAAUA,IACjB,KAAK,MAAQA,EACb,KAAK,KAAK,cAAe,KAAK,MAAM,EAExC,CAEQ,YAAYb,EAAoB,CACtC,KAAK,UAAYA,EACjB,KAAK,KAAK,QAASA,CAAK,CAC1B,CACF,CC3IO,MAAMoB,UAAmBzB,CAA4B,CAY1D,YAAYU,EAA0B,OACpC,MAAA,EAXF,KAAQ,MAAsBJ,EAAa,aAE3C,KAAQ,kBAAoB,EAI5B,KAAQ,eAAiB,IACzB,KAAQ,kBAAoB,IAC5B,KAAQ,wBAA0B,IAIhC,QAAQ,IAAI,6BAA6B,EACzC,KAAK,OAAS,CACZ,GAAGI,EACH,aAAc,CACZ,MAAO,KAAK,MAAIc,EAAAd,EAAO,eAAP,YAAAc,EAAqB,QAAS,IAAM,GAAI,CAAA,EAE1D,kBAAmBd,EAAO,mBAAqB,GAAA,CAEnD,CAEA,IAAI,QAAwB,CAC1B,MAAO,CACL,MAAO,KAAK,MACZ,kBAAmB,KAAK,kBACxB,UAAW,KAAK,UAChB,YAAa,KAAK,WAAA,CAEtB,CAEA,MAAM,SAAyB,CAC7B,GAAI,OAAK,QAAUJ,EAAa,YAAc,KAAK,QAAUA,EAAa,WAI1E,MAAK,SAASA,EAAa,UAAU,EAErC,GAAI,CAEF,KAAK,WAAa,MAAMoB,EAAAA,WAAW,QAAQ,IAAI,IAAI,KAAK,OAAO,QAAQ,EAAG,CACxE,UAAW,CACT,QAAS,EAAA,CACX,CACD,EAED,KAAK,SAASpB,EAAa,SAAS,EACpC,KAAK,gBAAkB,KACvB,KAAK,kBAAoB,EAGzB,KAAK,WAAW,OAAO,KAAK,IAAM,CAC5B,KAAK,QAAUA,EAAa,WAC9B,KAAK,oBAAA,CAET,CAAC,EAGD,KAAK,2BAAA,EAGL,MAAM,KAAK,kBAAA,EACX,MAAM,KAAK,qBAAA,CAEb,OAASD,EAAO,CACd,WAAK,YAAYA,CAAc,EAC/B,KAAK,kBAAA,EACCA,CACR,EACF,CAEA,MAAM,YAA4B,CAUhC,GATI,KAAK,iBACP,aAAa,KAAK,cAAc,EAChC,KAAK,eAAiB,QAIxB,MAAM,KAAK,kBAAA,EACX,MAAM,KAAK,qBAAA,EAEP,KAAK,WAAY,CACnB,GAAI,CACF,KAAK,WAAW,MAAA,CAClB,OAASA,EAAO,CACd,QAAQ,KAAK,4BAA6BA,CAAK,CACjD,CACA,KAAK,WAAa,MACpB,CAEA,KAAK,SAASC,EAAa,YAAY,EACvC,KAAK,YAAc,MACrB,CAKA,2BAAsC,CACpC,OAAO,MAAM,KAAK,KAAK,WAAW,MAAM,CAC1C,CAKA,8BAAyC,CACvC,OAAO,MAAM,KAAK,KAAK,cAAc,MAAM,CAC7C,CAOA,iBAAiBqB,EAAkC,CAEjD,MAAMC,EAAaD,EAAQ,IAAIjB,GAAUA,EAAO,SAAS,EACnDmB,EAAmB,IAAI,IAAID,CAAU,EAC3C,GAAIA,EAAW,SAAWC,EAAiB,KAAM,CAC/C,MAAMC,EAAaF,EAAW,OAAO,CAACG,EAAMC,IAAUJ,EAAW,QAAQG,CAAI,IAAMC,CAAK,EACxF,MAAM,IAAI,MAAM,mCAAmCF,EAAW,CAAC,CAAC,EAAE,CACpE,CAEA,MAAMG,EAAgB,IAAI,IAAIN,EAAQ,IAAIjB,GAAUA,EAAO,SAAS,CAAC,EAC/DwB,EAAgB,IAAI,IAAI,KAAK,WAAW,MAAM,EAGpD,UAAWC,KAAaD,EACjBD,EAAc,IAAIE,CAAS,GAC9B,KAAK,iBAAiBA,CAAS,EAKnC,UAAWzB,KAAUiB,EAAS,CAC5B,MAAMS,EAAW,KAAK,WAAW,IAAI1B,EAAO,SAAS,EAChD0B,EAKCA,EAAS,OAAO,WAAa1B,EAAO,WAGtC,KAAK,iBAAiBA,EAAO,SAAS,EACtC,KAAK,iBAAiBA,CAAM,GAP9B,KAAK,iBAAiBA,CAAM,CAUhC,CACF,CAOA,oBAAoBiB,EAAqC,SAEvD,MAAMC,EAAaD,EAAQ,IAAIjB,GAAUA,EAAO,SAAS,EACnDmB,EAAmB,IAAI,IAAID,CAAU,EAC3C,GAAIA,EAAW,SAAWC,EAAiB,KAAM,CAC/C,MAAMC,EAAaF,EAAW,OAAO,CAACG,EAAMC,IAAUJ,EAAW,QAAQG,CAAI,IAAMC,CAAK,EACxF,MAAM,IAAI,MAAM,sCAAsCF,EAAW,CAAC,CAAC,EAAE,CACvE,CAEA,MAAMG,EAAgB,IAAI,IAAIN,EAAQ,IAAIjB,GAAUA,EAAO,SAAS,CAAC,EAC/DwB,EAAgB,IAAI,IAAI,KAAK,cAAc,MAAM,EAGvD,UAAWC,KAAaD,EACjBD,EAAc,IAAIE,CAAS,GAC9B,KAAK,oBAAoBA,CAAS,EAKtC,UAAWzB,KAAUiB,EAAS,CAC5B,MAAMS,EAAW,KAAK,cAAc,IAAI1B,EAAO,SAAS,EACnD0B,GAKCA,EAAS,OAAO,WAAa1B,EAAO,YACpCc,EAAAY,EAAS,OAAO,QAAhB,YAAAZ,EAAuB,WAAUa,EAAA3B,EAAO,QAAP,YAAA2B,EAAc,UAGjD,KAAK,oBAAoB3B,EAAO,SAAS,EACzC,KAAK,oBAAoBA,CAAM,GARjC,KAAK,oBAAoBA,CAAM,CAWnC,CACF,CAEA,wBAAmC,CACjC,OAAO,MAAM,KAAK,KAAK,mBAAmB,CAC5C,CAOA,MAAM,KAAKyB,EAAmBlB,EAAiC,CAC7D,MAAMqB,EAAY,KAAK,WAAW,IAAIH,CAAS,EAC/C,GAAI,CAACG,EACH,MAAM,IAAI,MAAM,iCAAiCH,CAAS,EAAE,EAE9D,MAAMG,EAAU,KAAKrB,CAAI,CAC3B,CAMA,mBAAmBkB,EAAmB,CACpC,MAAMG,EAAY,KAAK,WAAW,IAAIH,CAAS,EAC/C,OAAOG,GAAA,YAAAA,EAAW,MACpB,CAMA,sBAAsBH,EAAmB,CACvC,MAAMI,EAAe,KAAK,cAAc,IAAIJ,CAAS,EACrD,OAAOI,GAAA,YAAAA,EAAc,MACvB,CAEQ,iBAAiB7B,EAAuC,CAC9D,MAAM4B,EAAY,IAAI7B,EAAaC,CAAM,EACzC,YAAK,WAAW,IAAIA,EAAO,UAAW4B,CAAS,EAG/CA,EAAU,GAAG,cAAgBE,GAAW,CACtC,KAAK,KAAK,uBAAwB9B,EAAO,UAAW8B,CAAM,CAC5D,CAAC,EAEDF,EAAU,GAAG,QAAUjC,GAAU,CAC/B,KAAK,KAAK,iBAAkBK,EAAO,UAAWL,CAAK,CACrD,CAAC,EAGG,KAAK,QAAUC,EAAa,WAAa,KAAK,YAChDgC,EAAU,MAAM,KAAK,WAAY,KAAK,OAAO,SAAS,EAAE,MAAMjC,GAAS,CACrE,QAAQ,MAAM,6BAA8BA,CAAK,CACnD,CAAC,EAGIiC,CACT,CAEQ,oBAAoB5B,EAA6C,CACvE,MAAM6B,EAAe,IAAIpB,EAAgBT,CAAM,EAC/C,YAAK,cAAc,IAAIA,EAAO,UAAW6B,CAAY,EAGrDA,EAAa,GAAG,OAASE,GAAa,CACpC,KAAK,KAAK,OAAQA,EAAS,UAAWA,EAAS,IAAI,CACrD,CAAC,EAEDF,EAAa,GAAG,cAAgBC,GAAW,CACzC,KAAK,KAAK,0BAA2B9B,EAAO,UAAW8B,CAAM,CAC/D,CAAC,EAEDD,EAAa,GAAG,QAAUlC,GAAU,CAClC,KAAK,KAAK,oBAAqBK,EAAO,UAAWL,CAAK,CACxD,CAAC,EAGG,KAAK,QAAUC,EAAa,WAAa,KAAK,YAChDiC,EAAa,MAAM,KAAK,WAAY,KAAK,OAAO,SAAS,EAAE,MAAMlC,GAAS,CACxE,QAAQ,MAAM,gCAAiCA,CAAK,CACtD,CAAC,EAGIkC,CACT,CAEQ,iBAAiBJ,EAAyB,CAChD,MAAMG,EAAY,KAAK,WAAW,IAAIH,CAAS,EAC3CG,IACFA,EAAU,KAAA,EAAO,MAAMjC,GAAS,CAC9B,QAAQ,KAAK,4BAA6BA,CAAK,CACjD,CAAC,EACD,KAAK,WAAW,OAAO8B,CAAS,EAEpC,CAEQ,oBAAoBA,EAAyB,CACnD,MAAMI,EAAe,KAAK,cAAc,IAAIJ,CAAS,EACjDI,IACFA,EAAa,KAAA,EAAO,MAAMlC,GAAS,CACjC,QAAQ,KAAK,+BAAgCA,CAAK,CACpD,CAAC,EACD,KAAK,cAAc,OAAO8B,CAAS,EAEvC,CAEA,MAAc,4BAA4C,CACxD,GAAK,KAAK,WAEV,GAAI,CAEF,MAAMO,EAAgB3B,EAAAA,KAAK,KAAK,KAAK,OAAO,SAAS,EAC/C4B,EAAY,KAAK,WAAW,UAAUD,CAAa,EAGzD,KAAK,qBAAqBC,CAAS,CAErC,OAAStC,EAAO,CACd,QAAQ,MAAM,0CAA2CA,CAAK,CAChE,CACF,CAEA,MAAc,qBAAqBsC,EAA2E,CAC5G,GAAI,CACF,OAAS,CACP,MAAMC,EAAe,MAAMD,EAAU,KAAA,EACrC,GAAI,CAACC,EAAc,MAEfA,EAAa,QACf,KAAK,oBAAoB,IAAIA,EAAa,IAAI,EAC9C,KAAK,KAAK,qBAAsB,CAC9B,KAAMA,EAAa,KACnB,OAAQ,EAAA,CACT,IAED,KAAK,oBAAoB,OAAOA,EAAa,IAAI,EACjD,KAAK,KAAK,qBAAsB,CAC9B,KAAMA,EAAa,KACnB,OAAQ,EAAA,CACT,EAEL,CACF,OAASvC,EAAO,CACd,QAAQ,MAAM,kCAAmCA,CAAK,CACxD,CACF,CAEA,MAAc,mBAAmC,CAC/C,GAAI,CAAC,KAAK,WAAY,OAEtB,MAAMwC,EAAW,MAAM,KAAK,KAAK,WAAW,OAAA,CAAQ,EAAE,IAAIP,GACxDA,EAAU,MAAM,KAAK,WAAa,KAAK,OAAO,SAAS,EAAE,MAAMjC,GAAS,CACtE,QAAQ,MAAM,+BAAgCA,CAAK,CACrD,CAAC,CAAA,EAGH,MAAM,QAAQ,WAAWwC,CAAQ,CACnC,CAEA,MAAc,sBAAsC,CAClD,GAAI,CAAC,KAAK,WAAY,OAEtB,MAAMA,EAAW,MAAM,KAAK,KAAK,cAAc,OAAA,CAAQ,EAAE,IAAIN,GAC3DA,EAAa,MAAM,KAAK,WAAa,KAAK,OAAO,SAAS,EAAE,MAAMlC,GAAS,CACzE,QAAQ,MAAM,kCAAmCA,CAAK,CACxD,CAAC,CAAA,EAGH,MAAM,QAAQ,WAAWwC,CAAQ,CACnC,CAEA,MAAc,mBAAmC,CAC/C,MAAMA,EAAW,MAAM,KAAK,KAAK,WAAW,OAAA,CAAQ,EAAE,IAAIP,GACxDA,EAAU,KAAA,EAAO,MAAMjC,GAAS,CAC9B,QAAQ,KAAK,4BAA6BA,CAAK,CACjD,CAAC,CAAA,EAGH,MAAM,QAAQ,WAAWwC,CAAQ,CACnC,CAEA,MAAc,sBAAsC,CAClD,MAAMA,EAAW,MAAM,KAAK,KAAK,cAAc,OAAA,CAAQ,EAAE,IAAIN,GAC3DA,EAAa,KAAA,EAAO,MAAMlC,GAAS,CACjC,QAAQ,KAAK,+BAAgCA,CAAK,CACpD,CAAC,CAAA,EAGH,MAAM,QAAQ,WAAWwC,CAAQ,CACnC,CAEQ,qBAA4B,CAClC,KAAK,SAASvC,EAAa,YAAY,EACvC,KAAK,kBAAA,CACP,CAEQ,mBAA0B,OAChC,MAAMiB,IAAQC,EAAA,KAAK,OAAO,eAAZ,YAAAA,EAA0B,QAAS,IAEjD,KAAK,SAASlB,EAAa,YAAY,EACvC,KAAK,oBAEL,KAAK,eAAiB,WAAW,IAAM,CACrC,KAAK,QAAA,EAAU,MAAMD,GAAS,CAC5B,QAAQ,MAAM,uBAAwBA,CAAK,CAC7C,CAAC,CACH,EAAGkB,CAAK,CACV,CAEQ,SAASL,EAA8B,CACzC,KAAK,QAAUA,IACjB,KAAK,MAAQA,EACb,KAAK,KAAK,cAAe,KAAK,MAAM,EAExC,CAEQ,YAAYb,EAAoB,CACtC,KAAK,UAAYA,EACjB,KAAK,KAAK,QAASA,CAAK,CAC1B,CACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { MoQSession } from './session';
|
|
2
|
+
export { MoQBroadcast } from './broadcast';
|
|
3
|
+
export { MoQSubscription } from './subscription';
|
|
4
|
+
export type { MoQSessionConfig, BroadcastConfig, SubscriptionConfig, SessionStatus, BroadcastStatus, SubscriptionStatus, DataReceived, SessionEvents, BroadcastEvents, SubscriptionEvents } from './types';
|
|
5
|
+
export { SessionState, BroadcastState, SubscriptionState } from './types';
|
|
6
|
+
export { MoQSession as default } from './session';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGjD,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,YAAY,EACZ,aAAa,EACb,eAAe,EACf,kBAAkB,EACnB,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,YAAY,EACZ,cAAc,EACd,iBAAiB,EAClB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,UAAU,IAAI,OAAO,EAAE,MAAM,WAAW,CAAC"}
|