@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.
package/dist/api.d.ts CHANGED
@@ -25,20 +25,29 @@ export interface Offer {
25
25
  expiresAt: number;
26
26
  answererPeerId?: string;
27
27
  }
28
+ export interface OfferRequest {
29
+ sdp: string;
30
+ }
28
31
  export interface ServiceRequest {
29
32
  username: string;
30
33
  serviceFqn: string;
31
- sdp: string;
34
+ offers: OfferRequest[];
32
35
  ttl?: number;
33
36
  isPublic?: boolean;
34
37
  metadata?: Record<string, any>;
35
38
  signature: string;
36
39
  message: string;
37
40
  }
41
+ export interface ServiceOffer {
42
+ offerId: string;
43
+ sdp: string;
44
+ createdAt: number;
45
+ expiresAt: number;
46
+ }
38
47
  export interface Service {
39
48
  serviceId: string;
40
49
  uuid: string;
41
- offerId: string;
50
+ offers: ServiceOffer[];
42
51
  username: string;
43
52
  serviceFqn: string;
44
53
  isPublic: boolean;
@@ -57,6 +66,10 @@ export declare class RondevuAPI {
57
66
  private baseUrl;
58
67
  private credentials?;
59
68
  constructor(baseUrl: string, credentials?: Credentials | undefined);
69
+ /**
70
+ * Set credentials for authentication
71
+ */
72
+ setCredentials(credentials: Credentials): void;
60
73
  /**
61
74
  * Authentication header
62
75
  */
@@ -119,15 +132,11 @@ export declare class RondevuAPI {
119
132
  sdp: string;
120
133
  }>;
121
134
  /**
122
- * Search services by username
135
+ * Search services by username - lists all services for a username
123
136
  */
124
137
  searchServicesByUsername(username: string): Promise<Service[]>;
125
138
  /**
126
- * Search services by FQN
127
- */
128
- searchServicesByFqn(serviceFqn: string): Promise<Service[]>;
129
- /**
130
- * Search services by username AND FQN
139
+ * Search services by username AND FQN - returns full service details
131
140
  */
132
141
  searchServices(username: string, serviceFqn: string): Promise<Service[]>;
133
142
  /**
package/dist/api.js CHANGED
@@ -28,6 +28,12 @@ export class RondevuAPI {
28
28
  this.baseUrl = baseUrl;
29
29
  this.credentials = credentials;
30
30
  }
31
+ /**
32
+ * Set credentials for authentication
33
+ */
34
+ setCredentials(credentials) {
35
+ this.credentials = credentials;
36
+ }
31
37
  /**
32
38
  * Authentication header
33
39
  */
@@ -148,14 +154,16 @@ export class RondevuAPI {
148
154
  const response = await fetch(`${this.baseUrl}/offers/${offerId}/answer`, {
149
155
  headers: this.getAuthHeader(),
150
156
  });
151
- if (response.status === 404) {
152
- return null; // No answer yet
153
- }
154
157
  if (!response.ok) {
158
+ // 404 means not yet answered
159
+ if (response.status === 404) {
160
+ return null;
161
+ }
155
162
  const error = await response.json().catch(() => ({ error: 'Unknown error' }));
156
163
  throw new Error(`Failed to get answer: ${error.error || response.statusText}`);
157
164
  }
158
- return await response.json();
165
+ const data = await response.json();
166
+ return { sdp: data.sdp };
159
167
  }
160
168
  /**
161
169
  * Search offers by topic
@@ -199,7 +207,8 @@ export class RondevuAPI {
199
207
  const error = await response.json().catch(() => ({ error: 'Unknown error' }));
200
208
  throw new Error(`Failed to get ICE candidates: ${error.error || response.statusText}`);
201
209
  }
202
- return await response.json();
210
+ const data = await response.json();
211
+ return data.candidates || [];
203
212
  }
204
213
  // ============================================
205
214
  // Services
@@ -208,7 +217,7 @@ export class RondevuAPI {
208
217
  * Publish a service
209
218
  */
210
219
  async publishService(service) {
211
- const response = await fetch(`${this.baseUrl}/services`, {
220
+ const response = await fetch(`${this.baseUrl}/users/${encodeURIComponent(service.username)}/services`, {
212
221
  method: 'POST',
213
222
  headers: {
214
223
  'Content-Type': 'application/json',
@@ -236,37 +245,31 @@ export class RondevuAPI {
236
245
  return await response.json();
237
246
  }
238
247
  /**
239
- * Search services by username
248
+ * Search services by username - lists all services for a username
240
249
  */
241
250
  async searchServicesByUsername(username) {
242
- const response = await fetch(`${this.baseUrl}/services?username=${encodeURIComponent(username)}`, { headers: this.getAuthHeader() });
251
+ const response = await fetch(`${this.baseUrl}/users/${encodeURIComponent(username)}/services`, { headers: this.getAuthHeader() });
243
252
  if (!response.ok) {
244
253
  const error = await response.json().catch(() => ({ error: 'Unknown error' }));
245
254
  throw new Error(`Failed to search services: ${error.error || response.statusText}`);
246
255
  }
247
- return await response.json();
256
+ const data = await response.json();
257
+ return data.services || [];
248
258
  }
249
259
  /**
250
- * Search services by FQN
251
- */
252
- async searchServicesByFqn(serviceFqn) {
253
- const response = await fetch(`${this.baseUrl}/services?serviceFqn=${encodeURIComponent(serviceFqn)}`, { headers: this.getAuthHeader() });
254
- if (!response.ok) {
255
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
256
- throw new Error(`Failed to search services: ${error.error || response.statusText}`);
257
- }
258
- return await response.json();
259
- }
260
- /**
261
- * Search services by username AND FQN
260
+ * Search services by username AND FQN - returns full service details
262
261
  */
263
262
  async searchServices(username, serviceFqn) {
264
- const response = await fetch(`${this.baseUrl}/services?username=${encodeURIComponent(username)}&serviceFqn=${encodeURIComponent(serviceFqn)}`, { headers: this.getAuthHeader() });
263
+ const response = await fetch(`${this.baseUrl}/users/${encodeURIComponent(username)}/services/${encodeURIComponent(serviceFqn)}`, { headers: this.getAuthHeader() });
265
264
  if (!response.ok) {
265
+ if (response.status === 404) {
266
+ return [];
267
+ }
266
268
  const error = await response.json().catch(() => ({ error: 'Unknown error' }));
267
269
  throw new Error(`Failed to search services: ${error.error || response.statusText}`);
268
270
  }
269
- return await response.json();
271
+ const service = await response.json();
272
+ return [service];
270
273
  }
271
274
  // ============================================
272
275
  // Usernames
@@ -275,7 +278,7 @@ export class RondevuAPI {
275
278
  * Check if username is available
276
279
  */
277
280
  async checkUsername(username) {
278
- const response = await fetch(`${this.baseUrl}/usernames/${encodeURIComponent(username)}/check`);
281
+ const response = await fetch(`${this.baseUrl}/users/${encodeURIComponent(username)}`);
279
282
  if (!response.ok) {
280
283
  const error = await response.json().catch(() => ({ error: 'Unknown error' }));
281
284
  throw new Error(`Failed to check username: ${error.error || response.statusText}`);
@@ -286,7 +289,7 @@ export class RondevuAPI {
286
289
  * Claim a username (requires Ed25519 signature)
287
290
  */
288
291
  async claimUsername(username, publicKey, signature, message) {
289
- const response = await fetch(`${this.baseUrl}/usernames/${encodeURIComponent(username)}`, {
292
+ const response = await fetch(`${this.baseUrl}/users/${encodeURIComponent(username)}`, {
290
293
  method: 'POST',
291
294
  headers: {
292
295
  'Content-Type': 'application/json',
@@ -0,0 +1,120 @@
1
+ import { ConnectionEvents, ConnectionInterface, Message, QueueMessageOptions, Signaler } from './types.js';
2
+ import { EventBus } from './event-bus.js';
3
+ import { WebRTCContext } from './webrtc-context';
4
+ export type WebRTCRondevuConnectionOptions = {
5
+ offer?: RTCSessionDescriptionInit | null;
6
+ context: WebRTCContext;
7
+ signaler: Signaler;
8
+ };
9
+ /**
10
+ * WebRTCRondevuConnection - WebRTC peer connection wrapper with Rondevu signaling
11
+ *
12
+ * Manages a WebRTC peer connection lifecycle including:
13
+ * - Automatic offer/answer creation based on role
14
+ * - ICE candidate exchange via Rondevu signaling server
15
+ * - Connection state management with type-safe events
16
+ * - Data channel creation and message handling
17
+ *
18
+ * The connection automatically determines its role (offerer or answerer) based on whether
19
+ * an offer is provided in the constructor. The offerer creates the data channel, while
20
+ * the answerer receives it via the 'datachannel' event.
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * // Offerer side (creates offer)
25
+ * const connection = new WebRTCRondevuConnection(
26
+ * 'conn-123',
27
+ * 'peer-username',
28
+ * 'chat.service@1.0.0'
29
+ * );
30
+ *
31
+ * await connection.ready; // Wait for local offer
32
+ * const sdp = connection.connection.localDescription!.sdp!;
33
+ * // Send sdp to signaling server...
34
+ *
35
+ * // Answerer side (receives offer)
36
+ * const connection = new WebRTCRondevuConnection(
37
+ * 'conn-123',
38
+ * 'peer-username',
39
+ * 'chat.service@1.0.0',
40
+ * { type: 'offer', sdp: remoteOfferSdp }
41
+ * );
42
+ *
43
+ * await connection.ready; // Wait for local answer
44
+ * const answerSdp = connection.connection.localDescription!.sdp!;
45
+ * // Send answer to signaling server...
46
+ *
47
+ * // Both sides: Set up signaler and listen for state changes
48
+ * connection.setSignaler(signaler);
49
+ * connection.events.on('state-change', (state) => {
50
+ * console.log('Connection state:', state);
51
+ * });
52
+ * ```
53
+ */
54
+ export declare class RTCDurableConnection implements ConnectionInterface {
55
+ private readonly side;
56
+ readonly expiresAt: number;
57
+ readonly lastActive: number;
58
+ readonly events: EventBus<ConnectionEvents>;
59
+ readonly ready: Promise<void>;
60
+ private iceBin;
61
+ private context;
62
+ private readonly signaler;
63
+ private _conn;
64
+ private _state;
65
+ private _dataChannel;
66
+ private messageQueue;
67
+ constructor({ context, offer, signaler }: WebRTCRondevuConnectionOptions);
68
+ /**
69
+ * Getter method for retrieving the current connection.
70
+ *
71
+ * @return {RTCPeerConnection|null} The current connection instance.
72
+ */
73
+ get connection(): RTCPeerConnection | null;
74
+ /**
75
+ * Update connection state and emit state-change event
76
+ */
77
+ private setState;
78
+ /**
79
+ * Start ICE candidate exchange when gathering begins
80
+ */
81
+ private startIce;
82
+ /**
83
+ * Stop ICE candidate exchange when gathering completes
84
+ */
85
+ private stopIce;
86
+ /**
87
+ * Disconnects the current connection and cleans up resources.
88
+ * Closes the active connection if it exists, resets the connection instance to null,
89
+ * stops the ICE process, and updates the state to 'disconnected'.
90
+ *
91
+ * @return {void} No return value.
92
+ */
93
+ disconnect(): void;
94
+ /**
95
+ * Current connection state
96
+ */
97
+ get state(): "connected" | "disconnected" | "connecting";
98
+ /**
99
+ * Setup data channel event listeners
100
+ */
101
+ private setupDataChannelListeners;
102
+ /**
103
+ * Flush the message queue
104
+ */
105
+ private flushQueue;
106
+ /**
107
+ * Queue a message for sending when connection is established
108
+ *
109
+ * @param message - Message to queue (string or ArrayBuffer)
110
+ * @param options - Queue options (e.g., expiration time)
111
+ */
112
+ queueMessage(message: Message, options?: QueueMessageOptions): Promise<void>;
113
+ /**
114
+ * Send a message immediately
115
+ *
116
+ * @param message - Message to send (string or ArrayBuffer)
117
+ * @returns Promise resolving to true if sent successfully
118
+ */
119
+ sendMessage(message: Message): Promise<boolean>;
120
+ }
@@ -0,0 +1,244 @@
1
+ import { isConnectionState, } from './types.js';
2
+ import { EventBus } from './event-bus.js';
3
+ import { createBin } from './bin.js';
4
+ /**
5
+ * WebRTCRondevuConnection - WebRTC peer connection wrapper with Rondevu signaling
6
+ *
7
+ * Manages a WebRTC peer connection lifecycle including:
8
+ * - Automatic offer/answer creation based on role
9
+ * - ICE candidate exchange via Rondevu signaling server
10
+ * - Connection state management with type-safe events
11
+ * - Data channel creation and message handling
12
+ *
13
+ * The connection automatically determines its role (offerer or answerer) based on whether
14
+ * an offer is provided in the constructor. The offerer creates the data channel, while
15
+ * the answerer receives it via the 'datachannel' event.
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * // Offerer side (creates offer)
20
+ * const connection = new WebRTCRondevuConnection(
21
+ * 'conn-123',
22
+ * 'peer-username',
23
+ * 'chat.service@1.0.0'
24
+ * );
25
+ *
26
+ * await connection.ready; // Wait for local offer
27
+ * const sdp = connection.connection.localDescription!.sdp!;
28
+ * // Send sdp to signaling server...
29
+ *
30
+ * // Answerer side (receives offer)
31
+ * const connection = new WebRTCRondevuConnection(
32
+ * 'conn-123',
33
+ * 'peer-username',
34
+ * 'chat.service@1.0.0',
35
+ * { type: 'offer', sdp: remoteOfferSdp }
36
+ * );
37
+ *
38
+ * await connection.ready; // Wait for local answer
39
+ * const answerSdp = connection.connection.localDescription!.sdp!;
40
+ * // Send answer to signaling server...
41
+ *
42
+ * // Both sides: Set up signaler and listen for state changes
43
+ * connection.setSignaler(signaler);
44
+ * connection.events.on('state-change', (state) => {
45
+ * console.log('Connection state:', state);
46
+ * });
47
+ * ```
48
+ */
49
+ export class RTCDurableConnection {
50
+ constructor({ context, offer, signaler }) {
51
+ this.expiresAt = 0;
52
+ this.lastActive = 0;
53
+ this.events = new EventBus();
54
+ this.iceBin = createBin();
55
+ this._conn = null;
56
+ this._state = 'disconnected';
57
+ this._dataChannel = null;
58
+ this.messageQueue = [];
59
+ this.context = context;
60
+ this.signaler = signaler;
61
+ this._conn = context.createPeerConnection();
62
+ this.side = offer ? 'answer' : 'offer';
63
+ // setup data channel
64
+ if (offer) {
65
+ this._conn.addEventListener('datachannel', e => {
66
+ this._dataChannel = e.channel;
67
+ this.setupDataChannelListeners(this._dataChannel);
68
+ });
69
+ }
70
+ else {
71
+ this._dataChannel = this._conn.createDataChannel('vu.ronde.protocol');
72
+ this.setupDataChannelListeners(this._dataChannel);
73
+ }
74
+ // setup description exchange
75
+ this.ready = offer
76
+ ? this._conn
77
+ .setRemoteDescription(offer)
78
+ .then(() => this._conn?.createAnswer())
79
+ .then(async (answer) => {
80
+ if (!answer || !this._conn)
81
+ throw new Error('Connection disappeared');
82
+ await this._conn.setLocalDescription(answer);
83
+ return await signaler.setAnswer(answer);
84
+ })
85
+ : this._conn.createOffer().then(async (offer) => {
86
+ if (!this._conn)
87
+ throw new Error('Connection disappeared');
88
+ await this._conn.setLocalDescription(offer);
89
+ return await signaler.setOffer(offer);
90
+ });
91
+ // propagate connection state changes
92
+ this._conn.addEventListener('connectionstatechange', () => {
93
+ console.log(this.side, 'connection state changed: ', this._conn.connectionState);
94
+ const state = isConnectionState(this._conn.connectionState)
95
+ ? this._conn.connectionState
96
+ : 'disconnected';
97
+ this.setState(state);
98
+ });
99
+ this._conn.addEventListener('iceconnectionstatechange', () => {
100
+ console.log(this.side, 'ice connection state changed: ', this._conn.iceConnectionState);
101
+ });
102
+ // start ICE candidate exchange when gathering begins
103
+ this._conn.addEventListener('icegatheringstatechange', () => {
104
+ if (this._conn.iceGatheringState === 'gathering') {
105
+ this.startIce();
106
+ }
107
+ else if (this._conn.iceGatheringState === 'complete') {
108
+ this.stopIce();
109
+ }
110
+ });
111
+ }
112
+ /**
113
+ * Getter method for retrieving the current connection.
114
+ *
115
+ * @return {RTCPeerConnection|null} The current connection instance.
116
+ */
117
+ get connection() {
118
+ return this._conn;
119
+ }
120
+ /**
121
+ * Update connection state and emit state-change event
122
+ */
123
+ setState(state) {
124
+ this._state = state;
125
+ this.events.emit('state-change', state);
126
+ }
127
+ /**
128
+ * Start ICE candidate exchange when gathering begins
129
+ */
130
+ startIce() {
131
+ const listener = ({ candidate }) => {
132
+ if (candidate)
133
+ this.signaler.addIceCandidate(candidate);
134
+ };
135
+ if (!this._conn)
136
+ throw new Error('Connection disappeared');
137
+ this._conn.addEventListener('icecandidate', listener);
138
+ this.iceBin(this.signaler.addListener((candidate) => this._conn?.addIceCandidate(candidate)), () => this._conn?.removeEventListener('icecandidate', listener));
139
+ }
140
+ /**
141
+ * Stop ICE candidate exchange when gathering completes
142
+ */
143
+ stopIce() {
144
+ this.iceBin.clean();
145
+ }
146
+ /**
147
+ * Disconnects the current connection and cleans up resources.
148
+ * Closes the active connection if it exists, resets the connection instance to null,
149
+ * stops the ICE process, and updates the state to 'disconnected'.
150
+ *
151
+ * @return {void} No return value.
152
+ */
153
+ disconnect() {
154
+ this._conn?.close();
155
+ this._conn = null;
156
+ this.stopIce();
157
+ this.setState('disconnected');
158
+ }
159
+ /**
160
+ * Current connection state
161
+ */
162
+ get state() {
163
+ return this._state;
164
+ }
165
+ /**
166
+ * Setup data channel event listeners
167
+ */
168
+ setupDataChannelListeners(channel) {
169
+ channel.addEventListener('message', e => {
170
+ this.events.emit('message', e.data);
171
+ });
172
+ channel.addEventListener('open', () => {
173
+ // Channel opened - flush queued messages
174
+ this.flushQueue().catch(err => {
175
+ console.error('Failed to flush message queue:', err);
176
+ });
177
+ });
178
+ channel.addEventListener('error', err => {
179
+ console.error('Data channel error:', err);
180
+ });
181
+ channel.addEventListener('close', () => {
182
+ console.log('Data channel closed');
183
+ });
184
+ }
185
+ /**
186
+ * Flush the message queue
187
+ */
188
+ async flushQueue() {
189
+ while (this.messageQueue.length > 0 && this._state === 'connected') {
190
+ const item = this.messageQueue.shift();
191
+ // Check expiration
192
+ if (item.options.expiresAt && Date.now() > item.options.expiresAt) {
193
+ continue;
194
+ }
195
+ const success = await this.sendMessage(item.message);
196
+ if (!success) {
197
+ // Re-queue on failure
198
+ this.messageQueue.unshift(item);
199
+ break;
200
+ }
201
+ }
202
+ }
203
+ /**
204
+ * Queue a message for sending when connection is established
205
+ *
206
+ * @param message - Message to queue (string or ArrayBuffer)
207
+ * @param options - Queue options (e.g., expiration time)
208
+ */
209
+ async queueMessage(message, options = {}) {
210
+ this.messageQueue.push({
211
+ message,
212
+ options,
213
+ timestamp: Date.now()
214
+ });
215
+ // Try immediate send if connected
216
+ if (this._state === 'connected') {
217
+ await this.flushQueue();
218
+ }
219
+ }
220
+ /**
221
+ * Send a message immediately
222
+ *
223
+ * @param message - Message to send (string or ArrayBuffer)
224
+ * @returns Promise resolving to true if sent successfully
225
+ */
226
+ async sendMessage(message) {
227
+ if (this._state !== 'connected' || !this._dataChannel) {
228
+ return false;
229
+ }
230
+ if (this._dataChannel.readyState !== 'open') {
231
+ return false;
232
+ }
233
+ try {
234
+ // TypeScript has trouble with the union type, so we cast to any
235
+ // Both string and ArrayBuffer are valid for RTCDataChannel.send()
236
+ this._dataChannel.send(message);
237
+ return true;
238
+ }
239
+ catch (err) {
240
+ console.error('Send failed:', err);
241
+ return false;
242
+ }
243
+ }
244
+ }
package/dist/index.d.ts CHANGED
@@ -5,10 +5,11 @@
5
5
  export { EventBus } from './event-bus.js';
6
6
  export { RondevuAPI } from './api.js';
7
7
  export { RondevuService } from './rondevu-service.js';
8
- export { RondevuSignaler } from './signaler.js';
8
+ export { RondevuSignaler } from './rondevu-signaler.js';
9
+ export { WebRTCContext } from './webrtc-context.js';
10
+ export { RTCDurableConnection } from './durable-connection';
9
11
  export { ServiceHost } from './service-host.js';
10
12
  export { ServiceClient } from './service-client.js';
11
- export { WebRTCRondevuConnection } from './connection.js';
12
13
  export { createBin } from './bin.js';
13
14
  export type { ConnectionInterface, QueueMessageOptions, Message, ConnectionEvents, Signaler, } from './types.js';
14
15
  export type { Credentials, Keypair, OfferRequest, Offer, ServiceRequest, Service, IceCandidate, } from './api.js';
@@ -16,3 +17,4 @@ export type { Binnable } from './bin.js';
16
17
  export type { RondevuServiceOptions, PublishServiceOptions } from './rondevu-service.js';
17
18
  export type { ServiceHostOptions, ServiceHostEvents } from './service-host.js';
18
19
  export type { ServiceClientOptions, ServiceClientEvents } from './service-client.js';
20
+ export type { PollingConfig } from './rondevu-signaler.js';
package/dist/index.js CHANGED
@@ -5,8 +5,9 @@
5
5
  export { EventBus } from './event-bus.js';
6
6
  export { RondevuAPI } from './api.js';
7
7
  export { RondevuService } from './rondevu-service.js';
8
- export { RondevuSignaler } from './signaler.js';
8
+ export { RondevuSignaler } from './rondevu-signaler.js';
9
+ export { WebRTCContext } from './webrtc-context.js';
10
+ export { RTCDurableConnection } from './durable-connection';
9
11
  export { ServiceHost } from './service-host.js';
10
12
  export { ServiceClient } from './service-client.js';
11
- export { WebRTCRondevuConnection } from './connection.js';
12
13
  export { createBin } from './bin.js';
@@ -0,0 +1,29 @@
1
+ import { RondevuService } from './rondevu-service.js';
2
+ import { Keypair } from './api.js';
3
+ export interface QuickStartOptions {
4
+ apiUrl: string;
5
+ username?: string;
6
+ keypair?: Keypair;
7
+ }
8
+ /**
9
+ * Quick start helper for initializing Rondevu service
10
+ *
11
+ * Simplifies initialization by:
12
+ * - Auto-generating username if not provided
13
+ * - Auto-generating keypair if not provided
14
+ * - Registering with the server
15
+ * - Claiming the username
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * // Simple usage with auto-generated username
20
+ * const service = await quickStart({ apiUrl: 'https://api.ronde.vu' })
21
+ *
22
+ * // With custom username
23
+ * const service = await quickStart({
24
+ * apiUrl: 'https://api.ronde.vu',
25
+ * username: 'my-username'
26
+ * })
27
+ * ```
28
+ */
29
+ export declare function quickStart(options: QuickStartOptions): Promise<RondevuService>;
@@ -0,0 +1,44 @@
1
+ import { RondevuService } from './rondevu-service.js';
2
+ /**
3
+ * Quick start helper for initializing Rondevu service
4
+ *
5
+ * Simplifies initialization by:
6
+ * - Auto-generating username if not provided
7
+ * - Auto-generating keypair if not provided
8
+ * - Registering with the server
9
+ * - Claiming the username
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * // Simple usage with auto-generated username
14
+ * const service = await quickStart({ apiUrl: 'https://api.ronde.vu' })
15
+ *
16
+ * // With custom username
17
+ * const service = await quickStart({
18
+ * apiUrl: 'https://api.ronde.vu',
19
+ * username: 'my-username'
20
+ * })
21
+ * ```
22
+ */
23
+ export async function quickStart(options) {
24
+ // Generate username if not provided
25
+ const username = options.username || `user-${generateId()}`;
26
+ // Create service
27
+ const serviceOptions = {
28
+ apiUrl: options.apiUrl,
29
+ username,
30
+ keypair: options.keypair
31
+ };
32
+ const service = new RondevuService(serviceOptions);
33
+ // Initialize (generates keypair and registers)
34
+ await service.initialize();
35
+ // Claim username
36
+ await service.claimUsername();
37
+ return service;
38
+ }
39
+ /**
40
+ * Generate a random ID
41
+ */
42
+ function generateId() {
43
+ return Math.random().toString(36).substring(2, 10);
44
+ }
@@ -0,0 +1,10 @@
1
+ import { RondevuService } from "./rondevu-service";
2
+ import { RTCDurableConnection } from "./durable-connection";
3
+ export declare class RondevuContext {
4
+ readonly rondevu: RondevuService;
5
+ readonly rtcConfig: RTCConfiguration;
6
+ private readonly context;
7
+ private readonly connections;
8
+ constructor(rondevu: RondevuService, rtcConfig: RTCConfiguration);
9
+ createConnection(service: string, host?: string, offer?: RTCSessionDescriptionInit): RTCDurableConnection;
10
+ }
@@ -0,0 +1,20 @@
1
+ import { WebRTCContext } from "./webrtc-context";
2
+ import { RTCDurableConnection } from "./durable-connection";
3
+ import { RondevuSignaler } from "./rondevu-signaler";
4
+ export class RondevuContext {
5
+ constructor(rondevu, rtcConfig) {
6
+ this.rondevu = rondevu;
7
+ this.rtcConfig = rtcConfig;
8
+ this.connections = [];
9
+ this.context = new WebRTCContext(rtcConfig);
10
+ }
11
+ createConnection(service, host, offer) {
12
+ const conn = new RTCDurableConnection({
13
+ context: this.context,
14
+ offer,
15
+ signaler: new RondevuSignaler(this.rondevu, service, host)
16
+ });
17
+ this.connections.push(conn);
18
+ return conn;
19
+ }
20
+ }