UNPKG

node-datachannel

Version:

WebRTC For Node.js and Electron. libdatachannel node bindings.

420 lines (415 loc) 16.9 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var index = require('../lib/index.cjs'); var RTCSessionDescription = require('./RTCSessionDescription.cjs'); var RTCDataChannel = require('./RTCDataChannel.cjs'); var RTCIceCandidate = require('./RTCIceCandidate.cjs'); var Events = require('./Events.cjs'); var RTCSctpTransport = require('./RTCSctpTransport.cjs'); var Exception = require('./Exception.cjs'); var __defProp = Object.defineProperty; var __typeError = (msg) => { throw TypeError(msg); }; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg); var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value); var __privateWrapper = (obj, member, setter, getter) => ({ set _(value) { __privateSet(obj, member, value); }, get _() { return __privateGet(obj, member, getter); } }); var _peerConnection, _localOffer, _localAnswer, _dataChannels, _dataChannelsClosed, _config, _canTrickleIceCandidates, _sctp, _localCandidates, _remoteCandidates; class RTCPeerConnection extends EventTarget { constructor(config = { iceServers: [], iceTransportPolicy: "all" }) { super(); __privateAdd(this, _peerConnection); __privateAdd(this, _localOffer); __privateAdd(this, _localAnswer); __privateAdd(this, _dataChannels); __privateAdd(this, _dataChannelsClosed, 0); __privateAdd(this, _config); __privateAdd(this, _canTrickleIceCandidates); __privateAdd(this, _sctp); __privateAdd(this, _localCandidates, []); __privateAdd(this, _remoteCandidates, []); // events __publicField(this, "onconnectionstatechange"); __publicField(this, "ondatachannel"); __publicField(this, "onicecandidate"); __publicField(this, "onicecandidateerror"); __publicField(this, "oniceconnectionstatechange"); __publicField(this, "onicegatheringstatechange"); __publicField(this, "onnegotiationneeded"); __publicField(this, "onsignalingstatechange"); __publicField(this, "ontrack"); this._checkConfiguration(config); __privateSet(this, _config, config); __privateSet(this, _localOffer, createDeferredPromise()); __privateSet(this, _localAnswer, createDeferredPromise()); __privateSet(this, _dataChannels, /* @__PURE__ */ new Set()); __privateSet(this, _canTrickleIceCandidates, null); try { const peerIdentity = config?.peerIdentity ?? `peer-${getRandomString(7)}`; __privateSet(this, _peerConnection, new index.PeerConnection( peerIdentity, { ...config, iceServers: config?.iceServers?.map((server) => { const urls = Array.isArray(server.urls) ? server.urls : [server.urls]; return urls.map((url) => { if (server.username && server.credential) { const [protocol, rest] = url.split(/:(.*)/); return `${protocol}:${server.username}:${server.credential}@${rest}`; } return url; }); }).flat() ?? [] } )); } catch (error) { if (!error || !error.message) throw new Exception.NotFoundError("Unknown error"); throw new Exception.SyntaxError(error.message); } __privateGet(this, _peerConnection).onStateChange(() => { this.dispatchEvent(new Event("connectionstatechange")); }); __privateGet(this, _peerConnection).onIceStateChange(() => { this.dispatchEvent(new Event("iceconnectionstatechange")); }); __privateGet(this, _peerConnection).onSignalingStateChange(() => { this.dispatchEvent(new Event("signalingstatechange")); }); __privateGet(this, _peerConnection).onGatheringStateChange(() => { this.dispatchEvent(new Event("icegatheringstatechange")); }); __privateGet(this, _peerConnection).onDataChannel((channel) => { const dc = new RTCDataChannel.default(channel); __privateGet(this, _dataChannels).add(dc); this.dispatchEvent(new Events.RTCDataChannelEvent("datachannel", { channel: dc })); }); __privateGet(this, _peerConnection).onLocalDescription((sdp, type) => { if (type === "offer") { __privateGet(this, _localOffer).resolve({ sdp, type }); } if (type === "answer") { __privateGet(this, _localAnswer).resolve({ sdp, type }); } }); __privateGet(this, _peerConnection).onLocalCandidate((candidate, sdpMid) => { if (sdpMid === "unspec") { __privateGet(this, _localAnswer).reject(new Error(`Invalid description type ${sdpMid}`)); return; } __privateGet(this, _localCandidates).push(new RTCIceCandidate.default({ candidate, sdpMid })); this.dispatchEvent(new Events.RTCPeerConnectionIceEvent(new RTCIceCandidate.default({ candidate, sdpMid }))); }); this.addEventListener("connectionstatechange", (e) => { if (this.onconnectionstatechange) this.onconnectionstatechange(e); }); this.addEventListener("signalingstatechange", (e) => { if (this.onsignalingstatechange) this.onsignalingstatechange(e); }); this.addEventListener("iceconnectionstatechange", (e) => { if (this.oniceconnectionstatechange) this.oniceconnectionstatechange(e); }); this.addEventListener("icegatheringstatechange", (e) => { if (this.onicegatheringstatechange) this.onicegatheringstatechange(e); }); this.addEventListener("datachannel", (e) => { if (this.ondatachannel) this.ondatachannel(e); }); this.addEventListener("icecandidate", (e) => { if (this.onicecandidate) this.onicecandidate(e); }); __privateSet(this, _sctp, new RTCSctpTransport.default({ pc: this, extraFunctions: { maxDataChannelId: () => { return __privateGet(this, _peerConnection).maxDataChannelId(); }, maxMessageSize: () => { return __privateGet(this, _peerConnection).maxMessageSize(); }, localCandidates: () => { return __privateGet(this, _localCandidates); }, remoteCandidates: () => { return __privateGet(this, _remoteCandidates); }, selectedCandidatePair: () => { return __privateGet(this, _peerConnection).getSelectedCandidatePair(); } } })); } static async generateCertificate() { throw new DOMException("Not implemented"); } _checkConfiguration(config) { if (config && config.iceServers === void 0) config.iceServers = []; if (config && config.iceTransportPolicy === void 0) config.iceTransportPolicy = "all"; if (config?.iceServers === null) throw new TypeError("IceServers cannot be null"); if (Array.isArray(config?.iceServers)) { for (let i = 0; i < config.iceServers.length; i++) { if (config.iceServers[i] === null) throw new TypeError("IceServers cannot be null"); if (config.iceServers[i] === void 0) throw new TypeError("IceServers cannot be undefined"); if (Object.keys(config.iceServers[i]).length === 0) throw new TypeError("IceServers cannot be empty"); if (typeof config.iceServers[i].urls === "string") config.iceServers[i].urls = [config.iceServers[i].urls]; if (config.iceServers[i].urls?.some((url) => url == "")) throw new Exception.SyntaxError("IceServers urls cannot be empty"); if (config.iceServers[i].urls?.some( (url) => { try { const parsedURL = new URL(url); return !/^(stun:|turn:|turns:)$/.test(parsedURL.protocol); } catch (error) { return true; } } )) throw new Exception.SyntaxError("IceServers urls wrong format"); if (config.iceServers[i].urls?.some((url) => url.startsWith("turn"))) { if (!config.iceServers[i].username) throw new Exception.InvalidAccessError("IceServers username cannot be null"); if (!config.iceServers[i].credential) throw new Exception.InvalidAccessError("IceServers username cannot be undefined"); } if (config.iceServers[i].urls?.length === 0) throw new Exception.SyntaxError("IceServers urls cannot be empty"); } } if (config && config.iceTransportPolicy && config.iceTransportPolicy !== "all" && config.iceTransportPolicy !== "relay") throw new TypeError('IceTransportPolicy must be either "all" or "relay"'); } setConfiguration(config) { this._checkConfiguration(config); __privateSet(this, _config, config); } get canTrickleIceCandidates() { return __privateGet(this, _canTrickleIceCandidates); } get connectionState() { return __privateGet(this, _peerConnection).state(); } get iceConnectionState() { let state = __privateGet(this, _peerConnection).iceState(); if (state == "completed") state = "connected"; return state; } get iceGatheringState() { return __privateGet(this, _peerConnection).gatheringState(); } get currentLocalDescription() { return new RTCSessionDescription.default(__privateGet(this, _peerConnection).localDescription()); } get currentRemoteDescription() { return new RTCSessionDescription.default(__privateGet(this, _peerConnection).remoteDescription()); } get localDescription() { return new RTCSessionDescription.default(__privateGet(this, _peerConnection).localDescription()); } get pendingLocalDescription() { return new RTCSessionDescription.default(__privateGet(this, _peerConnection).localDescription()); } get pendingRemoteDescription() { return new RTCSessionDescription.default(__privateGet(this, _peerConnection).remoteDescription()); } get remoteDescription() { return new RTCSessionDescription.default(__privateGet(this, _peerConnection).remoteDescription()); } get sctp() { return __privateGet(this, _sctp); } get signalingState() { return __privateGet(this, _peerConnection).signalingState(); } async addIceCandidate(candidate) { if (!candidate || !candidate.candidate) { return; } if (candidate.sdpMid === null && candidate.sdpMLineIndex === null) { throw new TypeError("sdpMid must be set"); } if (candidate.sdpMid === void 0 && candidate.sdpMLineIndex == void 0) { throw new TypeError("sdpMid must be set"); } if (candidate.sdpMid && candidate.sdpMid.length > 3) { throw new Exception.OperationError("Invalid sdpMid format"); } if (!candidate.sdpMid && candidate.sdpMLineIndex > 1) { throw new Exception.OperationError("This is only for test case."); } try { __privateGet(this, _peerConnection).addRemoteCandidate(candidate.candidate, candidate.sdpMid || "0"); __privateGet(this, _remoteCandidates).push( new RTCIceCandidate.default({ candidate: candidate.candidate, sdpMid: candidate.sdpMid || "0" }) ); } catch (error) { if (!error || !error.message) throw new Exception.NotFoundError("Unknown error"); if (error.message.includes("remote candidate without remote description")) throw new Exception.InvalidStateError(error.message); if (error.message.includes("Invalid candidate format")) throw new Exception.OperationError(error.message); throw new Exception.NotFoundError(error.message); } } // eslint-disable-next-line @typescript-eslint/no-unused-vars addTrack(_track, ..._streams) { throw new DOMException("Not implemented"); } // eslint-disable-next-line @typescript-eslint/no-unused-vars addTransceiver(_trackOrKind, _init) { throw new DOMException("Not implemented"); } close() { __privateGet(this, _dataChannels).forEach((channel) => { channel.close(); __privateWrapper(this, _dataChannelsClosed)._++; }); __privateGet(this, _peerConnection).close(); } createAnswer() { return __privateGet(this, _localAnswer); } createDataChannel(label, opts = {}) { const channel = __privateGet(this, _peerConnection).createDataChannel(label, opts); const dataChannel = new RTCDataChannel.default(channel, opts); __privateGet(this, _dataChannels).add(dataChannel); dataChannel.addEventListener("close", () => { __privateGet(this, _dataChannels).delete(dataChannel); __privateWrapper(this, _dataChannelsClosed)._++; }); return dataChannel; } createOffer() { return __privateGet(this, _localOffer); } getConfiguration() { return __privateGet(this, _config); } getReceivers() { throw new DOMException("Not implemented"); } getSenders() { throw new DOMException("Not implemented"); } getStats() { return new Promise((resolve) => { const report = /* @__PURE__ */ new Map(); const cp = __privateGet(this, _peerConnection)?.getSelectedCandidatePair(); const bytesSent = __privateGet(this, _peerConnection)?.bytesSent(); const bytesReceived = __privateGet(this, _peerConnection)?.bytesReceived(); const rtt = __privateGet(this, _peerConnection)?.rtt(); if (!cp) { return resolve(report); } const localIdRs = getRandomString(8); const localId = "RTCIceCandidate_" + localIdRs; report.set(localId, { id: localId, type: "local-candidate", timestamp: Date.now(), candidateType: cp.local.type, ip: cp.local.address, port: cp.local.port }); const remoteIdRs = getRandomString(8); const remoteId = "RTCIceCandidate_" + remoteIdRs; report.set(remoteId, { id: remoteId, type: "remote-candidate", timestamp: Date.now(), candidateType: cp.remote.type, ip: cp.remote.address, port: cp.remote.port }); const candidateId = "RTCIceCandidatePair_" + localIdRs + "_" + remoteIdRs; report.set(candidateId, { id: candidateId, type: "candidate-pair", timestamp: Date.now(), localCandidateId: localId, remoteCandidateId: remoteId, state: "succeeded", nominated: true, writable: true, bytesSent, bytesReceived, totalRoundTripTime: rtt, currentRoundTripTime: rtt }); const transportId = "RTCTransport_0_1"; report.set(transportId, { id: transportId, timestamp: Date.now(), type: "transport", bytesSent, bytesReceived, dtlsState: "connected", selectedCandidatePairId: candidateId, selectedCandidatePairChanges: 1 }); report.set("P", { id: "P", type: "peer-connection", timestamp: Date.now(), dataChannelsOpened: __privateGet(this, _dataChannels).size, dataChannelsClosed: __privateGet(this, _dataChannelsClosed) }); return resolve(report); }); } getTransceivers() { return []; } removeTrack() { throw new DOMException("Not implemented"); } restartIce() { throw new DOMException("Not implemented"); } async setLocalDescription(description) { if (description?.type !== "offer") { return; } __privateGet(this, _peerConnection).setLocalDescription(description?.type); } async setRemoteDescription(description) { if (description.sdp == null) { throw new DOMException("Remote SDP must be set"); } __privateGet(this, _peerConnection).setRemoteDescription(description.sdp, description.type); } } _peerConnection = new WeakMap(); _localOffer = new WeakMap(); _localAnswer = new WeakMap(); _dataChannels = new WeakMap(); _dataChannelsClosed = new WeakMap(); _config = new WeakMap(); _canTrickleIceCandidates = new WeakMap(); _sctp = new WeakMap(); _localCandidates = new WeakMap(); _remoteCandidates = new WeakMap(); function createDeferredPromise() { let resolve, reject; const promise = new Promise(function(_resolve, _reject) { resolve = _resolve; reject = _reject; }); promise.resolve = resolve; promise.reject = reject; return promise; } function getRandomString(length) { return Math.random().toString(36).substring(2, 2 + length); } exports.default = RTCPeerConnection; //# sourceMappingURL=RTCPeerConnection.cjs.map