@xtr-dev/rondevu-client 0.10.1 → 0.11.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.
@@ -7,7 +7,9 @@ export interface RondevuServiceOptions {
7
7
  }
8
8
  export interface PublishServiceOptions {
9
9
  serviceFqn: string;
10
- sdp: string;
10
+ offers: Array<{
11
+ sdp: string;
12
+ }>;
11
13
  ttl?: number;
12
14
  isPublic?: boolean;
13
15
  metadata?: Record<string, any>;
@@ -36,7 +38,7 @@ export interface PublishServiceOptions {
36
38
  * // Publish a service
37
39
  * const publishedService = await service.publishService({
38
40
  * serviceFqn: 'chat.app@1.0.0',
39
- * sdp: offerSdp,
41
+ * offers: [{ sdp: offerSdp }],
40
42
  * ttl: 300000,
41
43
  * isPublic: true,
42
44
  * })
@@ -66,6 +68,10 @@ export declare class RondevuService {
66
68
  * Get the current keypair (for backup/storage)
67
69
  */
68
70
  getKeypair(): Keypair | null;
71
+ /**
72
+ * Get the username
73
+ */
74
+ getUsername(): string;
69
75
  /**
70
76
  * Get the public key
71
77
  */
@@ -23,7 +23,7 @@ import { RondevuAPI } from './api.js';
23
23
  * // Publish a service
24
24
  * const publishedService = await service.publishService({
25
25
  * serviceFqn: 'chat.app@1.0.0',
26
- * sdp: offerSdp,
26
+ * offers: [{ sdp: offerSdp }],
27
27
  * ttl: 300000,
28
28
  * isPublic: true,
29
29
  * })
@@ -48,7 +48,7 @@ export class RondevuService {
48
48
  // Register with API if no credentials provided
49
49
  if (!this.api['credentials']) {
50
50
  const credentials = await this.api.register();
51
- this.api.credentials = credentials;
51
+ this.api.setCredentials(credentials);
52
52
  }
53
53
  }
54
54
  /**
@@ -70,7 +70,7 @@ export class RondevuService {
70
70
  throw new Error(`Username "${this.username}" is already claimed by another user`);
71
71
  }
72
72
  // Generate signature for username claim
73
- const message = `claim-username-${this.username}-${Date.now()}`;
73
+ const message = `claim:${this.username}:${Date.now()}`;
74
74
  const signature = await RondevuAPI.signMessage(message, this.keypair.privateKey);
75
75
  // Claim the username
76
76
  await this.api.claimUsername(this.username, this.keypair.publicKey, signature, message);
@@ -86,15 +86,15 @@ export class RondevuService {
86
86
  if (!this.usernameClaimed) {
87
87
  throw new Error('Username not claimed. Call claimUsername() first or the server will reject the service.');
88
88
  }
89
- const { serviceFqn, sdp, ttl, isPublic, metadata } = options;
89
+ const { serviceFqn, offers, ttl, isPublic, metadata } = options;
90
90
  // Generate signature for service publication
91
- const message = `publish-${this.username}-${serviceFqn}-${Date.now()}`;
91
+ const message = `publish:${this.username}:${serviceFqn}:${Date.now()}`;
92
92
  const signature = await RondevuAPI.signMessage(message, this.keypair.privateKey);
93
93
  // Create service request
94
94
  const serviceRequest = {
95
95
  username: this.username,
96
96
  serviceFqn,
97
- sdp,
97
+ offers,
98
98
  signature,
99
99
  message,
100
100
  ttl,
@@ -110,6 +110,12 @@ export class RondevuService {
110
110
  getKeypair() {
111
111
  return this.keypair;
112
112
  }
113
+ /**
114
+ * Get the username
115
+ */
116
+ getUsername() {
117
+ return this.username;
118
+ }
113
119
  /**
114
120
  * Get the public key
115
121
  */
@@ -0,0 +1,110 @@
1
+ import { Signaler } from './types.js';
2
+ import { RondevuService } from './rondevu-service.js';
3
+ import { Binnable } from './bin.js';
4
+ export interface PollingConfig {
5
+ initialInterval?: number;
6
+ maxInterval?: number;
7
+ backoffMultiplier?: number;
8
+ maxRetries?: number;
9
+ jitter?: boolean;
10
+ }
11
+ /**
12
+ * RondevuSignaler - Handles WebRTC signaling via Rondevu service
13
+ *
14
+ * Manages offer/answer exchange and ICE candidate polling for establishing
15
+ * WebRTC connections through the Rondevu signaling server.
16
+ *
17
+ * Supports configurable polling with exponential backoff and jitter to reduce
18
+ * server load and prevent thundering herd issues.
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * const signaler = new RondevuSignaler(
23
+ * rondevuService,
24
+ * 'chat.app@1.0.0',
25
+ * 'peer-username',
26
+ * { initialInterval: 500, maxInterval: 5000, jitter: true }
27
+ * )
28
+ *
29
+ * // For offerer:
30
+ * await signaler.setOffer(offer)
31
+ * signaler.addAnswerListener(answer => {
32
+ * // Handle remote answer
33
+ * })
34
+ *
35
+ * // For answerer:
36
+ * signaler.addOfferListener(offer => {
37
+ * // Handle remote offer
38
+ * })
39
+ * await signaler.setAnswer(answer)
40
+ * ```
41
+ */
42
+ export declare class RondevuSignaler implements Signaler {
43
+ private readonly rondevu;
44
+ private readonly service;
45
+ private readonly host?;
46
+ private offerId;
47
+ private serviceUuid;
48
+ private offerListeners;
49
+ private answerListeners;
50
+ private iceListeners;
51
+ private answerPollingTimeout;
52
+ private icePollingTimeout;
53
+ private lastIceTimestamp;
54
+ private isPolling;
55
+ private pollingConfig;
56
+ constructor(rondevu: RondevuService, service: string, host?: string | undefined, pollingConfig?: PollingConfig);
57
+ /**
58
+ * Publish an offer as a service
59
+ * Used by the offerer to make their offer available
60
+ */
61
+ setOffer(offer: RTCSessionDescriptionInit): Promise<void>;
62
+ /**
63
+ * Send an answer to the offerer
64
+ * Used by the answerer to respond to an offer
65
+ */
66
+ setAnswer(answer: RTCSessionDescriptionInit): Promise<void>;
67
+ /**
68
+ * Listen for incoming offers
69
+ * Used by the answerer to receive offers from the offerer
70
+ */
71
+ addOfferListener(callback: (offer: RTCSessionDescriptionInit) => void): Binnable;
72
+ /**
73
+ * Listen for incoming answers
74
+ * Used by the offerer to receive the answer from the answerer
75
+ */
76
+ addAnswerListener(callback: (answer: RTCSessionDescriptionInit) => void): Binnable;
77
+ /**
78
+ * Send an ICE candidate to the remote peer
79
+ */
80
+ addIceCandidate(candidate: RTCIceCandidate): Promise<void>;
81
+ /**
82
+ * Listen for ICE candidates from the remote peer
83
+ */
84
+ addListener(callback: (candidate: RTCIceCandidate) => void): Binnable;
85
+ /**
86
+ * Search for an offer from the host
87
+ * Used by the answerer to find the offerer's service
88
+ */
89
+ private searchForOffer;
90
+ /**
91
+ * Start polling for answer (offerer side) with exponential backoff
92
+ */
93
+ private startAnswerPolling;
94
+ /**
95
+ * Stop polling for answer
96
+ */
97
+ private stopAnswerPolling;
98
+ /**
99
+ * Start polling for ICE candidates with adaptive backoff
100
+ */
101
+ private startIcePolling;
102
+ /**
103
+ * Stop polling for ICE candidates
104
+ */
105
+ private stopIcePolling;
106
+ /**
107
+ * Stop all polling and cleanup
108
+ */
109
+ dispose(): void;
110
+ }
@@ -0,0 +1,372 @@
1
+ /**
2
+ * RondevuSignaler - Handles WebRTC signaling via Rondevu service
3
+ *
4
+ * Manages offer/answer exchange and ICE candidate polling for establishing
5
+ * WebRTC connections through the Rondevu signaling server.
6
+ *
7
+ * Supports configurable polling with exponential backoff and jitter to reduce
8
+ * server load and prevent thundering herd issues.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const signaler = new RondevuSignaler(
13
+ * rondevuService,
14
+ * 'chat.app@1.0.0',
15
+ * 'peer-username',
16
+ * { initialInterval: 500, maxInterval: 5000, jitter: true }
17
+ * )
18
+ *
19
+ * // For offerer:
20
+ * await signaler.setOffer(offer)
21
+ * signaler.addAnswerListener(answer => {
22
+ * // Handle remote answer
23
+ * })
24
+ *
25
+ * // For answerer:
26
+ * signaler.addOfferListener(offer => {
27
+ * // Handle remote offer
28
+ * })
29
+ * await signaler.setAnswer(answer)
30
+ * ```
31
+ */
32
+ export class RondevuSignaler {
33
+ constructor(rondevu, service, host, pollingConfig) {
34
+ this.rondevu = rondevu;
35
+ this.service = service;
36
+ this.host = host;
37
+ this.offerId = null;
38
+ this.serviceUuid = null;
39
+ this.offerListeners = [];
40
+ this.answerListeners = [];
41
+ this.iceListeners = [];
42
+ this.answerPollingTimeout = null;
43
+ this.icePollingTimeout = null;
44
+ this.lastIceTimestamp = 0;
45
+ this.isPolling = false;
46
+ this.pollingConfig = {
47
+ initialInterval: pollingConfig?.initialInterval ?? 500,
48
+ maxInterval: pollingConfig?.maxInterval ?? 5000,
49
+ backoffMultiplier: pollingConfig?.backoffMultiplier ?? 1.5,
50
+ maxRetries: pollingConfig?.maxRetries ?? 50,
51
+ jitter: pollingConfig?.jitter ?? true
52
+ };
53
+ }
54
+ /**
55
+ * Publish an offer as a service
56
+ * Used by the offerer to make their offer available
57
+ */
58
+ async setOffer(offer) {
59
+ if (!offer.sdp) {
60
+ throw new Error('Offer SDP is required');
61
+ }
62
+ // Publish service with the offer SDP
63
+ const publishedService = await this.rondevu.publishService({
64
+ serviceFqn: this.service,
65
+ offers: [{ sdp: offer.sdp }],
66
+ ttl: 300000, // 5 minutes
67
+ isPublic: true,
68
+ });
69
+ // Get the first offer from the published service
70
+ if (!publishedService.offers || publishedService.offers.length === 0) {
71
+ throw new Error('No offers returned from service publication');
72
+ }
73
+ this.offerId = publishedService.offers[0].offerId;
74
+ this.serviceUuid = publishedService.uuid;
75
+ // Start polling for answer
76
+ this.startAnswerPolling();
77
+ // Start polling for ICE candidates
78
+ this.startIcePolling();
79
+ }
80
+ /**
81
+ * Send an answer to the offerer
82
+ * Used by the answerer to respond to an offer
83
+ */
84
+ async setAnswer(answer) {
85
+ if (!answer.sdp) {
86
+ throw new Error('Answer SDP is required');
87
+ }
88
+ if (!this.offerId) {
89
+ throw new Error('No offer ID available. Must receive offer first.');
90
+ }
91
+ // Send answer to the offer
92
+ await this.rondevu.getAPI().answerOffer(this.offerId, answer.sdp);
93
+ // Start polling for ICE candidates
94
+ this.startIcePolling();
95
+ }
96
+ /**
97
+ * Listen for incoming offers
98
+ * Used by the answerer to receive offers from the offerer
99
+ */
100
+ addOfferListener(callback) {
101
+ this.offerListeners.push(callback);
102
+ // If we have a host, start searching for their service
103
+ if (this.host && !this.isPolling) {
104
+ this.searchForOffer();
105
+ }
106
+ // Return cleanup function
107
+ return () => {
108
+ const index = this.offerListeners.indexOf(callback);
109
+ if (index > -1) {
110
+ this.offerListeners.splice(index, 1);
111
+ }
112
+ };
113
+ }
114
+ /**
115
+ * Listen for incoming answers
116
+ * Used by the offerer to receive the answer from the answerer
117
+ */
118
+ addAnswerListener(callback) {
119
+ this.answerListeners.push(callback);
120
+ // Return cleanup function
121
+ return () => {
122
+ const index = this.answerListeners.indexOf(callback);
123
+ if (index > -1) {
124
+ this.answerListeners.splice(index, 1);
125
+ }
126
+ };
127
+ }
128
+ /**
129
+ * Send an ICE candidate to the remote peer
130
+ */
131
+ async addIceCandidate(candidate) {
132
+ if (!this.offerId) {
133
+ console.warn('Cannot send ICE candidate: no offer ID');
134
+ return;
135
+ }
136
+ const candidateData = candidate.toJSON();
137
+ // Skip empty candidates
138
+ if (!candidateData.candidate || candidateData.candidate === '') {
139
+ return;
140
+ }
141
+ try {
142
+ await this.rondevu.getAPI().addIceCandidates(this.offerId, [candidateData]);
143
+ }
144
+ catch (err) {
145
+ console.error('Failed to send ICE candidate:', err);
146
+ }
147
+ }
148
+ /**
149
+ * Listen for ICE candidates from the remote peer
150
+ */
151
+ addListener(callback) {
152
+ this.iceListeners.push(callback);
153
+ // Return cleanup function
154
+ return () => {
155
+ const index = this.iceListeners.indexOf(callback);
156
+ if (index > -1) {
157
+ this.iceListeners.splice(index, 1);
158
+ }
159
+ };
160
+ }
161
+ /**
162
+ * Search for an offer from the host
163
+ * Used by the answerer to find the offerer's service
164
+ */
165
+ async searchForOffer() {
166
+ if (!this.host) {
167
+ throw new Error('No host specified for offer search');
168
+ }
169
+ this.isPolling = true;
170
+ try {
171
+ // Search for services by username and service FQN
172
+ const services = await this.rondevu.getAPI().searchServices(this.host, this.service);
173
+ if (services.length === 0) {
174
+ console.warn(`No services found for ${this.host}/${this.service}`);
175
+ this.isPolling = false;
176
+ return;
177
+ }
178
+ // Get the first available service (already has full details from searchServices)
179
+ const service = services[0];
180
+ // Get the first available offer from the service
181
+ if (!service.offers || service.offers.length === 0) {
182
+ console.warn(`No offers available for service ${this.host}/${this.service}`);
183
+ this.isPolling = false;
184
+ return;
185
+ }
186
+ const firstOffer = service.offers[0];
187
+ this.offerId = firstOffer.offerId;
188
+ this.serviceUuid = service.uuid;
189
+ // Notify offer listeners
190
+ const offer = {
191
+ type: 'offer',
192
+ sdp: firstOffer.sdp,
193
+ };
194
+ this.offerListeners.forEach(listener => {
195
+ try {
196
+ listener(offer);
197
+ }
198
+ catch (err) {
199
+ console.error('Offer listener error:', err);
200
+ }
201
+ });
202
+ }
203
+ catch (err) {
204
+ console.error('Failed to search for offer:', err);
205
+ this.isPolling = false;
206
+ }
207
+ }
208
+ /**
209
+ * Start polling for answer (offerer side) with exponential backoff
210
+ */
211
+ startAnswerPolling() {
212
+ if (this.answerPollingTimeout || !this.offerId) {
213
+ return;
214
+ }
215
+ let interval = this.pollingConfig.initialInterval;
216
+ let retries = 0;
217
+ const poll = async () => {
218
+ if (!this.offerId) {
219
+ this.stopAnswerPolling();
220
+ return;
221
+ }
222
+ try {
223
+ const answer = await this.rondevu.getAPI().getAnswer(this.offerId);
224
+ if (answer && answer.sdp) {
225
+ // Got answer - notify listeners and stop polling
226
+ const answerDesc = {
227
+ type: 'answer',
228
+ sdp: answer.sdp,
229
+ };
230
+ this.answerListeners.forEach(listener => {
231
+ try {
232
+ listener(answerDesc);
233
+ }
234
+ catch (err) {
235
+ console.error('Answer listener error:', err);
236
+ }
237
+ });
238
+ // Stop polling once we get the answer
239
+ this.stopAnswerPolling();
240
+ return;
241
+ }
242
+ // No answer yet - exponential backoff
243
+ retries++;
244
+ if (retries > this.pollingConfig.maxRetries) {
245
+ console.warn('Max retries reached for answer polling');
246
+ this.stopAnswerPolling();
247
+ return;
248
+ }
249
+ interval = Math.min(interval * this.pollingConfig.backoffMultiplier, this.pollingConfig.maxInterval);
250
+ // Add jitter to prevent thundering herd
251
+ const finalInterval = this.pollingConfig.jitter
252
+ ? interval + Math.random() * 100
253
+ : interval;
254
+ this.answerPollingTimeout = setTimeout(poll, finalInterval);
255
+ }
256
+ catch (err) {
257
+ // 404 is expected when answer isn't available yet
258
+ if (err instanceof Error && !err.message?.includes('404')) {
259
+ console.error('Error polling for answer:', err);
260
+ }
261
+ // Retry with backoff
262
+ const finalInterval = this.pollingConfig.jitter
263
+ ? interval + Math.random() * 100
264
+ : interval;
265
+ this.answerPollingTimeout = setTimeout(poll, finalInterval);
266
+ }
267
+ };
268
+ poll(); // Start immediately
269
+ }
270
+ /**
271
+ * Stop polling for answer
272
+ */
273
+ stopAnswerPolling() {
274
+ if (this.answerPollingTimeout) {
275
+ clearTimeout(this.answerPollingTimeout);
276
+ this.answerPollingTimeout = null;
277
+ }
278
+ }
279
+ /**
280
+ * Start polling for ICE candidates with adaptive backoff
281
+ */
282
+ startIcePolling() {
283
+ if (this.icePollingTimeout || !this.offerId) {
284
+ return;
285
+ }
286
+ let interval = this.pollingConfig.initialInterval;
287
+ const poll = async () => {
288
+ if (!this.offerId) {
289
+ this.stopIcePolling();
290
+ return;
291
+ }
292
+ try {
293
+ const candidates = await this.rondevu
294
+ .getAPI()
295
+ .getIceCandidates(this.offerId, this.lastIceTimestamp);
296
+ let foundCandidates = false;
297
+ for (const item of candidates) {
298
+ if (item.candidate && item.candidate.candidate && item.candidate.candidate !== '') {
299
+ foundCandidates = true;
300
+ try {
301
+ const rtcCandidate = new RTCIceCandidate(item.candidate);
302
+ this.iceListeners.forEach(listener => {
303
+ try {
304
+ listener(rtcCandidate);
305
+ }
306
+ catch (err) {
307
+ console.error('ICE listener error:', err);
308
+ }
309
+ });
310
+ this.lastIceTimestamp = item.createdAt;
311
+ }
312
+ catch (err) {
313
+ console.warn('Failed to process ICE candidate:', err);
314
+ this.lastIceTimestamp = item.createdAt;
315
+ }
316
+ }
317
+ else {
318
+ this.lastIceTimestamp = item.createdAt;
319
+ }
320
+ }
321
+ // If candidates found, reset interval to initial value
322
+ // Otherwise, increase interval with backoff
323
+ if (foundCandidates) {
324
+ interval = this.pollingConfig.initialInterval;
325
+ }
326
+ else {
327
+ interval = Math.min(interval * this.pollingConfig.backoffMultiplier, this.pollingConfig.maxInterval);
328
+ }
329
+ // Add jitter
330
+ const finalInterval = this.pollingConfig.jitter
331
+ ? interval + Math.random() * 100
332
+ : interval;
333
+ this.icePollingTimeout = setTimeout(poll, finalInterval);
334
+ }
335
+ catch (err) {
336
+ // 404/410 means offer expired, stop polling
337
+ if (err instanceof Error && (err.message?.includes('404') || err.message?.includes('410'))) {
338
+ console.warn('Offer not found or expired, stopping ICE polling');
339
+ this.stopIcePolling();
340
+ }
341
+ else if (err instanceof Error && !err.message?.includes('404')) {
342
+ console.error('Error polling for ICE candidates:', err);
343
+ // Continue polling despite errors
344
+ const finalInterval = this.pollingConfig.jitter
345
+ ? interval + Math.random() * 100
346
+ : interval;
347
+ this.icePollingTimeout = setTimeout(poll, finalInterval);
348
+ }
349
+ }
350
+ };
351
+ poll(); // Start immediately
352
+ }
353
+ /**
354
+ * Stop polling for ICE candidates
355
+ */
356
+ stopIcePolling() {
357
+ if (this.icePollingTimeout) {
358
+ clearTimeout(this.icePollingTimeout);
359
+ this.icePollingTimeout = null;
360
+ }
361
+ }
362
+ /**
363
+ * Stop all polling and cleanup
364
+ */
365
+ dispose() {
366
+ this.stopAnswerPolling();
367
+ this.stopIcePolling();
368
+ this.offerListeners = [];
369
+ this.answerListeners = [];
370
+ this.iceListeners = [];
371
+ }
372
+ }
@@ -1,21 +1,17 @@
1
- import { WebRTCRondevuConnection } from './connection.js';
2
1
  import { RondevuService } from './rondevu-service.js';
2
+ import { RTCDurableConnection } from './durable-connection.js';
3
3
  import { EventBus } from './event-bus.js';
4
- import { ConnectionInterface } from './types.js';
5
4
  export interface ServiceClientOptions {
6
5
  username: string;
7
6
  serviceFqn: string;
8
7
  rondevuService: RondevuService;
9
8
  autoReconnect?: boolean;
10
- reconnectDelay?: number;
11
9
  maxReconnectAttempts?: number;
12
10
  rtcConfiguration?: RTCConfiguration;
13
11
  }
14
12
  export interface ServiceClientEvents {
15
- connected: ConnectionInterface;
16
- disconnected: {
17
- reason: string;
18
- };
13
+ connected: RTCDurableConnection;
14
+ disconnected: void;
19
15
  reconnecting: {
20
16
  attempt: number;
21
17
  maxAttempts: number;
@@ -23,72 +19,59 @@ export interface ServiceClientEvents {
23
19
  error: Error;
24
20
  }
25
21
  /**
26
- * ServiceClient - Connects to a hosted service
22
+ * ServiceClient - High-level wrapper for connecting to a WebRTC service
27
23
  *
28
- * Searches for available service offers and establishes a WebRTC connection.
29
- * Optionally supports automatic reconnection on failure.
24
+ * Simplifies client connection by handling:
25
+ * - Service discovery
26
+ * - Offer/answer exchange
27
+ * - ICE candidate polling
28
+ * - Automatic reconnection
30
29
  *
31
30
  * @example
32
31
  * ```typescript
33
- * const rondevuService = new RondevuService({
34
- * apiUrl: 'https://signal.example.com',
35
- * username: 'client-user',
32
+ * const client = new ServiceClient({
33
+ * username: 'host-user',
34
+ * serviceFqn: 'chat.app@1.0.0',
35
+ * rondevuService: myService
36
36
  * })
37
37
  *
38
- * await rondevuService.initialize()
39
- *
40
- * const client = new ServiceClient({
41
- * username: 'host-user',
42
- * serviceFqn: 'chat.app@1.0.0',
43
- * rondevuService,
44
- * autoReconnect: true,
38
+ * client.events.on('connected', conn => {
39
+ * conn.events.on('message', msg => console.log('Received:', msg))
40
+ * conn.sendMessage('Hello from client!')
45
41
  * })
46
42
  *
47
43
  * await client.connect()
48
- *
49
- * client.events.on('connected', (conn) => {
50
- * console.log('Connected to service')
51
- * conn.sendMessage('Hello!')
52
- * })
53
44
  * ```
54
45
  */
55
46
  export declare class ServiceClient {
56
- private readonly username;
57
- private readonly serviceFqn;
58
- private readonly rondevuService;
59
- private readonly autoReconnect;
60
- private readonly reconnectDelay;
61
- private readonly maxReconnectAttempts;
62
- private readonly rtcConfiguration?;
47
+ private options;
48
+ events: EventBus<ServiceClientEvents>;
49
+ private signaler;
50
+ private webrtcContext;
63
51
  private connection;
52
+ private autoReconnect;
53
+ private maxReconnectAttempts;
64
54
  private reconnectAttempts;
65
- private reconnectTimeout;
66
- private readonly bin;
67
55
  private isConnecting;
68
- readonly events: EventBus<ServiceClientEvents>;
69
56
  constructor(options: ServiceClientOptions);
70
57
  /**
71
58
  * Connect to the service
72
59
  */
73
- connect(): Promise<WebRTCRondevuConnection>;
60
+ connect(): Promise<RTCDurableConnection>;
74
61
  /**
75
62
  * Disconnect from the service
76
63
  */
77
- disconnect(): void;
78
- /**
79
- * Get the current connection
80
- */
81
- getConnection(): WebRTCRondevuConnection | null;
64
+ dispose(): void;
82
65
  /**
83
- * Check if currently connected
66
+ * @deprecated Use dispose() instead
84
67
  */
85
- isConnected(): boolean;
68
+ disconnect(): void;
86
69
  /**
87
- * Handle connection state changes
70
+ * Attempt to reconnect
88
71
  */
89
- private handleConnectionStateChange;
72
+ private attemptReconnect;
90
73
  /**
91
- * Schedule a reconnection attempt
74
+ * Get the current connection
92
75
  */
93
- private scheduleReconnect;
76
+ getConnection(): RTCDurableConnection | null;
94
77
  }