@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.
@@ -0,0 +1,204 @@
1
+ import { RondevuClient } from './client';
2
+ import { RondevuConnection } from './connection';
3
+ /**
4
+ * Main Rondevu WebRTC client with automatic connection management
5
+ */
6
+ export class Rondevu {
7
+ /**
8
+ * Creates a new Rondevu client instance
9
+ * @param options - Client configuration options
10
+ */
11
+ constructor(options) {
12
+ this.client = new RondevuClient({
13
+ baseUrl: options.baseUrl,
14
+ origin: options.origin,
15
+ fetch: options.fetch,
16
+ });
17
+ // Auto-generate peer ID if not provided
18
+ this.peerId = options.peerId || this.generatePeerId();
19
+ this.rtcConfig = options.rtcConfig;
20
+ this.pollingInterval = options.pollingInterval || 1000;
21
+ this.connectionTimeout = options.connectionTimeout || 30000;
22
+ }
23
+ /**
24
+ * Generate a unique peer ID
25
+ */
26
+ generatePeerId() {
27
+ return `rdv_${Math.random().toString(36).substring(2, 14)}`;
28
+ }
29
+ /**
30
+ * Update the peer ID (useful when user identity changes)
31
+ */
32
+ updatePeerId(newPeerId) {
33
+ this.peerId = newPeerId;
34
+ }
35
+ /**
36
+ * Create a new connection (offerer role)
37
+ * @param id - Connection identifier
38
+ * @param topic - Topic name for grouping connections
39
+ * @returns Promise that resolves to RondevuConnection
40
+ */
41
+ async create(id, topic) {
42
+ // Create peer connection
43
+ const pc = new RTCPeerConnection(this.rtcConfig);
44
+ // Create initial data channel for negotiation (required for offer creation)
45
+ pc.createDataChannel('_negotiation');
46
+ // Generate offer
47
+ const offer = await pc.createOffer();
48
+ await pc.setLocalDescription(offer);
49
+ // Wait for ICE gathering to complete
50
+ await this.waitForIceGathering(pc);
51
+ // Create session on server with custom code
52
+ await this.client.createOffer(topic, {
53
+ peerId: this.peerId,
54
+ offer: pc.localDescription.sdp,
55
+ code: id,
56
+ });
57
+ // Create connection object
58
+ const connectionParams = {
59
+ id,
60
+ topic,
61
+ role: 'offerer',
62
+ pc,
63
+ localPeerId: this.peerId,
64
+ remotePeerId: '', // Will be populated when answer is received
65
+ pollingInterval: this.pollingInterval,
66
+ connectionTimeout: this.connectionTimeout,
67
+ };
68
+ const connection = new RondevuConnection(connectionParams, this.client);
69
+ // Start polling for answer
70
+ connection.startPolling();
71
+ return connection;
72
+ }
73
+ /**
74
+ * Connect to an existing connection by ID (answerer role)
75
+ * @param id - Connection identifier
76
+ * @returns Promise that resolves to RondevuConnection
77
+ */
78
+ async connect(id) {
79
+ // Poll server to get session by ID
80
+ const sessionData = await this.findSessionById(id);
81
+ if (!sessionData) {
82
+ throw new Error(`Connection ${id} not found or expired`);
83
+ }
84
+ // Create peer connection
85
+ const pc = new RTCPeerConnection(this.rtcConfig);
86
+ // Set remote offer
87
+ await pc.setRemoteDescription({
88
+ type: 'offer',
89
+ sdp: sessionData.offer,
90
+ });
91
+ // Generate answer
92
+ const answer = await pc.createAnswer();
93
+ await pc.setLocalDescription(answer);
94
+ // Wait for ICE gathering
95
+ await this.waitForIceGathering(pc);
96
+ // Send answer to server
97
+ await this.client.sendAnswer({
98
+ code: id,
99
+ answer: pc.localDescription.sdp,
100
+ side: 'answerer',
101
+ });
102
+ // Create connection object
103
+ const connectionParams = {
104
+ id,
105
+ topic: sessionData.topic || 'unknown',
106
+ role: 'answerer',
107
+ pc,
108
+ localPeerId: this.peerId,
109
+ remotePeerId: sessionData.peerId,
110
+ pollingInterval: this.pollingInterval,
111
+ connectionTimeout: this.connectionTimeout,
112
+ };
113
+ const connection = new RondevuConnection(connectionParams, this.client);
114
+ // Start polling for ICE candidates
115
+ connection.startPolling();
116
+ return connection;
117
+ }
118
+ /**
119
+ * Join a topic and discover available peers (answerer role)
120
+ * @param topic - Topic name
121
+ * @param options - Optional join options for filtering and selection
122
+ * @returns Promise that resolves to RondevuConnection
123
+ */
124
+ async join(topic, options) {
125
+ // List sessions in topic
126
+ const { sessions } = await this.client.listSessions(topic);
127
+ // Filter out self (sessions with our peer ID)
128
+ let availableSessions = sessions.filter(session => session.peerId !== this.peerId);
129
+ // Apply custom filter if provided
130
+ if (options?.filter) {
131
+ availableSessions = availableSessions.filter(options.filter);
132
+ }
133
+ if (availableSessions.length === 0) {
134
+ throw new Error(`No available peers in topic: ${topic}`);
135
+ }
136
+ // Select session based on strategy
137
+ const selectedSession = this.selectSession(availableSessions, options?.select || 'first');
138
+ // Connect to selected session
139
+ return this.connect(selectedSession.code);
140
+ }
141
+ /**
142
+ * Select a session based on strategy
143
+ */
144
+ selectSession(sessions, strategy) {
145
+ switch (strategy) {
146
+ case 'first':
147
+ return sessions[0];
148
+ case 'newest':
149
+ return sessions.reduce((newest, session) => session.createdAt > newest.createdAt ? session : newest);
150
+ case 'oldest':
151
+ return sessions.reduce((oldest, session) => session.createdAt < oldest.createdAt ? session : oldest);
152
+ case 'random':
153
+ return sessions[Math.floor(Math.random() * sessions.length)];
154
+ default:
155
+ return sessions[0];
156
+ }
157
+ }
158
+ /**
159
+ * Wait for ICE gathering to complete
160
+ */
161
+ async waitForIceGathering(pc) {
162
+ if (pc.iceGatheringState === 'complete') {
163
+ return;
164
+ }
165
+ return new Promise((resolve) => {
166
+ const checkState = () => {
167
+ if (pc.iceGatheringState === 'complete') {
168
+ pc.removeEventListener('icegatheringstatechange', checkState);
169
+ resolve();
170
+ }
171
+ };
172
+ pc.addEventListener('icegatheringstatechange', checkState);
173
+ // Also set a timeout in case gathering takes too long
174
+ setTimeout(() => {
175
+ pc.removeEventListener('icegatheringstatechange', checkState);
176
+ resolve();
177
+ }, 5000);
178
+ });
179
+ }
180
+ /**
181
+ * Find a session by connection ID
182
+ * This requires polling since we don't know which topic it's in
183
+ */
184
+ async findSessionById(id) {
185
+ try {
186
+ // Try to poll for the session directly
187
+ // The poll endpoint should return the session data
188
+ const response = await this.client.poll(id, 'answerer');
189
+ const answererResponse = response;
190
+ if (answererResponse.offer) {
191
+ return {
192
+ code: id,
193
+ peerId: '', // Will be populated from session data
194
+ offer: answererResponse.offer,
195
+ topic: undefined,
196
+ };
197
+ }
198
+ return null;
199
+ }
200
+ catch (err) {
201
+ throw new Error(`Failed to find session ${id}: ${err.message}`);
202
+ }
203
+ }
204
+ }
package/dist/types.d.ts CHANGED
@@ -9,7 +9,7 @@ export interface Session {
9
9
  /** Unique session identifier (UUID) */
10
10
  code: string;
11
11
  /** Peer identifier/metadata */
12
- info: string;
12
+ peerId: string;
13
13
  /** Signaling data for peer connection */
14
14
  offer: string;
15
15
  /** Additional signaling data from offerer */
@@ -59,9 +59,11 @@ export interface ListSessionsResponse {
59
59
  */
60
60
  export interface CreateOfferRequest {
61
61
  /** Peer identifier/metadata (max 1024 characters) */
62
- info: string;
62
+ peerId: string;
63
63
  /** Signaling data for peer connection */
64
64
  offer: string;
65
+ /** Optional custom connection code (if not provided, server generates UUID) */
66
+ code?: string;
65
67
  }
66
68
  /**
67
69
  * Response from POST /:topic/offer
@@ -144,3 +146,61 @@ export interface RondevuClientOptions {
144
146
  /** Optional fetch implementation (for Node.js environments) */
145
147
  fetch?: typeof fetch;
146
148
  }
149
+ /**
150
+ * Configuration options for Rondevu WebRTC client
151
+ */
152
+ export interface RondevuOptions {
153
+ /** Base URL of the Rondevu server (e.g., 'https://example.com') */
154
+ baseUrl: string;
155
+ /** Peer identifier (optional, auto-generated if not provided) */
156
+ peerId?: string;
157
+ /** Origin header value for session isolation (defaults to baseUrl origin) */
158
+ origin?: string;
159
+ /** Optional fetch implementation (for Node.js environments) */
160
+ fetch?: typeof fetch;
161
+ /** WebRTC configuration (ICE servers, etc.) */
162
+ rtcConfig?: RTCConfiguration;
163
+ /** Polling interval in milliseconds (default: 1000) */
164
+ pollingInterval?: number;
165
+ /** Connection timeout in milliseconds (default: 30000) */
166
+ connectionTimeout?: number;
167
+ }
168
+ /**
169
+ * Options for joining a topic
170
+ */
171
+ export interface JoinOptions {
172
+ /** Filter function to select specific sessions */
173
+ filter?: (session: {
174
+ code: string;
175
+ peerId: string;
176
+ }) => boolean;
177
+ /** Selection strategy for choosing a session */
178
+ select?: 'first' | 'newest' | 'oldest' | 'random';
179
+ }
180
+ /**
181
+ * Connection role - whether this peer is creating or answering
182
+ */
183
+ export type ConnectionRole = 'offerer' | 'answerer';
184
+ /**
185
+ * Parameters for creating a RondevuConnection
186
+ */
187
+ export interface RondevuConnectionParams {
188
+ id: string;
189
+ topic: string;
190
+ role: ConnectionRole;
191
+ pc: RTCPeerConnection;
192
+ localPeerId: string;
193
+ remotePeerId: string;
194
+ pollingInterval: number;
195
+ connectionTimeout: number;
196
+ }
197
+ /**
198
+ * Event map for RondevuConnection events
199
+ */
200
+ export interface RondevuConnectionEvents {
201
+ connect: () => void;
202
+ disconnect: () => void;
203
+ error: (error: Error) => void;
204
+ datachannel: (channel: RTCDataChannel) => void;
205
+ stream: (stream: MediaStream) => void;
206
+ }
package/dist/types.js CHANGED
@@ -1,2 +1,4 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
1
+ // ============================================================================
2
+ // Signaling Types
3
+ // ============================================================================
4
+ export {};
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@xtr-dev/rondevu-client",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "TypeScript client for Rondevu peer signaling and discovery server",
5
+ "type": "module",
5
6
  "main": "dist/index.js",
6
7
  "types": "dist/index.d.ts",
7
8
  "scripts": {