@xtr-dev/rondevu-client 0.10.2 → 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/README.md CHANGED
@@ -17,10 +17,12 @@ TypeScript/JavaScript client for Rondevu, providing easy-to-use WebRTC connectio
17
17
 
18
18
  - **High-Level Wrappers**: ServiceHost and ServiceClient eliminate WebRTC boilerplate
19
19
  - **Username-Based Discovery**: Connect to peers by username, not complex offer/answer exchange
20
+ - **Semver-Compatible Matching**: Requesting chat@1.0.0 matches any compatible 1.x.x version
21
+ - **Privacy-First Design**: Services are hidden by default - no enumeration possible
20
22
  - **Automatic Reconnection**: Built-in retry logic with exponential backoff
21
23
  - **Message Queuing**: Messages sent while disconnected are queued and flushed on reconnect
22
24
  - **Cryptographic Username Claiming**: Secure ownership with Ed25519 signatures
23
- - **Service Publishing**: Package-style naming (chat.app@1.0.0)
25
+ - **Service Publishing**: Package-style naming (chat.app@1.0.0) with multiple simultaneous offers
24
26
  - **TypeScript**: Full type safety and autocomplete
25
27
  - **Configurable Polling**: Exponential backoff with jitter to reduce server load
26
28
 
@@ -373,7 +375,7 @@ await conn.queueMessage('This will be sent when connected', {
373
375
 
374
376
  ## Migration from v0.9.x
375
377
 
376
- v0.11.0 introduces high-level wrappers and RESTful API changes:
378
+ v0.11.0+ introduces high-level wrappers, RESTful API changes, and semver-compatible discovery:
377
379
 
378
380
  **API Changes:**
379
381
  - Server endpoints restructured (`/usernames/*` → `/users/*`)
@@ -381,6 +383,10 @@ v0.11.0 introduces high-level wrappers and RESTful API changes:
381
383
  - Message queue fully implemented
382
384
  - Configurable polling with exponential backoff
383
385
  - Removed deprecated `cleanup()` methods (use `dispose()`)
386
+ - **v0.11.0+**: Services use `offers` array instead of single `sdp`
387
+ - **v0.11.0+**: Semver-compatible service discovery (chat@1.0.0 matches 1.x.x)
388
+ - **v0.11.0+**: All services are hidden - no listing endpoint
389
+ - **v0.11.0+**: Services support multiple simultaneous offers for connection pooling
384
390
 
385
391
  **Migration Guide:**
386
392
 
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;
@@ -7,7 +7,9 @@ export interface RondevuServiceOptions {
7
7
  }
8
8
  export interface PublishServiceOptions {
9
9
  serviceFqn: string;
10
- sdp: string;
10
+ offers: Array<{
11
+ sdp: string;
12
+ }>;
11
13
  ttl?: number;
12
14
  isPublic?: boolean;
13
15
  metadata?: Record<string, any>;
@@ -36,7 +38,7 @@ export interface PublishServiceOptions {
36
38
  * // Publish a service
37
39
  * const publishedService = await service.publishService({
38
40
  * serviceFqn: 'chat.app@1.0.0',
39
- * sdp: offerSdp,
41
+ * offers: [{ sdp: offerSdp }],
40
42
  * ttl: 300000,
41
43
  * isPublic: true,
42
44
  * })
@@ -23,7 +23,7 @@ import { RondevuAPI } from './api.js';
23
23
  * // Publish a service
24
24
  * const publishedService = await service.publishService({
25
25
  * serviceFqn: 'chat.app@1.0.0',
26
- * sdp: offerSdp,
26
+ * offers: [{ sdp: offerSdp }],
27
27
  * ttl: 300000,
28
28
  * isPublic: true,
29
29
  * })
@@ -86,7 +86,7 @@ export class RondevuService {
86
86
  if (!this.usernameClaimed) {
87
87
  throw new Error('Username not claimed. Call claimUsername() first or the server will reject the service.');
88
88
  }
89
- const { serviceFqn, sdp, ttl, isPublic, metadata } = options;
89
+ const { serviceFqn, offers, ttl, isPublic, metadata } = options;
90
90
  // Generate signature for service publication
91
91
  const message = `publish:${this.username}:${serviceFqn}:${Date.now()}`;
92
92
  const signature = await RondevuAPI.signMessage(message, this.keypair.privateKey);
@@ -94,7 +94,7 @@ export class RondevuService {
94
94
  const serviceRequest = {
95
95
  username: this.username,
96
96
  serviceFqn,
97
- sdp,
97
+ offers,
98
98
  signature,
99
99
  message,
100
100
  ttl,
@@ -62,11 +62,15 @@ export class RondevuSignaler {
62
62
  // Publish service with the offer SDP
63
63
  const publishedService = await this.rondevu.publishService({
64
64
  serviceFqn: this.service,
65
- sdp: offer.sdp,
65
+ offers: [{ sdp: offer.sdp }],
66
66
  ttl: 300000, // 5 minutes
67
67
  isPublic: true,
68
68
  });
69
- this.offerId = publishedService.offerId;
69
+ // Get the first offer from the published service
70
+ if (!publishedService.offers || publishedService.offers.length === 0) {
71
+ throw new Error('No offers returned from service publication');
72
+ }
73
+ this.offerId = publishedService.offers[0].offerId;
70
74
  this.serviceUuid = publishedService.uuid;
71
75
  // Start polling for answer
72
76
  this.startAnswerPolling();
@@ -173,12 +177,19 @@ export class RondevuSignaler {
173
177
  }
174
178
  // Get the first available service (already has full details from searchServices)
175
179
  const service = services[0];
176
- this.offerId = service.offerId;
180
+ // Get the first available offer from the service
181
+ if (!service.offers || service.offers.length === 0) {
182
+ console.warn(`No offers available for service ${this.host}/${this.service}`);
183
+ this.isPolling = false;
184
+ return;
185
+ }
186
+ const firstOffer = service.offers[0];
187
+ this.offerId = firstOffer.offerId;
177
188
  this.serviceUuid = service.uuid;
178
189
  // Notify offer listeners
179
190
  const offer = {
180
191
  type: 'offer',
181
- sdp: service.sdp,
192
+ sdp: firstOffer.sdp,
182
193
  };
183
194
  this.offerListeners.forEach(listener => {
184
195
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xtr-dev/rondevu-client",
3
- "version": "0.10.2",
3
+ "version": "0.11.0",
4
4
  "description": "TypeScript client for Rondevu with durable WebRTC connections, automatic reconnection, and message queuing",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",