@xtr-dev/rondevu-webtorrent 0.0.2 → 0.0.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 CHANGED
@@ -147,14 +147,6 @@ interface RondevuConnectionManagerOptions {
147
147
  RTCSessionDescription: typeof RTCSessionDescription;
148
148
  RTCIceCandidate: typeof RTCIceCandidate;
149
149
  };
150
-
151
- /**
152
- * Optional prefix to add to all topics
153
- * Allows creating separate discovery pools for different environments or applications
154
- * @default '' (empty string - uses infoHash directly)
155
- * @example 'dev.xtr.player:' would create topics like 'dev.xtr.player:abc123...'
156
- */
157
- topicPrefix?: string;
158
150
  }
159
151
  ```
160
152
 
@@ -224,31 +216,6 @@ connectionManager.destroy();
224
216
 
225
217
  ## Advanced Usage
226
218
 
227
- ### Topic Prefix for Separate Discovery Pools
228
-
229
- Use a custom topic prefix to create isolated peer discovery pools. This is useful for:
230
- - Separating development, staging, and production environments
231
- - Creating application-specific discovery pools
232
- - Preventing peers from different versions/contexts from discovering each other
233
-
234
- ```typescript
235
- // Production environment
236
- const prodManager = new RondevuConnectionManager(client, {
237
- topicPrefix: 'prod.myapp:',
238
- debug: false
239
- });
240
-
241
- // Development environment
242
- const devManager = new RondevuConnectionManager(client, {
243
- topicPrefix: 'dev.myapp:',
244
- debug: true
245
- });
246
-
247
- // Now torrents with the same infoHash in prod and dev won't discover each other
248
- // Production uses topics like: 'prod.myapp:abc123...'
249
- // Development uses topics like: 'dev.myapp:abc123...'
250
- ```
251
-
252
219
  ### Custom RTCConfiguration
253
220
 
254
221
  Provide custom STUN/TURN servers for WebRTC connections:
@@ -1,11 +1,16 @@
1
1
  import WebTorrent from 'webtorrent';
2
- import { Credentials } from '@xtr-dev/rondevu-client';
2
+ /**
3
+ * Credential type for rondevu authentication
4
+ */
5
+ export interface Credential {
6
+ name: string;
7
+ secret: string;
8
+ }
3
9
  /**
4
10
  * WebRTC polyfill interface for Node.js environments
5
11
  */
6
12
  export interface WebRTCPolyfill {
7
13
  RTCPeerConnection: typeof RTCPeerConnection;
8
- RTCSessionDescription: typeof RTCSessionDescription;
9
14
  RTCIceCandidate: typeof RTCIceCandidate;
10
15
  }
11
16
  /**
@@ -35,19 +40,12 @@ export interface RondevuConnectionManagerOptions {
35
40
  /**
36
41
  * Existing rondevu credentials to reuse
37
42
  */
38
- credentials?: Credentials;
43
+ credential?: Credential;
39
44
  /**
40
45
  * WebRTC polyfill for Node.js (e.g., @roamhq/wrtc)
41
46
  * Required for WebRTC functionality in Node.js environments
42
47
  */
43
48
  wrtc?: WebRTCPolyfill;
44
- /**
45
- * Optional prefix to add to all topics. This allows creating separate discovery pools
46
- * for different environments or applications.
47
- * For example: 'dev.xtr.player:' would make topics like 'dev.xtr.player:abc123...'
48
- * (default: empty string - uses infoHash directly)
49
- */
50
- topicPrefix?: string;
51
49
  }
52
50
  /**
53
51
  * Connection manager that uses Rondevu for WebTorrent peer discovery via WebRTC.
@@ -57,21 +55,16 @@ export interface RondevuConnectionManagerOptions {
57
55
  export declare class RondevuConnectionManager {
58
56
  private client;
59
57
  private rondevu;
58
+ private torrentOfferHandles;
60
59
  private torrentPeers;
61
- private torrentOffers;
62
60
  private torrentCleanupHandlers;
63
61
  private refreshTimers;
64
- private torrentBloomFilters;
65
62
  private options;
66
63
  constructor(client: WebTorrent.Instance, options?: RondevuConnectionManagerOptions);
67
64
  /**
68
- * Initialize the connection manager by setting up event listeners and registering with rondevu
65
+ * Initialize the connection manager by setting up event listeners and connecting to rondevu
69
66
  */
70
67
  private initialize;
71
- /**
72
- * Build the full topic string from an infoHash, applying the configured prefix if any
73
- */
74
- private buildTopic;
75
68
  /**
76
69
  * Handle a torrent being added to the WebTorrent client
77
70
  */
@@ -80,10 +73,6 @@ export declare class RondevuConnectionManager {
80
73
  * Discover peers for a torrent using rondevu
81
74
  */
82
75
  private discoverPeersForTorrent;
83
- /**
84
- * Set up event handlers for a rondevu peer
85
- */
86
- private setupPeerHandlers;
87
76
  /**
88
77
  * Log a message if debug mode is enabled
89
78
  */
@@ -97,7 +86,7 @@ export declare class RondevuConnectionManager {
97
86
  */
98
87
  getStats(): {
99
88
  activeTorrents: number;
100
- peerId: string | undefined;
89
+ username: string | undefined;
101
90
  rondevuServer: string;
102
91
  torrents: {
103
92
  infoHash: string;
@@ -105,9 +94,9 @@ export declare class RondevuConnectionManager {
105
94
  }[];
106
95
  };
107
96
  /**
108
- * Get the rondevu credentials for persistence
97
+ * Get the rondevu credential for persistence
109
98
  */
110
- getCredentials(): Credentials | undefined;
99
+ getCredential(): Credential | undefined;
111
100
  /**
112
101
  * Clean up all resources and disconnect from rondevu
113
102
  */
@@ -1,4 +1,18 @@
1
- import { Rondevu, BloomFilter } from '@xtr-dev/rondevu-client';
1
+ import { Rondevu } from '@xtr-dev/rondevu-client';
2
+ /**
3
+ * Simple WebRTC adapter for Node.js using wrtc polyfill
4
+ */
5
+ class NodeWebRTCAdapter {
6
+ constructor(polyfills) {
7
+ this.polyfills = polyfills;
8
+ }
9
+ createPeerConnection(config) {
10
+ return new this.polyfills.RTCPeerConnection(config);
11
+ }
12
+ createIceCandidate(candidateInit) {
13
+ return new this.polyfills.RTCIceCandidate(candidateInit);
14
+ }
15
+ }
2
16
  /**
3
17
  * Connection manager that uses Rondevu for WebTorrent peer discovery via WebRTC.
4
18
  * This class acts as a plugin for WebTorrent, automatically discovering
@@ -6,11 +20,11 @@ import { Rondevu, BloomFilter } from '@xtr-dev/rondevu-client';
6
20
  */
7
21
  export class RondevuConnectionManager {
8
22
  constructor(client, options = {}) {
23
+ this.rondevu = null;
24
+ this.torrentOfferHandles = new Map();
9
25
  this.torrentPeers = new Map();
10
- this.torrentOffers = new Map();
11
26
  this.torrentCleanupHandlers = new Map();
12
27
  this.refreshTimers = new Map();
13
- this.torrentBloomFilters = new Map();
14
28
  this.client = client;
15
29
  this.options = {
16
30
  rondevuServer: options.rondevuServer,
@@ -19,33 +33,36 @@ export class RondevuConnectionManager {
19
33
  refreshInterval: options.refreshInterval ?? 30000,
20
34
  rtcConfig: options.rtcConfig,
21
35
  wrtc: options.wrtc,
22
- topicPrefix: options.topicPrefix ?? '',
36
+ credential: options.credential,
23
37
  };
24
- this.rondevu = new Rondevu({
25
- baseUrl: options.rondevuServer,
26
- credentials: options.credentials,
27
- RTCPeerConnection: options.wrtc?.RTCPeerConnection,
28
- RTCSessionDescription: options.wrtc?.RTCSessionDescription,
29
- RTCIceCandidate: options.wrtc?.RTCIceCandidate,
30
- });
31
38
  this.initialize();
32
39
  }
33
40
  /**
34
- * Initialize the connection manager by setting up event listeners and registering with rondevu
41
+ * Initialize the connection manager by setting up event listeners and connecting to rondevu
35
42
  */
36
43
  async initialize() {
37
44
  this.log('Initializing RondevuConnectionManager');
38
45
  try {
39
- if (!this.rondevu.isAuthenticated()) {
40
- const credentials = await this.rondevu.register();
41
- this.log(`Registered with rondevu: ${credentials.peerId}`);
46
+ // Build connection options
47
+ const connectOptions = {
48
+ apiUrl: this.options.rondevuServer,
49
+ credential: this.options.credential,
50
+ debug: this.options.debug,
51
+ };
52
+ // Add WebRTC adapter if wrtc polyfill provided
53
+ if (this.options.wrtc) {
54
+ connectOptions.webrtcAdapter = new NodeWebRTCAdapter(this.options.wrtc);
42
55
  }
43
- else {
44
- this.log(`Using existing credentials: ${this.rondevu.getCredentials()?.peerId}`);
56
+ // Add ICE servers if custom config provided
57
+ if (this.options.rtcConfig?.iceServers) {
58
+ connectOptions.iceServers = this.options.rtcConfig.iceServers;
45
59
  }
60
+ // Connect to rondevu (auto-generates credentials if not provided)
61
+ this.rondevu = await Rondevu.connect(connectOptions);
62
+ this.log(`Connected to rondevu as: ${this.rondevu.getName()}`);
46
63
  }
47
64
  catch (error) {
48
- this.log(`Failed to register with rondevu: ${error}`);
65
+ this.log(`Failed to connect to rondevu: ${error}`);
49
66
  return;
50
67
  }
51
68
  // Listen for new torrents being added
@@ -57,22 +74,18 @@ export class RondevuConnectionManager {
57
74
  this.handleTorrentAdded(torrent);
58
75
  });
59
76
  }
60
- /**
61
- * Build the full topic string from an infoHash, applying the configured prefix if any
62
- */
63
- buildTopic(infoHash) {
64
- return `${this.options.topicPrefix}${infoHash}`;
65
- }
66
77
  /**
67
78
  * Handle a torrent being added to the WebTorrent client
68
79
  */
69
80
  async handleTorrentAdded(torrent) {
81
+ if (!this.rondevu) {
82
+ this.log('Rondevu not initialized, skipping torrent');
83
+ return;
84
+ }
70
85
  const infoHash = torrent.infoHash;
71
86
  this.log(`Torrent added: ${infoHash}`);
72
87
  // Initialize tracking for this torrent
73
88
  this.torrentPeers.set(infoHash, new Set());
74
- this.torrentOffers.set(infoHash, []);
75
- this.torrentBloomFilters.set(infoHash, new BloomFilter(1024, 3));
76
89
  // Start discovering peers and creating offers
77
90
  await this.discoverPeersForTorrent(torrent);
78
91
  // Set up periodic peer refresh
@@ -88,6 +101,12 @@ export class RondevuConnectionManager {
88
101
  clearInterval(timer);
89
102
  this.refreshTimers.delete(infoHash);
90
103
  }
104
+ // Cancel our offer for this torrent
105
+ const offerHandle = this.torrentOfferHandles.get(infoHash);
106
+ if (offerHandle) {
107
+ offerHandle.cancel();
108
+ this.torrentOfferHandles.delete(infoHash);
109
+ }
91
110
  // Close all peer connections for this torrent
92
111
  const peers = this.torrentPeers.get(infoHash);
93
112
  if (peers) {
@@ -101,21 +120,6 @@ export class RondevuConnectionManager {
101
120
  });
102
121
  this.torrentPeers.delete(infoHash);
103
122
  }
104
- // Delete our offers for this torrent
105
- const offerIds = this.torrentOffers.get(infoHash);
106
- if (offerIds) {
107
- offerIds.forEach(async (offerId) => {
108
- try {
109
- await this.rondevu.offers.delete(offerId);
110
- }
111
- catch (error) {
112
- this.log(`Error deleting offer: ${error}`);
113
- }
114
- });
115
- this.torrentOffers.delete(infoHash);
116
- }
117
- // Clean up bloom filter
118
- this.torrentBloomFilters.delete(infoHash);
119
123
  this.torrentCleanupHandlers.delete(infoHash);
120
124
  };
121
125
  torrent.on('done', () => {
@@ -129,6 +133,8 @@ export class RondevuConnectionManager {
129
133
  * Discover peers for a torrent using rondevu
130
134
  */
131
135
  async discoverPeersForTorrent(torrent) {
136
+ if (!this.rondevu)
137
+ return;
132
138
  const infoHash = torrent.infoHash;
133
139
  const currentPeerCount = this.torrentPeers.get(infoHash)?.size ?? 0;
134
140
  // Check if we already have enough peers
@@ -137,50 +143,79 @@ export class RondevuConnectionManager {
137
143
  return;
138
144
  }
139
145
  try {
140
- // Create our own offer for this torrent using rondevu.createPeer() to get WebRTC polyfills
141
- const peer = this.rondevu.createPeer(this.options.rtcConfig);
142
- this.log(`Creating offer for torrent ${infoHash}`);
143
- const offerId = await peer.createOffer({
144
- topics: [this.buildTopic(infoHash)],
145
- ttl: 300000, // 5 minutes in milliseconds
146
- createDataChannel: true,
147
- dataChannelLabel: 'webtorrent',
148
- });
149
- this.torrentOffers.get(infoHash)?.push(offerId);
150
- // Set up peer connection handlers
151
- this.setupPeerHandlers(peer, torrent, true);
146
+ // Create offers for this torrent if we haven't already
147
+ if (!this.torrentOfferHandles.has(infoHash)) {
148
+ this.log(`Creating offers for torrent ${infoHash}`);
149
+ const offerHandle = await this.rondevu.offer({
150
+ tags: [infoHash],
151
+ maxOffers: 3, // Maintain pool of 3 offers
152
+ ttl: 300000, // 5 minutes
153
+ autoStart: true,
154
+ });
155
+ this.torrentOfferHandles.set(infoHash, offerHandle);
156
+ // Listen for incoming connections on our offers
157
+ this.rondevu.on('connection:opened', (_offerId, connection) => {
158
+ this.log(`Incoming connection for torrent ${infoHash}`);
159
+ connection.on('connected', () => {
160
+ // Add the WebRTC peer connection to the torrent
161
+ try {
162
+ const pc = connection.getPeerConnection();
163
+ if (pc) {
164
+ torrent.addPeer(pc);
165
+ this.log(`Added incoming peer to torrent ${infoHash}`);
166
+ }
167
+ }
168
+ catch (error) {
169
+ this.log(`Failed to add incoming peer: ${error}`);
170
+ }
171
+ });
172
+ });
173
+ }
152
174
  // Discover other peers' offers for this torrent
153
175
  this.log(`Discovering peers for torrent ${infoHash}`);
154
- const bloomFilter = this.torrentBloomFilters.get(infoHash);
155
- const offers = await this.rondevu.offers.findByTopic(this.buildTopic(infoHash), {
176
+ const result = await this.rondevu.discover([infoHash], {
156
177
  limit: this.options.maxPeersPerTorrent - currentPeerCount,
157
- bloomFilter: bloomFilter?.toBytes(),
158
178
  });
159
- this.log(`Found ${offers.length} offers for torrent ${infoHash}`);
160
- // Connect to discovered peers
161
- for (const remoteOffer of offers) {
179
+ this.log(`Found ${result.offers.length} offers for torrent ${infoHash}`);
180
+ // Connect to discovered peers using the simplified peer() API
181
+ for (const remoteOffer of result.offers) {
162
182
  // Skip our own offers
163
- if (remoteOffer.peerId === this.rondevu.getCredentials()?.peerId) {
183
+ if (remoteOffer.username === this.rondevu.getName()) {
164
184
  continue;
165
185
  }
166
- // Skip if already answered
167
- if (remoteOffer.answererPeerId) {
168
- continue;
169
- }
170
- // Add to bloom filter to avoid rediscovering
171
- bloomFilter?.add(remoteOffer.peerId);
172
186
  try {
173
- // Create peer using rondevu.createPeer() to get WebRTC polyfills
174
- const answerPeer = this.rondevu.createPeer(this.options.rtcConfig);
175
- this.log(`Answering offer ${remoteOffer.id} for torrent ${infoHash}`);
176
- await answerPeer.answer(remoteOffer.id, remoteOffer.sdp, {
177
- topics: [this.buildTopic(infoHash)],
187
+ this.log(`Connecting to peer ${remoteOffer.username} for torrent ${infoHash}`);
188
+ const peer = await this.rondevu.peer({
189
+ tags: [infoHash],
190
+ username: remoteOffer.username,
191
+ });
192
+ // Track this peer
193
+ this.torrentPeers.get(infoHash)?.add(peer);
194
+ // Set up peer handlers
195
+ peer.on('open', () => {
196
+ this.log(`Connected to peer ${peer.peerUsername} for torrent ${infoHash}`);
197
+ // Add the WebRTC peer connection to the torrent
198
+ try {
199
+ const pc = peer.peerConnection;
200
+ if (pc) {
201
+ torrent.addPeer(pc);
202
+ }
203
+ }
204
+ catch (error) {
205
+ this.log(`Failed to add WebRTC peer to torrent: ${error}`);
206
+ }
207
+ });
208
+ peer.on('close', () => {
209
+ this.log(`Peer disconnected for torrent ${infoHash}`);
210
+ this.torrentPeers.get(infoHash)?.delete(peer);
211
+ });
212
+ peer.on('error', (error) => {
213
+ this.log(`Peer error for torrent ${infoHash}: ${error.message}`);
214
+ this.torrentPeers.get(infoHash)?.delete(peer);
178
215
  });
179
- // Set up peer connection handlers
180
- this.setupPeerHandlers(answerPeer, torrent, false);
181
216
  }
182
217
  catch (error) {
183
- this.log(`Failed to answer offer ${remoteOffer.id}: ${error}`);
218
+ this.log(`Failed to connect to peer ${remoteOffer.username}: ${error}`);
184
219
  }
185
220
  }
186
221
  }
@@ -188,35 +223,6 @@ export class RondevuConnectionManager {
188
223
  this.log(`Error discovering peers for ${infoHash}: ${error}`);
189
224
  }
190
225
  }
191
- /**
192
- * Set up event handlers for a rondevu peer
193
- */
194
- setupPeerHandlers(peer, torrent, isOfferer) {
195
- const infoHash = torrent.infoHash;
196
- peer.on('connected', () => {
197
- this.log(`Peer connected for torrent ${infoHash}`);
198
- this.torrentPeers.get(infoHash)?.add(peer);
199
- // Add the WebRTC peer connection to the torrent
200
- // WebTorrent can use WebRTC peer connections directly
201
- try {
202
- torrent.addPeer(peer.pc);
203
- }
204
- catch (error) {
205
- this.log(`Failed to add WebRTC peer to torrent: ${error}`);
206
- }
207
- });
208
- peer.on('disconnected', () => {
209
- this.log(`Peer disconnected for torrent ${infoHash}`);
210
- this.torrentPeers.get(infoHash)?.delete(peer);
211
- });
212
- peer.on('failed', (error) => {
213
- this.log(`Peer failed for torrent ${infoHash}: ${error.message}`);
214
- this.torrentPeers.get(infoHash)?.delete(peer);
215
- });
216
- peer.on('datachannel', (channel) => {
217
- this.log(`Data channel opened for torrent ${infoHash}: ${channel.label}`);
218
- });
219
- }
220
226
  /**
221
227
  * Log a message if debug mode is enabled
222
228
  */
@@ -246,16 +252,16 @@ export class RondevuConnectionManager {
246
252
  }));
247
253
  return {
248
254
  activeTorrents: this.client.torrents.length,
249
- peerId: this.rondevu.getCredentials()?.peerId,
255
+ username: this.rondevu?.getName(),
250
256
  rondevuServer: this.options.rondevuServer ?? 'https://api.ronde.vu',
251
257
  torrents: torrentStats,
252
258
  };
253
259
  }
254
260
  /**
255
- * Get the rondevu credentials for persistence
261
+ * Get the rondevu credential for persistence
256
262
  */
257
- getCredentials() {
258
- return this.rondevu.getCredentials();
263
+ getCredential() {
264
+ return this.rondevu?.getCredential();
259
265
  }
260
266
  /**
261
267
  * Clean up all resources and disconnect from rondevu
@@ -266,8 +272,7 @@ export class RondevuConnectionManager {
266
272
  this.torrentCleanupHandlers.forEach((cleanup) => cleanup());
267
273
  this.torrentCleanupHandlers.clear();
268
274
  this.torrentPeers.clear();
269
- this.torrentOffers.clear();
275
+ this.torrentOfferHandles.clear();
270
276
  this.refreshTimers.clear();
271
- this.torrentBloomFilters.clear();
272
277
  }
273
278
  }
package/dist/index.d.ts CHANGED
@@ -1 +1 @@
1
- export { RondevuConnectionManager, RondevuConnectionManagerOptions } from './RondevuConnectionManager.js';
1
+ export { RondevuConnectionManager, RondevuConnectionManagerOptions, Credential, WebRTCPolyfill } from './RondevuConnectionManager.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xtr-dev/rondevu-webtorrent",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "WebTorrent peer discovery plugin using Rondevu WebRTC signaling",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -36,7 +36,7 @@
36
36
  "homepage": "https://github.com/xtr-dev/rondevu-webtorrent#readme",
37
37
  "dependencies": {
38
38
  "@roamhq/wrtc": "^0.9.1",
39
- "@xtr-dev/rondevu-client": "^0.7.4",
39
+ "@xtr-dev/rondevu-client": "^0.21.3",
40
40
  "webtorrent": "^2.8.4"
41
41
  },
42
42
  "devDependencies": {