@xtr-dev/rondevu-client 0.9.2 → 0.10.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.
Files changed (73) hide show
  1. package/dist/api.d.ts +147 -0
  2. package/dist/api.js +307 -0
  3. package/dist/bin.d.ts +35 -0
  4. package/dist/bin.js +35 -0
  5. package/dist/connection-manager.d.ts +104 -0
  6. package/dist/connection-manager.js +324 -0
  7. package/dist/connection.d.ts +112 -0
  8. package/dist/connection.js +194 -0
  9. package/dist/event-bus.d.ts +52 -0
  10. package/dist/event-bus.js +84 -0
  11. package/dist/index.d.ts +15 -11
  12. package/dist/index.js +9 -11
  13. package/dist/noop-signaler.d.ts +14 -0
  14. package/dist/noop-signaler.js +27 -0
  15. package/dist/rondevu-service.d.ts +81 -0
  16. package/dist/rondevu-service.js +131 -0
  17. package/dist/service-client.d.ts +94 -0
  18. package/dist/service-client.js +186 -0
  19. package/dist/service-host.d.ts +103 -0
  20. package/dist/service-host.js +186 -0
  21. package/dist/signaler.d.ts +25 -0
  22. package/dist/signaler.js +89 -0
  23. package/dist/types.d.ts +33 -0
  24. package/dist/types.js +2 -0
  25. package/dist/webrtc-context.d.ts +7 -0
  26. package/dist/webrtc-context.js +36 -0
  27. package/package.json +16 -2
  28. package/dist/auth.d.ts +0 -20
  29. package/dist/auth.js +0 -41
  30. package/dist/durable/channel.d.ts +0 -115
  31. package/dist/durable/channel.js +0 -301
  32. package/dist/durable/connection.d.ts +0 -125
  33. package/dist/durable/connection.js +0 -370
  34. package/dist/durable/reconnection.d.ts +0 -90
  35. package/dist/durable/reconnection.js +0 -127
  36. package/dist/durable/service.d.ts +0 -103
  37. package/dist/durable/service.js +0 -264
  38. package/dist/durable/types.d.ts +0 -149
  39. package/dist/durable/types.js +0 -28
  40. package/dist/event-emitter.d.ts +0 -54
  41. package/dist/event-emitter.js +0 -102
  42. package/dist/offer-pool.d.ts +0 -86
  43. package/dist/offer-pool.js +0 -145
  44. package/dist/offers.d.ts +0 -101
  45. package/dist/offers.js +0 -202
  46. package/dist/peer/answering-state.d.ts +0 -11
  47. package/dist/peer/answering-state.js +0 -39
  48. package/dist/peer/closed-state.d.ts +0 -8
  49. package/dist/peer/closed-state.js +0 -10
  50. package/dist/peer/connected-state.d.ts +0 -8
  51. package/dist/peer/connected-state.js +0 -11
  52. package/dist/peer/creating-offer-state.d.ts +0 -12
  53. package/dist/peer/creating-offer-state.js +0 -45
  54. package/dist/peer/exchanging-ice-state.d.ts +0 -17
  55. package/dist/peer/exchanging-ice-state.js +0 -64
  56. package/dist/peer/failed-state.d.ts +0 -10
  57. package/dist/peer/failed-state.js +0 -16
  58. package/dist/peer/idle-state.d.ts +0 -7
  59. package/dist/peer/idle-state.js +0 -14
  60. package/dist/peer/index.d.ts +0 -71
  61. package/dist/peer/index.js +0 -176
  62. package/dist/peer/state.d.ts +0 -23
  63. package/dist/peer/state.js +0 -63
  64. package/dist/peer/types.d.ts +0 -43
  65. package/dist/peer/types.js +0 -1
  66. package/dist/peer/waiting-for-answer-state.d.ts +0 -17
  67. package/dist/peer/waiting-for-answer-state.js +0 -60
  68. package/dist/rondevu.d.ts +0 -184
  69. package/dist/rondevu.js +0 -171
  70. package/dist/service-pool.d.ts +0 -123
  71. package/dist/service-pool.js +0 -488
  72. package/dist/usernames.d.ts +0 -79
  73. package/dist/usernames.js +0 -153
@@ -1,10 +0,0 @@
1
- import { PeerState } from './state.js';
2
- /**
3
- * Failed state - connection attempt failed
4
- */
5
- export declare class FailedState extends PeerState {
6
- private error;
7
- constructor(peer: any, error: Error);
8
- get name(): string;
9
- cleanup(): void;
10
- }
@@ -1,16 +0,0 @@
1
- import { PeerState } from './state.js';
2
- /**
3
- * Failed state - connection attempt failed
4
- */
5
- export class FailedState extends PeerState {
6
- constructor(peer, error) {
7
- super(peer);
8
- this.error = error;
9
- peer.emitEvent('failed', error);
10
- }
11
- get name() { return 'failed'; }
12
- cleanup() {
13
- // Connection is failed, clean up resources
14
- this.peer.pc.close();
15
- }
16
- }
@@ -1,7 +0,0 @@
1
- import { PeerState } from './state.js';
2
- import type { PeerOptions } from './types.js';
3
- export declare class IdleState extends PeerState {
4
- get name(): string;
5
- createOffer(options: PeerOptions): Promise<string>;
6
- answer(offerId: string, offerSdp: string, options: PeerOptions): Promise<void>;
7
- }
@@ -1,14 +0,0 @@
1
- import { PeerState } from './state.js';
2
- export class IdleState extends PeerState {
3
- get name() { return 'idle'; }
4
- async createOffer(options) {
5
- const { CreatingOfferState } = await import('./creating-offer-state.js');
6
- this.peer.setState(new CreatingOfferState(this.peer, options));
7
- return this.peer.state.createOffer(options);
8
- }
9
- async answer(offerId, offerSdp, options) {
10
- const { AnsweringState } = await import('./answering-state.js');
11
- this.peer.setState(new AnsweringState(this.peer));
12
- return this.peer.state.answer(offerId, offerSdp, options);
13
- }
14
- }
@@ -1,71 +0,0 @@
1
- import { RondevuOffers } from '../offers.js';
2
- import { EventEmitter } from '../event-emitter.js';
3
- import type { PeerOptions, PeerEvents } from './types.js';
4
- import { PeerState } from './state.js';
5
- export type { PeerTimeouts, PeerOptions, PeerEvents } from './types.js';
6
- /**
7
- * High-level WebRTC peer connection manager with state-based lifecycle
8
- * Handles offer/answer exchange, ICE candidates, timeouts, and error recovery
9
- */
10
- export default class RondevuPeer extends EventEmitter<PeerEvents> {
11
- pc: RTCPeerConnection;
12
- offersApi: RondevuOffers;
13
- offerId?: string;
14
- role?: 'offerer' | 'answerer';
15
- RTCPeerConnection: typeof RTCPeerConnection;
16
- RTCSessionDescription: typeof RTCSessionDescription;
17
- RTCIceCandidate: typeof RTCIceCandidate;
18
- private _state;
19
- private connectionStateChangeHandler?;
20
- private dataChannelHandler?;
21
- private trackHandler?;
22
- private iceCandidateErrorHandler?;
23
- /**
24
- * Current connection state name
25
- */
26
- get stateName(): string;
27
- /**
28
- * Current state object (internal use)
29
- */
30
- get state(): PeerState;
31
- /**
32
- * RTCPeerConnection state
33
- */
34
- get connectionState(): RTCPeerConnectionState;
35
- constructor(offersApi: RondevuOffers, rtcConfig?: RTCConfiguration, existingPeerConnection?: RTCPeerConnection, rtcPeerConnection?: typeof RTCPeerConnection, rtcSessionDescription?: typeof RTCSessionDescription, rtcIceCandidate?: typeof RTCIceCandidate);
36
- /**
37
- * Set up peer connection event handlers
38
- */
39
- private setupPeerConnection;
40
- /**
41
- * Set new state and emit state change event
42
- */
43
- setState(newState: PeerState): void;
44
- /**
45
- * Emit event (exposed for PeerState classes)
46
- * @internal
47
- */
48
- emitEvent<K extends keyof PeerEvents>(event: K, ...args: Parameters<PeerEvents[K]>): void;
49
- /**
50
- * Create an offer and advertise on topics
51
- */
52
- createOffer(options: PeerOptions): Promise<string>;
53
- /**
54
- * Answer an existing offer
55
- */
56
- answer(offerId: string, offerSdp: string, options: PeerOptions): Promise<void>;
57
- /**
58
- * Add a media track to the connection
59
- */
60
- addTrack(track: MediaStreamTrack, ...streams: MediaStream[]): RTCRtpSender;
61
- /**
62
- * Create a data channel for sending and receiving arbitrary data
63
- * This should typically be called by the offerer before creating the offer
64
- * The answerer will receive the channel via the 'datachannel' event
65
- */
66
- createDataChannel(label: string, options?: RTCDataChannelInit): RTCDataChannel;
67
- /**
68
- * Close the connection and clean up
69
- */
70
- close(): Promise<void>;
71
- }
@@ -1,176 +0,0 @@
1
- import { EventEmitter } from '../event-emitter.js';
2
- import { IdleState } from './idle-state.js';
3
- import { ConnectedState } from './connected-state.js';
4
- import { FailedState } from './failed-state.js';
5
- import { ClosedState } from './closed-state.js';
6
- /**
7
- * High-level WebRTC peer connection manager with state-based lifecycle
8
- * Handles offer/answer exchange, ICE candidates, timeouts, and error recovery
9
- */
10
- export default class RondevuPeer extends EventEmitter {
11
- /**
12
- * Current connection state name
13
- */
14
- get stateName() {
15
- return this._state.name;
16
- }
17
- /**
18
- * Current state object (internal use)
19
- */
20
- get state() {
21
- return this._state;
22
- }
23
- /**
24
- * RTCPeerConnection state
25
- */
26
- get connectionState() {
27
- return this.pc.connectionState;
28
- }
29
- constructor(offersApi, rtcConfig = {
30
- iceServers: [
31
- { urls: 'stun:stun.l.google.com:19302' },
32
- { urls: 'stun:stun1.l.google.com:19302' }
33
- ]
34
- }, existingPeerConnection, rtcPeerConnection, rtcSessionDescription, rtcIceCandidate) {
35
- super();
36
- this.offersApi = offersApi;
37
- // Use provided polyfills or fall back to globals
38
- this.RTCPeerConnection = rtcPeerConnection || (typeof globalThis.RTCPeerConnection !== 'undefined'
39
- ? globalThis.RTCPeerConnection
40
- : (() => {
41
- throw new Error('RTCPeerConnection is not available. Please provide it in the Rondevu constructor options for Node.js environments.');
42
- }));
43
- this.RTCSessionDescription = rtcSessionDescription || (typeof globalThis.RTCSessionDescription !== 'undefined'
44
- ? globalThis.RTCSessionDescription
45
- : (() => {
46
- throw new Error('RTCSessionDescription is not available. Please provide it in the Rondevu constructor options for Node.js environments.');
47
- }));
48
- this.RTCIceCandidate = rtcIceCandidate || (typeof globalThis.RTCIceCandidate !== 'undefined'
49
- ? globalThis.RTCIceCandidate
50
- : (() => {
51
- throw new Error('RTCIceCandidate is not available. Please provide it in the Rondevu constructor options for Node.js environments.');
52
- }));
53
- // Use existing peer connection if provided, otherwise create new one
54
- this.pc = existingPeerConnection || new this.RTCPeerConnection(rtcConfig);
55
- this._state = new IdleState(this);
56
- this.setupPeerConnection();
57
- }
58
- /**
59
- * Set up peer connection event handlers
60
- */
61
- setupPeerConnection() {
62
- this.connectionStateChangeHandler = () => {
63
- console.log(`🔌 Connection state changed: ${this.pc.connectionState}`);
64
- switch (this.pc.connectionState) {
65
- case 'connected':
66
- console.log('✅ WebRTC connection established');
67
- this.setState(new ConnectedState(this));
68
- this.emitEvent('connected');
69
- break;
70
- case 'disconnected':
71
- console.log('⚠️ WebRTC connection disconnected');
72
- this.emitEvent('disconnected');
73
- break;
74
- case 'failed':
75
- console.log('❌ WebRTC connection failed');
76
- this.setState(new FailedState(this, new Error('Connection failed')));
77
- break;
78
- case 'closed':
79
- console.log('🔒 WebRTC connection closed');
80
- this.setState(new ClosedState(this));
81
- this.emitEvent('disconnected');
82
- break;
83
- }
84
- };
85
- this.pc.addEventListener('connectionstatechange', this.connectionStateChangeHandler);
86
- // Add ICE connection state logging
87
- const iceConnectionStateHandler = () => {
88
- console.log(`🧊 ICE connection state: ${this.pc.iceConnectionState}`);
89
- };
90
- this.pc.addEventListener('iceconnectionstatechange', iceConnectionStateHandler);
91
- // Add ICE gathering state logging
92
- const iceGatheringStateHandler = () => {
93
- console.log(`🔍 ICE gathering state: ${this.pc.iceGatheringState}`);
94
- };
95
- this.pc.addEventListener('icegatheringstatechange', iceGatheringStateHandler);
96
- this.dataChannelHandler = (event) => {
97
- this.emitEvent('datachannel', event.channel);
98
- };
99
- this.pc.addEventListener('datachannel', this.dataChannelHandler);
100
- this.trackHandler = (event) => {
101
- this.emitEvent('track', event);
102
- };
103
- this.pc.addEventListener('track', this.trackHandler);
104
- this.iceCandidateErrorHandler = (event) => {
105
- const iceError = event;
106
- console.error(`❌ ICE candidate error: ${iceError.errorText || 'Unknown error'}`, {
107
- errorCode: iceError.errorCode,
108
- url: iceError.url,
109
- address: iceError.address,
110
- port: iceError.port
111
- });
112
- };
113
- this.pc.addEventListener('icecandidateerror', this.iceCandidateErrorHandler);
114
- }
115
- /**
116
- * Set new state and emit state change event
117
- */
118
- setState(newState) {
119
- this._state.cleanup();
120
- this._state = newState;
121
- this.emitEvent('state', newState.name);
122
- }
123
- /**
124
- * Emit event (exposed for PeerState classes)
125
- * @internal
126
- */
127
- emitEvent(event, ...args) {
128
- this.emit(event, ...args);
129
- }
130
- /**
131
- * Create an offer and advertise on topics
132
- */
133
- async createOffer(options) {
134
- return this._state.createOffer(options);
135
- }
136
- /**
137
- * Answer an existing offer
138
- */
139
- async answer(offerId, offerSdp, options) {
140
- return this._state.answer(offerId, offerSdp, options);
141
- }
142
- /**
143
- * Add a media track to the connection
144
- */
145
- addTrack(track, ...streams) {
146
- return this.pc.addTrack(track, ...streams);
147
- }
148
- /**
149
- * Create a data channel for sending and receiving arbitrary data
150
- * This should typically be called by the offerer before creating the offer
151
- * The answerer will receive the channel via the 'datachannel' event
152
- */
153
- createDataChannel(label, options) {
154
- return this.pc.createDataChannel(label, options);
155
- }
156
- /**
157
- * Close the connection and clean up
158
- */
159
- async close() {
160
- // Remove RTCPeerConnection event listeners
161
- if (this.connectionStateChangeHandler) {
162
- this.pc.removeEventListener('connectionstatechange', this.connectionStateChangeHandler);
163
- }
164
- if (this.dataChannelHandler) {
165
- this.pc.removeEventListener('datachannel', this.dataChannelHandler);
166
- }
167
- if (this.trackHandler) {
168
- this.pc.removeEventListener('track', this.trackHandler);
169
- }
170
- if (this.iceCandidateErrorHandler) {
171
- this.pc.removeEventListener('icecandidateerror', this.iceCandidateErrorHandler);
172
- }
173
- await this._state.close();
174
- this.removeAllListeners();
175
- }
176
- }
@@ -1,23 +0,0 @@
1
- import type { PeerOptions } from './types.js';
2
- import type RondevuPeer from './index.js';
3
- /**
4
- * Base class for peer connection states
5
- * Implements the State pattern for managing WebRTC connection lifecycle
6
- */
7
- export declare abstract class PeerState {
8
- protected peer: RondevuPeer;
9
- protected iceCandidateHandler?: (event: RTCPeerConnectionIceEvent) => void;
10
- constructor(peer: RondevuPeer);
11
- abstract get name(): string;
12
- createOffer(options: PeerOptions): Promise<string>;
13
- answer(offerId: string, offerSdp: string, options: PeerOptions): Promise<void>;
14
- handleAnswer(sdp: string): Promise<void>;
15
- handleIceCandidate(candidate: any): Promise<void>;
16
- /**
17
- * Setup trickle ICE candidate handler
18
- * Sends local ICE candidates to server as they are discovered
19
- */
20
- protected setupIceCandidateHandler(): void;
21
- cleanup(): void;
22
- close(): Promise<void>;
23
- }
@@ -1,63 +0,0 @@
1
- /**
2
- * Base class for peer connection states
3
- * Implements the State pattern for managing WebRTC connection lifecycle
4
- */
5
- export class PeerState {
6
- constructor(peer) {
7
- this.peer = peer;
8
- }
9
- async createOffer(options) {
10
- throw new Error(`Cannot create offer in ${this.name} state`);
11
- }
12
- async answer(offerId, offerSdp, options) {
13
- throw new Error(`Cannot answer in ${this.name} state`);
14
- }
15
- async handleAnswer(sdp) {
16
- throw new Error(`Cannot handle answer in ${this.name} state`);
17
- }
18
- async handleIceCandidate(candidate) {
19
- // ICE candidates can arrive in multiple states, so default is to add them
20
- if (this.peer.pc.remoteDescription) {
21
- await this.peer.pc.addIceCandidate(new this.peer.RTCIceCandidate(candidate));
22
- }
23
- }
24
- /**
25
- * Setup trickle ICE candidate handler
26
- * Sends local ICE candidates to server as they are discovered
27
- */
28
- setupIceCandidateHandler() {
29
- this.iceCandidateHandler = async (event) => {
30
- if (event.candidate && this.peer.offerId) {
31
- const candidateData = event.candidate.toJSON();
32
- if (candidateData.candidate && candidateData.candidate !== '') {
33
- const type = candidateData.candidate.includes('typ host') ? 'host' :
34
- candidateData.candidate.includes('typ srflx') ? 'srflx' :
35
- candidateData.candidate.includes('typ relay') ? 'relay' : 'unknown';
36
- console.log(`🧊 Generated ${type} ICE candidate:`, candidateData.candidate);
37
- try {
38
- await this.peer.offersApi.addIceCandidates(this.peer.offerId, [candidateData]);
39
- console.log(`✅ Sent ${type} ICE candidate to server`);
40
- }
41
- catch (err) {
42
- console.error(`❌ Error sending ${type} ICE candidate:`, err);
43
- }
44
- }
45
- }
46
- else if (!event.candidate) {
47
- console.log('🧊 ICE gathering complete (null candidate)');
48
- }
49
- };
50
- this.peer.pc.addEventListener('icecandidate', this.iceCandidateHandler);
51
- }
52
- cleanup() {
53
- // Clean up ICE candidate handler if it exists
54
- if (this.iceCandidateHandler) {
55
- this.peer.pc.removeEventListener('icecandidate', this.iceCandidateHandler);
56
- }
57
- }
58
- async close() {
59
- this.cleanup();
60
- const { ClosedState } = await import('./closed-state.js');
61
- this.peer.setState(new ClosedState(this.peer));
62
- }
63
- }
@@ -1,43 +0,0 @@
1
- /**
2
- * Timeout configurations for different connection phases
3
- */
4
- export interface PeerTimeouts {
5
- /** Timeout for ICE gathering (default: 10000ms) */
6
- iceGathering?: number;
7
- /** Timeout for waiting for answer (default: 30000ms) */
8
- waitingForAnswer?: number;
9
- /** Timeout for creating answer (default: 10000ms) */
10
- creatingAnswer?: number;
11
- /** Timeout for ICE connection (default: 30000ms) */
12
- iceConnection?: number;
13
- }
14
- /**
15
- * Options for creating a peer connection
16
- */
17
- export interface PeerOptions {
18
- /** RTCConfiguration for the peer connection */
19
- rtcConfig?: RTCConfiguration;
20
- /** Topics to advertise this connection under */
21
- topics: string[];
22
- /** How long the offer should live (milliseconds) */
23
- ttl?: number;
24
- /** Optional secret to protect the offer (max 128 characters) */
25
- secret?: string;
26
- /** Whether to create a data channel automatically (for offerer) */
27
- createDataChannel?: boolean;
28
- /** Label for the automatically created data channel */
29
- dataChannelLabel?: string;
30
- /** Timeout configurations */
31
- timeouts?: PeerTimeouts;
32
- }
33
- /**
34
- * Events emitted by RondevuPeer
35
- */
36
- export interface PeerEvents extends Record<string, (...args: any[]) => void> {
37
- 'state': (state: string) => void;
38
- 'connected': () => void;
39
- 'disconnected': () => void;
40
- 'failed': (error: Error) => void;
41
- 'datachannel': (channel: RTCDataChannel) => void;
42
- 'track': (event: RTCTrackEvent) => void;
43
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,17 +0,0 @@
1
- import { PeerState } from './state.js';
2
- import type { PeerOptions } from './types.js';
3
- import type RondevuPeer from './index.js';
4
- /**
5
- * Waiting for answer from another peer
6
- */
7
- export declare class WaitingForAnswerState extends PeerState {
8
- private offerId;
9
- private options;
10
- private pollingInterval?;
11
- private timeout?;
12
- constructor(peer: RondevuPeer, offerId: string, options: PeerOptions);
13
- get name(): string;
14
- private startPolling;
15
- handleAnswer(sdp: string): Promise<void>;
16
- cleanup(): void;
17
- }
@@ -1,60 +0,0 @@
1
- import { PeerState } from './state.js';
2
- /**
3
- * Waiting for answer from another peer
4
- */
5
- export class WaitingForAnswerState extends PeerState {
6
- constructor(peer, offerId, options) {
7
- super(peer);
8
- this.offerId = offerId;
9
- this.options = options;
10
- this.startPolling();
11
- }
12
- get name() { return 'waiting-for-answer'; }
13
- startPolling() {
14
- const answerTimeout = this.options.timeouts?.waitingForAnswer || 30000;
15
- this.timeout = setTimeout(async () => {
16
- this.cleanup();
17
- const { FailedState } = await import('./failed-state.js');
18
- this.peer.setState(new FailedState(this.peer, new Error('Timeout waiting for answer')));
19
- }, answerTimeout);
20
- this.pollingInterval = setInterval(async () => {
21
- try {
22
- const answers = await this.peer.offersApi.getAnswers();
23
- const myAnswer = answers.find((a) => a.offerId === this.offerId);
24
- if (myAnswer) {
25
- this.cleanup();
26
- await this.handleAnswer(myAnswer.sdp);
27
- }
28
- }
29
- catch (err) {
30
- console.error('Error polling for answers:', err);
31
- if (err instanceof Error && err.message.includes('not found')) {
32
- this.cleanup();
33
- const { FailedState } = await import('./failed-state.js');
34
- this.peer.setState(new FailedState(this.peer, new Error('Offer expired or not found')));
35
- }
36
- }
37
- }, 2000);
38
- }
39
- async handleAnswer(sdp) {
40
- try {
41
- await this.peer.pc.setRemoteDescription({
42
- type: 'answer',
43
- sdp
44
- });
45
- // Transition to exchanging ICE
46
- const { ExchangingIceState } = await import('./exchanging-ice-state.js');
47
- this.peer.setState(new ExchangingIceState(this.peer, this.offerId, this.options));
48
- }
49
- catch (error) {
50
- const { FailedState } = await import('./failed-state.js');
51
- this.peer.setState(new FailedState(this.peer, error));
52
- }
53
- }
54
- cleanup() {
55
- if (this.pollingInterval)
56
- clearInterval(this.pollingInterval);
57
- if (this.timeout)
58
- clearTimeout(this.timeout);
59
- }
60
- }