node-datachannel 0.10.1 → 0.12.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.
Files changed (48) hide show
  1. package/.github/workflows/build-linux.yml +7 -2
  2. package/.github/workflows/build-win.yml +3 -3
  3. package/.gitmodules +3 -0
  4. package/CMakeLists.txt +1 -1
  5. package/README.md +18 -4
  6. package/cmake/toolchain/ci.cmake +1 -1
  7. package/examples/client-server/client-benchmark.js +7 -9
  8. package/examples/client-server/client-periodic.js +7 -9
  9. package/examples/client-server/client.js +7 -9
  10. package/examples/client-server/package-lock.json +0 -27
  11. package/examples/client-server/package.json +0 -1
  12. package/examples/client-server/signaling-server.js +21 -10
  13. package/examples/electron-demo/README.md +1 -1
  14. package/examples/electron-demo/package-lock.json +316 -372
  15. package/examples/electron-demo/package.json +2 -1
  16. package/examples/electron-demo/webpack.main.config.js +10 -0
  17. package/examples/websocket/websocket-client.js +20 -0
  18. package/examples/websocket/websocket-server.js +22 -0
  19. package/jest.config.cjs +1 -1
  20. package/lib/index.cjs +52 -9
  21. package/lib/index.js +22 -15
  22. package/lib/node-datachannel.js +7 -0
  23. package/lib/websocket-server.js +34 -0
  24. package/package.json +3 -3
  25. package/polyfill/Events.js +5 -3
  26. package/polyfill/Exception.js +23 -0
  27. package/polyfill/README.md +4 -0
  28. package/polyfill/RTCDataChannel.js +19 -11
  29. package/polyfill/RTCError.d.ts +11 -0
  30. package/polyfill/RTCError.js +65 -0
  31. package/polyfill/RTCIceCandidate.js +22 -23
  32. package/polyfill/RTCPeerConnection.js +157 -42
  33. package/polyfill/index.cjs +350 -82
  34. package/polyfill/index.js +3 -0
  35. package/src/peer-connection-wrapper.cpp +11 -3
  36. package/src/web-socket-server-wrapper.cpp +23 -23
  37. package/src/web-socket-server-wrapper.h +8 -6
  38. package/src/web-socket-wrapper.cpp +7 -1
  39. package/test/connectivity.js +1 -23
  40. package/test/wpt-tests/README.md +46 -0
  41. package/test/wpt-tests/chrome-failed-tests.js +42 -0
  42. package/test/wpt-tests/index.js +96 -0
  43. package/test/wpt-tests/last-test-results.md +301 -0
  44. package/test/wpt-tests/package-lock.json +1712 -0
  45. package/test/wpt-tests/package.json +19 -0
  46. package/test/wpt-tests/wpt-test-list.js +139 -0
  47. package/test/wpt-tests/wpt.js +131 -0
  48. package/test/wpt.js +0 -50
@@ -2,9 +2,10 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var DOMException$1 = require('node-domexception');
5
+ require('node-domexception');
6
6
  var module$1 = require('module');
7
7
  var stream = require('stream');
8
+ var events = require('events');
8
9
 
9
10
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
10
11
  class RTCCertificate {
@@ -25,6 +26,26 @@ class RTCCertificate {
25
26
  }
26
27
  }
27
28
 
29
+ const InvalidStateError = (msg) => {
30
+ return new DOMException(msg, 'InvalidStateError');
31
+ };
32
+
33
+ const InvalidAccessError = (msg) => {
34
+ return new DOMException(msg, 'InvalidAccessError');
35
+ };
36
+
37
+ const NotFoundError = (msg) => {
38
+ return new DOMException(msg, 'NotFoundError');
39
+ };
40
+
41
+ const OperationError = (msg) => {
42
+ return new DOMException(msg, 'OperationError');
43
+ };
44
+
45
+ const SyntaxError = (msg) => {
46
+ return new DOMException(msg, 'SyntaxError');
47
+ };
48
+
28
49
  class _RTCDataChannel extends EventTarget {
29
50
  #dataChannel;
30
51
  #readyState;
@@ -35,6 +56,8 @@ class _RTCDataChannel extends EventTarget {
35
56
  #negotiated;
36
57
  #ordered;
37
58
 
59
+ #closeRequested = false;
60
+
38
61
  onbufferedamountlow;
39
62
  onclose;
40
63
  onclosing;
@@ -57,12 +80,20 @@ class _RTCDataChannel extends EventTarget {
57
80
  // forward dataChannel events
58
81
  this.#dataChannel.onOpen(() => {
59
82
  this.#readyState = 'open';
60
- this.dispatchEvent(new Event('open'));
83
+ this.dispatchEvent(new Event('open', { channel: this }));
61
84
  });
62
85
 
63
86
  this.#dataChannel.onClosed(() => {
64
- this.#readyState = 'closed';
65
- this.dispatchEvent(new Event('close'));
87
+ // Simulate closing event
88
+ if (!this.#closeRequested) {
89
+ this.#readyState = 'closing';
90
+ this.dispatchEvent(new Event('closing', { channel: this }));
91
+ }
92
+
93
+ setImmediate(() => {
94
+ this.#readyState = 'closed';
95
+ this.dispatchEvent(new Event('close', { channel: this }));
96
+ });
66
97
  });
67
98
 
68
99
  this.#dataChannel.onError((msg) => {
@@ -79,7 +110,7 @@ class _RTCDataChannel extends EventTarget {
79
110
  });
80
111
 
81
112
  this.#dataChannel.onBufferedAmountLow(() => {
82
- this.dispatchEvent(new Event('bufferedamountlow'));
113
+ this.dispatchEvent(new Event('bufferedamountlow', { channel: this }));
83
114
  });
84
115
 
85
116
  this.#dataChannel.onMessage((data) => {
@@ -87,7 +118,7 @@ class _RTCDataChannel extends EventTarget {
87
118
  data = data.buffer;
88
119
  }
89
120
 
90
- this.dispatchEvent(new MessageEvent('message', { data }));
121
+ this.dispatchEvent(new MessageEvent('message', { data, channel: this }));
91
122
  });
92
123
 
93
124
  // forward events to properties
@@ -113,7 +144,7 @@ class _RTCDataChannel extends EventTarget {
113
144
 
114
145
  set binaryType(type) {
115
146
  if (type !== 'blob' && type !== 'arraybuffer') {
116
- throw new DOMException$1(
147
+ throw new DOMException(
117
148
  "Failed to set the 'binaryType' property on 'RTCDataChannel': Unknown binary type : " + type,
118
149
  'TypeMismatchError',
119
150
  );
@@ -173,9 +204,8 @@ class _RTCDataChannel extends EventTarget {
173
204
 
174
205
  send(data) {
175
206
  if (this.#readyState !== 'open') {
176
- throw new DOMException$1(
207
+ throw InvalidStateError(
177
208
  "Failed to execute 'send' on 'RTCDataChannel': RTCDataChannel.readyState is not 'open'",
178
- 'InvalidStateError',
179
209
  );
180
210
  }
181
211
 
@@ -192,9 +222,7 @@ class _RTCDataChannel extends EventTarget {
192
222
  }
193
223
 
194
224
  close() {
195
- this.#readyState = 'closing';
196
- this.dispatchEvent(new Event('closing'));
197
-
225
+ this.#closeRequested = true;
198
226
  this.#dataChannel.close();
199
227
  }
200
228
  }
@@ -219,41 +247,40 @@ class _RTCIceCandidate {
219
247
  #tcpType;
220
248
  #type;
221
249
  #usernameFragment;
222
- #ip;
223
250
 
224
251
  constructor({ candidate, sdpMLineIndex, sdpMid, usernameFragment }) {
225
- if (candidate == null) {
226
- throw new DOMException$1('candidate must be specified');
227
- }
252
+ if (sdpMLineIndex == null && sdpMid == null)
253
+ throw new TypeError('At least one of sdpMLineIndex or sdpMid must be specified');
228
254
 
229
- this.#candidate = candidate;
230
- this.#sdpMLineIndex = sdpMLineIndex || null;
231
- this.#sdpMid = sdpMid || null;
232
- this.#usernameFragment = usernameFragment || null;
255
+ this.#candidate = candidate === null ? 'null' : candidate ?? '';
256
+ this.#sdpMLineIndex = sdpMLineIndex ?? null;
257
+ this.#sdpMid = sdpMid ?? null;
258
+ this.#usernameFragment = usernameFragment ?? null;
233
259
 
234
260
  if (candidate) {
235
261
  const fields = candidate.split(' ');
236
- this.#foundation = fields[0];
262
+ this.#foundation = fields[0].replace('candidate:', ''); // remove text candidate:
237
263
  this.#component = fields[1] == '1' ? 'rtp' : 'rtcp';
238
264
  this.#protocol = fields[2];
239
265
  this.#priority = parseInt(fields[3], 10);
240
- this.#ip = fields[4];
266
+ this.#address = fields[4];
241
267
  this.#port = parseInt(fields[5], 10);
242
268
  this.#type = fields[7];
243
- if (fields[6] === 'typ') {
244
- this.#tcpType = null;
245
- } else if (fields[6] === 'tcp') {
246
- this.#tcpType = fields[7];
247
- this.#type = fields[8];
248
- }
269
+ this.#tcpType = null;
270
+ this.#relatedAddress = null;
271
+ this.#relatedPort = null;
249
272
 
250
273
  // Parse the candidate string to extract relatedPort and relatedAddress
251
- for (let i = 9; i < fields.length; i++) {
274
+ for (let i = 8; i < fields.length; i++) {
252
275
  const field = fields[i];
253
- if (field.startsWith('raddr')) {
254
- this.#relatedAddress = field.split('=')[1];
255
- } else if (field.startsWith('rport')) {
256
- this.#relatedPort = parseInt(field.split('=')[1], 10);
276
+ if (field === 'raddr') {
277
+ this.#relatedAddress = fields[i + 1];
278
+ } else if (field === 'rport') {
279
+ this.#relatedPort = parseInt(fields[i + 1], 10);
280
+ }
281
+
282
+ if (this.#protocol === 'tcp' && field === 'tcptype') {
283
+ this.#tcpType = fields[i + 1];
257
284
  }
258
285
  }
259
286
  }
@@ -264,7 +291,7 @@ class _RTCIceCandidate {
264
291
  }
265
292
 
266
293
  get candidate() {
267
- return this.#candidate || '';
294
+ return this.#candidate;
268
295
  }
269
296
 
270
297
  get component() {
@@ -451,6 +478,11 @@ class _RTCDtlsTransport extends EventTarget {
451
478
  }
452
479
  }
453
480
 
481
+ // createRequire is native in node version >= 12
482
+ const require$1 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
483
+
484
+ const nodeDataChannel = require$1('../build/Release/node_datachannel.node');
485
+
454
486
  /**
455
487
  * Turns a node-datachannel DataChannel into a real Node.js stream, complete with buffering,
456
488
  * backpressure (up to a point - if the buffer fills up, messages are dropped), and
@@ -546,13 +578,65 @@ class DataChannelStream extends stream.Duplex {
546
578
  }
547
579
  }
548
580
 
549
- // createRequire is native in node version >= 12
550
- const require$1 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
581
+ class WebSocketServer extends events.EventEmitter {
582
+ #server;
583
+ #clients = [];
551
584
 
552
- const nodeDataChannel = require$1('../build/Release/node_datachannel.node');
585
+ constructor(options) {
586
+ super();
587
+ this.#server = new nodeDataChannel.WebSocketServer(options);
588
+
589
+ this.#server.onClient((client) => {
590
+ this.emit('client', client);
591
+ this.#clients.push(client);
592
+ });
593
+ }
594
+
595
+ port() {
596
+ return this.#server?.port() || 0;
597
+ }
598
+
599
+ stop() {
600
+ this.#clients.forEach((client) => {
601
+ client?.close();
602
+ });
603
+ this.#server?.stop();
604
+ this.#server = null;
605
+ this.removeAllListeners();
606
+ }
607
+
608
+ onClient(cb) {
609
+ if (this.#server) this.on('client', cb);
610
+ }
611
+ }
612
+
613
+ const {
614
+ initLogger,
615
+ cleanup,
616
+ preload,
617
+ setSctpSettings,
618
+ RtcpReceivingSession,
619
+ Track,
620
+ Video,
621
+ Audio,
622
+ DataChannel,
623
+ PeerConnection,
624
+ WebSocket,
625
+ } = nodeDataChannel;
553
626
 
554
627
  var NodeDataChannel = {
555
- ...nodeDataChannel,
628
+ initLogger,
629
+ cleanup,
630
+ preload,
631
+ setSctpSettings,
632
+ RtcpReceivingSession,
633
+ Track,
634
+ Video,
635
+ Audio,
636
+ DataChannel,
637
+ PeerConnection,
638
+ WebSocket,
639
+ WebSocketServer,
556
640
  DataChannelStream,
557
641
  };
558
642
 
@@ -611,10 +695,12 @@ class RTCPeerConnectionIceEvent extends Event {
611
695
  class RTCDataChannelEvent extends Event {
612
696
  #channel;
613
697
 
614
- constructor(channel) {
615
- super('datachannel');
698
+ constructor(type, eventInitDict) {
699
+ super(type);
700
+
701
+ if (type && !eventInitDict.channel) throw new TypeError('channel member is required');
616
702
 
617
- this.#channel = channel;
703
+ this.#channel = eventInitDict?.channel;
618
704
  }
619
705
 
620
706
  get channel() {
@@ -683,6 +769,7 @@ class _RTCPeerConnection extends EventTarget {
683
769
  #localOffer;
684
770
  #localAnswer;
685
771
  #dataChannels;
772
+ #dataChannelsClosed = 0;
686
773
  #config;
687
774
  #canTrickleIceCandidates;
688
775
  #sctp;
@@ -700,32 +787,106 @@ class _RTCPeerConnection extends EventTarget {
700
787
  onsignalingstatechange;
701
788
  ontrack;
702
789
 
703
- constructor(init = {}) {
790
+ _checkConfiguration(config) {
791
+ if (config && config.iceServers === undefined) config.iceServers = [];
792
+ if (config && config.iceTransportPolicy === undefined) config.iceTransportPolicy = 'all';
793
+
794
+ if (config?.iceServers === null) throw new TypeError('IceServers cannot be null');
795
+
796
+ // Check for all the properties of iceServers
797
+ if (Array.isArray(config?.iceServers)) {
798
+ for (let i = 0; i < config.iceServers.length; i++) {
799
+ if (config.iceServers[i] === null) throw new TypeError('IceServers cannot be null');
800
+ if (config.iceServers[i] === undefined) throw new TypeError('IceServers cannot be undefined');
801
+ if (Object.keys(config.iceServers[i]).length === 0) throw new TypeError('IceServers cannot be empty');
802
+
803
+ // If iceServers is string convert to array
804
+ if (typeof config.iceServers[i].urls === 'string')
805
+ config.iceServers[i].urls = [config.iceServers[i].urls];
806
+
807
+ // urls can not be empty
808
+ if (config.iceServers[i].urls?.some((url) => url == ''))
809
+ throw SyntaxError('IceServers urls cannot be empty');
810
+
811
+ // urls should be valid URLs and match the protocols "stun:|turn:|turns:"
812
+ if (
813
+ config.iceServers[i].urls?.some(
814
+ (url) => {
815
+ try {
816
+ const parsedURL = new URL(url);
817
+
818
+ return !/^(stun:|turn:|turns:)$/.test(parsedURL.protocol)
819
+ } catch (error) {
820
+ return true
821
+ }
822
+ },
823
+ )
824
+ )
825
+ throw SyntaxError('IceServers urls wrong format');
826
+
827
+ // If this is a turn server check for username and credential
828
+ if (config.iceServers[i].urls?.some((url) => url.startsWith('turn'))) {
829
+ if (!config.iceServers[i].username)
830
+ throw InvalidAccessError('IceServers username cannot be null');
831
+ if (!config.iceServers[i].credential)
832
+ throw InvalidAccessError('IceServers username cannot be undefined');
833
+ }
834
+
835
+ // length of urls can not be 0
836
+ if (config.iceServers[i].urls?.length === 0)
837
+ throw SyntaxError('IceServers urls cannot be empty');
838
+ }
839
+ }
840
+
841
+ if (
842
+ config &&
843
+ config.iceTransportPolicy &&
844
+ config.iceTransportPolicy !== 'all' &&
845
+ config.iceTransportPolicy !== 'relay'
846
+ )
847
+ throw new TypeError('IceTransportPolicy must be either "all" or "relay"');
848
+ }
849
+
850
+ setConfiguration(config) {
851
+ this._checkConfiguration(config);
852
+ this.#config = config;
853
+ }
854
+
855
+ constructor(config = { iceServers: [], iceTransportPolicy: 'all' }) {
704
856
  super();
705
857
 
706
- this.#config = init;
858
+ this._checkConfiguration(config);
859
+ this.#config = config;
707
860
  this.#localOffer = createDeferredPromise();
708
861
  this.#localAnswer = createDeferredPromise();
709
862
  this.#dataChannels = new Set();
710
863
  this.#canTrickleIceCandidates = null;
711
864
 
712
- this.#peerConnection = new NodeDataChannel.PeerConnection(init?.peerIdentity ?? `peer-${getRandomString(7)}`, {
713
- ...init,
714
- iceServers:
715
- init?.iceServers
716
- ?.map((server) => {
717
- const urls = Array.isArray(server.urls) ? server.urls : [server.urls];
718
-
719
- return urls.map((url) => {
720
- if (server.username && server.credential) {
721
- const [protocol, rest] = url.split(/:(.*)/);
722
- return `${protocol}:${server.username}:${server.credential}@${rest}`;
723
- }
724
- return url;
725
- });
726
- })
727
- .flat() ?? [],
728
- });
865
+ try {
866
+ this.#peerConnection = new NodeDataChannel.PeerConnection(
867
+ config?.peerIdentity ?? `peer-${getRandomString(7)}`,
868
+ {
869
+ ...config,
870
+ iceServers:
871
+ config?.iceServers
872
+ ?.map((server) => {
873
+ const urls = Array.isArray(server.urls) ? server.urls : [server.urls];
874
+
875
+ return urls.map((url) => {
876
+ if (server.username && server.credential) {
877
+ const [protocol, rest] = url.split(/:(.*)/);
878
+ return `${protocol}:${server.username}:${server.credential}@${rest}`;
879
+ }
880
+ return url;
881
+ });
882
+ })
883
+ .flat() ?? [],
884
+ },
885
+ );
886
+ } catch (error) {
887
+ if (!error || !error.message) throw NotFoundError('Unknown error');
888
+ throw SyntaxError(error.message);
889
+ }
729
890
 
730
891
  // forward peerConnection events
731
892
  this.#peerConnection.onStateChange(() => {
@@ -745,9 +906,9 @@ class _RTCPeerConnection extends EventTarget {
745
906
  });
746
907
 
747
908
  this.#peerConnection.onDataChannel((channel) => {
748
- const dataChannel = new _RTCDataChannel(channel);
749
- this.#dataChannels.add(dataChannel);
750
- this.dispatchEvent(new RTCDataChannelEvent(dataChannel));
909
+ const dc = new _RTCDataChannel(channel);
910
+ this.#dataChannels.add(dc);
911
+ this.dispatchEvent(new RTCDataChannelEvent('datachannel', { channel: dc }));
751
912
  });
752
913
 
753
914
  this.#peerConnection.onLocalDescription((sdp, type) => {
@@ -821,7 +982,11 @@ class _RTCPeerConnection extends EventTarget {
821
982
  }
822
983
 
823
984
  get iceConnectionState() {
824
- return this.#peerConnection.iceState();
985
+ let state = this.#peerConnection.iceState();
986
+ // libdatachannel uses 'completed' instead of 'connected'
987
+ // see /webrtc/getstats.html
988
+ if (state == 'completed') state = 'connected';
989
+ return state;
825
990
  }
826
991
 
827
992
  get iceGatheringState() {
@@ -861,14 +1026,45 @@ class _RTCPeerConnection extends EventTarget {
861
1026
  }
862
1027
 
863
1028
  async addIceCandidate(candidate) {
864
- if (candidate == null || candidate.candidate == null) {
865
- throw new DOMException('Candidate invalid');
1029
+ if (!candidate || !candidate.candidate) {
1030
+ return;
1031
+ }
1032
+
1033
+ if (candidate.sdpMid === null && candidate.sdpMLineIndex === null) {
1034
+ throw new TypeError('sdpMid must be set');
1035
+ }
1036
+
1037
+ if (candidate.sdpMid === undefined && candidate.sdpMLineIndex == undefined) {
1038
+ throw new TypeError('sdpMid must be set');
1039
+ }
1040
+
1041
+ // Reject if sdpMid format is not valid
1042
+ // ??
1043
+ if (candidate.sdpMid && candidate.sdpMid.length > 3) {
1044
+ // console.log(candidate.sdpMid);
1045
+ throw OperationError('Invalid sdpMid format');
866
1046
  }
867
1047
 
868
- this.#remoteCandidates.push(
869
- new _RTCIceCandidate({ candidate: candidate.candidate, sdpMid: candidate.sdpMid || '0' }),
870
- );
871
- this.#peerConnection.addRemoteCandidate(candidate.candidate, candidate.sdpMid || '0');
1048
+ // We don't care about sdpMLineIndex, just for test
1049
+ if (!candidate.sdpMid && candidate.sdpMLineIndex > 1) {
1050
+ throw OperationError('This is only for test case.');
1051
+ }
1052
+
1053
+ try {
1054
+ this.#peerConnection.addRemoteCandidate(candidate.candidate, candidate.sdpMid || '0');
1055
+ this.#remoteCandidates.push(
1056
+ new _RTCIceCandidate({ candidate: candidate.candidate, sdpMid: candidate.sdpMid || '0' }),
1057
+ );
1058
+ } catch (error) {
1059
+ if (!error || !error.message) throw NotFoundError('Unknown error');
1060
+
1061
+ // Check error Message if contains specific message
1062
+ if (error.message.includes('remote candidate without remote description'))
1063
+ throw InvalidStateError(error.message);
1064
+ if (error.message.includes('Invalid candidate format')) throw OperationError(error.message);
1065
+
1066
+ throw NotFoundError(error.message);
1067
+ }
872
1068
  }
873
1069
 
874
1070
  addTrack(track, ...streams) {
@@ -883,6 +1079,7 @@ class _RTCPeerConnection extends EventTarget {
883
1079
  // close all channels before shutting down
884
1080
  this.#dataChannels.forEach((channel) => {
885
1081
  channel.close();
1082
+ this.#dataChannelsClosed++;
886
1083
  });
887
1084
 
888
1085
  this.#peerConnection.close();
@@ -900,6 +1097,7 @@ class _RTCPeerConnection extends EventTarget {
900
1097
  this.#dataChannels.add(dataChannel);
901
1098
  dataChannel.addEventListener('close', () => {
902
1099
  this.#dataChannels.delete(dataChannel);
1100
+ this.#dataChannelsClosed++;
903
1101
  });
904
1102
 
905
1103
  return dataChannel;
@@ -933,7 +1131,7 @@ class _RTCPeerConnection extends EventTarget {
933
1131
  let localId = 'RTCIceCandidate_' + localIdRs;
934
1132
  report.set(localId, {
935
1133
  id: localId,
936
- type: 'localcandidate',
1134
+ type: 'local-candidate',
937
1135
  timestamp: Date.now(),
938
1136
  candidateType: cp.local.type,
939
1137
  ip: cp.local.address,
@@ -944,7 +1142,7 @@ class _RTCPeerConnection extends EventTarget {
944
1142
  let remoteId = 'RTCIceCandidate_' + remoteIdRs;
945
1143
  report.set(remoteId, {
946
1144
  id: remoteId,
947
- type: 'remotecandidate',
1145
+ type: 'remote-candidate',
948
1146
  timestamp: Date.now(),
949
1147
  candidateType: cp.remote.type,
950
1148
  ip: cp.remote.address,
@@ -979,6 +1177,15 @@ class _RTCPeerConnection extends EventTarget {
979
1177
  selectedCandidatePairChanges: 1,
980
1178
  });
981
1179
 
1180
+ // peer-connection'
1181
+ report.set('P', {
1182
+ id: 'P',
1183
+ type: 'peer-connection',
1184
+ timestamp: Date.now(),
1185
+ dataChannelsOpened: this.#dataChannels.size,
1186
+ dataChannelsClosed: this.#dataChannelsClosed,
1187
+ });
1188
+
982
1189
  return resolve(report);
983
1190
  });
984
1191
  }
@@ -995,20 +1202,13 @@ class _RTCPeerConnection extends EventTarget {
995
1202
  throw new DOMException('Not implemented');
996
1203
  }
997
1204
 
998
- setConfiguration(config) {
999
- this.#config = config;
1000
- }
1001
-
1002
1205
  async setLocalDescription(description) {
1003
- if (description == null || description.type == null) {
1004
- throw new DOMException('Local description type must be set');
1005
- }
1006
-
1007
- if (description.type !== 'offer') {
1206
+ if (description?.type !== 'offer') {
1008
1207
  // any other type causes libdatachannel to throw
1009
1208
  return;
1010
1209
  }
1011
- this.#peerConnection.setLocalDescription(description.type);
1210
+
1211
+ this.#peerConnection.setLocalDescription(description?.type);
1012
1212
  }
1013
1213
 
1014
1214
  async setRemoteDescription(description) {
@@ -1039,6 +1239,72 @@ function getRandomString(length) {
1039
1239
  .substring(2, 2 + length);
1040
1240
  }
1041
1241
 
1242
+ let RTCError$1 = class RTCError extends DOMException {
1243
+ constructor(init, message = '') {
1244
+ super(message, 'OperationError');
1245
+
1246
+ if (!init || !init.errorDetail) throw new TypeError('Cannot construct RTCError, errorDetail is required');
1247
+ if (
1248
+ [
1249
+ 'data-channel-failure',
1250
+ 'dtls-failure',
1251
+ 'fingerprint-failure',
1252
+ 'hardware-encoder-error',
1253
+ 'hardware-encoder-not-available',
1254
+ 'sctp-failure',
1255
+ 'sdp-syntax-error',
1256
+ ].indexOf(init.errorDetail) === -1
1257
+ )
1258
+ throw new TypeError('Cannot construct RTCError, errorDetail is invalid');
1259
+
1260
+ this._errorDetail = init.errorDetail;
1261
+ this._receivedAlert = init.receivedAlert ?? null;
1262
+ this._sctpCauseCode = init.sctpCauseCode ?? null;
1263
+ this._sdpLineNumber = init.sdpLineNumber ?? null;
1264
+ this._sentAlert = init.sentAlert ?? null;
1265
+ }
1266
+
1267
+ get errorDetail() {
1268
+ return this._errorDetail;
1269
+ }
1270
+
1271
+ set errorDetail(value) {
1272
+ throw new TypeError('Cannot set errorDetail, it is read-only');
1273
+ }
1274
+
1275
+ get receivedAlert() {
1276
+ return this._receivedAlert;
1277
+ }
1278
+
1279
+ set receivedAlert(value) {
1280
+ throw new TypeError('Cannot set receivedAlert, it is read-only');
1281
+ }
1282
+
1283
+ get sctpCauseCode() {
1284
+ return this._sctpCauseCode;
1285
+ }
1286
+
1287
+ set sctpCauseCode(value) {
1288
+ throw new TypeError('Cannot set sctpCauseCode, it is read-only');
1289
+ }
1290
+
1291
+ get sdpLineNumber() {
1292
+ return this._sdpLineNumber;
1293
+ }
1294
+
1295
+ set sdpLineNumber(value) {
1296
+ throw new TypeError('Cannot set sdpLineNumber, it is read-only');
1297
+ }
1298
+
1299
+ get sentAlert() {
1300
+ return this._sentAlert;
1301
+ }
1302
+
1303
+ set sentAlert(value) {
1304
+ throw new TypeError('Cannot set sentAlert, it is read-only');
1305
+ }
1306
+ };
1307
+
1042
1308
  var index = {
1043
1309
  RTCCertificate,
1044
1310
  RTCDataChannel: _RTCDataChannel,
@@ -1050,12 +1316,14 @@ var index = {
1050
1316
  RTCSessionDescription: _RTCSessionDescription,
1051
1317
  RTCDataChannelEvent,
1052
1318
  RTCPeerConnectionIceEvent,
1319
+ RTCError: RTCError$1,
1053
1320
  };
1054
1321
 
1055
1322
  exports.RTCCertificate = RTCCertificate;
1056
1323
  exports.RTCDataChannel = _RTCDataChannel;
1057
1324
  exports.RTCDataChannelEvent = RTCDataChannelEvent;
1058
1325
  exports.RTCDtlsTransport = _RTCDtlsTransport;
1326
+ exports.RTCError = RTCError$1;
1059
1327
  exports.RTCIceCandidate = _RTCIceCandidate;
1060
1328
  exports.RTCIceTransport = _RTCIceTransport;
1061
1329
  exports.RTCPeerConnection = _RTCPeerConnection;
package/polyfill/index.js CHANGED
@@ -7,6 +7,7 @@ import RTCPeerConnection from './RTCPeerConnection.js';
7
7
  import RTCSctpTransport from './RTCSctpTransport.js';
8
8
  import RTCSessionDescription from './RTCSessionDescription.js';
9
9
  import { RTCDataChannelEvent, RTCPeerConnectionIceEvent } from './Events.js';
10
+ import RTCError from './RTCError.js';
10
11
 
11
12
  export {
12
13
  RTCCertificate,
@@ -19,6 +20,7 @@ export {
19
20
  RTCSessionDescription,
20
21
  RTCDataChannelEvent,
21
22
  RTCPeerConnectionIceEvent,
23
+ RTCError,
22
24
  };
23
25
 
24
26
  export default {
@@ -32,4 +34,5 @@ export default {
32
34
  RTCSessionDescription,
33
35
  RTCDataChannelEvent,
34
36
  RTCPeerConnectionIceEvent,
37
+ RTCError,
35
38
  };