@xtr-dev/rondevu-client 0.7.12 → 0.8.1
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 +459 -404
- package/dist/discovery.d.ts +93 -0
- package/dist/discovery.js +164 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +6 -2
- package/dist/offer-pool.d.ts +74 -0
- package/dist/offer-pool.js +119 -0
- package/dist/rondevu.d.ts +16 -1
- package/dist/rondevu.js +29 -2
- package/dist/service-pool.d.ts +115 -0
- package/dist/service-pool.js +339 -0
- package/dist/services.d.ts +79 -0
- package/dist/services.js +206 -0
- package/dist/usernames.d.ts +79 -0
- package/dist/usernames.js +147 -0
- package/package.json +3 -3
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import RondevuPeer from './peer/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Service info from discovery
|
|
4
|
+
*/
|
|
5
|
+
export interface ServiceInfo {
|
|
6
|
+
uuid: string;
|
|
7
|
+
isPublic: boolean;
|
|
8
|
+
serviceFqn?: string;
|
|
9
|
+
metadata?: Record<string, any>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Service list result
|
|
13
|
+
*/
|
|
14
|
+
export interface ServiceListResult {
|
|
15
|
+
username: string;
|
|
16
|
+
services: ServiceInfo[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Service query result
|
|
20
|
+
*/
|
|
21
|
+
export interface ServiceQueryResult {
|
|
22
|
+
uuid: string;
|
|
23
|
+
allowed: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Service details
|
|
27
|
+
*/
|
|
28
|
+
export interface ServiceDetails {
|
|
29
|
+
serviceId: string;
|
|
30
|
+
username: string;
|
|
31
|
+
serviceFqn: string;
|
|
32
|
+
offerId: string;
|
|
33
|
+
sdp: string;
|
|
34
|
+
isPublic: boolean;
|
|
35
|
+
metadata?: Record<string, any>;
|
|
36
|
+
createdAt: number;
|
|
37
|
+
expiresAt: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Connect result
|
|
41
|
+
*/
|
|
42
|
+
export interface ConnectResult {
|
|
43
|
+
peer: RondevuPeer;
|
|
44
|
+
channel: RTCDataChannel;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Rondevu Discovery API
|
|
48
|
+
* Handles service discovery and connections
|
|
49
|
+
*/
|
|
50
|
+
export declare class RondevuDiscovery {
|
|
51
|
+
private baseUrl;
|
|
52
|
+
private credentials;
|
|
53
|
+
private offersApi;
|
|
54
|
+
constructor(baseUrl: string, credentials: {
|
|
55
|
+
peerId: string;
|
|
56
|
+
secret: string;
|
|
57
|
+
});
|
|
58
|
+
/**
|
|
59
|
+
* Lists all services for a username
|
|
60
|
+
* Returns UUIDs only for private services, full details for public
|
|
61
|
+
*/
|
|
62
|
+
listServices(username: string): Promise<ServiceListResult>;
|
|
63
|
+
/**
|
|
64
|
+
* Queries a service by FQN
|
|
65
|
+
* Returns UUID if service exists and is allowed
|
|
66
|
+
*/
|
|
67
|
+
queryService(username: string, serviceFqn: string): Promise<ServiceQueryResult>;
|
|
68
|
+
/**
|
|
69
|
+
* Gets service details by UUID
|
|
70
|
+
*/
|
|
71
|
+
getServiceDetails(uuid: string): Promise<ServiceDetails>;
|
|
72
|
+
/**
|
|
73
|
+
* Connects to a service by UUID
|
|
74
|
+
*/
|
|
75
|
+
connectToService(uuid: string, options?: {
|
|
76
|
+
rtcConfig?: RTCConfiguration;
|
|
77
|
+
onConnected?: () => void;
|
|
78
|
+
onData?: (data: any) => void;
|
|
79
|
+
}): Promise<RondevuPeer>;
|
|
80
|
+
/**
|
|
81
|
+
* Convenience method: Query and connect in one call
|
|
82
|
+
* Returns both peer and data channel
|
|
83
|
+
*/
|
|
84
|
+
connect(username: string, serviceFqn: string, options?: {
|
|
85
|
+
rtcConfig?: RTCConfiguration;
|
|
86
|
+
}): Promise<ConnectResult>;
|
|
87
|
+
/**
|
|
88
|
+
* Convenience method: Connect to service by UUID with channel
|
|
89
|
+
*/
|
|
90
|
+
connectByUuid(uuid: string, options?: {
|
|
91
|
+
rtcConfig?: RTCConfiguration;
|
|
92
|
+
}): Promise<ConnectResult>;
|
|
93
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import RondevuPeer from './peer/index.js';
|
|
2
|
+
import { RondevuOffers } from './offers.js';
|
|
3
|
+
/**
|
|
4
|
+
* Rondevu Discovery API
|
|
5
|
+
* Handles service discovery and connections
|
|
6
|
+
*/
|
|
7
|
+
export class RondevuDiscovery {
|
|
8
|
+
constructor(baseUrl, credentials) {
|
|
9
|
+
this.baseUrl = baseUrl;
|
|
10
|
+
this.credentials = credentials;
|
|
11
|
+
this.offersApi = new RondevuOffers(baseUrl, credentials);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Lists all services for a username
|
|
15
|
+
* Returns UUIDs only for private services, full details for public
|
|
16
|
+
*/
|
|
17
|
+
async listServices(username) {
|
|
18
|
+
const response = await fetch(`${this.baseUrl}/usernames/${username}/services`);
|
|
19
|
+
if (!response.ok) {
|
|
20
|
+
throw new Error('Failed to list services');
|
|
21
|
+
}
|
|
22
|
+
const data = await response.json();
|
|
23
|
+
return {
|
|
24
|
+
username: data.username,
|
|
25
|
+
services: data.services
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Queries a service by FQN
|
|
30
|
+
* Returns UUID if service exists and is allowed
|
|
31
|
+
*/
|
|
32
|
+
async queryService(username, serviceFqn) {
|
|
33
|
+
const response = await fetch(`${this.baseUrl}/index/${username}/query`, {
|
|
34
|
+
method: 'POST',
|
|
35
|
+
headers: { 'Content-Type': 'application/json' },
|
|
36
|
+
body: JSON.stringify({ serviceFqn })
|
|
37
|
+
});
|
|
38
|
+
if (!response.ok) {
|
|
39
|
+
const error = await response.json();
|
|
40
|
+
throw new Error(error.error || 'Service not found');
|
|
41
|
+
}
|
|
42
|
+
const data = await response.json();
|
|
43
|
+
return {
|
|
44
|
+
uuid: data.uuid,
|
|
45
|
+
allowed: data.allowed
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Gets service details by UUID
|
|
50
|
+
*/
|
|
51
|
+
async getServiceDetails(uuid) {
|
|
52
|
+
const response = await fetch(`${this.baseUrl}/services/${uuid}`);
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
const error = await response.json();
|
|
55
|
+
throw new Error(error.error || 'Service not found');
|
|
56
|
+
}
|
|
57
|
+
const data = await response.json();
|
|
58
|
+
return {
|
|
59
|
+
serviceId: data.serviceId,
|
|
60
|
+
username: data.username,
|
|
61
|
+
serviceFqn: data.serviceFqn,
|
|
62
|
+
offerId: data.offerId,
|
|
63
|
+
sdp: data.sdp,
|
|
64
|
+
isPublic: data.isPublic,
|
|
65
|
+
metadata: data.metadata,
|
|
66
|
+
createdAt: data.createdAt,
|
|
67
|
+
expiresAt: data.expiresAt
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Connects to a service by UUID
|
|
72
|
+
*/
|
|
73
|
+
async connectToService(uuid, options) {
|
|
74
|
+
// Get service details
|
|
75
|
+
const service = await this.getServiceDetails(uuid);
|
|
76
|
+
// Create peer with the offer
|
|
77
|
+
const peer = new RondevuPeer(this.offersApi, options?.rtcConfig || {
|
|
78
|
+
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
|
|
79
|
+
});
|
|
80
|
+
// Set up event handlers
|
|
81
|
+
if (options?.onConnected) {
|
|
82
|
+
peer.on('connected', options.onConnected);
|
|
83
|
+
}
|
|
84
|
+
if (options?.onData) {
|
|
85
|
+
peer.on('datachannel', (channel) => {
|
|
86
|
+
channel.onmessage = (e) => options.onData(e.data);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
// Answer the offer
|
|
90
|
+
await peer.answer(service.offerId, service.sdp, {
|
|
91
|
+
topics: [], // V2 doesn't use topics
|
|
92
|
+
rtcConfig: options?.rtcConfig
|
|
93
|
+
});
|
|
94
|
+
return peer;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Convenience method: Query and connect in one call
|
|
98
|
+
* Returns both peer and data channel
|
|
99
|
+
*/
|
|
100
|
+
async connect(username, serviceFqn, options) {
|
|
101
|
+
// Query service
|
|
102
|
+
const query = await this.queryService(username, serviceFqn);
|
|
103
|
+
if (!query.allowed) {
|
|
104
|
+
throw new Error('Service access denied');
|
|
105
|
+
}
|
|
106
|
+
// Get service details
|
|
107
|
+
const service = await this.getServiceDetails(query.uuid);
|
|
108
|
+
// Create peer
|
|
109
|
+
const peer = new RondevuPeer(this.offersApi, options?.rtcConfig || {
|
|
110
|
+
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
|
|
111
|
+
});
|
|
112
|
+
// Answer the offer
|
|
113
|
+
await peer.answer(service.offerId, service.sdp, {
|
|
114
|
+
topics: [], // V2 doesn't use topics
|
|
115
|
+
rtcConfig: options?.rtcConfig
|
|
116
|
+
});
|
|
117
|
+
// Wait for data channel
|
|
118
|
+
const channel = await new Promise((resolve, reject) => {
|
|
119
|
+
const timeout = setTimeout(() => {
|
|
120
|
+
reject(new Error('Timeout waiting for data channel'));
|
|
121
|
+
}, 30000);
|
|
122
|
+
peer.on('datachannel', (ch) => {
|
|
123
|
+
clearTimeout(timeout);
|
|
124
|
+
resolve(ch);
|
|
125
|
+
});
|
|
126
|
+
peer.on('failed', (error) => {
|
|
127
|
+
clearTimeout(timeout);
|
|
128
|
+
reject(error);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
return { peer, channel };
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Convenience method: Connect to service by UUID with channel
|
|
135
|
+
*/
|
|
136
|
+
async connectByUuid(uuid, options) {
|
|
137
|
+
// Get service details
|
|
138
|
+
const service = await this.getServiceDetails(uuid);
|
|
139
|
+
// Create peer
|
|
140
|
+
const peer = new RondevuPeer(this.offersApi, options?.rtcConfig || {
|
|
141
|
+
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
|
|
142
|
+
});
|
|
143
|
+
// Answer the offer
|
|
144
|
+
await peer.answer(service.offerId, service.sdp, {
|
|
145
|
+
topics: [], // V2 doesn't use topics
|
|
146
|
+
rtcConfig: options?.rtcConfig
|
|
147
|
+
});
|
|
148
|
+
// Wait for data channel
|
|
149
|
+
const channel = await new Promise((resolve, reject) => {
|
|
150
|
+
const timeout = setTimeout(() => {
|
|
151
|
+
reject(new Error('Timeout waiting for data channel'));
|
|
152
|
+
}, 30000);
|
|
153
|
+
peer.on('datachannel', (ch) => {
|
|
154
|
+
clearTimeout(timeout);
|
|
155
|
+
resolve(ch);
|
|
156
|
+
});
|
|
157
|
+
peer.on('failed', (error) => {
|
|
158
|
+
clearTimeout(timeout);
|
|
159
|
+
reject(error);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
return { peer, channel };
|
|
163
|
+
}
|
|
164
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,12 @@ export { RondevuAuth } from './auth.js';
|
|
|
8
8
|
export type { Credentials, FetchFunction } from './auth.js';
|
|
9
9
|
export { RondevuOffers } from './offers.js';
|
|
10
10
|
export type { CreateOfferRequest, Offer, IceCandidate, TopicInfo } from './offers.js';
|
|
11
|
-
export { BloomFilter } from './bloom.js';
|
|
12
11
|
export { default as RondevuPeer } from './peer/index.js';
|
|
13
12
|
export type { PeerOptions, PeerEvents, PeerTimeouts } from './peer/index.js';
|
|
13
|
+
export { RondevuUsername } from './usernames.js';
|
|
14
|
+
export type { UsernameClaimResult, UsernameCheckResult } from './usernames.js';
|
|
15
|
+
export { RondevuServices } from './services.js';
|
|
16
|
+
export type { ServicePublishResult, PublishServiceOptions, ServiceHandle } from './services.js';
|
|
17
|
+
export { RondevuDiscovery } from './discovery.js';
|
|
18
|
+
export type { ServiceInfo, ServiceListResult, ServiceQueryResult, ServiceDetails, ConnectResult } from './discovery.js';
|
|
19
|
+
export type { PoolStatus, PooledServiceHandle } from './service-pool.js';
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,11 @@ export { Rondevu } from './rondevu.js';
|
|
|
8
8
|
export { RondevuAuth } from './auth.js';
|
|
9
9
|
// Export offers API
|
|
10
10
|
export { RondevuOffers } from './offers.js';
|
|
11
|
-
// Export bloom filter
|
|
12
|
-
export { BloomFilter } from './bloom.js';
|
|
13
11
|
// Export peer manager
|
|
14
12
|
export { default as RondevuPeer } from './peer/index.js';
|
|
13
|
+
// Export username API
|
|
14
|
+
export { RondevuUsername } from './usernames.js';
|
|
15
|
+
// Export services API
|
|
16
|
+
export { RondevuServices } from './services.js';
|
|
17
|
+
// Export discovery API
|
|
18
|
+
export { RondevuDiscovery } from './discovery.js';
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { RondevuOffers, Offer } from './offers.js';
|
|
2
|
+
/**
|
|
3
|
+
* Represents an offer that has been answered
|
|
4
|
+
*/
|
|
5
|
+
export interface AnsweredOffer {
|
|
6
|
+
offerId: string;
|
|
7
|
+
answererId: string;
|
|
8
|
+
sdp: string;
|
|
9
|
+
answeredAt: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Configuration options for the offer pool
|
|
13
|
+
*/
|
|
14
|
+
export interface OfferPoolOptions {
|
|
15
|
+
/** Number of simultaneous open offers to maintain */
|
|
16
|
+
poolSize: number;
|
|
17
|
+
/** Polling interval in milliseconds (default: 2000ms) */
|
|
18
|
+
pollingInterval?: number;
|
|
19
|
+
/** Callback invoked when an offer is answered */
|
|
20
|
+
onAnswered: (answer: AnsweredOffer) => Promise<void>;
|
|
21
|
+
/** Callback to create new offers when refilling the pool */
|
|
22
|
+
onRefill: (count: number) => Promise<Offer[]>;
|
|
23
|
+
/** Error handler for pool operations */
|
|
24
|
+
onError: (error: Error, context: string) => void;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Manages a pool of offers with automatic polling and refill
|
|
28
|
+
*
|
|
29
|
+
* The OfferPool maintains a configurable number of simultaneous offers,
|
|
30
|
+
* polls for answers periodically, and automatically refills the pool
|
|
31
|
+
* when offers are consumed.
|
|
32
|
+
*/
|
|
33
|
+
export declare class OfferPool {
|
|
34
|
+
private offersApi;
|
|
35
|
+
private options;
|
|
36
|
+
private offers;
|
|
37
|
+
private polling;
|
|
38
|
+
private pollingTimer?;
|
|
39
|
+
private lastPollTime;
|
|
40
|
+
private readonly pollingInterval;
|
|
41
|
+
constructor(offersApi: RondevuOffers, options: OfferPoolOptions);
|
|
42
|
+
/**
|
|
43
|
+
* Add offers to the pool
|
|
44
|
+
*/
|
|
45
|
+
addOffers(offers: Offer[]): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Start polling for answers
|
|
48
|
+
*/
|
|
49
|
+
start(): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Stop polling for answers
|
|
52
|
+
*/
|
|
53
|
+
stop(): Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Poll for answers and refill the pool if needed
|
|
56
|
+
*/
|
|
57
|
+
private poll;
|
|
58
|
+
/**
|
|
59
|
+
* Get the current number of active offers in the pool
|
|
60
|
+
*/
|
|
61
|
+
getActiveOfferCount(): number;
|
|
62
|
+
/**
|
|
63
|
+
* Get all active offer IDs
|
|
64
|
+
*/
|
|
65
|
+
getActiveOfferIds(): string[];
|
|
66
|
+
/**
|
|
67
|
+
* Get the last poll timestamp
|
|
68
|
+
*/
|
|
69
|
+
getLastPollTime(): number;
|
|
70
|
+
/**
|
|
71
|
+
* Check if the pool is currently polling
|
|
72
|
+
*/
|
|
73
|
+
isPolling(): boolean;
|
|
74
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manages a pool of offers with automatic polling and refill
|
|
3
|
+
*
|
|
4
|
+
* The OfferPool maintains a configurable number of simultaneous offers,
|
|
5
|
+
* polls for answers periodically, and automatically refills the pool
|
|
6
|
+
* when offers are consumed.
|
|
7
|
+
*/
|
|
8
|
+
export class OfferPool {
|
|
9
|
+
constructor(offersApi, options) {
|
|
10
|
+
this.offersApi = offersApi;
|
|
11
|
+
this.options = options;
|
|
12
|
+
this.offers = new Map();
|
|
13
|
+
this.polling = false;
|
|
14
|
+
this.lastPollTime = 0;
|
|
15
|
+
this.pollingInterval = options.pollingInterval || 2000;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Add offers to the pool
|
|
19
|
+
*/
|
|
20
|
+
async addOffers(offers) {
|
|
21
|
+
for (const offer of offers) {
|
|
22
|
+
this.offers.set(offer.id, offer);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Start polling for answers
|
|
27
|
+
*/
|
|
28
|
+
async start() {
|
|
29
|
+
if (this.polling) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
this.polling = true;
|
|
33
|
+
// Do an immediate poll
|
|
34
|
+
await this.poll().catch((error) => {
|
|
35
|
+
this.options.onError(error, 'initial-poll');
|
|
36
|
+
});
|
|
37
|
+
// Start polling interval
|
|
38
|
+
this.pollingTimer = setInterval(async () => {
|
|
39
|
+
if (this.polling) {
|
|
40
|
+
await this.poll().catch((error) => {
|
|
41
|
+
this.options.onError(error, 'poll');
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}, this.pollingInterval);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Stop polling for answers
|
|
48
|
+
*/
|
|
49
|
+
async stop() {
|
|
50
|
+
this.polling = false;
|
|
51
|
+
if (this.pollingTimer) {
|
|
52
|
+
clearInterval(this.pollingTimer);
|
|
53
|
+
this.pollingTimer = undefined;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Poll for answers and refill the pool if needed
|
|
58
|
+
*/
|
|
59
|
+
async poll() {
|
|
60
|
+
try {
|
|
61
|
+
// Get all answers from server
|
|
62
|
+
const answers = await this.offersApi.getAnswers();
|
|
63
|
+
// Filter for our pool's offers
|
|
64
|
+
const myAnswers = answers.filter(a => this.offers.has(a.offerId));
|
|
65
|
+
// Process each answer
|
|
66
|
+
for (const answer of myAnswers) {
|
|
67
|
+
// Notify ServicePool
|
|
68
|
+
await this.options.onAnswered({
|
|
69
|
+
offerId: answer.offerId,
|
|
70
|
+
answererId: answer.answererId,
|
|
71
|
+
sdp: answer.sdp,
|
|
72
|
+
answeredAt: answer.answeredAt
|
|
73
|
+
});
|
|
74
|
+
// Remove consumed offer from pool
|
|
75
|
+
this.offers.delete(answer.offerId);
|
|
76
|
+
}
|
|
77
|
+
// Immediate refill if below pool size
|
|
78
|
+
if (this.offers.size < this.options.poolSize) {
|
|
79
|
+
const needed = this.options.poolSize - this.offers.size;
|
|
80
|
+
try {
|
|
81
|
+
const newOffers = await this.options.onRefill(needed);
|
|
82
|
+
await this.addOffers(newOffers);
|
|
83
|
+
}
|
|
84
|
+
catch (refillError) {
|
|
85
|
+
this.options.onError(refillError, 'refill');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
this.lastPollTime = Date.now();
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
// Don't crash the pool on errors - let error handler deal with it
|
|
92
|
+
this.options.onError(error, 'poll');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Get the current number of active offers in the pool
|
|
97
|
+
*/
|
|
98
|
+
getActiveOfferCount() {
|
|
99
|
+
return this.offers.size;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Get all active offer IDs
|
|
103
|
+
*/
|
|
104
|
+
getActiveOfferIds() {
|
|
105
|
+
return Array.from(this.offers.keys());
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Get the last poll timestamp
|
|
109
|
+
*/
|
|
110
|
+
getLastPollTime() {
|
|
111
|
+
return this.lastPollTime;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Check if the pool is currently polling
|
|
115
|
+
*/
|
|
116
|
+
isPolling() {
|
|
117
|
+
return this.polling;
|
|
118
|
+
}
|
|
119
|
+
}
|
package/dist/rondevu.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { RondevuAuth, Credentials, FetchFunction } from './auth.js';
|
|
2
2
|
import { RondevuOffers } from './offers.js';
|
|
3
|
+
import { RondevuUsername } from './usernames.js';
|
|
4
|
+
import { RondevuServices } from './services.js';
|
|
5
|
+
import { RondevuDiscovery } from './discovery.js';
|
|
3
6
|
import RondevuPeer from './peer/index.js';
|
|
4
7
|
export interface RondevuOptions {
|
|
5
8
|
/**
|
|
@@ -58,7 +61,10 @@ export interface RondevuOptions {
|
|
|
58
61
|
}
|
|
59
62
|
export declare class Rondevu {
|
|
60
63
|
readonly auth: RondevuAuth;
|
|
64
|
+
readonly usernames: RondevuUsername;
|
|
61
65
|
private _offers?;
|
|
66
|
+
private _services?;
|
|
67
|
+
private _discovery?;
|
|
62
68
|
private credentials?;
|
|
63
69
|
private baseUrl;
|
|
64
70
|
private fetchFn?;
|
|
@@ -67,9 +73,18 @@ export declare class Rondevu {
|
|
|
67
73
|
private rtcIceCandidate?;
|
|
68
74
|
constructor(options?: RondevuOptions);
|
|
69
75
|
/**
|
|
70
|
-
* Get offers API (requires authentication)
|
|
76
|
+
* Get offers API (low-level access, requires authentication)
|
|
77
|
+
* For most use cases, use services and discovery APIs instead
|
|
71
78
|
*/
|
|
72
79
|
get offers(): RondevuOffers;
|
|
80
|
+
/**
|
|
81
|
+
* Get services API (requires authentication)
|
|
82
|
+
*/
|
|
83
|
+
get services(): RondevuServices;
|
|
84
|
+
/**
|
|
85
|
+
* Get discovery API (requires authentication)
|
|
86
|
+
*/
|
|
87
|
+
get discovery(): RondevuDiscovery;
|
|
73
88
|
/**
|
|
74
89
|
* Register and initialize authenticated client
|
|
75
90
|
* Generates a cryptographically random peer ID (128-bit)
|
package/dist/rondevu.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { RondevuAuth } from './auth.js';
|
|
2
2
|
import { RondevuOffers } from './offers.js';
|
|
3
|
+
import { RondevuUsername } from './usernames.js';
|
|
4
|
+
import { RondevuServices } from './services.js';
|
|
5
|
+
import { RondevuDiscovery } from './discovery.js';
|
|
3
6
|
import RondevuPeer from './peer/index.js';
|
|
4
7
|
export class Rondevu {
|
|
5
8
|
constructor(options = {}) {
|
|
@@ -9,13 +12,17 @@ export class Rondevu {
|
|
|
9
12
|
this.rtcSessionDescription = options.RTCSessionDescription;
|
|
10
13
|
this.rtcIceCandidate = options.RTCIceCandidate;
|
|
11
14
|
this.auth = new RondevuAuth(this.baseUrl, this.fetchFn);
|
|
15
|
+
this.usernames = new RondevuUsername(this.baseUrl);
|
|
12
16
|
if (options.credentials) {
|
|
13
17
|
this.credentials = options.credentials;
|
|
14
18
|
this._offers = new RondevuOffers(this.baseUrl, this.credentials, this.fetchFn);
|
|
19
|
+
this._services = new RondevuServices(this.baseUrl, this.credentials);
|
|
20
|
+
this._discovery = new RondevuDiscovery(this.baseUrl, this.credentials);
|
|
15
21
|
}
|
|
16
22
|
}
|
|
17
23
|
/**
|
|
18
|
-
* Get offers API (requires authentication)
|
|
24
|
+
* Get offers API (low-level access, requires authentication)
|
|
25
|
+
* For most use cases, use services and discovery APIs instead
|
|
19
26
|
*/
|
|
20
27
|
get offers() {
|
|
21
28
|
if (!this._offers) {
|
|
@@ -23,14 +30,34 @@ export class Rondevu {
|
|
|
23
30
|
}
|
|
24
31
|
return this._offers;
|
|
25
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Get services API (requires authentication)
|
|
35
|
+
*/
|
|
36
|
+
get services() {
|
|
37
|
+
if (!this._services) {
|
|
38
|
+
throw new Error('Not authenticated. Call register() first or provide credentials.');
|
|
39
|
+
}
|
|
40
|
+
return this._services;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Get discovery API (requires authentication)
|
|
44
|
+
*/
|
|
45
|
+
get discovery() {
|
|
46
|
+
if (!this._discovery) {
|
|
47
|
+
throw new Error('Not authenticated. Call register() first or provide credentials.');
|
|
48
|
+
}
|
|
49
|
+
return this._discovery;
|
|
50
|
+
}
|
|
26
51
|
/**
|
|
27
52
|
* Register and initialize authenticated client
|
|
28
53
|
* Generates a cryptographically random peer ID (128-bit)
|
|
29
54
|
*/
|
|
30
55
|
async register() {
|
|
31
56
|
this.credentials = await this.auth.register();
|
|
32
|
-
// Create
|
|
57
|
+
// Create API instances
|
|
33
58
|
this._offers = new RondevuOffers(this.baseUrl, this.credentials, this.fetchFn);
|
|
59
|
+
this._services = new RondevuServices(this.baseUrl, this.credentials);
|
|
60
|
+
this._discovery = new RondevuDiscovery(this.baseUrl, this.credentials);
|
|
34
61
|
return this.credentials;
|
|
35
62
|
}
|
|
36
63
|
/**
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import RondevuPeer from './peer/index.js';
|
|
2
|
+
import { ServiceHandle } from './services.js';
|
|
3
|
+
/**
|
|
4
|
+
* Status information about the pool
|
|
5
|
+
*/
|
|
6
|
+
export interface PoolStatus {
|
|
7
|
+
/** Number of active offers in the pool */
|
|
8
|
+
activeOffers: number;
|
|
9
|
+
/** Number of currently connected peers */
|
|
10
|
+
activeConnections: number;
|
|
11
|
+
/** Total number of connections handled since start */
|
|
12
|
+
totalConnectionsHandled: number;
|
|
13
|
+
/** Number of failed offer creation attempts */
|
|
14
|
+
failedOfferCreations: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Configuration options for a pooled service
|
|
18
|
+
*/
|
|
19
|
+
export interface ServicePoolOptions {
|
|
20
|
+
/** Username that owns the service */
|
|
21
|
+
username: string;
|
|
22
|
+
/** Private key for signing service operations */
|
|
23
|
+
privateKey: string;
|
|
24
|
+
/** Fully qualified service name (e.g., com.example.chat@1.0.0) */
|
|
25
|
+
serviceFqn: string;
|
|
26
|
+
/** WebRTC configuration */
|
|
27
|
+
rtcConfig?: RTCConfiguration;
|
|
28
|
+
/** Whether the service is publicly discoverable */
|
|
29
|
+
isPublic?: boolean;
|
|
30
|
+
/** Optional metadata for the service */
|
|
31
|
+
metadata?: Record<string, any>;
|
|
32
|
+
/** Time-to-live for offers in milliseconds */
|
|
33
|
+
ttl?: number;
|
|
34
|
+
/** Handler invoked for each new connection */
|
|
35
|
+
handler: (channel: RTCDataChannel, peer: RondevuPeer, connectionId: string) => void;
|
|
36
|
+
/** Number of simultaneous open offers to maintain (default: 1) */
|
|
37
|
+
poolSize?: number;
|
|
38
|
+
/** Polling interval in milliseconds (default: 2000ms) */
|
|
39
|
+
pollingInterval?: number;
|
|
40
|
+
/** Callback for pool status updates */
|
|
41
|
+
onPoolStatus?: (status: PoolStatus) => void;
|
|
42
|
+
/** Error handler for pool operations */
|
|
43
|
+
onError?: (error: Error, context: string) => void;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Extended service handle with pool-specific methods
|
|
47
|
+
*/
|
|
48
|
+
export interface PooledServiceHandle extends ServiceHandle {
|
|
49
|
+
/** Get current pool status */
|
|
50
|
+
getStatus: () => PoolStatus;
|
|
51
|
+
/** Manually add offers to the pool */
|
|
52
|
+
addOffers: (count: number) => Promise<void>;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Manages a pooled service with multiple concurrent connections
|
|
56
|
+
*
|
|
57
|
+
* ServicePool coordinates offer creation, answer polling, and connection
|
|
58
|
+
* management for services that need to handle multiple simultaneous connections.
|
|
59
|
+
*/
|
|
60
|
+
export declare class ServicePool {
|
|
61
|
+
private baseUrl;
|
|
62
|
+
private credentials;
|
|
63
|
+
private options;
|
|
64
|
+
private offerPool?;
|
|
65
|
+
private connections;
|
|
66
|
+
private status;
|
|
67
|
+
private serviceId?;
|
|
68
|
+
private uuid?;
|
|
69
|
+
private offersApi;
|
|
70
|
+
private usernameApi;
|
|
71
|
+
constructor(baseUrl: string, credentials: {
|
|
72
|
+
peerId: string;
|
|
73
|
+
secret: string;
|
|
74
|
+
}, options: ServicePoolOptions);
|
|
75
|
+
/**
|
|
76
|
+
* Start the pooled service
|
|
77
|
+
*/
|
|
78
|
+
start(): Promise<PooledServiceHandle>;
|
|
79
|
+
/**
|
|
80
|
+
* Stop the pooled service and clean up
|
|
81
|
+
*/
|
|
82
|
+
stop(): Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* Handle an answered offer by setting up the connection
|
|
85
|
+
*/
|
|
86
|
+
private handleConnection;
|
|
87
|
+
/**
|
|
88
|
+
* Create multiple offers
|
|
89
|
+
*/
|
|
90
|
+
private createOffers;
|
|
91
|
+
/**
|
|
92
|
+
* Publish the initial service (creates first offer)
|
|
93
|
+
*/
|
|
94
|
+
private publishInitialService;
|
|
95
|
+
/**
|
|
96
|
+
* Manually add offers to the pool
|
|
97
|
+
*/
|
|
98
|
+
private manualRefill;
|
|
99
|
+
/**
|
|
100
|
+
* Get current pool status
|
|
101
|
+
*/
|
|
102
|
+
private getStatus;
|
|
103
|
+
/**
|
|
104
|
+
* Update status and notify listeners
|
|
105
|
+
*/
|
|
106
|
+
private updateStatus;
|
|
107
|
+
/**
|
|
108
|
+
* Handle errors
|
|
109
|
+
*/
|
|
110
|
+
private handleError;
|
|
111
|
+
/**
|
|
112
|
+
* Generate a unique connection ID
|
|
113
|
+
*/
|
|
114
|
+
private generateConnectionId;
|
|
115
|
+
}
|