@xtr-dev/rondevu-client 0.3.5 → 0.4.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 +401 -60
- package/dist/auth.d.ts +18 -0
- package/dist/auth.js +39 -0
- package/dist/bloom.d.ts +30 -0
- package/dist/bloom.js +73 -0
- package/dist/connection.d.ts +94 -49
- package/dist/connection.js +214 -211
- package/dist/index.d.ts +8 -3
- package/dist/index.js +9 -5
- package/dist/offers.d.ts +108 -0
- package/dist/offers.js +214 -0
- package/dist/rondevu.d.ts +39 -42
- package/dist/rondevu.js +33 -175
- package/package.json +2 -2
package/dist/connection.js
CHANGED
|
@@ -1,280 +1,283 @@
|
|
|
1
|
-
import { EventEmitter } from './event-emitter.js';
|
|
2
1
|
/**
|
|
3
|
-
*
|
|
2
|
+
* High-level WebRTC connection manager for Rondevu
|
|
3
|
+
* Handles offer/answer exchange, ICE candidates, and connection lifecycle
|
|
4
4
|
*/
|
|
5
|
-
export class RondevuConnection
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
this.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
this.
|
|
17
|
-
this.dataChannels = new Map();
|
|
18
|
-
this.pollingIntervalMs = params.pollingInterval;
|
|
19
|
-
this.connectionTimeoutMs = params.connectionTimeout;
|
|
20
|
-
this.wrtc = params.wrtc;
|
|
21
|
-
// Use injected WebRTC polyfill or fall back to global
|
|
22
|
-
this.RTCIceCandidate = params.wrtc?.RTCIceCandidate || globalThis.RTCIceCandidate;
|
|
23
|
-
this.setupEventHandlers();
|
|
24
|
-
this.startConnectionTimeout();
|
|
5
|
+
export class RondevuConnection {
|
|
6
|
+
/**
|
|
7
|
+
* Current connection state
|
|
8
|
+
*/
|
|
9
|
+
get connectionState() {
|
|
10
|
+
return this.pc.connectionState;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The offer ID for this connection
|
|
14
|
+
*/
|
|
15
|
+
get id() {
|
|
16
|
+
return this.offerId;
|
|
25
17
|
}
|
|
26
18
|
/**
|
|
27
|
-
*
|
|
19
|
+
* Get the primary data channel (if created)
|
|
28
20
|
*/
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
21
|
+
get channel() {
|
|
22
|
+
return this.dataChannel;
|
|
23
|
+
}
|
|
24
|
+
constructor(offersApi, rtcConfig = {
|
|
25
|
+
iceServers: [
|
|
26
|
+
{ urls: 'stun:stun.l.google.com:19302' },
|
|
27
|
+
{ urls: 'stun:stun1.l.google.com:19302' }
|
|
28
|
+
]
|
|
29
|
+
}) {
|
|
30
|
+
this.rtcConfig = rtcConfig;
|
|
31
|
+
this.lastIceTimestamp = Date.now();
|
|
32
|
+
this.eventListeners = new Map();
|
|
33
|
+
this.pendingIceCandidates = [];
|
|
34
|
+
this.offersApi = offersApi;
|
|
35
|
+
this.pc = new RTCPeerConnection(rtcConfig);
|
|
36
|
+
this.setupPeerConnection();
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Set up peer connection event handlers
|
|
40
|
+
*/
|
|
41
|
+
setupPeerConnection() {
|
|
42
|
+
this.pc.onicecandidate = async (event) => {
|
|
43
|
+
if (event.candidate) {
|
|
44
|
+
// Convert RTCIceCandidate to RTCIceCandidateInit (plain object)
|
|
45
|
+
const candidateData = {
|
|
46
|
+
candidate: event.candidate.candidate,
|
|
47
|
+
sdpMid: event.candidate.sdpMid,
|
|
48
|
+
sdpMLineIndex: event.candidate.sdpMLineIndex,
|
|
49
|
+
usernameFragment: event.candidate.usernameFragment,
|
|
50
|
+
};
|
|
51
|
+
if (this.offerId) {
|
|
52
|
+
// offerId is set, send immediately (trickle ICE)
|
|
53
|
+
try {
|
|
54
|
+
await this.offersApi.addIceCandidates(this.offerId, [candidateData]);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
console.error('Error sending ICE candidate:', err);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
// offerId not set yet, buffer the candidate
|
|
62
|
+
this.pendingIceCandidates.push(candidateData);
|
|
63
|
+
}
|
|
36
64
|
}
|
|
37
65
|
};
|
|
38
|
-
// Connection state changes
|
|
39
66
|
this.pc.onconnectionstatechange = () => {
|
|
40
|
-
this.
|
|
67
|
+
switch (this.pc.connectionState) {
|
|
68
|
+
case 'connecting':
|
|
69
|
+
this.emit('connecting');
|
|
70
|
+
break;
|
|
71
|
+
case 'connected':
|
|
72
|
+
this.emit('connected');
|
|
73
|
+
// Stop polling once connected - we have all the ICE candidates we need
|
|
74
|
+
this.stopPolling();
|
|
75
|
+
break;
|
|
76
|
+
case 'disconnected':
|
|
77
|
+
case 'failed':
|
|
78
|
+
case 'closed':
|
|
79
|
+
this.emit('disconnected');
|
|
80
|
+
this.stopPolling();
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
41
83
|
};
|
|
42
|
-
// Remote data channels
|
|
43
84
|
this.pc.ondatachannel = (event) => {
|
|
44
|
-
this.
|
|
85
|
+
this.dataChannel = event.channel;
|
|
86
|
+
this.emit('datachannel', event.channel);
|
|
45
87
|
};
|
|
46
|
-
// Remote media streams
|
|
47
88
|
this.pc.ontrack = (event) => {
|
|
48
|
-
|
|
49
|
-
this.emit('stream', event.streams[0]);
|
|
50
|
-
}
|
|
89
|
+
this.emit('track', event);
|
|
51
90
|
};
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const state = this.pc.iceConnectionState;
|
|
55
|
-
if (state === 'failed' || state === 'closed') {
|
|
56
|
-
this.emit('error', new Error(`ICE connection ${state}`));
|
|
57
|
-
if (state === 'failed') {
|
|
58
|
-
this.close();
|
|
59
|
-
}
|
|
60
|
-
}
|
|
91
|
+
this.pc.onicecandidateerror = (event) => {
|
|
92
|
+
console.error('ICE candidate error:', event);
|
|
61
93
|
};
|
|
62
94
|
}
|
|
63
95
|
/**
|
|
64
|
-
*
|
|
96
|
+
* Flush buffered ICE candidates (trickle ICE support)
|
|
65
97
|
*/
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
break;
|
|
77
|
-
case 'disconnected':
|
|
78
|
-
this.emit('disconnect');
|
|
79
|
-
break;
|
|
80
|
-
case 'failed':
|
|
81
|
-
this.emit('error', new Error('Connection failed'));
|
|
82
|
-
this.close();
|
|
83
|
-
break;
|
|
84
|
-
case 'closed':
|
|
85
|
-
this.emit('disconnect');
|
|
86
|
-
break;
|
|
98
|
+
async flushPendingIceCandidates() {
|
|
99
|
+
if (this.pendingIceCandidates.length > 0 && this.offerId) {
|
|
100
|
+
try {
|
|
101
|
+
await this.offersApi.addIceCandidates(this.offerId, this.pendingIceCandidates);
|
|
102
|
+
this.pendingIceCandidates = [];
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
console.error('Error flushing pending ICE candidates:', err);
|
|
106
|
+
}
|
|
87
107
|
}
|
|
88
108
|
}
|
|
89
109
|
/**
|
|
90
|
-
*
|
|
110
|
+
* Create an offer and advertise on topics
|
|
91
111
|
*/
|
|
92
|
-
async
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
catch (err) {
|
|
101
|
-
throw new Error(`Failed to send ICE candidate: ${err.message}`);
|
|
112
|
+
async createOffer(options) {
|
|
113
|
+
this.role = 'offerer';
|
|
114
|
+
// Create data channel if requested
|
|
115
|
+
if (options.createDataChannel !== false) {
|
|
116
|
+
this.dataChannel = this.pc.createDataChannel(options.dataChannelLabel || 'data');
|
|
117
|
+
this.emit('datachannel', this.dataChannel);
|
|
102
118
|
}
|
|
119
|
+
// Create WebRTC offer
|
|
120
|
+
const offer = await this.pc.createOffer();
|
|
121
|
+
await this.pc.setLocalDescription(offer);
|
|
122
|
+
// Create offer on Rondevu server
|
|
123
|
+
const offers = await this.offersApi.create([{
|
|
124
|
+
sdp: offer.sdp,
|
|
125
|
+
topics: options.topics,
|
|
126
|
+
ttl: options.ttl || 300000
|
|
127
|
+
}]);
|
|
128
|
+
this.offerId = offers[0].id;
|
|
129
|
+
// Flush any ICE candidates that were generated during offer creation
|
|
130
|
+
await this.flushPendingIceCandidates();
|
|
131
|
+
// Start polling for answers
|
|
132
|
+
this.startAnswerPolling();
|
|
133
|
+
return this.offerId;
|
|
103
134
|
}
|
|
104
135
|
/**
|
|
105
|
-
*
|
|
136
|
+
* Answer an existing offer
|
|
106
137
|
*/
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
this.poll().catch((err) => {
|
|
114
|
-
this.emit('error', new Error(`Poll error: ${err.message}`));
|
|
138
|
+
async answer(offerId, offerSdp) {
|
|
139
|
+
this.role = 'answerer';
|
|
140
|
+
// Set remote description
|
|
141
|
+
await this.pc.setRemoteDescription({
|
|
142
|
+
type: 'offer',
|
|
143
|
+
sdp: offerSdp
|
|
115
144
|
});
|
|
116
|
-
//
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
clearInterval(this.pollingInterval);
|
|
130
|
-
this.pollingInterval = undefined;
|
|
131
|
-
}
|
|
145
|
+
// Create answer
|
|
146
|
+
const answer = await this.pc.createAnswer();
|
|
147
|
+
await this.pc.setLocalDescription(answer);
|
|
148
|
+
// Send answer to server FIRST
|
|
149
|
+
// This registers us as the answerer before ICE candidates arrive
|
|
150
|
+
await this.offersApi.answer(offerId, answer.sdp);
|
|
151
|
+
// Now set offerId to enable ICE candidate sending
|
|
152
|
+
// This prevents a race condition where ICE candidates arrive before answer is registered
|
|
153
|
+
this.offerId = offerId;
|
|
154
|
+
// Flush any ICE candidates that were generated during answer creation
|
|
155
|
+
await this.flushPendingIceCandidates();
|
|
156
|
+
// Start polling for ICE candidates
|
|
157
|
+
this.startIcePolling();
|
|
132
158
|
}
|
|
133
159
|
/**
|
|
134
|
-
*
|
|
160
|
+
* Start polling for answers (offerer only)
|
|
135
161
|
*/
|
|
136
|
-
|
|
137
|
-
if (this.
|
|
138
|
-
this.stopPolling();
|
|
162
|
+
startAnswerPolling() {
|
|
163
|
+
if (this.role !== 'offerer' || !this.offerId)
|
|
139
164
|
return;
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
if (offererResponse.answer && !this.pc.currentRemoteDescription) {
|
|
165
|
+
this.answerPollingInterval = setInterval(async () => {
|
|
166
|
+
try {
|
|
167
|
+
const answers = await this.offersApi.getAnswers();
|
|
168
|
+
const myAnswer = answers.find(a => a.offerId === this.offerId);
|
|
169
|
+
if (myAnswer) {
|
|
170
|
+
// Set remote description
|
|
147
171
|
await this.pc.setRemoteDescription({
|
|
148
172
|
type: 'answer',
|
|
149
|
-
sdp:
|
|
173
|
+
sdp: myAnswer.sdp
|
|
150
174
|
});
|
|
175
|
+
// Stop answer polling, start ICE polling
|
|
176
|
+
this.stopAnswerPolling();
|
|
177
|
+
this.startIcePolling();
|
|
151
178
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
catch (err) {
|
|
160
|
-
console.warn('Failed to add ICE candidate:', err);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
console.error('Error polling for answers:', err);
|
|
182
|
+
// Stop polling if offer expired/not found
|
|
183
|
+
if (err instanceof Error && err.message.includes('not found')) {
|
|
184
|
+
this.stopPolling();
|
|
163
185
|
}
|
|
164
186
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
187
|
+
}, 2000);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Start polling for ICE candidates
|
|
191
|
+
*/
|
|
192
|
+
startIcePolling() {
|
|
193
|
+
if (!this.offerId)
|
|
194
|
+
return;
|
|
195
|
+
this.icePollingInterval = setInterval(async () => {
|
|
196
|
+
if (!this.offerId)
|
|
197
|
+
return;
|
|
198
|
+
try {
|
|
199
|
+
const candidates = await this.offersApi.getIceCandidates(this.offerId, this.lastIceTimestamp);
|
|
200
|
+
for (const cand of candidates) {
|
|
201
|
+
// Use the candidate object directly - it's already RTCIceCandidateInit
|
|
202
|
+
await this.pc.addIceCandidate(new RTCIceCandidate(cand.candidate));
|
|
203
|
+
this.lastIceTimestamp = cand.createdAt;
|
|
179
204
|
}
|
|
180
205
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
206
|
+
catch (err) {
|
|
207
|
+
console.error('Error polling for ICE candidates:', err);
|
|
208
|
+
// Stop polling if offer expired/not found
|
|
209
|
+
if (err instanceof Error && err.message.includes('not found')) {
|
|
210
|
+
this.stopPolling();
|
|
211
|
+
}
|
|
187
212
|
}
|
|
188
|
-
|
|
189
|
-
}
|
|
213
|
+
}, 1000);
|
|
190
214
|
}
|
|
191
215
|
/**
|
|
192
|
-
*
|
|
216
|
+
* Stop answer polling
|
|
193
217
|
*/
|
|
194
|
-
|
|
195
|
-
this.
|
|
196
|
-
|
|
218
|
+
stopAnswerPolling() {
|
|
219
|
+
if (this.answerPollingInterval) {
|
|
220
|
+
clearInterval(this.answerPollingInterval);
|
|
221
|
+
this.answerPollingInterval = undefined;
|
|
222
|
+
}
|
|
197
223
|
}
|
|
198
224
|
/**
|
|
199
|
-
*
|
|
225
|
+
* Stop ICE polling
|
|
200
226
|
*/
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
this.dataChannels.set(label, channel);
|
|
227
|
+
stopIcePolling() {
|
|
228
|
+
if (this.icePollingInterval) {
|
|
229
|
+
clearInterval(this.icePollingInterval);
|
|
230
|
+
this.icePollingInterval = undefined;
|
|
206
231
|
}
|
|
207
|
-
return channel;
|
|
208
232
|
}
|
|
209
233
|
/**
|
|
210
|
-
*
|
|
234
|
+
* Stop all polling
|
|
211
235
|
*/
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
});
|
|
236
|
+
stopPolling() {
|
|
237
|
+
this.stopAnswerPolling();
|
|
238
|
+
this.stopIcePolling();
|
|
216
239
|
}
|
|
217
240
|
/**
|
|
218
|
-
*
|
|
241
|
+
* Add event listener
|
|
219
242
|
*/
|
|
220
|
-
|
|
221
|
-
|
|
243
|
+
on(event, listener) {
|
|
244
|
+
if (!this.eventListeners.has(event)) {
|
|
245
|
+
this.eventListeners.set(event, new Set());
|
|
246
|
+
}
|
|
247
|
+
this.eventListeners.get(event).add(listener);
|
|
222
248
|
}
|
|
223
249
|
/**
|
|
224
|
-
*
|
|
250
|
+
* Remove event listener
|
|
225
251
|
*/
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
}
|
|
232
|
-
}, this.connectionTimeoutMs);
|
|
252
|
+
off(event, listener) {
|
|
253
|
+
const listeners = this.eventListeners.get(event);
|
|
254
|
+
if (listeners) {
|
|
255
|
+
listeners.delete(listener);
|
|
256
|
+
}
|
|
233
257
|
}
|
|
234
258
|
/**
|
|
235
|
-
*
|
|
259
|
+
* Emit event
|
|
236
260
|
*/
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
261
|
+
emit(event, ...args) {
|
|
262
|
+
const listeners = this.eventListeners.get(event);
|
|
263
|
+
if (listeners) {
|
|
264
|
+
listeners.forEach(listener => {
|
|
265
|
+
listener(...args);
|
|
266
|
+
});
|
|
241
267
|
}
|
|
242
268
|
}
|
|
243
269
|
/**
|
|
244
|
-
*
|
|
245
|
-
* This ends the session for all connected peers
|
|
270
|
+
* Add a media track to the connection
|
|
246
271
|
*/
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
await this.client.leave(this.id);
|
|
250
|
-
}
|
|
251
|
-
catch (err) {
|
|
252
|
-
// Ignore errors - session might already be expired
|
|
253
|
-
console.debug('Leave error (ignored):', err);
|
|
254
|
-
}
|
|
255
|
-
this.close();
|
|
272
|
+
addTrack(track, ...streams) {
|
|
273
|
+
return this.pc.addTrack(track, ...streams);
|
|
256
274
|
}
|
|
257
275
|
/**
|
|
258
|
-
* Close the connection and
|
|
276
|
+
* Close the connection and clean up
|
|
259
277
|
*/
|
|
260
278
|
close() {
|
|
261
|
-
if (this.isClosed) {
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
this.isClosed = true;
|
|
265
279
|
this.stopPolling();
|
|
266
|
-
this.
|
|
267
|
-
|
|
268
|
-
this.dataChannels.forEach(dc => {
|
|
269
|
-
if (dc.readyState === 'open' || dc.readyState === 'connecting') {
|
|
270
|
-
dc.close();
|
|
271
|
-
}
|
|
272
|
-
});
|
|
273
|
-
this.dataChannels.clear();
|
|
274
|
-
// Close peer connection
|
|
275
|
-
if (this.pc.connectionState !== 'closed') {
|
|
276
|
-
this.pc.close();
|
|
277
|
-
}
|
|
278
|
-
this.emit('disconnect');
|
|
280
|
+
this.pc.close();
|
|
281
|
+
this.eventListeners.clear();
|
|
279
282
|
}
|
|
280
283
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @xtr-dev/rondevu-client
|
|
3
|
-
* WebRTC peer signaling and discovery client
|
|
3
|
+
* WebRTC peer signaling and discovery client with topic-based discovery
|
|
4
4
|
*/
|
|
5
5
|
export { Rondevu } from './rondevu.js';
|
|
6
|
+
export type { RondevuOptions } from './rondevu.js';
|
|
7
|
+
export { RondevuAuth } from './auth.js';
|
|
8
|
+
export type { Credentials, FetchFunction } from './auth.js';
|
|
9
|
+
export { RondevuOffers } from './offers.js';
|
|
10
|
+
export type { CreateOfferRequest, Offer, IceCandidate, TopicInfo } from './offers.js';
|
|
11
|
+
export { BloomFilter } from './bloom.js';
|
|
6
12
|
export { RondevuConnection } from './connection.js';
|
|
7
|
-
export {
|
|
8
|
-
export type { RondevuOptions, ConnectionRole, RondevuConnectionParams, RondevuConnectionEvents, WebRTCPolyfill, Side, CreateOfferRequest, CreateOfferResponse, AnswerRequest, AnswerResponse, PollRequest, PollOffererResponse, PollAnswererResponse, PollResponse, VersionResponse, HealthResponse, ErrorResponse, RondevuClientOptions, } from './types.js';
|
|
13
|
+
export type { ConnectionOptions, RondevuConnectionEvents } from './connection.js';
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @xtr-dev/rondevu-client
|
|
3
|
-
* WebRTC peer signaling and discovery client
|
|
3
|
+
* WebRTC peer signaling and discovery client with topic-based discovery
|
|
4
4
|
*/
|
|
5
|
-
// Export main
|
|
5
|
+
// Export main client class
|
|
6
6
|
export { Rondevu } from './rondevu.js';
|
|
7
|
-
// Export
|
|
7
|
+
// Export authentication
|
|
8
|
+
export { RondevuAuth } from './auth.js';
|
|
9
|
+
// Export offers API
|
|
10
|
+
export { RondevuOffers } from './offers.js';
|
|
11
|
+
// Export bloom filter
|
|
12
|
+
export { BloomFilter } from './bloom.js';
|
|
13
|
+
// Export connection manager
|
|
8
14
|
export { RondevuConnection } from './connection.js';
|
|
9
|
-
// Export low-level signaling API (for advanced usage)
|
|
10
|
-
export { RondevuAPI } from './client.js';
|
package/dist/offers.d.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { Credentials, FetchFunction } from './auth.js';
|
|
2
|
+
export interface CreateOfferRequest {
|
|
3
|
+
id?: string;
|
|
4
|
+
sdp: string;
|
|
5
|
+
topics: string[];
|
|
6
|
+
ttl?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface Offer {
|
|
9
|
+
id: string;
|
|
10
|
+
peerId: string;
|
|
11
|
+
sdp: string;
|
|
12
|
+
topics: string[];
|
|
13
|
+
createdAt?: number;
|
|
14
|
+
expiresAt: number;
|
|
15
|
+
lastSeen: number;
|
|
16
|
+
answererPeerId?: string;
|
|
17
|
+
answerSdp?: string;
|
|
18
|
+
answeredAt?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* RTCIceCandidateInit interface for environments without native WebRTC types
|
|
22
|
+
*/
|
|
23
|
+
export interface RTCIceCandidateInit {
|
|
24
|
+
candidate?: string;
|
|
25
|
+
sdpMid?: string | null;
|
|
26
|
+
sdpMLineIndex?: number | null;
|
|
27
|
+
usernameFragment?: string | null;
|
|
28
|
+
}
|
|
29
|
+
export interface IceCandidate {
|
|
30
|
+
candidate: RTCIceCandidateInit;
|
|
31
|
+
peerId: string;
|
|
32
|
+
role: 'offerer' | 'answerer';
|
|
33
|
+
createdAt: number;
|
|
34
|
+
}
|
|
35
|
+
export interface TopicInfo {
|
|
36
|
+
topic: string;
|
|
37
|
+
activePeers: number;
|
|
38
|
+
}
|
|
39
|
+
export declare class RondevuOffers {
|
|
40
|
+
private baseUrl;
|
|
41
|
+
private credentials;
|
|
42
|
+
private fetchFn;
|
|
43
|
+
constructor(baseUrl: string, credentials: Credentials, fetchFn?: FetchFunction);
|
|
44
|
+
/**
|
|
45
|
+
* Create one or more offers
|
|
46
|
+
*/
|
|
47
|
+
create(offers: CreateOfferRequest[]): Promise<Offer[]>;
|
|
48
|
+
/**
|
|
49
|
+
* Find offers by topic with optional bloom filter
|
|
50
|
+
*/
|
|
51
|
+
findByTopic(topic: string, options?: {
|
|
52
|
+
bloomFilter?: Uint8Array;
|
|
53
|
+
limit?: number;
|
|
54
|
+
}): Promise<Offer[]>;
|
|
55
|
+
/**
|
|
56
|
+
* Get all offers from a specific peer
|
|
57
|
+
*/
|
|
58
|
+
getByPeerId(peerId: string): Promise<{
|
|
59
|
+
offers: Offer[];
|
|
60
|
+
topics: string[];
|
|
61
|
+
}>;
|
|
62
|
+
/**
|
|
63
|
+
* Get topics with active peer counts (paginated)
|
|
64
|
+
*/
|
|
65
|
+
getTopics(options?: {
|
|
66
|
+
limit?: number;
|
|
67
|
+
offset?: number;
|
|
68
|
+
}): Promise<{
|
|
69
|
+
topics: TopicInfo[];
|
|
70
|
+
total: number;
|
|
71
|
+
limit: number;
|
|
72
|
+
offset: number;
|
|
73
|
+
}>;
|
|
74
|
+
/**
|
|
75
|
+
* Get own offers
|
|
76
|
+
*/
|
|
77
|
+
getMine(): Promise<Offer[]>;
|
|
78
|
+
/**
|
|
79
|
+
* Update offer heartbeat
|
|
80
|
+
*/
|
|
81
|
+
heartbeat(offerId: string): Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* Delete an offer
|
|
84
|
+
*/
|
|
85
|
+
delete(offerId: string): Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* Answer an offer
|
|
88
|
+
*/
|
|
89
|
+
answer(offerId: string, sdp: string): Promise<void>;
|
|
90
|
+
/**
|
|
91
|
+
* Get answers to your offers
|
|
92
|
+
*/
|
|
93
|
+
getAnswers(): Promise<Array<{
|
|
94
|
+
offerId: string;
|
|
95
|
+
answererId: string;
|
|
96
|
+
sdp: string;
|
|
97
|
+
answeredAt: number;
|
|
98
|
+
topics: string[];
|
|
99
|
+
}>>;
|
|
100
|
+
/**
|
|
101
|
+
* Post ICE candidates for an offer
|
|
102
|
+
*/
|
|
103
|
+
addIceCandidates(offerId: string, candidates: RTCIceCandidateInit[]): Promise<void>;
|
|
104
|
+
/**
|
|
105
|
+
* Get ICE candidates for an offer
|
|
106
|
+
*/
|
|
107
|
+
getIceCandidates(offerId: string, since?: number): Promise<IceCandidate[]>;
|
|
108
|
+
}
|