node-rtc-connection 1.0.12 → 1.0.15

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.
@@ -1,346 +0,0 @@
1
- const EventEmitter = require('events');
2
-
3
- /**
4
- * RTCDataChannel represents a bidirectional data channel between two peers.
5
- * Ported from Chromium's implementation, DataChannel-only functionality.
6
- */
7
- class RTCDataChannel extends EventEmitter {
8
- constructor(nativeChannel, peerConnectionHandler) {
9
- super();
10
-
11
- this._nativeChannel = nativeChannel;
12
- this._peerConnectionHandler = peerConnectionHandler;
13
- this._state = 'connecting';
14
- this._binaryType = 'arraybuffer';
15
- this._bufferedAmount = 0;
16
- this._bufferedAmountLowThreshold = 0;
17
- this._closed = false;
18
- this._scheduledEvents = [];
19
-
20
- // Setup native channel observers
21
- this._setupObservers();
22
- }
23
-
24
- /**
25
- * The label specified when creating the data channel
26
- */
27
- get label() {
28
- return this._nativeChannel ? this._nativeChannel.label : '';
29
- }
30
-
31
- /**
32
- * Whether the channel is ordered or allows out-of-order delivery
33
- */
34
- get ordered() {
35
- return this._nativeChannel ? this._nativeChannel.ordered : true;
36
- }
37
-
38
- /**
39
- * Maximum packet lifetime in milliseconds
40
- */
41
- get maxPacketLifeTime() {
42
- if (!this._nativeChannel) return null;
43
- const lifetime = this._nativeChannel.maxPacketLifeTime;
44
- return lifetime >= 0 ? lifetime : null;
45
- }
46
-
47
- /**
48
- * Maximum number of retransmit attempts
49
- */
50
- get maxRetransmits() {
51
- if (!this._nativeChannel) return null;
52
- const retransmits = this._nativeChannel.maxRetransmits;
53
- return retransmits >= 0 ? retransmits : null;
54
- }
55
-
56
- /**
57
- * Subprotocol name
58
- */
59
- get protocol() {
60
- return this._nativeChannel ? this._nativeChannel.protocol : '';
61
- }
62
-
63
- /**
64
- * Whether the channel was negotiated by the application or the WebRTC layer
65
- */
66
- get negotiated() {
67
- return this._nativeChannel ? this._nativeChannel.negotiated : false;
68
- }
69
-
70
- /**
71
- * The ID for this data channel
72
- */
73
- get id() {
74
- if (!this._nativeChannel) return null;
75
- const channelId = this._nativeChannel.id;
76
- return channelId >= 0 ? channelId : null;
77
- }
78
-
79
- /**
80
- * The state of the data channel
81
- * Values: 'connecting', 'open', 'closing', 'closed'
82
- */
83
- get readyState() {
84
- return this._state;
85
- }
86
-
87
- /**
88
- * The number of bytes currently queued to be sent
89
- */
90
- get bufferedAmount() {
91
- return this._bufferedAmount;
92
- }
93
-
94
- /**
95
- * Threshold for the bufferedAmount at which bufferedamountlow event fires
96
- */
97
- get bufferedAmountLowThreshold() {
98
- return this._bufferedAmountLowThreshold;
99
- }
100
-
101
- set bufferedAmountLowThreshold(value) {
102
- this._bufferedAmountLowThreshold = value;
103
- }
104
-
105
- /**
106
- * Format for received binary data: 'blob' or 'arraybuffer'
107
- */
108
- get binaryType() {
109
- return this._binaryType;
110
- }
111
-
112
- set binaryType(value) {
113
- if (value !== 'blob' && value !== 'arraybuffer') {
114
- throw new Error('binaryType must be either "blob" or "arraybuffer"');
115
- }
116
- this._binaryType = value;
117
- }
118
-
119
- /**
120
- * Send data over the channel
121
- * @param {string|ArrayBuffer|ArrayBufferView} data - Data to send
122
- */
123
- send(data) {
124
- if (this._state !== 'open') {
125
- throw new Error('RTCDataChannel.send() called on a channel that is not open');
126
- }
127
-
128
- if (!this._nativeChannel) {
129
- throw new Error('Native data channel is not available');
130
- }
131
-
132
- let buffer;
133
- let isBinary = false;
134
-
135
- if (typeof data === 'string') {
136
- buffer = Buffer.from(data, 'utf8');
137
- isBinary = false;
138
- } else if (data instanceof ArrayBuffer) {
139
- buffer = Buffer.from(data);
140
- isBinary = true;
141
- } else if (ArrayBuffer.isView(data)) {
142
- buffer = Buffer.from(data.buffer, data.byteOffset, data.byteLength);
143
- isBinary = true;
144
- } else {
145
- throw new Error('Unsupported data type');
146
- }
147
-
148
- const length = buffer.length;
149
-
150
- // Validate send length
151
- if (length > 65536) { // Maximum WebRTC message size
152
- throw new Error('Message too long');
153
- }
154
-
155
- this._bufferedAmount += length;
156
-
157
- // Send through native channel
158
- try {
159
- this._nativeChannel.send({
160
- data: buffer,
161
- binary: isBinary
162
- });
163
- } catch (error) {
164
- throw new Error(`Failed to send data: ${error.message}`);
165
- }
166
- }
167
-
168
- /**
169
- * Close the data channel
170
- */
171
- close() {
172
- if (this._closed) {
173
- return;
174
- }
175
-
176
- this._closed = true;
177
-
178
- if (this._nativeChannel) {
179
- this._nativeChannel.close();
180
- }
181
-
182
- if (this._state === 'closing' || this._state === 'closed') {
183
- return;
184
- }
185
-
186
- this._setState('closing');
187
- }
188
-
189
- /**
190
- * Setup observers for the native channel
191
- * @private
192
- */
193
- _setupObservers() {
194
- if (!this._nativeChannel) {
195
- return;
196
- }
197
-
198
- // State change observer
199
- this._nativeChannel.on('statechange', (state) => {
200
- this._onStateChange(state);
201
- });
202
-
203
- // Message observer
204
- this._nativeChannel.on('message', (buffer) => {
205
- this._onMessage(buffer);
206
- });
207
-
208
- // Buffered amount change observer
209
- this._nativeChannel.on('bufferedamountlow', (amount) => {
210
- this._onBufferedAmountChange(amount);
211
- });
212
-
213
- // Error observer
214
- this._nativeChannel.on('error', (error) => {
215
- this.emit('error', error);
216
- });
217
- }
218
-
219
- /**
220
- * Handle state change from native channel
221
- * @private
222
- */
223
- _onStateChange(state) {
224
- let newState;
225
- let shouldEmitOpen = false;
226
- let shouldEmitClosing = false;
227
- let shouldEmitClose = false;
228
-
229
- switch (state) {
230
- case 0: // kConnecting
231
- newState = 'connecting';
232
- break;
233
- case 1: // kOpen
234
- newState = 'open';
235
- shouldEmitOpen = true;
236
- break;
237
- case 2: // kClosing
238
- newState = 'closing';
239
- shouldEmitClosing = true;
240
- break;
241
- case 3: // kClosed
242
- newState = 'closed';
243
- shouldEmitClose = true;
244
- break;
245
- default:
246
- return;
247
- }
248
-
249
- // Only update state if it actually changed
250
- if (this._readyState !== newState) {
251
- this._setState(newState);
252
- }
253
-
254
- // Emit events AFTER state is updated
255
- if (shouldEmitOpen) {
256
- this.emit('open');
257
- }
258
- if (shouldEmitClosing) {
259
- this.emit('closing');
260
- }
261
- if (shouldEmitClose) {
262
- this.emit('close');
263
- }
264
- }
265
-
266
- /**
267
- * Handle incoming message from native channel
268
- * @private
269
- */
270
- _onMessage(buffer) {
271
- if (this._state !== 'open') {
272
- return;
273
- }
274
-
275
- let data;
276
- if (buffer.binary) {
277
- if (this._binaryType === 'arraybuffer') {
278
- data = buffer.data.buffer.slice(
279
- buffer.data.byteOffset,
280
- buffer.data.byteOffset + buffer.data.byteLength
281
- );
282
- } else {
283
- // For 'blob' type, we'll just pass the buffer
284
- // In a browser environment, this would be converted to a Blob
285
- data = buffer.data;
286
- }
287
- } else {
288
- data = buffer.data.toString('utf8');
289
- }
290
-
291
- this.emit('message', { data });
292
- }
293
-
294
- /**
295
- * Handle buffered amount change from native channel
296
- * @private
297
- */
298
- _onBufferedAmountChange(newAmount) {
299
- const previousAmount = this._bufferedAmount;
300
- this._bufferedAmount = newAmount;
301
-
302
- if (previousAmount > this._bufferedAmountLowThreshold &&
303
- newAmount <= this._bufferedAmountLowThreshold) {
304
- this.emit('bufferedamountlow');
305
- }
306
- }
307
-
308
- /**
309
- * Set the channel state
310
- * @private
311
- */
312
- _setState(state) {
313
- if (this._state === state) {
314
- return;
315
- }
316
- this._state = state;
317
- }
318
-
319
- /**
320
- * Set state to open without dispatching event (used for remote channels)
321
- */
322
- setStateToOpenWithoutEvent() {
323
- this._setState('open');
324
- }
325
-
326
- /**
327
- * Dispatch open event (used for remote channels)
328
- */
329
- dispatchOpenEvent() {
330
- this.emit('open');
331
- }
332
-
333
- /**
334
- * Dispose of resources
335
- */
336
- dispose() {
337
- if (this._nativeChannel) {
338
- this._nativeChannel.removeAllListeners();
339
- this._nativeChannel = null;
340
- }
341
- this._peerConnectionHandler = null;
342
- this.removeAllListeners();
343
- }
344
- }
345
-
346
- module.exports = RTCDataChannel;
@@ -1,50 +0,0 @@
1
- /**
2
- * RTCDataChannelEvent is fired when a data channel is added to the connection.
3
- * Ported from Chromium's implementation.
4
- */
5
- class RTCDataChannelEvent {
6
- constructor(type, eventInitDict = {}) {
7
- this._type = type;
8
- this._channel = eventInitDict.channel || null;
9
- this._bubbles = eventInitDict.bubbles || false;
10
- this._cancelable = eventInitDict.cancelable || false;
11
- this._timestamp = Date.now();
12
- }
13
-
14
- /**
15
- * The event type
16
- */
17
- get type() {
18
- return this._type;
19
- }
20
-
21
- /**
22
- * The RTCDataChannel associated with the event
23
- */
24
- get channel() {
25
- return this._channel;
26
- }
27
-
28
- /**
29
- * Whether the event bubbles
30
- */
31
- get bubbles() {
32
- return this._bubbles;
33
- }
34
-
35
- /**
36
- * Whether the event is cancelable
37
- */
38
- get cancelable() {
39
- return this._cancelable;
40
- }
41
-
42
- /**
43
- * The timestamp when the event was created
44
- */
45
- get timeStamp() {
46
- return this._timestamp;
47
- }
48
- }
49
-
50
- module.exports = RTCDataChannelEvent;
package/src/RTCError.js DELETED
@@ -1,66 +0,0 @@
1
- /**
2
- * RTCErrorEvent represents an error event.
3
- * Ported from Chromium's implementation.
4
- */
5
- class RTCErrorEvent {
6
- constructor(type, eventInitDict = {}) {
7
- this._type = type;
8
- this._error = eventInitDict.error || null;
9
- this._bubbles = eventInitDict.bubbles || false;
10
- this._cancelable = eventInitDict.cancelable || false;
11
- this._timestamp = Date.now();
12
- }
13
-
14
- /**
15
- * The event type
16
- */
17
- get type() {
18
- return this._type;
19
- }
20
-
21
- /**
22
- * The error associated with the event
23
- */
24
- get error() {
25
- return this._error;
26
- }
27
-
28
- /**
29
- * Whether the event bubbles
30
- */
31
- get bubbles() {
32
- return this._bubbles;
33
- }
34
-
35
- /**
36
- * Whether the event is cancelable
37
- */
38
- get cancelable() {
39
- return this._cancelable;
40
- }
41
-
42
- /**
43
- * The timestamp when the event was created
44
- */
45
- get timeStamp() {
46
- return this._timestamp;
47
- }
48
- }
49
-
50
- /**
51
- * RTCError represents a WebRTC-specific error.
52
- */
53
- class RTCError extends Error {
54
- constructor(errorDetail, message) {
55
- super(message);
56
- this.name = 'RTCError';
57
- this.errorDetail = errorDetail;
58
- this.sdpLineNumber = null;
59
- this.httpRequestStatusCode = null;
60
- this.sctpCauseCode = null;
61
- this.receivedAlert = null;
62
- this.sentAlert = null;
63
- }
64
- }
65
-
66
- module.exports = { RTCErrorEvent, RTCError };
@@ -1,184 +0,0 @@
1
- /**
2
- * RTCIceCandidate represents an ICE candidate.
3
- * Ported from Chromium's implementation.
4
- */
5
- class RTCIceCandidate {
6
- constructor(candidateInitDict = {}) {
7
- this._candidate = candidateInitDict.candidate || '';
8
- this._sdpMid = candidateInitDict.sdpMid || null;
9
- this._sdpMLineIndex = candidateInitDict.sdpMLineIndex !== undefined
10
- ? candidateInitDict.sdpMLineIndex
11
- : null;
12
- this._usernameFragment = candidateInitDict.usernameFragment || null;
13
-
14
- // Parse candidate string if provided
15
- if (this._candidate) {
16
- this._parseCandidateString();
17
- }
18
- }
19
-
20
- /**
21
- * The candidate string
22
- */
23
- get candidate() {
24
- return this._candidate;
25
- }
26
-
27
- /**
28
- * The media stream identification tag
29
- */
30
- get sdpMid() {
31
- return this._sdpMid;
32
- }
33
-
34
- /**
35
- * The media line index
36
- */
37
- get sdpMLineIndex() {
38
- return this._sdpMLineIndex;
39
- }
40
-
41
- /**
42
- * The username fragment
43
- */
44
- get usernameFragment() {
45
- return this._usernameFragment;
46
- }
47
-
48
- /**
49
- * The foundation
50
- */
51
- get foundation() {
52
- return this._foundation || null;
53
- }
54
-
55
- /**
56
- * The component (RTP or RTCP)
57
- */
58
- get component() {
59
- return this._component || null;
60
- }
61
-
62
- /**
63
- * The priority
64
- */
65
- get priority() {
66
- return this._priority || null;
67
- }
68
-
69
- /**
70
- * The IP address
71
- */
72
- get address() {
73
- return this._address || null;
74
- }
75
-
76
- /**
77
- * The protocol (udp or tcp)
78
- */
79
- get protocol() {
80
- return this._protocol || null;
81
- }
82
-
83
- /**
84
- * The port
85
- */
86
- get port() {
87
- return this._port || null;
88
- }
89
-
90
- /**
91
- * The candidate type (host, srflx, prflx, or relay)
92
- */
93
- get type() {
94
- return this._type || null;
95
- }
96
-
97
- /**
98
- * The TCP type (active, passive, or so)
99
- */
100
- get tcpType() {
101
- return this._tcpType || null;
102
- }
103
-
104
- /**
105
- * The related address (for non-host candidates)
106
- */
107
- get relatedAddress() {
108
- return this._relatedAddress || null;
109
- }
110
-
111
- /**
112
- * The related port (for non-host candidates)
113
- */
114
- get relatedPort() {
115
- return this._relatedPort || null;
116
- }
117
-
118
- /**
119
- * Parse the candidate string to extract individual fields
120
- * @private
121
- */
122
- _parseCandidateString() {
123
- if (!this._candidate || !this._candidate.startsWith('candidate:')) {
124
- return;
125
- }
126
-
127
- // Basic parsing of candidate string
128
- // Format: candidate:<foundation> <component> <protocol> <priority> <address> <port> typ <type> ...
129
- const parts = this._candidate.split(' ');
130
-
131
- if (parts.length >= 8) {
132
- this._foundation = parts[0].replace('candidate:', '');
133
- this._component = parts[1];
134
- this._protocol = parts[2];
135
- this._priority = parseInt(parts[3], 10);
136
- this._address = parts[4];
137
- this._port = parseInt(parts[5], 10);
138
- // parts[6] is 'typ'
139
- this._type = parts[7];
140
-
141
- // Parse optional fields
142
- for (let i = 8; i < parts.length; i += 2) {
143
- const key = parts[i];
144
- const value = parts[i + 1];
145
-
146
- switch (key) {
147
- case 'raddr':
148
- this._relatedAddress = value;
149
- break;
150
- case 'rport':
151
- this._relatedPort = parseInt(value, 10);
152
- break;
153
- case 'tcptype':
154
- this._tcpType = value;
155
- break;
156
- case 'ufrag':
157
- this._usernameFragment = value;
158
- break;
159
- }
160
- }
161
- }
162
- }
163
-
164
- /**
165
- * Convert to JSON
166
- */
167
- toJSON() {
168
- return {
169
- candidate: this._candidate,
170
- sdpMid: this._sdpMid,
171
- sdpMLineIndex: this._sdpMLineIndex,
172
- usernameFragment: this._usernameFragment
173
- };
174
- }
175
-
176
- /**
177
- * Convert to string
178
- */
179
- toString() {
180
- return `RTCIceCandidate { candidate: "${this._candidate}", sdpMid: "${this._sdpMid}", sdpMLineIndex: ${this._sdpMLineIndex} }`;
181
- }
182
- }
183
-
184
- module.exports = RTCIceCandidate;