@xtr-dev/rondevu-client 0.20.1 → 0.21.3
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 +83 -385
- package/dist/api/batcher.d.ts +60 -38
- package/dist/api/batcher.js +121 -77
- package/dist/api/client.d.ts +104 -61
- package/dist/api/client.js +273 -185
- package/dist/connections/answerer.d.ts +15 -6
- package/dist/connections/answerer.js +56 -19
- package/dist/connections/base.d.ts +6 -4
- package/dist/connections/base.js +26 -16
- package/dist/connections/config.d.ts +30 -0
- package/dist/connections/config.js +20 -0
- package/dist/connections/events.d.ts +6 -6
- package/dist/connections/offerer.d.ts +37 -8
- package/dist/connections/offerer.js +92 -24
- package/dist/core/ice-config.d.ts +35 -0
- package/dist/core/ice-config.js +111 -0
- package/dist/core/index.d.ts +18 -18
- package/dist/core/index.js +18 -13
- package/dist/core/offer-pool.d.ts +30 -11
- package/dist/core/offer-pool.js +90 -76
- package/dist/core/peer.d.ts +158 -0
- package/dist/core/peer.js +254 -0
- package/dist/core/polling-manager.d.ts +71 -0
- package/dist/core/polling-manager.js +122 -0
- package/dist/core/rondevu-errors.d.ts +59 -0
- package/dist/core/rondevu-errors.js +75 -0
- package/dist/core/rondevu-types.d.ts +125 -0
- package/dist/core/rondevu-types.js +6 -0
- package/dist/core/rondevu.d.ts +106 -209
- package/dist/core/rondevu.js +222 -349
- package/dist/crypto/adapter.d.ts +25 -9
- package/dist/crypto/node.d.ts +27 -5
- package/dist/crypto/node.js +96 -25
- package/dist/crypto/web.d.ts +26 -4
- package/dist/crypto/web.js +102 -25
- package/dist/utils/message-buffer.js +4 -4
- package/dist/webrtc/adapter.d.ts +22 -0
- package/dist/webrtc/adapter.js +5 -0
- package/dist/webrtc/browser.d.ts +12 -0
- package/dist/webrtc/browser.js +15 -0
- package/dist/webrtc/node.d.ts +32 -0
- package/dist/webrtc/node.js +32 -0
- package/package.json +17 -6
|
@@ -10,16 +10,16 @@ import { AsyncLock } from '../utils/async-lock.js';
|
|
|
10
10
|
export class OffererConnection extends RondevuConnection {
|
|
11
11
|
constructor(options) {
|
|
12
12
|
// Force reconnectEnabled: false for offerer connections (offers are ephemeral)
|
|
13
|
-
super(undefined, {
|
|
14
|
-
|
|
15
|
-
reconnectEnabled: false
|
|
16
|
-
});
|
|
13
|
+
super(undefined, { ...options.config, reconnectEnabled: false }, options.webrtcAdapter);
|
|
14
|
+
this._peerUsername = null;
|
|
17
15
|
// Rotation tracking
|
|
18
16
|
this.rotationLock = new AsyncLock();
|
|
19
17
|
this.rotating = false;
|
|
20
18
|
this.rotationAttempts = 0;
|
|
19
|
+
// ICE candidate buffering (for candidates received before answer is processed)
|
|
20
|
+
this.pendingIceCandidates = [];
|
|
21
21
|
this.api = options.api;
|
|
22
|
-
this.
|
|
22
|
+
this.ownerUsername = options.ownerUsername;
|
|
23
23
|
this.offerId = options.offerId;
|
|
24
24
|
// Use the already-created peer connection and data channel
|
|
25
25
|
this.pc = options.pc;
|
|
@@ -33,7 +33,7 @@ export class OffererConnection extends RondevuConnection {
|
|
|
33
33
|
if (!this.pc)
|
|
34
34
|
throw new Error('Peer connection not provided');
|
|
35
35
|
// Setup peer connection event handlers
|
|
36
|
-
this.pc.onicecandidate =
|
|
36
|
+
this.pc.onicecandidate = event => this.handleIceCandidate(event);
|
|
37
37
|
this.pc.oniceconnectionstatechange = () => this.handleIceConnectionStateChange();
|
|
38
38
|
this.pc.onconnectionstatechange = () => this.handleConnectionStateChange();
|
|
39
39
|
this.pc.onicegatheringstatechange = () => this.handleIceGatheringStateChange();
|
|
@@ -67,8 +67,10 @@ export class OffererConnection extends RondevuConnection {
|
|
|
67
67
|
throw new Error('Received different answer after already processing one (protocol violation)');
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
|
-
// Validate state
|
|
71
|
-
if (this.state !== ConnectionState.SIGNALING &&
|
|
70
|
+
// Validate state - allow SIGNALING, CHECKING, and FAILED (for late-arriving answers before rotation)
|
|
71
|
+
if (this.state !== ConnectionState.SIGNALING &&
|
|
72
|
+
this.state !== ConnectionState.CHECKING &&
|
|
73
|
+
this.state !== ConnectionState.FAILED) {
|
|
72
74
|
this.debug(`Cannot process answer in state ${this.state}`);
|
|
73
75
|
return;
|
|
74
76
|
}
|
|
@@ -80,8 +82,17 @@ export class OffererConnection extends RondevuConnection {
|
|
|
80
82
|
type: 'answer',
|
|
81
83
|
sdp,
|
|
82
84
|
});
|
|
85
|
+
// Store the peer username
|
|
86
|
+
this._peerUsername = answererId;
|
|
83
87
|
this.debug(`Answer processed successfully from ${answererId}`);
|
|
84
88
|
this.emit('answer:processed', this.offerId, answererId);
|
|
89
|
+
// Apply any buffered ICE candidates that arrived before the answer
|
|
90
|
+
if (this.pendingIceCandidates.length > 0) {
|
|
91
|
+
this.debug(`Applying ${this.pendingIceCandidates.length} buffered ICE candidates`);
|
|
92
|
+
const buffered = this.pendingIceCandidates;
|
|
93
|
+
this.pendingIceCandidates = [];
|
|
94
|
+
this.applyIceCandidates(buffered);
|
|
95
|
+
}
|
|
85
96
|
}
|
|
86
97
|
catch (error) {
|
|
87
98
|
// Reset flags on error so we can try again
|
|
@@ -118,11 +129,13 @@ export class OffererConnection extends RondevuConnection {
|
|
|
118
129
|
this.offerId = newOfferId;
|
|
119
130
|
this.pc = newPc;
|
|
120
131
|
this.dc = newDc || null;
|
|
121
|
-
// 3. Reset answer processing flags
|
|
132
|
+
// 3. Reset answer processing flags, peer username, and pending candidates
|
|
122
133
|
this.answerProcessed = false;
|
|
123
134
|
this.answerSdpFingerprint = null;
|
|
135
|
+
this._peerUsername = null;
|
|
136
|
+
this.pendingIceCandidates = [];
|
|
124
137
|
// 4. Setup event handlers for new peer connection
|
|
125
|
-
this.pc.onicecandidate =
|
|
138
|
+
this.pc.onicecandidate = event => this.handleIceCandidate(event);
|
|
126
139
|
this.pc.oniceconnectionstatechange = () => this.handleIceConnectionStateChange();
|
|
127
140
|
this.pc.onconnectionstatechange = () => this.handleConnectionStateChange();
|
|
128
141
|
this.pc.onicegatheringstatechange = () => this.handleIceGatheringStateChange();
|
|
@@ -166,7 +179,7 @@ export class OffererConnection extends RondevuConnection {
|
|
|
166
179
|
const data = encoder.encode(sdp);
|
|
167
180
|
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
|
|
168
181
|
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
169
|
-
return hashArray.map(
|
|
182
|
+
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
|
|
170
183
|
}
|
|
171
184
|
else {
|
|
172
185
|
// Fallback: use simple string hash
|
|
@@ -186,14 +199,14 @@ export class OffererConnection extends RondevuConnection {
|
|
|
186
199
|
this.debug('Generated local ICE candidate');
|
|
187
200
|
// Send ICE candidate to server
|
|
188
201
|
this.api
|
|
189
|
-
.addOfferIceCandidates(this.
|
|
202
|
+
.addOfferIceCandidates(this.offerId, [
|
|
190
203
|
{
|
|
191
204
|
candidate: candidate.candidate,
|
|
192
205
|
sdpMLineIndex: candidate.sdpMLineIndex,
|
|
193
206
|
sdpMid: candidate.sdpMid,
|
|
194
207
|
},
|
|
195
208
|
])
|
|
196
|
-
.catch(
|
|
209
|
+
.catch(error => {
|
|
197
210
|
this.debug('Failed to send ICE candidate:', error);
|
|
198
211
|
});
|
|
199
212
|
}
|
|
@@ -204,10 +217,10 @@ export class OffererConnection extends RondevuConnection {
|
|
|
204
217
|
return this.api;
|
|
205
218
|
}
|
|
206
219
|
/**
|
|
207
|
-
* Get the
|
|
220
|
+
* Get the owner username
|
|
208
221
|
*/
|
|
209
|
-
|
|
210
|
-
return this.
|
|
222
|
+
getOwnerUsername() {
|
|
223
|
+
return this.ownerUsername;
|
|
211
224
|
}
|
|
212
225
|
/**
|
|
213
226
|
* Offerers accept all ICE candidates (no filtering)
|
|
@@ -216,17 +229,25 @@ export class OffererConnection extends RondevuConnection {
|
|
|
216
229
|
return null;
|
|
217
230
|
}
|
|
218
231
|
/**
|
|
219
|
-
* Attempt to reconnect
|
|
232
|
+
* Attempt to reconnect (required by abstract base class)
|
|
233
|
+
*
|
|
234
|
+
* For OffererConnection, traditional reconnection is NOT used.
|
|
235
|
+
* Instead, the OfferPool handles failures via offer rotation:
|
|
220
236
|
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
237
|
+
* 1. When this connection fails, the 'failed' event is emitted
|
|
238
|
+
* 2. OfferPool detects the failure and calls createNewOfferForRotation()
|
|
239
|
+
* 3. The new offer is published to the server
|
|
240
|
+
* 4. This connection is rebound via rebindToOffer()
|
|
241
|
+
*
|
|
242
|
+
* This approach ensures the answerer always gets a fresh offer
|
|
243
|
+
* rather than trying to reconnect to a stale one.
|
|
244
|
+
*
|
|
245
|
+
* @see OfferPool.createNewOfferForRotation() - creates replacement offer
|
|
246
|
+
* @see OffererConnection.rebindToOffer() - rebinds connection to new offer
|
|
223
247
|
*/
|
|
224
248
|
attemptReconnect() {
|
|
225
|
-
this.debug('Reconnection
|
|
226
|
-
|
|
227
|
-
// which creates entirely new offers. We don't reconnect the same offer.
|
|
228
|
-
// Just emit failure and let the parent handle it.
|
|
229
|
-
this.emit('reconnect:failed', new Error('Offerer reconnection handled by parent'));
|
|
249
|
+
this.debug('Reconnection delegated to OfferPool rotation mechanism');
|
|
250
|
+
this.emit('reconnect:failed', new Error('Offerer uses rotation, not reconnection'));
|
|
230
251
|
}
|
|
231
252
|
/**
|
|
232
253
|
* Get the offer ID
|
|
@@ -234,5 +255,52 @@ export class OffererConnection extends RondevuConnection {
|
|
|
234
255
|
getOfferId() {
|
|
235
256
|
return this.offerId;
|
|
236
257
|
}
|
|
258
|
+
/**
|
|
259
|
+
* Get the peer username (who answered this offer)
|
|
260
|
+
* Returns null if no answer has been processed yet
|
|
261
|
+
*/
|
|
262
|
+
get peerUsername() {
|
|
263
|
+
return this._peerUsername;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Handle remote ICE candidates received from polling
|
|
267
|
+
* Called by OfferPool when poll:ice event is received
|
|
268
|
+
*/
|
|
269
|
+
handleRemoteIceCandidates(candidates) {
|
|
270
|
+
if (!this.pc) {
|
|
271
|
+
this.debug('Cannot add ICE candidates: peer connection not initialized');
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
// If answer hasn't been processed yet, buffer the candidates
|
|
275
|
+
if (!this.answerProcessed) {
|
|
276
|
+
this.debug(`Buffering ${candidates.length} ICE candidates (waiting for answer)`);
|
|
277
|
+
this.pendingIceCandidates.push(...candidates);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
// Answer is processed, apply candidates immediately
|
|
281
|
+
this.applyIceCandidates(candidates);
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Apply ICE candidates to the peer connection
|
|
285
|
+
*/
|
|
286
|
+
applyIceCandidates(candidates) {
|
|
287
|
+
if (!this.pc)
|
|
288
|
+
return;
|
|
289
|
+
for (const iceCandidate of candidates) {
|
|
290
|
+
// Offerer accepts answerer's candidates (no role filtering needed here
|
|
291
|
+
// since OfferPool already filters by offerId)
|
|
292
|
+
if (iceCandidate.candidate) {
|
|
293
|
+
const rtcCandidate = this.webrtcAdapter.createIceCandidate(iceCandidate.candidate);
|
|
294
|
+
this.pc
|
|
295
|
+
.addIceCandidate(rtcCandidate)
|
|
296
|
+
.then(() => {
|
|
297
|
+
this.emit('ice:candidate:remote', rtcCandidate);
|
|
298
|
+
})
|
|
299
|
+
.catch(error => {
|
|
300
|
+
this.debug('Failed to add ICE candidate:', error);
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
237
305
|
}
|
|
238
306
|
OffererConnection.MAX_ROTATION_ATTEMPTS = 5;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ICE Configuration Types and Presets
|
|
3
|
+
*
|
|
4
|
+
* Provides typed ICE server presets for common WebRTC configurations.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Available ICE server preset names
|
|
8
|
+
*/
|
|
9
|
+
export type IceServerPreset = 'rondevu' | 'rondevu-relay' | 'rondevu-ipv4' | 'rondevu-ipv4-relay' | 'google-stun' | 'public-stun';
|
|
10
|
+
/**
|
|
11
|
+
* ICE preset configuration containing servers and optional transport policy.
|
|
12
|
+
* The iceTransportPolicy belongs on RTCConfiguration, not RTCIceServer.
|
|
13
|
+
*/
|
|
14
|
+
export interface IcePresetConfig {
|
|
15
|
+
iceServers: RTCIceServer[];
|
|
16
|
+
iceTransportPolicy?: RTCIceTransportPolicy;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Pre-configured ICE server presets.
|
|
20
|
+
*
|
|
21
|
+
* - `rondevu`: Official Rondevu TURN/STUN servers (recommended)
|
|
22
|
+
* - `rondevu-relay`: Same as rondevu but forces relay mode (hides client IPs)
|
|
23
|
+
* - `rondevu-ipv4`: Direct IPv4 address (for networks with DNS issues)
|
|
24
|
+
* - `rondevu-ipv4-relay`: IPv4 with forced relay mode
|
|
25
|
+
* - `google-stun`: Google's free STUN servers (no relay, direct connections only)
|
|
26
|
+
* - `public-stun`: Multiple public STUN servers for redundancy
|
|
27
|
+
*/
|
|
28
|
+
export declare const ICE_SERVER_PRESETS: Record<IceServerPreset, IcePresetConfig>;
|
|
29
|
+
/**
|
|
30
|
+
* Get the full RTCConfiguration for a preset or custom ICE servers.
|
|
31
|
+
*
|
|
32
|
+
* @param iceServers - Either a preset name or custom ICE servers array
|
|
33
|
+
* @returns Partial RTCConfiguration with iceServers and optional iceTransportPolicy
|
|
34
|
+
*/
|
|
35
|
+
export declare function getIceConfiguration(iceServers?: IceServerPreset | RTCIceServer[]): Pick<RTCConfiguration, 'iceServers' | 'iceTransportPolicy'>;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ICE Configuration Types and Presets
|
|
3
|
+
*
|
|
4
|
+
* Provides typed ICE server presets for common WebRTC configurations.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Pre-configured ICE server presets.
|
|
8
|
+
*
|
|
9
|
+
* - `rondevu`: Official Rondevu TURN/STUN servers (recommended)
|
|
10
|
+
* - `rondevu-relay`: Same as rondevu but forces relay mode (hides client IPs)
|
|
11
|
+
* - `rondevu-ipv4`: Direct IPv4 address (for networks with DNS issues)
|
|
12
|
+
* - `rondevu-ipv4-relay`: IPv4 with forced relay mode
|
|
13
|
+
* - `google-stun`: Google's free STUN servers (no relay, direct connections only)
|
|
14
|
+
* - `public-stun`: Multiple public STUN servers for redundancy
|
|
15
|
+
*/
|
|
16
|
+
export const ICE_SERVER_PRESETS = {
|
|
17
|
+
rondevu: {
|
|
18
|
+
iceServers: [
|
|
19
|
+
{ urls: 'stun:relay.ronde.vu:3478' },
|
|
20
|
+
{
|
|
21
|
+
urls: [
|
|
22
|
+
'turns:relay.ronde.vu:5349?transport=tcp',
|
|
23
|
+
'turn:relay.ronde.vu:3478?transport=tcp',
|
|
24
|
+
'turn:relay.ronde.vu:3478?transport=udp',
|
|
25
|
+
],
|
|
26
|
+
username: 'webrtcuser',
|
|
27
|
+
credential: 'changeme',
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
'rondevu-relay': {
|
|
32
|
+
iceServers: [
|
|
33
|
+
{ urls: 'stun:relay.ronde.vu:3478' },
|
|
34
|
+
{
|
|
35
|
+
urls: [
|
|
36
|
+
'turns:relay.ronde.vu:5349?transport=tcp',
|
|
37
|
+
'turn:relay.ronde.vu:3478?transport=tcp',
|
|
38
|
+
'turn:relay.ronde.vu:3478?transport=udp',
|
|
39
|
+
],
|
|
40
|
+
username: 'webrtcuser',
|
|
41
|
+
credential: 'changeme',
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
iceTransportPolicy: 'relay', // Force relay mode - hides client IPs
|
|
45
|
+
},
|
|
46
|
+
'rondevu-ipv4': {
|
|
47
|
+
iceServers: [
|
|
48
|
+
{ urls: 'stun:57.129.61.67:3478' },
|
|
49
|
+
{
|
|
50
|
+
urls: [
|
|
51
|
+
'turn:57.129.61.67:3478?transport=tcp',
|
|
52
|
+
'turn:57.129.61.67:3478?transport=udp',
|
|
53
|
+
],
|
|
54
|
+
username: 'webrtcuser',
|
|
55
|
+
credential: 'changeme',
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
'rondevu-ipv4-relay': {
|
|
60
|
+
iceServers: [
|
|
61
|
+
{ urls: 'stun:57.129.61.67:3478' },
|
|
62
|
+
{
|
|
63
|
+
urls: [
|
|
64
|
+
'turn:57.129.61.67:3478?transport=tcp',
|
|
65
|
+
'turn:57.129.61.67:3478?transport=udp',
|
|
66
|
+
],
|
|
67
|
+
username: 'webrtcuser',
|
|
68
|
+
credential: 'changeme',
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
iceTransportPolicy: 'relay',
|
|
72
|
+
},
|
|
73
|
+
'google-stun': {
|
|
74
|
+
iceServers: [
|
|
75
|
+
{ urls: 'stun:stun.l.google.com:19302' },
|
|
76
|
+
{ urls: 'stun:stun1.l.google.com:19302' },
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
'public-stun': {
|
|
80
|
+
iceServers: [
|
|
81
|
+
{ urls: 'stun:stun.l.google.com:19302' },
|
|
82
|
+
{ urls: 'stun:stun1.l.google.com:19302' },
|
|
83
|
+
{ urls: 'stun:stun.cloudflare.com:3478' },
|
|
84
|
+
{ urls: 'stun:stun.relay.metered.ca:80' },
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Get the full RTCConfiguration for a preset or custom ICE servers.
|
|
90
|
+
*
|
|
91
|
+
* @param iceServers - Either a preset name or custom ICE servers array
|
|
92
|
+
* @returns Partial RTCConfiguration with iceServers and optional iceTransportPolicy
|
|
93
|
+
*/
|
|
94
|
+
export function getIceConfiguration(iceServers) {
|
|
95
|
+
if (typeof iceServers === 'string') {
|
|
96
|
+
const preset = ICE_SERVER_PRESETS[iceServers];
|
|
97
|
+
return {
|
|
98
|
+
iceServers: preset.iceServers,
|
|
99
|
+
iceTransportPolicy: preset.iceTransportPolicy,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
// Default to rondevu preset if no ICE servers specified
|
|
103
|
+
if (!iceServers) {
|
|
104
|
+
const preset = ICE_SERVER_PRESETS.rondevu;
|
|
105
|
+
return {
|
|
106
|
+
iceServers: preset.iceServers,
|
|
107
|
+
iceTransportPolicy: preset.iceTransportPolicy,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return { iceServers };
|
|
111
|
+
}
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @xtr-dev/rondevu-client
|
|
3
3
|
* WebRTC peer signaling client
|
|
4
|
+
*
|
|
5
|
+
* Simple API:
|
|
6
|
+
* const rondevu = await Rondevu.connect()
|
|
7
|
+
*
|
|
8
|
+
* // Host: publish offers (auto-starts)
|
|
9
|
+
* const offer = await rondevu.offer({ tags: ['chat'], maxOffers: 5 })
|
|
10
|
+
* rondevu.on('connection:opened', (id, conn) => { ... })
|
|
11
|
+
* // Later: offer.cancel()
|
|
12
|
+
*
|
|
13
|
+
* // Guest: connect to a peer
|
|
14
|
+
* const peer = await rondevu.peer({ tags: ['chat'] })
|
|
15
|
+
* peer.on('open', () => peer.send('Hello!'))
|
|
4
16
|
*/
|
|
5
|
-
export { Rondevu
|
|
6
|
-
export {
|
|
7
|
-
export {
|
|
8
|
-
export {
|
|
9
|
-
export {
|
|
10
|
-
export {
|
|
11
|
-
export { ExponentialBackoff } from '../utils/exponential-backoff.js';
|
|
12
|
-
export { MessageBuffer } from '../utils/message-buffer.js';
|
|
13
|
-
export { WebCryptoAdapter } from '../crypto/web.js';
|
|
14
|
-
export { NodeCryptoAdapter } from '../crypto/node.js';
|
|
15
|
-
export type { Signaler, Binnable, } from './types.js';
|
|
16
|
-
export type { Keypair, OfferRequest, ServiceRequest, Service, ServiceOffer, IceCandidate, } from '../api/client.js';
|
|
17
|
-
export type { RondevuOptions, PublishServiceOptions, ConnectToServiceOptions, ConnectionContext, OfferContext, OfferFactory, ActiveOffer, FindServiceOptions, ServiceResult, PaginatedServiceResult } from './rondevu.js';
|
|
18
|
-
export type { CryptoAdapter } from '../crypto/adapter.js';
|
|
19
|
-
export type { ConnectionConfig, } from '../connections/config.js';
|
|
20
|
-
export type { ConnectionState, BufferedMessage, ReconnectInfo, StateChangeInfo, ConnectionEventMap, ConnectionEventName, ConnectionEventArgs, } from '../connections/events.js';
|
|
21
|
-
export type { OffererOptions, } from '../connections/offerer.js';
|
|
22
|
-
export type { AnswererOptions, } from '../connections/answerer.js';
|
|
17
|
+
export { Rondevu } from './rondevu.js';
|
|
18
|
+
export { Peer } from './peer.js';
|
|
19
|
+
export { ICE_SERVER_PRESETS } from './ice-config.js';
|
|
20
|
+
export type { RondevuOptions, OfferOptions, OfferHandle, DiscoverOptions, DiscoverResult, } from './rondevu.js';
|
|
21
|
+
export type { PeerState, PeerOptions } from './peer.js';
|
|
22
|
+
export type { IceServerPreset } from './ice-config.js';
|
package/dist/core/index.js
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @xtr-dev/rondevu-client
|
|
3
3
|
* WebRTC peer signaling client
|
|
4
|
+
*
|
|
5
|
+
* Simple API:
|
|
6
|
+
* const rondevu = await Rondevu.connect()
|
|
7
|
+
*
|
|
8
|
+
* // Host: publish offers (auto-starts)
|
|
9
|
+
* const offer = await rondevu.offer({ tags: ['chat'], maxOffers: 5 })
|
|
10
|
+
* rondevu.on('connection:opened', (id, conn) => { ... })
|
|
11
|
+
* // Later: offer.cancel()
|
|
12
|
+
*
|
|
13
|
+
* // Guest: connect to a peer
|
|
14
|
+
* const peer = await rondevu.peer({ tags: ['chat'] })
|
|
15
|
+
* peer.on('open', () => peer.send('Hello!'))
|
|
4
16
|
*/
|
|
5
|
-
|
|
6
|
-
export {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export {
|
|
11
|
-
export { AnswererConnection } from '../connections/answerer.js';
|
|
12
|
-
// Export utilities
|
|
13
|
-
export { ExponentialBackoff } from '../utils/exponential-backoff.js';
|
|
14
|
-
export { MessageBuffer } from '../utils/message-buffer.js';
|
|
15
|
-
// Export crypto adapters
|
|
16
|
-
export { WebCryptoAdapter } from '../crypto/web.js';
|
|
17
|
-
export { NodeCryptoAdapter } from '../crypto/node.js';
|
|
17
|
+
// Main entry point
|
|
18
|
+
export { Rondevu } from './rondevu.js';
|
|
19
|
+
// Simplified peer connection
|
|
20
|
+
export { Peer } from './peer.js';
|
|
21
|
+
// ICE server configuration presets
|
|
22
|
+
export { ICE_SERVER_PRESETS } from './ice-config.js';
|
|
@@ -2,53 +2,59 @@ import { EventEmitter } from 'eventemitter3';
|
|
|
2
2
|
import { RondevuAPI } from '../api/client.js';
|
|
3
3
|
import { OffererConnection } from '../connections/offerer.js';
|
|
4
4
|
import { ConnectionConfig } from '../connections/config.js';
|
|
5
|
+
import { WebRTCAdapter } from '../webrtc/adapter.js';
|
|
6
|
+
import type { PollAnswerEvent, PollIceEvent } from './polling-manager.js';
|
|
5
7
|
export type OfferFactory = (pc: RTCPeerConnection) => Promise<{
|
|
6
8
|
dc?: RTCDataChannel;
|
|
7
9
|
offer: RTCSessionDescriptionInit;
|
|
8
10
|
}>;
|
|
9
11
|
export interface OfferPoolOptions {
|
|
10
12
|
api: RondevuAPI;
|
|
11
|
-
|
|
13
|
+
tags: string[];
|
|
14
|
+
ownerUsername: string;
|
|
12
15
|
maxOffers: number;
|
|
13
16
|
offerFactory: OfferFactory;
|
|
14
17
|
ttl: number;
|
|
15
18
|
iceServers: RTCIceServer[];
|
|
19
|
+
iceTransportPolicy?: RTCIceTransportPolicy;
|
|
20
|
+
webrtcAdapter: WebRTCAdapter;
|
|
16
21
|
connectionConfig?: Partial<ConnectionConfig>;
|
|
17
22
|
debugEnabled?: boolean;
|
|
18
23
|
}
|
|
19
24
|
interface OfferPoolEvents {
|
|
20
25
|
'connection:opened': (offerId: string, connection: OffererConnection) => void;
|
|
21
|
-
'offer:created': (offerId: string,
|
|
26
|
+
'offer:created': (offerId: string, tags: string[]) => void;
|
|
22
27
|
'offer:failed': (offerId: string, error: Error) => void;
|
|
23
28
|
'connection:rotated': (oldOfferId: string, newOfferId: string, connection: OffererConnection) => void;
|
|
24
29
|
}
|
|
25
30
|
/**
|
|
26
|
-
* OfferPool manages a pool of WebRTC offers for
|
|
31
|
+
* OfferPool manages a pool of WebRTC offers for published tags.
|
|
27
32
|
* Maintains a target number of active offers and automatically replaces
|
|
28
33
|
* offers that fail or get answered.
|
|
29
34
|
*/
|
|
30
35
|
export declare class OfferPool extends EventEmitter<OfferPoolEvents> {
|
|
31
36
|
private readonly api;
|
|
32
|
-
private readonly
|
|
37
|
+
private readonly tags;
|
|
38
|
+
private readonly ownerUsername;
|
|
33
39
|
private readonly maxOffers;
|
|
34
40
|
private readonly offerFactory;
|
|
35
41
|
private readonly ttl;
|
|
36
42
|
private readonly iceServers;
|
|
43
|
+
private readonly iceTransportPolicy?;
|
|
44
|
+
private readonly webrtcAdapter;
|
|
37
45
|
private readonly connectionConfig?;
|
|
38
46
|
private readonly debugEnabled;
|
|
39
47
|
private readonly activeConnections;
|
|
40
48
|
private readonly fillLock;
|
|
41
49
|
private running;
|
|
42
|
-
private pollingInterval;
|
|
43
|
-
private lastPollTimestamp;
|
|
44
|
-
private static readonly POLLING_INTERVAL_MS;
|
|
45
50
|
constructor(options: OfferPoolOptions);
|
|
46
51
|
/**
|
|
47
|
-
* Start filling offers
|
|
52
|
+
* Start filling offers
|
|
53
|
+
* Polling is managed externally by Rondevu's PollingManager
|
|
48
54
|
*/
|
|
49
55
|
start(): Promise<void>;
|
|
50
56
|
/**
|
|
51
|
-
* Stop filling offers
|
|
57
|
+
* Stop filling offers
|
|
52
58
|
* Closes all active connections
|
|
53
59
|
*/
|
|
54
60
|
stop(): void;
|
|
@@ -73,6 +79,13 @@ export declare class OfferPool extends EventEmitter<OfferPoolEvents> {
|
|
|
73
79
|
* Uses AsyncLock to prevent concurrent fills
|
|
74
80
|
*/
|
|
75
81
|
private fillOffers;
|
|
82
|
+
/**
|
|
83
|
+
* Create and publish an offer to the server.
|
|
84
|
+
* Shared logic used by both createOffer() and createNewOfferForRotation().
|
|
85
|
+
*
|
|
86
|
+
* @returns The offer ID, RTCPeerConnection, and optional data channel
|
|
87
|
+
*/
|
|
88
|
+
private createOfferAndPublish;
|
|
76
89
|
/**
|
|
77
90
|
* Create a new offer for rotation (reuses existing creation logic)
|
|
78
91
|
* Similar to createOffer() but only creates the offer, doesn't create connection
|
|
@@ -83,9 +96,15 @@ export declare class OfferPool extends EventEmitter<OfferPoolEvents> {
|
|
|
83
96
|
*/
|
|
84
97
|
private createOffer;
|
|
85
98
|
/**
|
|
86
|
-
*
|
|
99
|
+
* Handle poll:answer event from PollingManager
|
|
100
|
+
* Called by Rondevu when a poll:answer event is received
|
|
101
|
+
*/
|
|
102
|
+
handlePollAnswer(data: PollAnswerEvent): Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* Handle poll:ice event from PollingManager
|
|
105
|
+
* Called by Rondevu when a poll:ice event is received
|
|
87
106
|
*/
|
|
88
|
-
|
|
107
|
+
handlePollIce(data: PollIceEvent): void;
|
|
89
108
|
/**
|
|
90
109
|
* Debug logging (only if debug enabled)
|
|
91
110
|
*/
|