@xtr-dev/rondevu-client 0.0.1 → 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/dist/client.d.ts CHANGED
@@ -40,22 +40,22 @@ export declare class RondevuClient {
40
40
  * ```typescript
41
41
  * const client = new RondevuClient({ baseUrl: 'https://example.com' });
42
42
  * const { sessions } = await client.listSessions('my-room');
43
- * const otherPeers = sessions.filter(s => s.info !== myPeerId);
43
+ * const otherPeers = sessions.filter(s => s.peerId !== myPeerId);
44
44
  * ```
45
45
  */
46
46
  listSessions(topic: string): Promise<ListSessionsResponse>;
47
47
  /**
48
48
  * Announces peer availability and creates a new session
49
49
  *
50
- * @param topic - Topic identifier for grouping peers (max 256 characters)
51
- * @param request - Offer details including peer info and signaling data
50
+ * @param topic - Topic identifier for grouping peers (max 1024 characters)
51
+ * @param request - Offer details including peer ID and signaling data
52
52
  * @returns Unique session code (UUID)
53
53
  *
54
54
  * @example
55
55
  * ```typescript
56
56
  * const client = new RondevuClient({ baseUrl: 'https://example.com' });
57
57
  * const { code } = await client.createOffer('my-room', {
58
- * info: 'peer-123',
58
+ * peerId: 'peer-123',
59
59
  * offer: signalingData
60
60
  * });
61
61
  * console.log('Session code:', code);
package/dist/client.js CHANGED
@@ -1,10 +1,7 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RondevuClient = void 0;
4
1
  /**
5
2
  * HTTP client for Rondevu peer signaling and discovery server
6
3
  */
7
- class RondevuClient {
4
+ export class RondevuClient {
8
5
  /**
9
6
  * Creates a new Rondevu client instance
10
7
  * @param options - Client configuration options
@@ -12,7 +9,7 @@ class RondevuClient {
12
9
  constructor(options) {
13
10
  this.baseUrl = options.baseUrl.replace(/\/$/, ''); // Remove trailing slash
14
11
  this.origin = options.origin || new URL(this.baseUrl).origin;
15
- this.fetchImpl = options.fetch || fetch;
12
+ this.fetchImpl = options.fetch || globalThis.fetch.bind(globalThis);
16
13
  }
17
14
  /**
18
15
  * Makes an HTTP request to the Rondevu server
@@ -70,7 +67,7 @@ class RondevuClient {
70
67
  * ```typescript
71
68
  * const client = new RondevuClient({ baseUrl: 'https://example.com' });
72
69
  * const { sessions } = await client.listSessions('my-room');
73
- * const otherPeers = sessions.filter(s => s.info !== myPeerId);
70
+ * const otherPeers = sessions.filter(s => s.peerId !== myPeerId);
74
71
  * ```
75
72
  */
76
73
  async listSessions(topic) {
@@ -81,15 +78,15 @@ class RondevuClient {
81
78
  /**
82
79
  * Announces peer availability and creates a new session
83
80
  *
84
- * @param topic - Topic identifier for grouping peers (max 256 characters)
85
- * @param request - Offer details including peer info and signaling data
81
+ * @param topic - Topic identifier for grouping peers (max 1024 characters)
82
+ * @param request - Offer details including peer ID and signaling data
86
83
  * @returns Unique session code (UUID)
87
84
  *
88
85
  * @example
89
86
  * ```typescript
90
87
  * const client = new RondevuClient({ baseUrl: 'https://example.com' });
91
88
  * const { code } = await client.createOffer('my-room', {
92
- * info: 'peer-123',
89
+ * peerId: 'peer-123',
93
90
  * offer: signalingData
94
91
  * });
95
92
  * console.log('Session code:', code);
@@ -179,4 +176,3 @@ class RondevuClient {
179
176
  });
180
177
  }
181
178
  }
182
- exports.RondevuClient = RondevuClient;
@@ -0,0 +1,75 @@
1
+ import { EventEmitter } from './event-emitter';
2
+ import { RondevuClient } from './client';
3
+ import { RondevuConnectionParams } from './types';
4
+ /**
5
+ * Represents a WebRTC connection with automatic signaling and ICE exchange
6
+ */
7
+ export declare class RondevuConnection extends EventEmitter {
8
+ readonly id: string;
9
+ readonly topic: string;
10
+ readonly role: 'offerer' | 'answerer';
11
+ readonly remotePeerId: string;
12
+ private pc;
13
+ private client;
14
+ private localPeerId;
15
+ private dataChannels;
16
+ private pollingInterval?;
17
+ private pollingIntervalMs;
18
+ private connectionTimeoutMs;
19
+ private connectionTimer?;
20
+ private isPolling;
21
+ private isClosed;
22
+ constructor(params: RondevuConnectionParams, client: RondevuClient);
23
+ /**
24
+ * Setup RTCPeerConnection event handlers
25
+ */
26
+ private setupEventHandlers;
27
+ /**
28
+ * Handle RTCPeerConnection state changes
29
+ */
30
+ private handleConnectionStateChange;
31
+ /**
32
+ * Send an ICE candidate to the remote peer via signaling server
33
+ */
34
+ private sendIceCandidate;
35
+ /**
36
+ * Start polling for remote session data (answer/candidates)
37
+ */
38
+ startPolling(): void;
39
+ /**
40
+ * Stop polling
41
+ */
42
+ private stopPolling;
43
+ /**
44
+ * Poll the signaling server for remote data
45
+ */
46
+ private poll;
47
+ /**
48
+ * Handle remotely created data channel
49
+ */
50
+ private handleRemoteDataChannel;
51
+ /**
52
+ * Get or create a data channel
53
+ */
54
+ dataChannel(label: string, options?: RTCDataChannelInit): RTCDataChannel;
55
+ /**
56
+ * Add a local media stream to the connection
57
+ */
58
+ addStream(stream: MediaStream): void;
59
+ /**
60
+ * Get the underlying RTCPeerConnection for advanced usage
61
+ */
62
+ getPeerConnection(): RTCPeerConnection;
63
+ /**
64
+ * Start connection timeout
65
+ */
66
+ private startConnectionTimeout;
67
+ /**
68
+ * Clear connection timeout
69
+ */
70
+ private clearConnectionTimeout;
71
+ /**
72
+ * Close the connection and cleanup resources
73
+ */
74
+ close(): void;
75
+ }
@@ -0,0 +1,260 @@
1
+ import { EventEmitter } from './event-emitter';
2
+ /**
3
+ * Represents a WebRTC connection with automatic signaling and ICE exchange
4
+ */
5
+ export class RondevuConnection extends EventEmitter {
6
+ constructor(params, client) {
7
+ super();
8
+ this.isPolling = false;
9
+ this.isClosed = false;
10
+ this.id = params.id;
11
+ this.topic = params.topic;
12
+ this.role = params.role;
13
+ this.pc = params.pc;
14
+ this.localPeerId = params.localPeerId;
15
+ this.remotePeerId = params.remotePeerId;
16
+ this.client = client;
17
+ this.dataChannels = new Map();
18
+ this.pollingIntervalMs = params.pollingInterval;
19
+ this.connectionTimeoutMs = params.connectionTimeout;
20
+ this.setupEventHandlers();
21
+ this.startConnectionTimeout();
22
+ }
23
+ /**
24
+ * Setup RTCPeerConnection event handlers
25
+ */
26
+ setupEventHandlers() {
27
+ // ICE candidate gathering
28
+ this.pc.onicecandidate = (event) => {
29
+ if (event.candidate && !this.isClosed) {
30
+ this.sendIceCandidate(event.candidate).catch((err) => {
31
+ this.emit('error', new Error(`Failed to send ICE candidate: ${err.message}`));
32
+ });
33
+ }
34
+ };
35
+ // Connection state changes
36
+ this.pc.onconnectionstatechange = () => {
37
+ this.handleConnectionStateChange();
38
+ };
39
+ // Remote data channels
40
+ this.pc.ondatachannel = (event) => {
41
+ this.handleRemoteDataChannel(event.channel);
42
+ };
43
+ // Remote media streams
44
+ this.pc.ontrack = (event) => {
45
+ if (event.streams && event.streams[0]) {
46
+ this.emit('stream', event.streams[0]);
47
+ }
48
+ };
49
+ // ICE connection state changes
50
+ this.pc.oniceconnectionstatechange = () => {
51
+ const state = this.pc.iceConnectionState;
52
+ if (state === 'failed' || state === 'closed') {
53
+ this.emit('error', new Error(`ICE connection ${state}`));
54
+ if (state === 'failed') {
55
+ this.close();
56
+ }
57
+ }
58
+ };
59
+ }
60
+ /**
61
+ * Handle RTCPeerConnection state changes
62
+ */
63
+ handleConnectionStateChange() {
64
+ const state = this.pc.connectionState;
65
+ switch (state) {
66
+ case 'connected':
67
+ this.clearConnectionTimeout();
68
+ this.stopPolling();
69
+ this.emit('connect');
70
+ break;
71
+ case 'disconnected':
72
+ this.emit('disconnect');
73
+ break;
74
+ case 'failed':
75
+ this.emit('error', new Error('Connection failed'));
76
+ this.close();
77
+ break;
78
+ case 'closed':
79
+ this.emit('disconnect');
80
+ break;
81
+ }
82
+ }
83
+ /**
84
+ * Send an ICE candidate to the remote peer via signaling server
85
+ */
86
+ async sendIceCandidate(candidate) {
87
+ try {
88
+ await this.client.sendAnswer({
89
+ code: this.id,
90
+ candidate: JSON.stringify(candidate.toJSON()),
91
+ side: this.role,
92
+ });
93
+ }
94
+ catch (err) {
95
+ throw new Error(`Failed to send ICE candidate: ${err.message}`);
96
+ }
97
+ }
98
+ /**
99
+ * Start polling for remote session data (answer/candidates)
100
+ */
101
+ startPolling() {
102
+ if (this.isPolling || this.isClosed) {
103
+ return;
104
+ }
105
+ this.isPolling = true;
106
+ // Poll immediately
107
+ this.poll().catch((err) => {
108
+ this.emit('error', new Error(`Poll error: ${err.message}`));
109
+ });
110
+ // Set up interval polling
111
+ this.pollingInterval = setInterval(() => {
112
+ this.poll().catch((err) => {
113
+ this.emit('error', new Error(`Poll error: ${err.message}`));
114
+ });
115
+ }, this.pollingIntervalMs);
116
+ }
117
+ /**
118
+ * Stop polling
119
+ */
120
+ stopPolling() {
121
+ this.isPolling = false;
122
+ if (this.pollingInterval) {
123
+ clearInterval(this.pollingInterval);
124
+ this.pollingInterval = undefined;
125
+ }
126
+ }
127
+ /**
128
+ * Poll the signaling server for remote data
129
+ */
130
+ async poll() {
131
+ if (this.isClosed) {
132
+ this.stopPolling();
133
+ return;
134
+ }
135
+ try {
136
+ const response = await this.client.poll(this.id, this.role);
137
+ if (this.role === 'offerer') {
138
+ const offererResponse = response;
139
+ // Apply answer if received and not yet applied
140
+ if (offererResponse.answer && !this.pc.currentRemoteDescription) {
141
+ await this.pc.setRemoteDescription({
142
+ type: 'answer',
143
+ sdp: offererResponse.answer,
144
+ });
145
+ }
146
+ // Apply ICE candidates
147
+ if (offererResponse.answerCandidates && offererResponse.answerCandidates.length > 0) {
148
+ for (const candidateStr of offererResponse.answerCandidates) {
149
+ try {
150
+ const candidate = JSON.parse(candidateStr);
151
+ await this.pc.addIceCandidate(new RTCIceCandidate(candidate));
152
+ }
153
+ catch (err) {
154
+ console.warn('Failed to add ICE candidate:', err);
155
+ }
156
+ }
157
+ }
158
+ }
159
+ else {
160
+ // Answerer role
161
+ const answererResponse = response;
162
+ // Apply ICE candidates from offerer
163
+ if (answererResponse.offerCandidates && answererResponse.offerCandidates.length > 0) {
164
+ for (const candidateStr of answererResponse.offerCandidates) {
165
+ try {
166
+ const candidate = JSON.parse(candidateStr);
167
+ await this.pc.addIceCandidate(new RTCIceCandidate(candidate));
168
+ }
169
+ catch (err) {
170
+ console.warn('Failed to add ICE candidate:', err);
171
+ }
172
+ }
173
+ }
174
+ }
175
+ }
176
+ catch (err) {
177
+ // Session not found or expired
178
+ if (err.message.includes('404') || err.message.includes('not found')) {
179
+ this.emit('error', new Error('Session not found or expired'));
180
+ this.close();
181
+ }
182
+ throw err;
183
+ }
184
+ }
185
+ /**
186
+ * Handle remotely created data channel
187
+ */
188
+ handleRemoteDataChannel(channel) {
189
+ this.dataChannels.set(channel.label, channel);
190
+ this.emit('datachannel', channel);
191
+ }
192
+ /**
193
+ * Get or create a data channel
194
+ */
195
+ dataChannel(label, options) {
196
+ let channel = this.dataChannels.get(label);
197
+ if (!channel) {
198
+ channel = this.pc.createDataChannel(label, options);
199
+ this.dataChannels.set(label, channel);
200
+ }
201
+ return channel;
202
+ }
203
+ /**
204
+ * Add a local media stream to the connection
205
+ */
206
+ addStream(stream) {
207
+ stream.getTracks().forEach(track => {
208
+ this.pc.addTrack(track, stream);
209
+ });
210
+ }
211
+ /**
212
+ * Get the underlying RTCPeerConnection for advanced usage
213
+ */
214
+ getPeerConnection() {
215
+ return this.pc;
216
+ }
217
+ /**
218
+ * Start connection timeout
219
+ */
220
+ startConnectionTimeout() {
221
+ this.connectionTimer = setTimeout(() => {
222
+ if (this.pc.connectionState !== 'connected') {
223
+ this.emit('error', new Error('Connection timeout'));
224
+ this.close();
225
+ }
226
+ }, this.connectionTimeoutMs);
227
+ }
228
+ /**
229
+ * Clear connection timeout
230
+ */
231
+ clearConnectionTimeout() {
232
+ if (this.connectionTimer) {
233
+ clearTimeout(this.connectionTimer);
234
+ this.connectionTimer = undefined;
235
+ }
236
+ }
237
+ /**
238
+ * Close the connection and cleanup resources
239
+ */
240
+ close() {
241
+ if (this.isClosed) {
242
+ return;
243
+ }
244
+ this.isClosed = true;
245
+ this.stopPolling();
246
+ this.clearConnectionTimeout();
247
+ // Close all data channels
248
+ this.dataChannels.forEach(dc => {
249
+ if (dc.readyState === 'open' || dc.readyState === 'connecting') {
250
+ dc.close();
251
+ }
252
+ });
253
+ this.dataChannels.clear();
254
+ // Close peer connection
255
+ if (this.pc.connectionState !== 'closed') {
256
+ this.pc.close();
257
+ }
258
+ this.emit('disconnect');
259
+ }
260
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Simple EventEmitter implementation for browser and Node.js compatibility
3
+ */
4
+ export declare class EventEmitter {
5
+ private events;
6
+ constructor();
7
+ /**
8
+ * Register an event listener
9
+ */
10
+ on(event: string, listener: Function): this;
11
+ /**
12
+ * Register a one-time event listener
13
+ */
14
+ once(event: string, listener: Function): this;
15
+ /**
16
+ * Remove an event listener
17
+ */
18
+ off(event: string, listener: Function): this;
19
+ /**
20
+ * Emit an event
21
+ */
22
+ emit(event: string, ...args: any[]): boolean;
23
+ /**
24
+ * Remove all listeners for an event (or all events if not specified)
25
+ */
26
+ removeAllListeners(event?: string): this;
27
+ /**
28
+ * Get listener count for an event
29
+ */
30
+ listenerCount(event: string): number;
31
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Simple EventEmitter implementation for browser and Node.js compatibility
3
+ */
4
+ export class EventEmitter {
5
+ constructor() {
6
+ this.events = new Map();
7
+ }
8
+ /**
9
+ * Register an event listener
10
+ */
11
+ on(event, listener) {
12
+ if (!this.events.has(event)) {
13
+ this.events.set(event, new Set());
14
+ }
15
+ this.events.get(event).add(listener);
16
+ return this;
17
+ }
18
+ /**
19
+ * Register a one-time event listener
20
+ */
21
+ once(event, listener) {
22
+ const onceWrapper = (...args) => {
23
+ this.off(event, onceWrapper);
24
+ listener.apply(this, args);
25
+ };
26
+ return this.on(event, onceWrapper);
27
+ }
28
+ /**
29
+ * Remove an event listener
30
+ */
31
+ off(event, listener) {
32
+ const listeners = this.events.get(event);
33
+ if (listeners) {
34
+ listeners.delete(listener);
35
+ if (listeners.size === 0) {
36
+ this.events.delete(event);
37
+ }
38
+ }
39
+ return this;
40
+ }
41
+ /**
42
+ * Emit an event
43
+ */
44
+ emit(event, ...args) {
45
+ const listeners = this.events.get(event);
46
+ if (!listeners || listeners.size === 0) {
47
+ return false;
48
+ }
49
+ listeners.forEach(listener => {
50
+ try {
51
+ listener.apply(this, args);
52
+ }
53
+ catch (err) {
54
+ console.error(`Error in ${event} event listener:`, err);
55
+ }
56
+ });
57
+ return true;
58
+ }
59
+ /**
60
+ * Remove all listeners for an event (or all events if not specified)
61
+ */
62
+ removeAllListeners(event) {
63
+ if (event) {
64
+ this.events.delete(event);
65
+ }
66
+ else {
67
+ this.events.clear();
68
+ }
69
+ return this;
70
+ }
71
+ /**
72
+ * Get listener count for an event
73
+ */
74
+ listenerCount(event) {
75
+ const listeners = this.events.get(event);
76
+ return listeners ? listeners.size : 0;
77
+ }
78
+ }
package/dist/index.d.ts CHANGED
@@ -1,30 +1,8 @@
1
1
  /**
2
- * TypeScript client for Rondevu peer signaling server
3
- *
4
- * @example
5
- * ```typescript
6
- * import { RondevuClient } from '@xtr-dev/rondevu-client';
7
- *
8
- * const client = new RondevuClient({
9
- * baseUrl: 'https://rondevu.example.com'
10
- * });
11
- *
12
- * // Create an offer
13
- * const { code } = await client.createOffer('my-room', {
14
- * info: 'peer-123',
15
- * offer: signalingData
16
- * });
17
- *
18
- * // Discover peers
19
- * const { sessions } = await client.listSessions('my-room');
20
- *
21
- * // Send answer
22
- * await client.sendAnswer({
23
- * code: sessions[0].code,
24
- * answer: answerData,
25
- * side: 'answerer'
26
- * });
27
- * ```
2
+ * @xtr-dev/rondevu-client
3
+ * WebRTC peer signaling and discovery client
28
4
  */
5
+ export { Rondevu } from './rondevu';
6
+ export { RondevuConnection } from './connection';
29
7
  export { RondevuClient } from './client';
30
- export * from './types';
8
+ export type { RondevuOptions, JoinOptions, ConnectionRole, RondevuConnectionParams, RondevuConnectionEvents, Side, Session, TopicInfo, Pagination, ListTopicsResponse, ListSessionsResponse, CreateOfferRequest, CreateOfferResponse, AnswerRequest, AnswerResponse, PollRequest, PollOffererResponse, PollAnswererResponse, PollResponse, HealthResponse, ErrorResponse, RondevuClientOptions, } from './types';
package/dist/index.js CHANGED
@@ -1,48 +1,10 @@
1
- "use strict";
2
1
  /**
3
- * TypeScript client for Rondevu peer signaling server
4
- *
5
- * @example
6
- * ```typescript
7
- * import { RondevuClient } from '@xtr-dev/rondevu-client';
8
- *
9
- * const client = new RondevuClient({
10
- * baseUrl: 'https://rondevu.example.com'
11
- * });
12
- *
13
- * // Create an offer
14
- * const { code } = await client.createOffer('my-room', {
15
- * info: 'peer-123',
16
- * offer: signalingData
17
- * });
18
- *
19
- * // Discover peers
20
- * const { sessions } = await client.listSessions('my-room');
21
- *
22
- * // Send answer
23
- * await client.sendAnswer({
24
- * code: sessions[0].code,
25
- * answer: answerData,
26
- * side: 'answerer'
27
- * });
28
- * ```
2
+ * @xtr-dev/rondevu-client
3
+ * WebRTC peer signaling and discovery client
29
4
  */
30
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
31
- if (k2 === undefined) k2 = k;
32
- var desc = Object.getOwnPropertyDescriptor(m, k);
33
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
34
- desc = { enumerable: true, get: function() { return m[k]; } };
35
- }
36
- Object.defineProperty(o, k2, desc);
37
- }) : (function(o, m, k, k2) {
38
- if (k2 === undefined) k2 = k;
39
- o[k2] = m[k];
40
- }));
41
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
42
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
43
- };
44
- Object.defineProperty(exports, "__esModule", { value: true });
45
- exports.RondevuClient = void 0;
46
- var client_1 = require("./client");
47
- Object.defineProperty(exports, "RondevuClient", { enumerable: true, get: function () { return client_1.RondevuClient; } });
48
- __exportStar(require("./types"), exports);
5
+ // Export main WebRTC client class
6
+ export { Rondevu } from './rondevu';
7
+ // Export connection class
8
+ export { RondevuConnection } from './connection';
9
+ // Export low-level signaling client (for advanced usage)
10
+ export { RondevuClient } from './client';
@@ -0,0 +1,58 @@
1
+ import { RondevuConnection } from './connection';
2
+ import { RondevuOptions, JoinOptions } from './types';
3
+ /**
4
+ * Main Rondevu WebRTC client with automatic connection management
5
+ */
6
+ export declare class Rondevu {
7
+ readonly peerId: string;
8
+ private client;
9
+ private rtcConfig?;
10
+ private pollingInterval;
11
+ private connectionTimeout;
12
+ /**
13
+ * Creates a new Rondevu client instance
14
+ * @param options - Client configuration options
15
+ */
16
+ constructor(options: RondevuOptions);
17
+ /**
18
+ * Generate a unique peer ID
19
+ */
20
+ private generatePeerId;
21
+ /**
22
+ * Update the peer ID (useful when user identity changes)
23
+ */
24
+ updatePeerId(newPeerId: string): void;
25
+ /**
26
+ * Create a new connection (offerer role)
27
+ * @param id - Connection identifier
28
+ * @param topic - Topic name for grouping connections
29
+ * @returns Promise that resolves to RondevuConnection
30
+ */
31
+ create(id: string, topic: string): Promise<RondevuConnection>;
32
+ /**
33
+ * Connect to an existing connection by ID (answerer role)
34
+ * @param id - Connection identifier
35
+ * @returns Promise that resolves to RondevuConnection
36
+ */
37
+ connect(id: string): Promise<RondevuConnection>;
38
+ /**
39
+ * Join a topic and discover available peers (answerer role)
40
+ * @param topic - Topic name
41
+ * @param options - Optional join options for filtering and selection
42
+ * @returns Promise that resolves to RondevuConnection
43
+ */
44
+ join(topic: string, options?: JoinOptions): Promise<RondevuConnection>;
45
+ /**
46
+ * Select a session based on strategy
47
+ */
48
+ private selectSession;
49
+ /**
50
+ * Wait for ICE gathering to complete
51
+ */
52
+ private waitForIceGathering;
53
+ /**
54
+ * Find a session by connection ID
55
+ * This requires polling since we don't know which topic it's in
56
+ */
57
+ private findSessionById;
58
+ }