@xtr-dev/rondevu-client 0.12.4 → 0.13.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 +49 -21
- package/dist/api.js +40 -38
- package/dist/index.d.ts +3 -12
- package/dist/index.js +1 -7
- package/dist/rondevu-signaler.d.ts +4 -5
- package/dist/rondevu-signaler.js +23 -39
- package/dist/rondevu.d.ts +167 -0
- package/dist/{rondevu-service.js → rondevu.js} +119 -51
- package/dist/types.d.ts +8 -21
- package/dist/types.js +4 -6
- package/package.json +1 -1
- package/dist/bin.d.ts +0 -35
- package/dist/bin.js +0 -35
- package/dist/connection-manager.d.ts +0 -104
- package/dist/connection-manager.js +0 -324
- package/dist/connection.d.ts +0 -112
- package/dist/connection.js +0 -194
- package/dist/durable-connection.d.ts +0 -120
- package/dist/durable-connection.js +0 -244
- package/dist/event-bus.d.ts +0 -52
- package/dist/event-bus.js +0 -84
- package/dist/noop-signaler.d.ts +0 -14
- package/dist/noop-signaler.js +0 -27
- package/dist/quick-start.d.ts +0 -29
- package/dist/quick-start.js +0 -44
- package/dist/rondevu-context.d.ts +0 -10
- package/dist/rondevu-context.js +0 -20
- package/dist/rondevu-service.d.ts +0 -87
- package/dist/service-client.d.ts +0 -77
- package/dist/service-client.js +0 -158
- package/dist/service-host.d.ts +0 -67
- package/dist/service-host.js +0 -120
- package/dist/signaler.d.ts +0 -25
- package/dist/signaler.js +0 -89
- package/dist/webrtc-context.d.ts +0 -5
- package/dist/webrtc-context.js +0 -35
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { RondevuAPI, Credentials, Keypair, Service } from './api.js';
|
|
2
|
-
export interface RondevuServiceOptions {
|
|
3
|
-
apiUrl: string;
|
|
4
|
-
username: string;
|
|
5
|
-
keypair?: Keypair;
|
|
6
|
-
credentials?: Credentials;
|
|
7
|
-
}
|
|
8
|
-
export interface PublishServiceOptions {
|
|
9
|
-
serviceFqn: string;
|
|
10
|
-
offers: Array<{
|
|
11
|
-
sdp: string;
|
|
12
|
-
}>;
|
|
13
|
-
ttl?: number;
|
|
14
|
-
isPublic?: boolean;
|
|
15
|
-
metadata?: Record<string, any>;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* RondevuService - High-level service management with automatic signature handling
|
|
19
|
-
*
|
|
20
|
-
* Provides a simplified API for:
|
|
21
|
-
* - Username claiming with Ed25519 signatures
|
|
22
|
-
* - Service publishing with automatic signature generation
|
|
23
|
-
* - Keypair management
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* ```typescript
|
|
27
|
-
* // Initialize service (generates keypair automatically)
|
|
28
|
-
* const service = new RondevuService({
|
|
29
|
-
* apiUrl: 'https://signal.example.com',
|
|
30
|
-
* username: 'myusername',
|
|
31
|
-
* })
|
|
32
|
-
*
|
|
33
|
-
* await service.initialize()
|
|
34
|
-
*
|
|
35
|
-
* // Claim username (one time)
|
|
36
|
-
* await service.claimUsername()
|
|
37
|
-
*
|
|
38
|
-
* // Publish a service
|
|
39
|
-
* const publishedService = await service.publishService({
|
|
40
|
-
* serviceFqn: 'chat.app@1.0.0',
|
|
41
|
-
* offers: [{ sdp: offerSdp }],
|
|
42
|
-
* ttl: 300000,
|
|
43
|
-
* isPublic: true,
|
|
44
|
-
* })
|
|
45
|
-
* ```
|
|
46
|
-
*/
|
|
47
|
-
export declare class RondevuService {
|
|
48
|
-
private readonly api;
|
|
49
|
-
private readonly username;
|
|
50
|
-
private keypair;
|
|
51
|
-
private usernameClaimed;
|
|
52
|
-
constructor(options: RondevuServiceOptions);
|
|
53
|
-
/**
|
|
54
|
-
* Initialize the service - generates keypair if not provided
|
|
55
|
-
* Call this before using other methods
|
|
56
|
-
*/
|
|
57
|
-
initialize(): Promise<void>;
|
|
58
|
-
/**
|
|
59
|
-
* Claim the username with Ed25519 signature
|
|
60
|
-
* Should be called once before publishing services
|
|
61
|
-
*/
|
|
62
|
-
claimUsername(): Promise<void>;
|
|
63
|
-
/**
|
|
64
|
-
* Publish a service with automatic signature generation
|
|
65
|
-
*/
|
|
66
|
-
publishService(options: PublishServiceOptions): Promise<Service>;
|
|
67
|
-
/**
|
|
68
|
-
* Get the current keypair (for backup/storage)
|
|
69
|
-
*/
|
|
70
|
-
getKeypair(): Keypair | null;
|
|
71
|
-
/**
|
|
72
|
-
* Get the username
|
|
73
|
-
*/
|
|
74
|
-
getUsername(): string;
|
|
75
|
-
/**
|
|
76
|
-
* Get the public key
|
|
77
|
-
*/
|
|
78
|
-
getPublicKey(): string | null;
|
|
79
|
-
/**
|
|
80
|
-
* Check if username has been claimed (checks with server)
|
|
81
|
-
*/
|
|
82
|
-
isUsernameClaimed(): Promise<boolean>;
|
|
83
|
-
/**
|
|
84
|
-
* Access to underlying API for advanced operations
|
|
85
|
-
*/
|
|
86
|
-
getAPI(): RondevuAPI;
|
|
87
|
-
}
|
package/dist/service-client.d.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { RondevuService } from './rondevu-service.js';
|
|
2
|
-
import { RTCDurableConnection } from './durable-connection.js';
|
|
3
|
-
import { EventBus } from './event-bus.js';
|
|
4
|
-
export interface ServiceClientOptions {
|
|
5
|
-
username: string;
|
|
6
|
-
serviceFqn: string;
|
|
7
|
-
rondevuService: RondevuService;
|
|
8
|
-
autoReconnect?: boolean;
|
|
9
|
-
maxReconnectAttempts?: number;
|
|
10
|
-
rtcConfiguration?: RTCConfiguration;
|
|
11
|
-
}
|
|
12
|
-
export interface ServiceClientEvents {
|
|
13
|
-
connected: RTCDurableConnection;
|
|
14
|
-
disconnected: void;
|
|
15
|
-
reconnecting: {
|
|
16
|
-
attempt: number;
|
|
17
|
-
maxAttempts: number;
|
|
18
|
-
};
|
|
19
|
-
error: Error;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* ServiceClient - High-level wrapper for connecting to a WebRTC service
|
|
23
|
-
*
|
|
24
|
-
* Simplifies client connection by handling:
|
|
25
|
-
* - Service discovery
|
|
26
|
-
* - Offer/answer exchange
|
|
27
|
-
* - ICE candidate polling
|
|
28
|
-
* - Automatic reconnection
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* ```typescript
|
|
32
|
-
* const client = new ServiceClient({
|
|
33
|
-
* username: 'host-user',
|
|
34
|
-
* serviceFqn: 'chat.app@1.0.0',
|
|
35
|
-
* rondevuService: myService
|
|
36
|
-
* })
|
|
37
|
-
*
|
|
38
|
-
* client.events.on('connected', conn => {
|
|
39
|
-
* conn.events.on('message', msg => console.log('Received:', msg))
|
|
40
|
-
* conn.sendMessage('Hello from client!')
|
|
41
|
-
* })
|
|
42
|
-
*
|
|
43
|
-
* await client.connect()
|
|
44
|
-
* ```
|
|
45
|
-
*/
|
|
46
|
-
export declare class ServiceClient {
|
|
47
|
-
private options;
|
|
48
|
-
events: EventBus<ServiceClientEvents>;
|
|
49
|
-
private signaler;
|
|
50
|
-
private webrtcContext;
|
|
51
|
-
private connection;
|
|
52
|
-
private autoReconnect;
|
|
53
|
-
private maxReconnectAttempts;
|
|
54
|
-
private reconnectAttempts;
|
|
55
|
-
private isConnecting;
|
|
56
|
-
constructor(options: ServiceClientOptions);
|
|
57
|
-
/**
|
|
58
|
-
* Connect to the service
|
|
59
|
-
*/
|
|
60
|
-
connect(): Promise<RTCDurableConnection>;
|
|
61
|
-
/**
|
|
62
|
-
* Disconnect from the service
|
|
63
|
-
*/
|
|
64
|
-
dispose(): void;
|
|
65
|
-
/**
|
|
66
|
-
* @deprecated Use dispose() instead
|
|
67
|
-
*/
|
|
68
|
-
disconnect(): void;
|
|
69
|
-
/**
|
|
70
|
-
* Attempt to reconnect
|
|
71
|
-
*/
|
|
72
|
-
private attemptReconnect;
|
|
73
|
-
/**
|
|
74
|
-
* Get the current connection
|
|
75
|
-
*/
|
|
76
|
-
getConnection(): RTCDurableConnection | null;
|
|
77
|
-
}
|
package/dist/service-client.js
DELETED
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
import { RondevuSignaler } from './rondevu-signaler.js';
|
|
2
|
-
import { WebRTCContext } from './webrtc-context.js';
|
|
3
|
-
import { RTCDurableConnection } from './durable-connection.js';
|
|
4
|
-
import { EventBus } from './event-bus.js';
|
|
5
|
-
/**
|
|
6
|
-
* ServiceClient - High-level wrapper for connecting to a WebRTC service
|
|
7
|
-
*
|
|
8
|
-
* Simplifies client connection by handling:
|
|
9
|
-
* - Service discovery
|
|
10
|
-
* - Offer/answer exchange
|
|
11
|
-
* - ICE candidate polling
|
|
12
|
-
* - Automatic reconnection
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```typescript
|
|
16
|
-
* const client = new ServiceClient({
|
|
17
|
-
* username: 'host-user',
|
|
18
|
-
* serviceFqn: 'chat.app@1.0.0',
|
|
19
|
-
* rondevuService: myService
|
|
20
|
-
* })
|
|
21
|
-
*
|
|
22
|
-
* client.events.on('connected', conn => {
|
|
23
|
-
* conn.events.on('message', msg => console.log('Received:', msg))
|
|
24
|
-
* conn.sendMessage('Hello from client!')
|
|
25
|
-
* })
|
|
26
|
-
*
|
|
27
|
-
* await client.connect()
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
export class ServiceClient {
|
|
31
|
-
constructor(options) {
|
|
32
|
-
this.options = options;
|
|
33
|
-
this.signaler = null;
|
|
34
|
-
this.connection = null;
|
|
35
|
-
this.reconnectAttempts = 0;
|
|
36
|
-
this.isConnecting = false;
|
|
37
|
-
this.events = new EventBus();
|
|
38
|
-
this.webrtcContext = new WebRTCContext(options.rtcConfiguration);
|
|
39
|
-
this.autoReconnect = options.autoReconnect !== undefined ? options.autoReconnect : true;
|
|
40
|
-
this.maxReconnectAttempts = options.maxReconnectAttempts || 5;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Connect to the service
|
|
44
|
-
*/
|
|
45
|
-
async connect() {
|
|
46
|
-
if (this.isConnecting) {
|
|
47
|
-
throw new Error('Connection already in progress');
|
|
48
|
-
}
|
|
49
|
-
if (this.connection) {
|
|
50
|
-
throw new Error('Already connected. Disconnect first.');
|
|
51
|
-
}
|
|
52
|
-
this.isConnecting = true;
|
|
53
|
-
try {
|
|
54
|
-
// Create signaler
|
|
55
|
-
this.signaler = new RondevuSignaler(this.options.rondevuService, this.options.serviceFqn, this.options.username);
|
|
56
|
-
// Wait for remote offer from signaler
|
|
57
|
-
const remoteOffer = await new Promise((resolve, reject) => {
|
|
58
|
-
const timeout = setTimeout(() => {
|
|
59
|
-
reject(new Error('Service discovery timeout'));
|
|
60
|
-
}, 30000);
|
|
61
|
-
this.signaler.addOfferListener((offer) => {
|
|
62
|
-
clearTimeout(timeout);
|
|
63
|
-
resolve(offer);
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
// Create connection with remote offer (makes us the answerer)
|
|
67
|
-
const connection = new RTCDurableConnection({
|
|
68
|
-
context: this.webrtcContext,
|
|
69
|
-
signaler: this.signaler,
|
|
70
|
-
offer: remoteOffer
|
|
71
|
-
});
|
|
72
|
-
// Wait for connection to be ready
|
|
73
|
-
await connection.ready;
|
|
74
|
-
// Set up connection event listeners
|
|
75
|
-
connection.events.on('state-change', (state) => {
|
|
76
|
-
if (state === 'connected') {
|
|
77
|
-
this.reconnectAttempts = 0;
|
|
78
|
-
this.events.emit('connected', connection);
|
|
79
|
-
}
|
|
80
|
-
else if (state === 'disconnected') {
|
|
81
|
-
this.events.emit('disconnected', undefined);
|
|
82
|
-
if (this.autoReconnect && this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
83
|
-
this.attemptReconnect();
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
this.connection = connection;
|
|
88
|
-
this.isConnecting = false;
|
|
89
|
-
return connection;
|
|
90
|
-
}
|
|
91
|
-
catch (err) {
|
|
92
|
-
this.isConnecting = false;
|
|
93
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
94
|
-
this.events.emit('error', error);
|
|
95
|
-
throw error;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Disconnect from the service
|
|
100
|
-
*/
|
|
101
|
-
dispose() {
|
|
102
|
-
if (this.signaler) {
|
|
103
|
-
this.signaler.dispose();
|
|
104
|
-
this.signaler = null;
|
|
105
|
-
}
|
|
106
|
-
if (this.connection) {
|
|
107
|
-
this.connection.disconnect();
|
|
108
|
-
this.connection = null;
|
|
109
|
-
}
|
|
110
|
-
this.isConnecting = false;
|
|
111
|
-
this.reconnectAttempts = 0;
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* @deprecated Use dispose() instead
|
|
115
|
-
*/
|
|
116
|
-
disconnect() {
|
|
117
|
-
this.dispose();
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Attempt to reconnect
|
|
121
|
-
*/
|
|
122
|
-
async attemptReconnect() {
|
|
123
|
-
this.reconnectAttempts++;
|
|
124
|
-
this.events.emit('reconnecting', {
|
|
125
|
-
attempt: this.reconnectAttempts,
|
|
126
|
-
maxAttempts: this.maxReconnectAttempts
|
|
127
|
-
});
|
|
128
|
-
// Cleanup old connection
|
|
129
|
-
if (this.signaler) {
|
|
130
|
-
this.signaler.dispose();
|
|
131
|
-
this.signaler = null;
|
|
132
|
-
}
|
|
133
|
-
if (this.connection) {
|
|
134
|
-
this.connection = null;
|
|
135
|
-
}
|
|
136
|
-
// Wait a bit before reconnecting
|
|
137
|
-
await new Promise(resolve => setTimeout(resolve, 1000 * this.reconnectAttempts));
|
|
138
|
-
try {
|
|
139
|
-
await this.connect();
|
|
140
|
-
}
|
|
141
|
-
catch (err) {
|
|
142
|
-
console.error('Reconnection attempt failed:', err);
|
|
143
|
-
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
144
|
-
this.attemptReconnect();
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
const error = new Error('Max reconnection attempts reached');
|
|
148
|
-
this.events.emit('error', error);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Get the current connection
|
|
154
|
-
*/
|
|
155
|
-
getConnection() {
|
|
156
|
-
return this.connection;
|
|
157
|
-
}
|
|
158
|
-
}
|
package/dist/service-host.d.ts
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { RondevuService } from './rondevu-service.js';
|
|
2
|
-
import { RTCDurableConnection } from './durable-connection.js';
|
|
3
|
-
import { EventBus } from './event-bus.js';
|
|
4
|
-
export interface ServiceHostOptions {
|
|
5
|
-
service: string;
|
|
6
|
-
rondevuService: RondevuService;
|
|
7
|
-
maxPeers?: number;
|
|
8
|
-
ttl?: number;
|
|
9
|
-
isPublic?: boolean;
|
|
10
|
-
rtcConfiguration?: RTCConfiguration;
|
|
11
|
-
metadata?: Record<string, any>;
|
|
12
|
-
}
|
|
13
|
-
export interface ServiceHostEvents {
|
|
14
|
-
connection: RTCDurableConnection;
|
|
15
|
-
error: Error;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* ServiceHost - High-level wrapper for hosting a WebRTC service
|
|
19
|
-
*
|
|
20
|
-
* Simplifies hosting by handling:
|
|
21
|
-
* - Offer/answer exchange
|
|
22
|
-
* - ICE candidate polling
|
|
23
|
-
* - Connection pool management
|
|
24
|
-
* - Automatic reconnection
|
|
25
|
-
*
|
|
26
|
-
* @example
|
|
27
|
-
* ```typescript
|
|
28
|
-
* const host = new ServiceHost({
|
|
29
|
-
* service: 'chat.app@1.0.0',
|
|
30
|
-
* rondevuService: myService,
|
|
31
|
-
* maxPeers: 5
|
|
32
|
-
* })
|
|
33
|
-
*
|
|
34
|
-
* host.events.on('connection', conn => {
|
|
35
|
-
* conn.events.on('message', msg => console.log('Received:', msg))
|
|
36
|
-
* conn.sendMessage('Hello!')
|
|
37
|
-
* })
|
|
38
|
-
*
|
|
39
|
-
* await host.start()
|
|
40
|
-
* ```
|
|
41
|
-
*/
|
|
42
|
-
export declare class ServiceHost {
|
|
43
|
-
private options;
|
|
44
|
-
events: EventBus<ServiceHostEvents>;
|
|
45
|
-
private signaler;
|
|
46
|
-
private webrtcContext;
|
|
47
|
-
private connections;
|
|
48
|
-
private maxPeers;
|
|
49
|
-
private running;
|
|
50
|
-
constructor(options: ServiceHostOptions);
|
|
51
|
-
/**
|
|
52
|
-
* Start hosting the service
|
|
53
|
-
*/
|
|
54
|
-
start(): Promise<void>;
|
|
55
|
-
/**
|
|
56
|
-
* Create the next connection for incoming peers
|
|
57
|
-
*/
|
|
58
|
-
private createNextConnection;
|
|
59
|
-
/**
|
|
60
|
-
* Stop hosting the service
|
|
61
|
-
*/
|
|
62
|
-
dispose(): void;
|
|
63
|
-
/**
|
|
64
|
-
* Get all active connections
|
|
65
|
-
*/
|
|
66
|
-
getConnections(): RTCDurableConnection[];
|
|
67
|
-
}
|
package/dist/service-host.js
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import { RondevuSignaler } from './rondevu-signaler.js';
|
|
2
|
-
import { WebRTCContext } from './webrtc-context.js';
|
|
3
|
-
import { RTCDurableConnection } from './durable-connection.js';
|
|
4
|
-
import { EventBus } from './event-bus.js';
|
|
5
|
-
/**
|
|
6
|
-
* ServiceHost - High-level wrapper for hosting a WebRTC service
|
|
7
|
-
*
|
|
8
|
-
* Simplifies hosting by handling:
|
|
9
|
-
* - Offer/answer exchange
|
|
10
|
-
* - ICE candidate polling
|
|
11
|
-
* - Connection pool management
|
|
12
|
-
* - Automatic reconnection
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```typescript
|
|
16
|
-
* const host = new ServiceHost({
|
|
17
|
-
* service: 'chat.app@1.0.0',
|
|
18
|
-
* rondevuService: myService,
|
|
19
|
-
* maxPeers: 5
|
|
20
|
-
* })
|
|
21
|
-
*
|
|
22
|
-
* host.events.on('connection', conn => {
|
|
23
|
-
* conn.events.on('message', msg => console.log('Received:', msg))
|
|
24
|
-
* conn.sendMessage('Hello!')
|
|
25
|
-
* })
|
|
26
|
-
*
|
|
27
|
-
* await host.start()
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
export class ServiceHost {
|
|
31
|
-
constructor(options) {
|
|
32
|
-
this.options = options;
|
|
33
|
-
this.signaler = null;
|
|
34
|
-
this.connections = [];
|
|
35
|
-
this.running = false;
|
|
36
|
-
this.events = new EventBus();
|
|
37
|
-
this.webrtcContext = new WebRTCContext(options.rtcConfiguration);
|
|
38
|
-
this.maxPeers = options.maxPeers || 5;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Start hosting the service
|
|
42
|
-
*/
|
|
43
|
-
async start() {
|
|
44
|
-
if (this.running) {
|
|
45
|
-
throw new Error('ServiceHost already running');
|
|
46
|
-
}
|
|
47
|
-
this.running = true;
|
|
48
|
-
// Create signaler
|
|
49
|
-
this.signaler = new RondevuSignaler(this.options.rondevuService, this.options.service);
|
|
50
|
-
// Create first connection (offerer)
|
|
51
|
-
const connection = new RTCDurableConnection({
|
|
52
|
-
context: this.webrtcContext,
|
|
53
|
-
signaler: this.signaler,
|
|
54
|
-
offer: null // null means we're the offerer
|
|
55
|
-
});
|
|
56
|
-
// Wait for connection to be ready
|
|
57
|
-
await connection.ready;
|
|
58
|
-
// Set up connection event listeners
|
|
59
|
-
connection.events.on('state-change', (state) => {
|
|
60
|
-
if (state === 'connected') {
|
|
61
|
-
this.connections.push(connection);
|
|
62
|
-
this.events.emit('connection', connection);
|
|
63
|
-
// Create next connection if under maxPeers
|
|
64
|
-
if (this.connections.length < this.maxPeers) {
|
|
65
|
-
this.createNextConnection().catch(err => {
|
|
66
|
-
console.error('Failed to create next connection:', err);
|
|
67
|
-
this.events.emit('error', err);
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
else if (state === 'disconnected') {
|
|
72
|
-
// Remove from connections list
|
|
73
|
-
const index = this.connections.indexOf(connection);
|
|
74
|
-
if (index > -1) {
|
|
75
|
-
this.connections.splice(index, 1);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
// Publish service with the offer
|
|
80
|
-
const offer = connection.connection?.localDescription;
|
|
81
|
-
if (!offer?.sdp) {
|
|
82
|
-
throw new Error('Offer SDP is empty');
|
|
83
|
-
}
|
|
84
|
-
await this.signaler.setOffer(offer);
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Create the next connection for incoming peers
|
|
88
|
-
*/
|
|
89
|
-
async createNextConnection() {
|
|
90
|
-
if (!this.signaler || !this.running) {
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
// For now, we'll use the same offer for all connections
|
|
94
|
-
// In a production scenario, you'd create multiple offers
|
|
95
|
-
// This is a limitation of the current service model
|
|
96
|
-
// which publishes one offer per service
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Stop hosting the service
|
|
100
|
-
*/
|
|
101
|
-
dispose() {
|
|
102
|
-
this.running = false;
|
|
103
|
-
// Cleanup signaler
|
|
104
|
-
if (this.signaler) {
|
|
105
|
-
this.signaler.dispose();
|
|
106
|
-
this.signaler = null;
|
|
107
|
-
}
|
|
108
|
-
// Disconnect all connections
|
|
109
|
-
for (const conn of this.connections) {
|
|
110
|
-
conn.disconnect();
|
|
111
|
-
}
|
|
112
|
-
this.connections = [];
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Get all active connections
|
|
116
|
-
*/
|
|
117
|
-
getConnections() {
|
|
118
|
-
return [...this.connections];
|
|
119
|
-
}
|
|
120
|
-
}
|
package/dist/signaler.d.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { Signaler } from './types.js';
|
|
2
|
-
import { Binnable } from './bin.js';
|
|
3
|
-
import { RondevuAPI } from './api.js';
|
|
4
|
-
/**
|
|
5
|
-
* RondevuSignaler - Handles ICE candidate exchange via Rondevu API
|
|
6
|
-
* Uses polling to retrieve remote candidates
|
|
7
|
-
*/
|
|
8
|
-
export declare class RondevuSignaler implements Signaler {
|
|
9
|
-
private api;
|
|
10
|
-
private offerId;
|
|
11
|
-
constructor(api: RondevuAPI, offerId: string);
|
|
12
|
-
addOfferListener(callback: (offer: RTCSessionDescriptionInit) => void): Binnable;
|
|
13
|
-
addAnswerListener(callback: (answer: RTCSessionDescriptionInit) => void): Binnable;
|
|
14
|
-
setOffer(offer: RTCSessionDescriptionInit): Promise<void>;
|
|
15
|
-
setAnswer(answer: RTCSessionDescriptionInit): Promise<void>;
|
|
16
|
-
/**
|
|
17
|
-
* Send a local ICE candidate to signaling server
|
|
18
|
-
*/
|
|
19
|
-
addIceCandidate(candidate: RTCIceCandidate): Promise<void>;
|
|
20
|
-
/**
|
|
21
|
-
* Poll for remote ICE candidates and call callback for each one
|
|
22
|
-
* Returns cleanup function to stop polling
|
|
23
|
-
*/
|
|
24
|
-
addListener(callback: (candidate: RTCIceCandidate) => void): Binnable;
|
|
25
|
-
}
|
package/dist/signaler.js
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RondevuSignaler - Handles ICE candidate exchange via Rondevu API
|
|
3
|
-
* Uses polling to retrieve remote candidates
|
|
4
|
-
*/
|
|
5
|
-
export class RondevuSignaler {
|
|
6
|
-
constructor(api, offerId) {
|
|
7
|
-
this.api = api;
|
|
8
|
-
this.offerId = offerId;
|
|
9
|
-
}
|
|
10
|
-
addOfferListener(callback) {
|
|
11
|
-
throw new Error('Method not implemented.');
|
|
12
|
-
}
|
|
13
|
-
addAnswerListener(callback) {
|
|
14
|
-
throw new Error('Method not implemented.');
|
|
15
|
-
}
|
|
16
|
-
setOffer(offer) {
|
|
17
|
-
throw new Error('Method not implemented.');
|
|
18
|
-
}
|
|
19
|
-
setAnswer(answer) {
|
|
20
|
-
throw new Error('Method not implemented.');
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Send a local ICE candidate to signaling server
|
|
24
|
-
*/
|
|
25
|
-
async addIceCandidate(candidate) {
|
|
26
|
-
const candidateData = candidate.toJSON();
|
|
27
|
-
// Skip empty candidates
|
|
28
|
-
if (!candidateData.candidate || candidateData.candidate === '') {
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
await this.api.addIceCandidates(this.offerId, [candidateData]);
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Poll for remote ICE candidates and call callback for each one
|
|
35
|
-
* Returns cleanup function to stop polling
|
|
36
|
-
*/
|
|
37
|
-
addListener(callback) {
|
|
38
|
-
let lastTimestamp = 0;
|
|
39
|
-
let polling = true;
|
|
40
|
-
const poll = async () => {
|
|
41
|
-
while (polling) {
|
|
42
|
-
try {
|
|
43
|
-
const candidates = await this.api.getIceCandidates(this.offerId, lastTimestamp);
|
|
44
|
-
// Process each candidate
|
|
45
|
-
for (const item of candidates) {
|
|
46
|
-
if (item.candidate &&
|
|
47
|
-
item.candidate.candidate &&
|
|
48
|
-
item.candidate.candidate !== '') {
|
|
49
|
-
try {
|
|
50
|
-
const rtcCandidate = new RTCIceCandidate(item.candidate);
|
|
51
|
-
callback(rtcCandidate);
|
|
52
|
-
lastTimestamp = item.createdAt;
|
|
53
|
-
}
|
|
54
|
-
catch (err) {
|
|
55
|
-
console.warn('Failed to process ICE candidate:', err);
|
|
56
|
-
lastTimestamp = item.createdAt;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
lastTimestamp = item.createdAt;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
catch (err) {
|
|
65
|
-
// If offer not found or expired, stop polling
|
|
66
|
-
if (err instanceof Error &&
|
|
67
|
-
(err.message.includes('404') || err.message.includes('410'))) {
|
|
68
|
-
console.warn('Offer not found or expired, stopping ICE polling');
|
|
69
|
-
polling = false;
|
|
70
|
-
break;
|
|
71
|
-
}
|
|
72
|
-
console.error('Error polling for ICE candidates:', err);
|
|
73
|
-
}
|
|
74
|
-
// Poll every second
|
|
75
|
-
if (polling) {
|
|
76
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
// Start polling in the background
|
|
81
|
-
poll().then(() => {
|
|
82
|
-
console.log('ICE polling started');
|
|
83
|
-
});
|
|
84
|
-
// Return cleanup function
|
|
85
|
-
return () => {
|
|
86
|
-
polling = false;
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
}
|
package/dist/webrtc-context.d.ts
DELETED
package/dist/webrtc-context.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
const DEFAULT_RTC_CONFIGURATION = {
|
|
2
|
-
iceServers: [
|
|
3
|
-
{
|
|
4
|
-
urls: 'stun:stun.relay.metered.ca:80',
|
|
5
|
-
},
|
|
6
|
-
{
|
|
7
|
-
urls: 'turn:standard.relay.metered.ca:80',
|
|
8
|
-
username: 'c53a9c971da5e6f3bc959d8d',
|
|
9
|
-
credential: 'QaccPqtPPaxyokXp',
|
|
10
|
-
},
|
|
11
|
-
{
|
|
12
|
-
urls: 'turn:standard.relay.metered.ca:80?transport=tcp',
|
|
13
|
-
username: 'c53a9c971da5e6f3bc959d8d',
|
|
14
|
-
credential: 'QaccPqtPPaxyokXp',
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
urls: 'turn:standard.relay.metered.ca:443',
|
|
18
|
-
username: 'c53a9c971da5e6f3bc959d8d',
|
|
19
|
-
credential: 'QaccPqtPPaxyokXp',
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
urls: 'turns:standard.relay.metered.ca:443?transport=tcp',
|
|
23
|
-
username: 'c53a9c971da5e6f3bc959d8d',
|
|
24
|
-
credential: 'QaccPqtPPaxyokXp',
|
|
25
|
-
},
|
|
26
|
-
],
|
|
27
|
-
};
|
|
28
|
-
export class WebRTCContext {
|
|
29
|
-
constructor(config) {
|
|
30
|
-
this.config = config;
|
|
31
|
-
}
|
|
32
|
-
createPeerConnection() {
|
|
33
|
-
return new RTCPeerConnection(this.config || DEFAULT_RTC_CONFIGURATION);
|
|
34
|
-
}
|
|
35
|
-
}
|