@xtr-dev/rondevu-client 0.9.1 → 0.10.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.
Files changed (73) hide show
  1. package/dist/api.d.ts +147 -0
  2. package/dist/api.js +307 -0
  3. package/dist/bin.d.ts +35 -0
  4. package/dist/bin.js +35 -0
  5. package/dist/connection-manager.d.ts +104 -0
  6. package/dist/connection-manager.js +324 -0
  7. package/dist/connection.d.ts +112 -0
  8. package/dist/connection.js +194 -0
  9. package/dist/event-bus.d.ts +52 -0
  10. package/dist/event-bus.js +84 -0
  11. package/dist/index.d.ts +15 -11
  12. package/dist/index.js +9 -11
  13. package/dist/noop-signaler.d.ts +14 -0
  14. package/dist/noop-signaler.js +27 -0
  15. package/dist/rondevu-service.d.ts +81 -0
  16. package/dist/rondevu-service.js +131 -0
  17. package/dist/service-client.d.ts +92 -0
  18. package/dist/service-client.js +185 -0
  19. package/dist/service-host.d.ts +101 -0
  20. package/dist/service-host.js +185 -0
  21. package/dist/signaler.d.ts +25 -0
  22. package/dist/signaler.js +89 -0
  23. package/dist/types.d.ts +33 -0
  24. package/dist/types.js +2 -0
  25. package/dist/webrtc-context.d.ts +6 -0
  26. package/dist/webrtc-context.js +34 -0
  27. package/package.json +16 -2
  28. package/dist/auth.d.ts +0 -20
  29. package/dist/auth.js +0 -41
  30. package/dist/durable/channel.d.ts +0 -115
  31. package/dist/durable/channel.js +0 -301
  32. package/dist/durable/connection.d.ts +0 -125
  33. package/dist/durable/connection.js +0 -370
  34. package/dist/durable/reconnection.d.ts +0 -90
  35. package/dist/durable/reconnection.js +0 -127
  36. package/dist/durable/service.d.ts +0 -103
  37. package/dist/durable/service.js +0 -264
  38. package/dist/durable/types.d.ts +0 -149
  39. package/dist/durable/types.js +0 -28
  40. package/dist/event-emitter.d.ts +0 -54
  41. package/dist/event-emitter.js +0 -102
  42. package/dist/offer-pool.d.ts +0 -86
  43. package/dist/offer-pool.js +0 -145
  44. package/dist/offers.d.ts +0 -101
  45. package/dist/offers.js +0 -202
  46. package/dist/peer/answering-state.d.ts +0 -11
  47. package/dist/peer/answering-state.js +0 -39
  48. package/dist/peer/closed-state.d.ts +0 -8
  49. package/dist/peer/closed-state.js +0 -10
  50. package/dist/peer/connected-state.d.ts +0 -8
  51. package/dist/peer/connected-state.js +0 -11
  52. package/dist/peer/creating-offer-state.d.ts +0 -12
  53. package/dist/peer/creating-offer-state.js +0 -45
  54. package/dist/peer/exchanging-ice-state.d.ts +0 -17
  55. package/dist/peer/exchanging-ice-state.js +0 -64
  56. package/dist/peer/failed-state.d.ts +0 -10
  57. package/dist/peer/failed-state.js +0 -16
  58. package/dist/peer/idle-state.d.ts +0 -7
  59. package/dist/peer/idle-state.js +0 -14
  60. package/dist/peer/index.d.ts +0 -71
  61. package/dist/peer/index.js +0 -176
  62. package/dist/peer/state.d.ts +0 -23
  63. package/dist/peer/state.js +0 -63
  64. package/dist/peer/types.d.ts +0 -43
  65. package/dist/peer/types.js +0 -1
  66. package/dist/peer/waiting-for-answer-state.d.ts +0 -17
  67. package/dist/peer/waiting-for-answer-state.js +0 -60
  68. package/dist/rondevu.d.ts +0 -184
  69. package/dist/rondevu.js +0 -171
  70. package/dist/service-pool.d.ts +0 -123
  71. package/dist/service-pool.js +0 -417
  72. package/dist/usernames.d.ts +0 -79
  73. package/dist/usernames.js +0 -153
@@ -1,145 +0,0 @@
1
- /**
2
- * Manages a pool of offers with automatic polling and refill
3
- *
4
- * The OfferPool maintains a configurable number of simultaneous offers,
5
- * polls for answers periodically, and automatically refills the pool
6
- * when offers are consumed.
7
- */
8
- export class OfferPool {
9
- constructor(offersApi, options) {
10
- this.offersApi = offersApi;
11
- this.options = options;
12
- this.offers = new Map();
13
- this.peerConnections = new Map();
14
- this.dataChannels = new Map();
15
- this.polling = false;
16
- this.lastPollTime = 0;
17
- this.pollingInterval = options.pollingInterval || 2000;
18
- }
19
- /**
20
- * Add offers to the pool with their peer connections and data channels
21
- */
22
- async addOffers(offers, peerConnections, dataChannels) {
23
- for (let i = 0; i < offers.length; i++) {
24
- const offer = offers[i];
25
- this.offers.set(offer.id, offer);
26
- if (peerConnections && peerConnections[i]) {
27
- this.peerConnections.set(offer.id, peerConnections[i]);
28
- }
29
- if (dataChannels && dataChannels[i]) {
30
- this.dataChannels.set(offer.id, dataChannels[i]);
31
- }
32
- }
33
- }
34
- /**
35
- * Start polling for answers
36
- */
37
- async start() {
38
- if (this.polling) {
39
- return;
40
- }
41
- this.polling = true;
42
- // Do an immediate poll
43
- await this.poll().catch((error) => {
44
- this.options.onError(error, 'initial-poll');
45
- });
46
- // Start polling interval
47
- this.pollingTimer = setInterval(async () => {
48
- if (this.polling) {
49
- await this.poll().catch((error) => {
50
- this.options.onError(error, 'poll');
51
- });
52
- }
53
- }, this.pollingInterval);
54
- }
55
- /**
56
- * Stop polling for answers
57
- */
58
- async stop() {
59
- this.polling = false;
60
- if (this.pollingTimer) {
61
- clearInterval(this.pollingTimer);
62
- this.pollingTimer = undefined;
63
- }
64
- }
65
- /**
66
- * Poll for answers and refill the pool if needed
67
- */
68
- async poll() {
69
- try {
70
- // Get all answers from server
71
- const answers = await this.offersApi.getAnswers();
72
- // Filter for our pool's offers
73
- const myAnswers = answers.filter(a => this.offers.has(a.offerId));
74
- // Process each answer
75
- for (const answer of myAnswers) {
76
- // Get the original offer, peer connection, and data channel
77
- const offer = this.offers.get(answer.offerId);
78
- const pc = this.peerConnections.get(answer.offerId);
79
- const channel = this.dataChannels.get(answer.offerId);
80
- if (!offer || !pc) {
81
- continue; // Offer or peer connection already consumed, skip
82
- }
83
- // Remove from pool BEFORE processing to prevent duplicate processing
84
- this.offers.delete(answer.offerId);
85
- this.peerConnections.delete(answer.offerId);
86
- this.dataChannels.delete(answer.offerId);
87
- // Notify ServicePool with answer, original peer connection, and data channel
88
- await this.options.onAnswered({
89
- offerId: answer.offerId,
90
- answererId: answer.answererId,
91
- sdp: answer.sdp,
92
- peerConnection: pc,
93
- dataChannel: channel,
94
- answeredAt: answer.answeredAt
95
- });
96
- }
97
- // Immediate refill if below pool size
98
- if (this.offers.size < this.options.poolSize) {
99
- const needed = this.options.poolSize - this.offers.size;
100
- try {
101
- const result = await this.options.onRefill(needed);
102
- await this.addOffers(result.offers, result.peerConnections, result.dataChannels);
103
- }
104
- catch (refillError) {
105
- this.options.onError(refillError, 'refill');
106
- }
107
- }
108
- this.lastPollTime = Date.now();
109
- }
110
- catch (error) {
111
- // Don't crash the pool on errors - let error handler deal with it
112
- this.options.onError(error, 'poll');
113
- }
114
- }
115
- /**
116
- * Get the current number of active offers in the pool
117
- */
118
- getActiveOfferCount() {
119
- return this.offers.size;
120
- }
121
- /**
122
- * Get all active offer IDs
123
- */
124
- getActiveOfferIds() {
125
- return Array.from(this.offers.keys());
126
- }
127
- /**
128
- * Get all active peer connections
129
- */
130
- getActivePeerConnections() {
131
- return Array.from(this.peerConnections.values());
132
- }
133
- /**
134
- * Get the last poll timestamp
135
- */
136
- getLastPollTime() {
137
- return this.lastPollTime;
138
- }
139
- /**
140
- * Check if the pool is currently polling
141
- */
142
- isPolling() {
143
- return this.polling;
144
- }
145
- }
package/dist/offers.d.ts DELETED
@@ -1,101 +0,0 @@
1
- import { Credentials, FetchFunction } from './auth.js';
2
- export interface CreateOfferRequest {
3
- sdp: string;
4
- topics: string[];
5
- ttl?: number;
6
- secret?: string;
7
- info?: string;
8
- }
9
- export interface Offer {
10
- id: string;
11
- peerId: string;
12
- sdp: string;
13
- topics: string[];
14
- createdAt?: number;
15
- expiresAt: number;
16
- lastSeen: number;
17
- secret?: string;
18
- hasSecret?: boolean;
19
- info?: string;
20
- answererPeerId?: string;
21
- answerSdp?: string;
22
- answeredAt?: number;
23
- }
24
- export interface IceCandidate {
25
- candidate: any;
26
- peerId: string;
27
- role: 'offerer' | 'answerer';
28
- createdAt: number;
29
- }
30
- export interface TopicInfo {
31
- topic: string;
32
- activePeers: number;
33
- }
34
- export declare class RondevuOffers {
35
- private baseUrl;
36
- private credentials;
37
- private fetchFn;
38
- constructor(baseUrl: string, credentials: Credentials, fetchFn?: FetchFunction);
39
- /**
40
- * Create one or more offers
41
- */
42
- create(offers: CreateOfferRequest[]): Promise<Offer[]>;
43
- /**
44
- * Find offers by topic with optional bloom filter
45
- */
46
- findByTopic(topic: string, options?: {
47
- bloomFilter?: Uint8Array;
48
- limit?: number;
49
- }): Promise<Offer[]>;
50
- /**
51
- * Get all offers from a specific peer
52
- */
53
- getByPeerId(peerId: string): Promise<{
54
- offers: Offer[];
55
- topics: string[];
56
- }>;
57
- /**
58
- * Get topics with active peer counts (paginated)
59
- */
60
- getTopics(options?: {
61
- limit?: number;
62
- offset?: number;
63
- startsWith?: string;
64
- }): Promise<{
65
- topics: TopicInfo[];
66
- total: number;
67
- limit: number;
68
- offset: number;
69
- startsWith?: string;
70
- }>;
71
- /**
72
- * Get own offers
73
- */
74
- getMine(): Promise<Offer[]>;
75
- /**
76
- * Delete an offer
77
- */
78
- delete(offerId: string): Promise<void>;
79
- /**
80
- * Answer an offer
81
- */
82
- answer(offerId: string, sdp: string, secret?: string): Promise<void>;
83
- /**
84
- * Get answers to your offers
85
- */
86
- getAnswers(): Promise<Array<{
87
- offerId: string;
88
- answererId: string;
89
- sdp: string;
90
- answeredAt: number;
91
- topics: string[];
92
- }>>;
93
- /**
94
- * Post ICE candidates for an offer
95
- */
96
- addIceCandidates(offerId: string, candidates: any[]): Promise<void>;
97
- /**
98
- * Get ICE candidates for an offer
99
- */
100
- getIceCandidates(offerId: string, since?: number): Promise<IceCandidate[]>;
101
- }
package/dist/offers.js DELETED
@@ -1,202 +0,0 @@
1
- import { RondevuAuth } from './auth.js';
2
- export class RondevuOffers {
3
- constructor(baseUrl, credentials, fetchFn) {
4
- this.baseUrl = baseUrl;
5
- this.credentials = credentials;
6
- // Use provided fetch or fall back to global fetch
7
- this.fetchFn = fetchFn || ((...args) => {
8
- if (typeof globalThis.fetch === 'function') {
9
- return globalThis.fetch(...args);
10
- }
11
- throw new Error('fetch is not available. Please provide a fetch implementation in the constructor options.');
12
- });
13
- }
14
- /**
15
- * Create one or more offers
16
- */
17
- async create(offers) {
18
- const response = await this.fetchFn(`${this.baseUrl}/offers`, {
19
- method: 'POST',
20
- headers: {
21
- 'Content-Type': 'application/json',
22
- Authorization: RondevuAuth.createAuthHeader(this.credentials),
23
- },
24
- body: JSON.stringify({ offers }),
25
- });
26
- if (!response.ok) {
27
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
28
- throw new Error(`Failed to create offers: ${error.error || response.statusText}`);
29
- }
30
- const data = await response.json();
31
- return data.offers;
32
- }
33
- /**
34
- * Find offers by topic with optional bloom filter
35
- */
36
- async findByTopic(topic, options) {
37
- const params = new URLSearchParams();
38
- if (options?.bloomFilter) {
39
- // Convert to base64
40
- const binaryString = String.fromCharCode(...Array.from(options.bloomFilter));
41
- const base64 = typeof btoa !== 'undefined'
42
- ? btoa(binaryString)
43
- : (typeof Buffer !== 'undefined' ? Buffer.from(options.bloomFilter).toString('base64') : '');
44
- params.set('bloom', base64);
45
- }
46
- if (options?.limit) {
47
- params.set('limit', options.limit.toString());
48
- }
49
- const url = `${this.baseUrl}/offers/by-topic/${encodeURIComponent(topic)}${params.toString() ? '?' + params.toString() : ''}`;
50
- const response = await this.fetchFn(url, {
51
- method: 'GET',
52
- });
53
- if (!response.ok) {
54
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
55
- throw new Error(`Failed to find offers: ${error.error || response.statusText}`);
56
- }
57
- const data = await response.json();
58
- return data.offers;
59
- }
60
- /**
61
- * Get all offers from a specific peer
62
- */
63
- async getByPeerId(peerId) {
64
- const response = await this.fetchFn(`${this.baseUrl}/peers/${encodeURIComponent(peerId)}/offers`, {
65
- method: 'GET',
66
- });
67
- if (!response.ok) {
68
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
69
- throw new Error(`Failed to get peer offers: ${error.error || response.statusText}`);
70
- }
71
- return await response.json();
72
- }
73
- /**
74
- * Get topics with active peer counts (paginated)
75
- */
76
- async getTopics(options) {
77
- const params = new URLSearchParams();
78
- if (options?.limit) {
79
- params.set('limit', options.limit.toString());
80
- }
81
- if (options?.offset) {
82
- params.set('offset', options.offset.toString());
83
- }
84
- if (options?.startsWith) {
85
- params.set('startsWith', options.startsWith);
86
- }
87
- const url = `${this.baseUrl}/topics${params.toString() ? '?' + params.toString() : ''}`;
88
- const response = await this.fetchFn(url, {
89
- method: 'GET',
90
- });
91
- if (!response.ok) {
92
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
93
- throw new Error(`Failed to get topics: ${error.error || response.statusText}`);
94
- }
95
- return await response.json();
96
- }
97
- /**
98
- * Get own offers
99
- */
100
- async getMine() {
101
- const response = await this.fetchFn(`${this.baseUrl}/offers/mine`, {
102
- method: 'GET',
103
- headers: {
104
- Authorization: RondevuAuth.createAuthHeader(this.credentials),
105
- },
106
- });
107
- if (!response.ok) {
108
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
109
- throw new Error(`Failed to get own offers: ${error.error || response.statusText}`);
110
- }
111
- const data = await response.json();
112
- return data.offers;
113
- }
114
- /**
115
- * Delete an offer
116
- */
117
- async delete(offerId) {
118
- const response = await this.fetchFn(`${this.baseUrl}/offers/${encodeURIComponent(offerId)}`, {
119
- method: 'DELETE',
120
- headers: {
121
- Authorization: RondevuAuth.createAuthHeader(this.credentials),
122
- },
123
- });
124
- if (!response.ok) {
125
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
126
- throw new Error(`Failed to delete offer: ${error.error || response.statusText}`);
127
- }
128
- }
129
- /**
130
- * Answer an offer
131
- */
132
- async answer(offerId, sdp, secret) {
133
- const response = await this.fetchFn(`${this.baseUrl}/offers/${encodeURIComponent(offerId)}/answer`, {
134
- method: 'POST',
135
- headers: {
136
- 'Content-Type': 'application/json',
137
- Authorization: RondevuAuth.createAuthHeader(this.credentials),
138
- },
139
- body: JSON.stringify({ sdp, secret }),
140
- });
141
- if (!response.ok) {
142
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
143
- throw new Error(`Failed to answer offer: ${error.error || response.statusText}`);
144
- }
145
- }
146
- /**
147
- * Get answers to your offers
148
- */
149
- async getAnswers() {
150
- const response = await this.fetchFn(`${this.baseUrl}/offers/answers`, {
151
- method: 'GET',
152
- headers: {
153
- Authorization: RondevuAuth.createAuthHeader(this.credentials),
154
- },
155
- });
156
- if (!response.ok) {
157
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
158
- throw new Error(`Failed to get answers: ${error.error || response.statusText}`);
159
- }
160
- const data = await response.json();
161
- return data.answers;
162
- }
163
- /**
164
- * Post ICE candidates for an offer
165
- */
166
- async addIceCandidates(offerId, candidates) {
167
- const response = await this.fetchFn(`${this.baseUrl}/offers/${encodeURIComponent(offerId)}/ice-candidates`, {
168
- method: 'POST',
169
- headers: {
170
- 'Content-Type': 'application/json',
171
- Authorization: RondevuAuth.createAuthHeader(this.credentials),
172
- },
173
- body: JSON.stringify({ candidates }),
174
- });
175
- if (!response.ok) {
176
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
177
- throw new Error(`Failed to add ICE candidates: ${error.error || response.statusText}`);
178
- }
179
- }
180
- /**
181
- * Get ICE candidates for an offer
182
- */
183
- async getIceCandidates(offerId, since) {
184
- const params = new URLSearchParams();
185
- if (since !== undefined) {
186
- params.set('since', since.toString());
187
- }
188
- const url = `${this.baseUrl}/offers/${encodeURIComponent(offerId)}/ice-candidates${params.toString() ? '?' + params.toString() : ''}`;
189
- const response = await this.fetchFn(url, {
190
- method: 'GET',
191
- headers: {
192
- Authorization: RondevuAuth.createAuthHeader(this.credentials),
193
- },
194
- });
195
- if (!response.ok) {
196
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
197
- throw new Error(`Failed to get ICE candidates: ${error.error || response.statusText}`);
198
- }
199
- const data = await response.json();
200
- return data.candidates;
201
- }
202
- }
@@ -1,11 +0,0 @@
1
- import { PeerState } from './state.js';
2
- import type { PeerOptions } from './types.js';
3
- import type RondevuPeer from './index.js';
4
- /**
5
- * Answering an offer and sending to server
6
- */
7
- export declare class AnsweringState extends PeerState {
8
- constructor(peer: RondevuPeer);
9
- get name(): string;
10
- answer(offerId: string, offerSdp: string, options: PeerOptions): Promise<void>;
11
- }
@@ -1,39 +0,0 @@
1
- import { PeerState } from './state.js';
2
- /**
3
- * Answering an offer and sending to server
4
- */
5
- export class AnsweringState extends PeerState {
6
- constructor(peer) {
7
- super(peer);
8
- }
9
- get name() { return 'answering'; }
10
- async answer(offerId, offerSdp, options) {
11
- try {
12
- this.peer.role = 'answerer';
13
- this.peer.offerId = offerId;
14
- // Set remote description
15
- await this.peer.pc.setRemoteDescription({
16
- type: 'offer',
17
- sdp: offerSdp
18
- });
19
- // Create answer
20
- const answer = await this.peer.pc.createAnswer();
21
- // Send answer to server BEFORE setLocalDescription
22
- // This registers us as the answerer so ICE candidates will be accepted
23
- await this.peer.offersApi.answer(offerId, answer.sdp, options.secret);
24
- // Enable trickle ICE - set up handler before ICE gathering starts
25
- this.setupIceCandidateHandler();
26
- // Set local description - ICE gathering starts here
27
- // Server already knows we're the answerer, so candidates will be accepted
28
- await this.peer.pc.setLocalDescription(answer);
29
- // Transition to exchanging ICE
30
- const { ExchangingIceState } = await import('./exchanging-ice-state.js');
31
- this.peer.setState(new ExchangingIceState(this.peer, offerId, options));
32
- }
33
- catch (error) {
34
- const { FailedState } = await import('./failed-state.js');
35
- this.peer.setState(new FailedState(this.peer, error));
36
- throw error;
37
- }
38
- }
39
- }
@@ -1,8 +0,0 @@
1
- import { PeerState } from './state.js';
2
- /**
3
- * Closed state - connection has been terminated
4
- */
5
- export declare class ClosedState extends PeerState {
6
- get name(): string;
7
- cleanup(): void;
8
- }
@@ -1,10 +0,0 @@
1
- import { PeerState } from './state.js';
2
- /**
3
- * Closed state - connection has been terminated
4
- */
5
- export class ClosedState extends PeerState {
6
- get name() { return 'closed'; }
7
- cleanup() {
8
- this.peer.pc.close();
9
- }
10
- }
@@ -1,8 +0,0 @@
1
- import { PeerState } from './state.js';
2
- /**
3
- * Connected state - peer connection is established
4
- */
5
- export declare class ConnectedState extends PeerState {
6
- get name(): string;
7
- cleanup(): void;
8
- }
@@ -1,11 +0,0 @@
1
- import { PeerState } from './state.js';
2
- /**
3
- * Connected state - peer connection is established
4
- */
5
- export class ConnectedState extends PeerState {
6
- get name() { return 'connected'; }
7
- cleanup() {
8
- // Keep connection alive, but stop any polling
9
- // The peer connection will handle disconnects via onconnectionstatechange
10
- }
11
- }
@@ -1,12 +0,0 @@
1
- import { PeerState } from './state.js';
2
- import type { PeerOptions } from './types.js';
3
- import type RondevuPeer from './index.js';
4
- /**
5
- * Creating offer and sending to server
6
- */
7
- export declare class CreatingOfferState extends PeerState {
8
- private options;
9
- constructor(peer: RondevuPeer, options: PeerOptions);
10
- get name(): string;
11
- createOffer(options: PeerOptions): Promise<string>;
12
- }
@@ -1,45 +0,0 @@
1
- import { PeerState } from './state.js';
2
- /**
3
- * Creating offer and sending to server
4
- */
5
- export class CreatingOfferState extends PeerState {
6
- constructor(peer, options) {
7
- super(peer);
8
- this.options = options;
9
- }
10
- get name() { return 'creating-offer'; }
11
- async createOffer(options) {
12
- try {
13
- this.peer.role = 'offerer';
14
- // Create data channel if requested
15
- if (options.createDataChannel !== false) {
16
- const channel = this.peer.pc.createDataChannel(options.dataChannelLabel || 'data');
17
- this.peer.emitEvent('datachannel', channel);
18
- }
19
- // Enable trickle ICE - set up handler before ICE gathering starts
20
- // Handler will check this.peer.offerId before sending
21
- this.setupIceCandidateHandler();
22
- // Create WebRTC offer
23
- const offer = await this.peer.pc.createOffer();
24
- await this.peer.pc.setLocalDescription(offer); // ICE gathering starts here
25
- // Send offer to server immediately (don't wait for ICE)
26
- const offers = await this.peer.offersApi.create([{
27
- sdp: offer.sdp,
28
- topics: options.topics,
29
- ttl: options.ttl || 300000,
30
- secret: options.secret
31
- }]);
32
- const offerId = offers[0].id;
33
- this.peer.offerId = offerId; // Now handler can send candidates
34
- // Transition to waiting for answer
35
- const { WaitingForAnswerState } = await import('./waiting-for-answer-state.js');
36
- this.peer.setState(new WaitingForAnswerState(this.peer, offerId, options));
37
- return offerId;
38
- }
39
- catch (error) {
40
- const { FailedState } = await import('./failed-state.js');
41
- this.peer.setState(new FailedState(this.peer, error));
42
- throw error;
43
- }
44
- }
45
- }
@@ -1,17 +0,0 @@
1
- import { PeerState } from './state.js';
2
- import type { PeerOptions } from './types.js';
3
- import type RondevuPeer from './index.js';
4
- /**
5
- * Exchanging ICE candidates and waiting for connection
6
- */
7
- export declare class ExchangingIceState extends PeerState {
8
- private offerId;
9
- private options;
10
- private pollingInterval?;
11
- private timeout?;
12
- private lastIceTimestamp;
13
- constructor(peer: RondevuPeer, offerId: string, options: PeerOptions);
14
- get name(): string;
15
- private startPolling;
16
- cleanup(): void;
17
- }
@@ -1,64 +0,0 @@
1
- import { PeerState } from './state.js';
2
- /**
3
- * Exchanging ICE candidates and waiting for connection
4
- */
5
- export class ExchangingIceState extends PeerState {
6
- constructor(peer, offerId, options) {
7
- super(peer);
8
- this.offerId = offerId;
9
- this.options = options;
10
- this.lastIceTimestamp = 0;
11
- this.startPolling();
12
- }
13
- get name() { return 'exchanging-ice'; }
14
- startPolling() {
15
- const connectionTimeout = this.options.timeouts?.iceConnection || 30000;
16
- this.timeout = setTimeout(async () => {
17
- this.cleanup();
18
- const { FailedState } = await import('./failed-state.js');
19
- this.peer.setState(new FailedState(this.peer, new Error('ICE connection timeout')));
20
- }, connectionTimeout);
21
- this.pollingInterval = setInterval(async () => {
22
- try {
23
- const candidates = await this.peer.offersApi.getIceCandidates(this.offerId, this.lastIceTimestamp);
24
- if (candidates.length > 0) {
25
- console.log(`📥 Received ${candidates.length} remote ICE candidate(s)`);
26
- }
27
- for (const cand of candidates) {
28
- if (cand.candidate && cand.candidate.candidate && cand.candidate.candidate !== '') {
29
- const type = cand.candidate.candidate.includes('typ host') ? 'host' :
30
- cand.candidate.candidate.includes('typ srflx') ? 'srflx' :
31
- cand.candidate.candidate.includes('typ relay') ? 'relay' : 'unknown';
32
- console.log(`🧊 Adding remote ${type} ICE candidate:`, cand.candidate.candidate);
33
- try {
34
- await this.peer.pc.addIceCandidate(new this.peer.RTCIceCandidate(cand.candidate));
35
- console.log(`✅ Added remote ${type} ICE candidate`);
36
- this.lastIceTimestamp = cand.createdAt;
37
- }
38
- catch (err) {
39
- console.warn(`⚠️ Failed to add remote ${type} ICE candidate:`, err);
40
- this.lastIceTimestamp = cand.createdAt;
41
- }
42
- }
43
- else {
44
- this.lastIceTimestamp = cand.createdAt;
45
- }
46
- }
47
- }
48
- catch (err) {
49
- console.error('❌ Error polling for ICE candidates:', err);
50
- if (err instanceof Error && err.message.includes('not found')) {
51
- this.cleanup();
52
- const { FailedState } = await import('./failed-state.js');
53
- this.peer.setState(new FailedState(this.peer, new Error('Offer expired or not found')));
54
- }
55
- }
56
- }, 1000);
57
- }
58
- cleanup() {
59
- if (this.pollingInterval)
60
- clearInterval(this.pollingInterval);
61
- if (this.timeout)
62
- clearTimeout(this.timeout);
63
- }
64
- }