UNPKG

peerjs

Version:
1,226 lines (1,145 loc) 72 kB
var $2QID2$peerjsjsbinarypack = require("peerjs-js-binarypack"); var $2QID2$webrtcadapter = require("webrtc-adapter"); var $2QID2$eventemitter3 = require("eventemitter3"); var $2QID2$msgpackmsgpack = require("@msgpack/msgpack"); function $parcel$defineInteropFlag(a) { Object.defineProperty(a, '__esModule', {value: true, configurable: true}); } function $parcel$exportWildcard(dest, source) { Object.keys(source).forEach(function(key) { if (key === 'default' || key === '__esModule' || Object.prototype.hasOwnProperty.call(dest, key)) { return; } Object.defineProperty(dest, key, { enumerable: true, get: function get() { return source[key]; } }); }); return dest; } function $parcel$export(e, n, v, s) { Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true}); } function $parcel$interopDefault(a) { return a && a.__esModule ? a.default : a; } $parcel$defineInteropFlag(module.exports); $parcel$export(module.exports, "default", () => $8c8bca0fa9aa4b8b$export$2e2bcd8739ae039); $parcel$export(module.exports, "util", () => $b83e6a166cc3008f$export$7debb50ef11d5e0b); $parcel$export(module.exports, "BufferedConnection", () => $8d5124d0cf36ebe0$export$ff7c9d4c11d94e8b); $parcel$export(module.exports, "StreamConnection", () => $544799118fa637e6$export$72aa44612e2200cd); $parcel$export(module.exports, "MsgPack", () => $7e477efb76e02214$export$80f5de1a66c4d624); $parcel$export(module.exports, "Peer", () => $2ddecb16305b5a82$export$ecd1fc136c422448); $parcel$export(module.exports, "MsgPackPeer", () => $8c8805059443e9b3$export$d72c7bf8eef50853); $parcel$export(module.exports, "PeerError", () => $cf62563e7a9fbce5$export$98871882f492de82); class $7ce5389b504cc06c$export$f1c5f4c9cb95390b { constructor(){ this.chunkedMTU = 16300 // The original 60000 bytes setting does not work when sending data from Firefox to Chrome, which is "cut off" after 16384 bytes and delivered individually. ; // Binary stuff this._dataCount = 1; this.chunk = (blob)=>{ const chunks = []; const size = blob.byteLength; const total = Math.ceil(size / this.chunkedMTU); let index = 0; let start = 0; while(start < size){ const end = Math.min(size, start + this.chunkedMTU); const b = blob.slice(start, end); const chunk = { __peerData: this._dataCount, n: index, data: b, total: total }; chunks.push(chunk); start = end; index++; } this._dataCount++; return chunks; }; } } function $7ce5389b504cc06c$export$52c89ebcdc4f53f2(bufs) { let size = 0; for (const buf of bufs)size += buf.byteLength; const result = new Uint8Array(size); let offset = 0; for (const buf of bufs){ result.set(buf, offset); offset += buf.byteLength; } return result; } const $07e4f6a369d1179a$var$webRTCAdapter = //@ts-ignore (0, ($parcel$interopDefault($2QID2$webrtcadapter))).default || (0, ($parcel$interopDefault($2QID2$webrtcadapter))); const $07e4f6a369d1179a$export$25be9502477c137d = new class { isWebRTCSupported() { return typeof RTCPeerConnection !== "undefined"; } isBrowserSupported() { const browser = this.getBrowser(); const version = this.getVersion(); const validBrowser = this.supportedBrowsers.includes(browser); if (!validBrowser) return false; if (browser === "chrome") return version >= this.minChromeVersion; if (browser === "firefox") return version >= this.minFirefoxVersion; if (browser === "safari") return !this.isIOS && version >= this.minSafariVersion; return false; } getBrowser() { return $07e4f6a369d1179a$var$webRTCAdapter.browserDetails.browser; } getVersion() { return $07e4f6a369d1179a$var$webRTCAdapter.browserDetails.version || 0; } isUnifiedPlanSupported() { const browser = this.getBrowser(); const version = $07e4f6a369d1179a$var$webRTCAdapter.browserDetails.version || 0; if (browser === "chrome" && version < this.minChromeVersion) return false; if (browser === "firefox" && version >= this.minFirefoxVersion) return true; if (!window.RTCRtpTransceiver || !("currentDirection" in RTCRtpTransceiver.prototype)) return false; let tempPc; let supported = false; try { tempPc = new RTCPeerConnection(); tempPc.addTransceiver("audio"); supported = true; } catch (e) {} finally{ if (tempPc) tempPc.close(); } return supported; } toString() { return `Supports: browser:${this.getBrowser()} version:${this.getVersion()} isIOS:${this.isIOS} isWebRTCSupported:${this.isWebRTCSupported()} isBrowserSupported:${this.isBrowserSupported()} isUnifiedPlanSupported:${this.isUnifiedPlanSupported()}`; } constructor(){ this.isIOS = typeof navigator !== "undefined" ? [ "iPad", "iPhone", "iPod" ].includes(navigator.platform) : false; this.supportedBrowsers = [ "firefox", "chrome", "safari" ]; this.minFirefoxVersion = 59; this.minChromeVersion = 72; this.minSafariVersion = 605; } }(); const $706cd7d90eca90d6$export$f35f128fd59ea256 = (id)=>{ // Allow empty ids return !id || /^[A-Za-z0-9]+(?:[ _-][A-Za-z0-9]+)*$/.test(id); }; const $6a375544f634961e$export$4e61f672936bec77 = ()=>Math.random().toString(36).slice(2); const $b83e6a166cc3008f$var$DEFAULT_CONFIG = { iceServers: [ { urls: "stun:stun.l.google.com:19302" }, { urls: [ "turn:eu-0.turn.peerjs.com:3478", "turn:us-0.turn.peerjs.com:3478" ], username: "peerjs", credential: "peerjsp" } ], sdpSemantics: "unified-plan" }; class $b83e6a166cc3008f$export$f8f26dd395d7e1bd extends (0, $7ce5389b504cc06c$export$f1c5f4c9cb95390b) { noop() {} blobToArrayBuffer(blob, cb) { const fr = new FileReader(); fr.onload = function(evt) { if (evt.target) cb(evt.target.result); }; fr.readAsArrayBuffer(blob); return fr; } binaryStringToArrayBuffer(binary) { const byteArray = new Uint8Array(binary.length); for(let i = 0; i < binary.length; i++)byteArray[i] = binary.charCodeAt(i) & 0xff; return byteArray.buffer; } isSecure() { return location.protocol === "https:"; } constructor(...args){ super(...args), this.CLOUD_HOST = "0.peerjs.com", this.CLOUD_PORT = 443, // Browsers that need chunking: this.chunkedBrowsers = { Chrome: 1, chrome: 1 }, // Returns browser-agnostic default config this.defaultConfig = $b83e6a166cc3008f$var$DEFAULT_CONFIG, this.browser = (0, $07e4f6a369d1179a$export$25be9502477c137d).getBrowser(), this.browserVersion = (0, $07e4f6a369d1179a$export$25be9502477c137d).getVersion(), this.pack = $2QID2$peerjsjsbinarypack.pack, this.unpack = $2QID2$peerjsjsbinarypack.unpack, /** * A hash of WebRTC features mapped to booleans that correspond to whether the feature is supported by the current browser. * * :::caution * Only the properties documented here are guaranteed to be present on `util.supports` * ::: */ this.supports = function() { const supported = { browser: (0, $07e4f6a369d1179a$export$25be9502477c137d).isBrowserSupported(), webRTC: (0, $07e4f6a369d1179a$export$25be9502477c137d).isWebRTCSupported(), audioVideo: false, data: false, binaryBlob: false, reliable: false }; if (!supported.webRTC) return supported; let pc; try { pc = new RTCPeerConnection($b83e6a166cc3008f$var$DEFAULT_CONFIG); supported.audioVideo = true; let dc; try { dc = pc.createDataChannel("_PEERJSTEST", { ordered: true }); supported.data = true; supported.reliable = !!dc.ordered; // Binary test try { dc.binaryType = "blob"; supported.binaryBlob = !(0, $07e4f6a369d1179a$export$25be9502477c137d).isIOS; } catch (e) {} } catch (e) {} finally{ if (dc) dc.close(); } } catch (e) {} finally{ if (pc) pc.close(); } return supported; }(), // Ensure alphanumeric ids this.validateId = (0, $706cd7d90eca90d6$export$f35f128fd59ea256), this.randomToken = (0, $6a375544f634961e$export$4e61f672936bec77); } } const $b83e6a166cc3008f$export$7debb50ef11d5e0b = new $b83e6a166cc3008f$export$f8f26dd395d7e1bd(); const $df9d8b89ee908b8b$var$LOG_PREFIX = "PeerJS: "; var $df9d8b89ee908b8b$export$243e62d78d3b544d = /*#__PURE__*/ function(LogLevel) { /** * Prints no logs. */ LogLevel[LogLevel["Disabled"] = 0] = "Disabled"; /** * Prints only errors. */ LogLevel[LogLevel["Errors"] = 1] = "Errors"; /** * Prints errors and warnings. */ LogLevel[LogLevel["Warnings"] = 2] = "Warnings"; /** * Prints all logs. */ LogLevel[LogLevel["All"] = 3] = "All"; return LogLevel; }({}); class $df9d8b89ee908b8b$var$Logger { get logLevel() { return this._logLevel; } set logLevel(logLevel) { this._logLevel = logLevel; } log(...args) { if (this._logLevel >= 3) this._print(3, ...args); } warn(...args) { if (this._logLevel >= 2) this._print(2, ...args); } error(...args) { if (this._logLevel >= 1) this._print(1, ...args); } setLogFunction(fn) { this._print = fn; } _print(logLevel, ...rest) { const copy = [ $df9d8b89ee908b8b$var$LOG_PREFIX, ...rest ]; for(const i in copy)if (copy[i] instanceof Error) copy[i] = "(" + copy[i].name + ") " + copy[i].message; if (logLevel >= 3) console.log(...copy); else if (logLevel >= 2) console.warn("WARNING", ...copy); else if (logLevel >= 1) console.error("ERROR", ...copy); } constructor(){ this._logLevel = 0; } } var $df9d8b89ee908b8b$export$2e2bcd8739ae039 = new $df9d8b89ee908b8b$var$Logger(); var $1a7e7edd560505fc$exports = {}; $parcel$export($1a7e7edd560505fc$exports, "ConnectionType", () => $1a7e7edd560505fc$export$3157d57b4135e3bc); $parcel$export($1a7e7edd560505fc$exports, "PeerErrorType", () => $1a7e7edd560505fc$export$9547aaa2e39030ff); $parcel$export($1a7e7edd560505fc$exports, "BaseConnectionErrorType", () => $1a7e7edd560505fc$export$7974935686149686); $parcel$export($1a7e7edd560505fc$exports, "DataConnectionErrorType", () => $1a7e7edd560505fc$export$49ae800c114df41d); $parcel$export($1a7e7edd560505fc$exports, "SerializationType", () => $1a7e7edd560505fc$export$89f507cf986a947); $parcel$export($1a7e7edd560505fc$exports, "SocketEventType", () => $1a7e7edd560505fc$export$3b5c4a4b6354f023); $parcel$export($1a7e7edd560505fc$exports, "ServerMessageType", () => $1a7e7edd560505fc$export$adb4a1754da6f10d); var $1a7e7edd560505fc$export$3157d57b4135e3bc = /*#__PURE__*/ function(ConnectionType) { ConnectionType["Data"] = "data"; ConnectionType["Media"] = "media"; return ConnectionType; }({}); var $1a7e7edd560505fc$export$9547aaa2e39030ff = /*#__PURE__*/ function(PeerErrorType) { /** * The client's browser does not support some or all WebRTC features that you are trying to use. */ PeerErrorType["BrowserIncompatible"] = "browser-incompatible"; /** * You've already disconnected this peer from the server and can no longer make any new connections on it. */ PeerErrorType["Disconnected"] = "disconnected"; /** * The ID passed into the Peer constructor contains illegal characters. */ PeerErrorType["InvalidID"] = "invalid-id"; /** * The API key passed into the Peer constructor contains illegal characters or is not in the system (cloud server only). */ PeerErrorType["InvalidKey"] = "invalid-key"; /** * Lost or cannot establish a connection to the signalling server. */ PeerErrorType["Network"] = "network"; /** * The peer you're trying to connect to does not exist. */ PeerErrorType["PeerUnavailable"] = "peer-unavailable"; /** * PeerJS is being used securely, but the cloud server does not support SSL. Use a custom PeerServer. */ PeerErrorType["SslUnavailable"] = "ssl-unavailable"; /** * Unable to reach the server. */ PeerErrorType["ServerError"] = "server-error"; /** * An error from the underlying socket. */ PeerErrorType["SocketError"] = "socket-error"; /** * The underlying socket closed unexpectedly. */ PeerErrorType["SocketClosed"] = "socket-closed"; /** * The ID passed into the Peer constructor is already taken. * * :::caution * This error is not fatal if your peer has open peer-to-peer connections. * This can happen if you attempt to {@apilink Peer.reconnect} a peer that has been disconnected from the server, * but its old ID has now been taken. * ::: */ PeerErrorType["UnavailableID"] = "unavailable-id"; /** * Native WebRTC errors. */ PeerErrorType["WebRTC"] = "webrtc"; return PeerErrorType; }({}); var $1a7e7edd560505fc$export$7974935686149686 = /*#__PURE__*/ function(BaseConnectionErrorType) { BaseConnectionErrorType["NegotiationFailed"] = "negotiation-failed"; BaseConnectionErrorType["ConnectionClosed"] = "connection-closed"; return BaseConnectionErrorType; }({}); var $1a7e7edd560505fc$export$49ae800c114df41d = /*#__PURE__*/ function(DataConnectionErrorType) { DataConnectionErrorType["NotOpenYet"] = "not-open-yet"; DataConnectionErrorType["MessageToBig"] = "message-too-big"; return DataConnectionErrorType; }({}); var $1a7e7edd560505fc$export$89f507cf986a947 = /*#__PURE__*/ function(SerializationType) { SerializationType["Binary"] = "binary"; SerializationType["BinaryUTF8"] = "binary-utf8"; SerializationType["JSON"] = "json"; SerializationType["None"] = "raw"; return SerializationType; }({}); var $1a7e7edd560505fc$export$3b5c4a4b6354f023 = /*#__PURE__*/ function(SocketEventType) { SocketEventType["Message"] = "message"; SocketEventType["Disconnected"] = "disconnected"; SocketEventType["Error"] = "error"; SocketEventType["Close"] = "close"; return SocketEventType; }({}); var $1a7e7edd560505fc$export$adb4a1754da6f10d = /*#__PURE__*/ function(ServerMessageType) { ServerMessageType["Heartbeat"] = "HEARTBEAT"; ServerMessageType["Candidate"] = "CANDIDATE"; ServerMessageType["Offer"] = "OFFER"; ServerMessageType["Answer"] = "ANSWER"; ServerMessageType["Open"] = "OPEN"; ServerMessageType["Error"] = "ERROR"; ServerMessageType["IdTaken"] = "ID-TAKEN"; ServerMessageType["InvalidKey"] = "INVALID-KEY"; ServerMessageType["Leave"] = "LEAVE"; ServerMessageType["Expire"] = "EXPIRE"; return ServerMessageType; }({}); const $3a25eea6a06ee968$export$83d89fbfd8236492 = "1.5.5"; class $e5e868bf3ea73e5b$export$4798917dbf149b79 extends (0, $2QID2$eventemitter3.EventEmitter) { constructor(secure, host, port, path, key, pingInterval = 5000){ super(), this.pingInterval = pingInterval, this._disconnected = true, this._messagesQueue = []; const wsProtocol = secure ? "wss://" : "ws://"; this._baseUrl = wsProtocol + host + ":" + port + path + "peerjs?key=" + key; } start(id, token) { this._id = id; const wsUrl = `${this._baseUrl}&id=${id}&token=${token}`; if (!!this._socket || !this._disconnected) return; this._socket = new WebSocket(wsUrl + "&version=" + (0, $3a25eea6a06ee968$export$83d89fbfd8236492)); this._disconnected = false; this._socket.onmessage = (event)=>{ let data; try { data = JSON.parse(event.data); (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log("Server message received:", data); } catch (e) { (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log("Invalid server message", event.data); return; } this.emit((0, $1a7e7edd560505fc$export$3b5c4a4b6354f023).Message, data); }; this._socket.onclose = (event)=>{ if (this._disconnected) return; (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log("Socket closed.", event); this._cleanup(); this._disconnected = true; this.emit((0, $1a7e7edd560505fc$export$3b5c4a4b6354f023).Disconnected); }; // Take care of the queue of connections if necessary and make sure Peer knows // socket is open. this._socket.onopen = ()=>{ if (this._disconnected) return; this._sendQueuedMessages(); (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log("Socket open"); this._scheduleHeartbeat(); }; } _scheduleHeartbeat() { this._wsPingTimer = setTimeout(()=>{ this._sendHeartbeat(); }, this.pingInterval); } _sendHeartbeat() { if (!this._wsOpen()) { (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log(`Cannot send heartbeat, because socket closed`); return; } const message = JSON.stringify({ type: (0, $1a7e7edd560505fc$export$adb4a1754da6f10d).Heartbeat }); this._socket.send(message); this._scheduleHeartbeat(); } /** Is the websocket currently open? */ _wsOpen() { return !!this._socket && this._socket.readyState === 1; } /** Send queued messages. */ _sendQueuedMessages() { //Create copy of queue and clear it, //because send method push the message back to queue if smth will go wrong const copiedQueue = [ ...this._messagesQueue ]; this._messagesQueue = []; for (const message of copiedQueue)this.send(message); } /** Exposed send for DC & Peer. */ send(data) { if (this._disconnected) return; // If we didn't get an ID yet, we can't yet send anything so we should queue // up these messages. if (!this._id) { this._messagesQueue.push(data); return; } if (!data.type) { this.emit((0, $1a7e7edd560505fc$export$3b5c4a4b6354f023).Error, "Invalid message"); return; } if (!this._wsOpen()) return; const message = JSON.stringify(data); this._socket.send(message); } close() { if (this._disconnected) return; this._cleanup(); this._disconnected = true; } _cleanup() { if (this._socket) { this._socket.onopen = this._socket.onmessage = this._socket.onclose = null; this._socket.close(); this._socket = undefined; } clearTimeout(this._wsPingTimer); } } class $a8347a6741c5df8a$export$89e6bb5ad64bf4a { constructor(connection){ this.connection = connection; } /** Returns a PeerConnection object set up correctly (for data, media). */ startConnection(options) { const peerConnection = this._startPeerConnection(); // Set the connection's PC. this.connection.peerConnection = peerConnection; if (this.connection.type === (0, $1a7e7edd560505fc$export$3157d57b4135e3bc).Media && options._stream) this._addTracksToConnection(options._stream, peerConnection); // What do we need to do now? if (options.originator) { const dataConnection = this.connection; const config = { ordered: !!options.reliable }; const dataChannel = peerConnection.createDataChannel(dataConnection.label, config); dataConnection._initializeDataChannel(dataChannel); this._makeOffer(); } else this.handleSDP("OFFER", options.sdp); } /** Start a PC. */ _startPeerConnection() { (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log("Creating RTCPeerConnection."); const peerConnection = new RTCPeerConnection(this.connection.provider.options.config); this._setupListeners(peerConnection); return peerConnection; } /** Set up various WebRTC listeners. */ _setupListeners(peerConnection) { const peerId = this.connection.peer; const connectionId = this.connection.connectionId; const connectionType = this.connection.type; const provider = this.connection.provider; // ICE CANDIDATES. (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log("Listening for ICE candidates."); peerConnection.onicecandidate = (evt)=>{ if (!evt.candidate || !evt.candidate.candidate) return; (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log(`Received ICE candidates for ${peerId}:`, evt.candidate); provider.socket.send({ type: (0, $1a7e7edd560505fc$export$adb4a1754da6f10d).Candidate, payload: { candidate: evt.candidate, type: connectionType, connectionId: connectionId }, dst: peerId }); }; peerConnection.oniceconnectionstatechange = ()=>{ switch(peerConnection.iceConnectionState){ case "failed": (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log("iceConnectionState is failed, closing connections to " + peerId); this.connection.emitError((0, $1a7e7edd560505fc$export$7974935686149686).NegotiationFailed, "Negotiation of connection to " + peerId + " failed."); this.connection.close(); break; case "closed": (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log("iceConnectionState is closed, closing connections to " + peerId); this.connection.emitError((0, $1a7e7edd560505fc$export$7974935686149686).ConnectionClosed, "Connection to " + peerId + " closed."); this.connection.close(); break; case "disconnected": (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log("iceConnectionState changed to disconnected on the connection with " + peerId); break; case "completed": peerConnection.onicecandidate = ()=>{}; break; } this.connection.emit("iceStateChanged", peerConnection.iceConnectionState); }; // DATACONNECTION. (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log("Listening for data channel"); // Fired between offer and answer, so options should already be saved // in the options hash. peerConnection.ondatachannel = (evt)=>{ (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log("Received data channel"); const dataChannel = evt.channel; const connection = provider.getConnection(peerId, connectionId); connection._initializeDataChannel(dataChannel); }; // MEDIACONNECTION. (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log("Listening for remote stream"); peerConnection.ontrack = (evt)=>{ (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log("Received remote stream"); const stream = evt.streams[0]; const connection = provider.getConnection(peerId, connectionId); if (connection.type === (0, $1a7e7edd560505fc$export$3157d57b4135e3bc).Media) { const mediaConnection = connection; this._addStreamToMediaConnection(stream, mediaConnection); } }; } cleanup() { (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log("Cleaning up PeerConnection to " + this.connection.peer); const peerConnection = this.connection.peerConnection; if (!peerConnection) return; this.connection.peerConnection = null; //unsubscribe from all PeerConnection's events peerConnection.onicecandidate = peerConnection.oniceconnectionstatechange = peerConnection.ondatachannel = peerConnection.ontrack = ()=>{}; const peerConnectionNotClosed = peerConnection.signalingState !== "closed"; let dataChannelNotClosed = false; const dataChannel = this.connection.dataChannel; if (dataChannel) dataChannelNotClosed = !!dataChannel.readyState && dataChannel.readyState !== "closed"; if (peerConnectionNotClosed || dataChannelNotClosed) peerConnection.close(); } async _makeOffer() { const peerConnection = this.connection.peerConnection; const provider = this.connection.provider; try { const offer = await peerConnection.createOffer(this.connection.options.constraints); (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log("Created offer."); if (this.connection.options.sdpTransform && typeof this.connection.options.sdpTransform === "function") offer.sdp = this.connection.options.sdpTransform(offer.sdp) || offer.sdp; try { await peerConnection.setLocalDescription(offer); (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log("Set localDescription:", offer, `for:${this.connection.peer}`); let payload = { sdp: offer, type: this.connection.type, connectionId: this.connection.connectionId, metadata: this.connection.metadata }; if (this.connection.type === (0, $1a7e7edd560505fc$export$3157d57b4135e3bc).Data) { const dataConnection = this.connection; payload = { ...payload, label: dataConnection.label, reliable: dataConnection.reliable, serialization: dataConnection.serialization }; } provider.socket.send({ type: (0, $1a7e7edd560505fc$export$adb4a1754da6f10d).Offer, payload: payload, dst: this.connection.peer }); } catch (err) { // TODO: investigate why _makeOffer is being called from the answer if (err != "OperationError: Failed to set local offer sdp: Called in wrong state: kHaveRemoteOffer") { provider.emitError((0, $1a7e7edd560505fc$export$9547aaa2e39030ff).WebRTC, err); (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log("Failed to setLocalDescription, ", err); } } } catch (err_1) { provider.emitError((0, $1a7e7edd560505fc$export$9547aaa2e39030ff).WebRTC, err_1); (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log("Failed to createOffer, ", err_1); } } async _makeAnswer() { const peerConnection = this.connection.peerConnection; const provider = this.connection.provider; try { const answer = await peerConnection.createAnswer(); (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log("Created answer."); if (this.connection.options.sdpTransform && typeof this.connection.options.sdpTransform === "function") answer.sdp = this.connection.options.sdpTransform(answer.sdp) || answer.sdp; try { await peerConnection.setLocalDescription(answer); (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log(`Set localDescription:`, answer, `for:${this.connection.peer}`); provider.socket.send({ type: (0, $1a7e7edd560505fc$export$adb4a1754da6f10d).Answer, payload: { sdp: answer, type: this.connection.type, connectionId: this.connection.connectionId }, dst: this.connection.peer }); } catch (err) { provider.emitError((0, $1a7e7edd560505fc$export$9547aaa2e39030ff).WebRTC, err); (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log("Failed to setLocalDescription, ", err); } } catch (err_1) { provider.emitError((0, $1a7e7edd560505fc$export$9547aaa2e39030ff).WebRTC, err_1); (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log("Failed to create answer, ", err_1); } } /** Handle an SDP. */ async handleSDP(type, sdp) { sdp = new RTCSessionDescription(sdp); const peerConnection = this.connection.peerConnection; const provider = this.connection.provider; (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log("Setting remote description", sdp); const self = this; try { await peerConnection.setRemoteDescription(sdp); (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log(`Set remoteDescription:${type} for:${this.connection.peer}`); if (type === "OFFER") await self._makeAnswer(); } catch (err) { provider.emitError((0, $1a7e7edd560505fc$export$9547aaa2e39030ff).WebRTC, err); (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log("Failed to setRemoteDescription, ", err); } } /** Handle a candidate. */ async handleCandidate(ice) { (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log(`handleCandidate:`, ice); try { await this.connection.peerConnection.addIceCandidate(ice); (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log(`Added ICE candidate for:${this.connection.peer}`); } catch (err) { this.connection.provider.emitError((0, $1a7e7edd560505fc$export$9547aaa2e39030ff).WebRTC, err); (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log("Failed to handleCandidate, ", err); } } _addTracksToConnection(stream, peerConnection) { (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log(`add tracks from stream ${stream.id} to peer connection`); if (!peerConnection.addTrack) return (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).error(`Your browser does't support RTCPeerConnection#addTrack. Ignored.`); stream.getTracks().forEach((track)=>{ peerConnection.addTrack(track, stream); }); } _addStreamToMediaConnection(stream, mediaConnection) { (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log(`add stream ${stream.id} to media connection ${mediaConnection.connectionId}`); mediaConnection.addStream(stream); } } class $cf62563e7a9fbce5$export$6a678e589c8a4542 extends (0, $2QID2$eventemitter3.EventEmitter) { /** * Emits a typed error message. * * @internal */ emitError(type, err) { (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).error("Error:", err); // @ts-ignore this.emit("error", new $cf62563e7a9fbce5$export$98871882f492de82(`${type}`, err)); } } class $cf62563e7a9fbce5$export$98871882f492de82 extends Error { /** * @internal */ constructor(type, err){ if (typeof err === "string") super(err); else { super(); Object.assign(this, err); } this.type = type; } } class $cb834ab0363d9153$export$23a2a68283c24d80 extends (0, $cf62563e7a9fbce5$export$6a678e589c8a4542) { /** * Whether the media connection is active (e.g. your call has been answered). * You can check this if you want to set a maximum wait time for a one-sided call. */ get open() { return this._open; } constructor(/** * The ID of the peer on the other end of this connection. */ peer, provider, options){ super(), this.peer = peer, this.provider = provider, this.options = options, this._open = false; this.metadata = options.metadata; } } class $f3a554d4328c6b5f$export$4a84e95a2324ac29 extends (0, $cb834ab0363d9153$export$23a2a68283c24d80) { static #_ = this.ID_PREFIX = "mc_"; /** * For media connections, this is always 'media'. */ get type() { return (0, $1a7e7edd560505fc$export$3157d57b4135e3bc).Media; } get localStream() { return this._localStream; } get remoteStream() { return this._remoteStream; } constructor(peerId, provider, options){ super(peerId, provider, options); this._localStream = this.options._stream; this.connectionId = this.options.connectionId || $f3a554d4328c6b5f$export$4a84e95a2324ac29.ID_PREFIX + (0, $b83e6a166cc3008f$export$7debb50ef11d5e0b).randomToken(); this._negotiator = new (0, $a8347a6741c5df8a$export$89e6bb5ad64bf4a)(this); if (this._localStream) this._negotiator.startConnection({ _stream: this._localStream, originator: true }); } /** Called by the Negotiator when the DataChannel is ready. */ _initializeDataChannel(dc) { this.dataChannel = dc; this.dataChannel.onopen = ()=>{ (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log(`DC#${this.connectionId} dc connection success`); this.emit("willCloseOnRemote"); }; this.dataChannel.onclose = ()=>{ (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log(`DC#${this.connectionId} dc closed for:`, this.peer); this.close(); }; } addStream(remoteStream) { (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log("Receiving stream", remoteStream); this._remoteStream = remoteStream; super.emit("stream", remoteStream); // Should we call this `open`? } /** * @internal */ handleMessage(message) { const type = message.type; const payload = message.payload; switch(message.type){ case (0, $1a7e7edd560505fc$export$adb4a1754da6f10d).Answer: // Forward to negotiator this._negotiator.handleSDP(type, payload.sdp); this._open = true; break; case (0, $1a7e7edd560505fc$export$adb4a1754da6f10d).Candidate: this._negotiator.handleCandidate(payload.candidate); break; default: (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).warn(`Unrecognized message type:${type} from peer:${this.peer}`); break; } } /** * When receiving a {@apilink PeerEvents | `call`} event on a peer, you can call * `answer` on the media connection provided by the callback to accept the call * and optionally send your own media stream. * * @param stream A WebRTC media stream. * @param options * @returns */ answer(stream, options = {}) { if (this._localStream) { (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).warn("Local stream already exists on this MediaConnection. Are you answering a call twice?"); return; } this._localStream = stream; if (options && options.sdpTransform) this.options.sdpTransform = options.sdpTransform; this._negotiator.startConnection({ ...this.options._payload, _stream: stream }); // Retrieve lost messages stored because PeerConnection not set up. const messages = this.provider._getMessages(this.connectionId); for (const message of messages)this.handleMessage(message); this._open = true; } /** * Exposed functionality for users. */ /** * Closes the media connection. */ close() { if (this._negotiator) { this._negotiator.cleanup(); this._negotiator = null; } this._localStream = null; this._remoteStream = null; if (this.provider) { this.provider._removeConnection(this); this.provider = null; } if (this.options && this.options._stream) this.options._stream = null; if (!this.open) return; this._open = false; super.emit("close"); } } class $684fc411629b137b$export$2c4e825dc9120f87 { constructor(_options){ this._options = _options; } _buildRequest(method) { const protocol = this._options.secure ? "https" : "http"; const { host: host, port: port, path: path, key: key } = this._options; const url = new URL(`${protocol}://${host}:${port}${path}${key}/${method}`); // TODO: Why timestamp, why random? url.searchParams.set("ts", `${Date.now()}${Math.random()}`); url.searchParams.set("version", (0, $3a25eea6a06ee968$export$83d89fbfd8236492)); return fetch(url.href, { referrerPolicy: this._options.referrerPolicy }); } /** Get a unique ID from the server via XHR and initialize with it. */ async retrieveId() { try { const response = await this._buildRequest("id"); if (response.status !== 200) throw new Error(`Error. Status:${response.status}`); return response.text(); } catch (error) { (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).error("Error retrieving ID", error); let pathError = ""; if (this._options.path === "/" && this._options.host !== (0, $b83e6a166cc3008f$export$7debb50ef11d5e0b).CLOUD_HOST) pathError = " If you passed in a `path` to your self-hosted PeerServer, you'll also need to pass in that same path when creating a new Peer."; throw new Error("Could not get an ID from the server." + pathError); } } /** @deprecated */ async listAllPeers() { try { const response = await this._buildRequest("peers"); if (response.status !== 200) { if (response.status === 401) { let helpfulError = ""; if (this._options.host === (0, $b83e6a166cc3008f$export$7debb50ef11d5e0b).CLOUD_HOST) helpfulError = "It looks like you're using the cloud server. You can email team@peerjs.com to enable peer listing for your API key."; else helpfulError = "You need to enable `allow_discovery` on your self-hosted PeerServer to use this feature."; throw new Error("It doesn't look like you have permission to list peers IDs. " + helpfulError); } throw new Error(`Error. Status:${response.status}`); } return response.json(); } catch (error) { (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).error("Error retrieving list peers", error); throw new Error("Could not get list peers from the server." + error); } } } class $f188f8cb0f63b180$export$d365f7ad9d7df9c9 extends (0, $cb834ab0363d9153$export$23a2a68283c24d80) { static #_ = this.ID_PREFIX = "dc_"; static #_2 = this.MAX_BUFFERED_AMOUNT = 8388608; get type() { return (0, $1a7e7edd560505fc$export$3157d57b4135e3bc).Data; } constructor(peerId, provider, options){ super(peerId, provider, options); this.connectionId = this.options.connectionId || $f188f8cb0f63b180$export$d365f7ad9d7df9c9.ID_PREFIX + (0, $6a375544f634961e$export$4e61f672936bec77)(); this.label = this.options.label || this.connectionId; this.reliable = !!this.options.reliable; this._negotiator = new (0, $a8347a6741c5df8a$export$89e6bb5ad64bf4a)(this); this._negotiator.startConnection(this.options._payload || { originator: true, reliable: this.reliable }); } /** Called by the Negotiator when the DataChannel is ready. */ _initializeDataChannel(dc) { this.dataChannel = dc; this.dataChannel.onopen = ()=>{ (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log(`DC#${this.connectionId} dc connection success`); this._open = true; this.emit("open"); }; this.dataChannel.onmessage = (e)=>{ (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log(`DC#${this.connectionId} dc onmessage:`, e.data); // this._handleDataMessage(e); }; this.dataChannel.onclose = ()=>{ (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log(`DC#${this.connectionId} dc closed for:`, this.peer); this.close(); }; } /** * Exposed functionality for users. */ /** Allows user to close connection. */ close(options) { if (options?.flush) { this.send({ __peerData: { type: "close" } }); return; } if (this._negotiator) { this._negotiator.cleanup(); this._negotiator = null; } if (this.provider) { this.provider._removeConnection(this); this.provider = null; } if (this.dataChannel) { this.dataChannel.onopen = null; this.dataChannel.onmessage = null; this.dataChannel.onclose = null; this.dataChannel = null; } if (!this.open) return; this._open = false; super.emit("close"); } /** Allows user to send data. */ send(data, chunked = false) { if (!this.open) { this.emitError((0, $1a7e7edd560505fc$export$49ae800c114df41d).NotOpenYet, "Connection is not open. You should listen for the `open` event before sending messages."); return; } return this._send(data, chunked); } async handleMessage(message) { const payload = message.payload; switch(message.type){ case (0, $1a7e7edd560505fc$export$adb4a1754da6f10d).Answer: await this._negotiator.handleSDP(message.type, payload.sdp); break; case (0, $1a7e7edd560505fc$export$adb4a1754da6f10d).Candidate: await this._negotiator.handleCandidate(payload.candidate); break; default: (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).warn("Unrecognized message type:", message.type, "from peer:", this.peer); break; } } } class $8d5124d0cf36ebe0$export$ff7c9d4c11d94e8b extends (0, $f188f8cb0f63b180$export$d365f7ad9d7df9c9) { get bufferSize() { return this._bufferSize; } _initializeDataChannel(dc) { super._initializeDataChannel(dc); this.dataChannel.binaryType = "arraybuffer"; this.dataChannel.addEventListener("message", (e)=>this._handleDataMessage(e)); } _bufferedSend(msg) { if (this._buffering || !this._trySend(msg)) { this._buffer.push(msg); this._bufferSize = this._buffer.length; } } // Returns true if the send succeeds. _trySend(msg) { if (!this.open) return false; if (this.dataChannel.bufferedAmount > (0, $f188f8cb0f63b180$export$d365f7ad9d7df9c9).MAX_BUFFERED_AMOUNT) { this._buffering = true; setTimeout(()=>{ this._buffering = false; this._tryBuffer(); }, 50); return false; } try { this.dataChannel.send(msg); } catch (e) { (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).error(`DC#:${this.connectionId} Error when sending:`, e); this._buffering = true; this.close(); return false; } return true; } // Try to send the first message in the buffer. _tryBuffer() { if (!this.open) return; if (this._buffer.length === 0) return; const msg = this._buffer[0]; if (this._trySend(msg)) { this._buffer.shift(); this._bufferSize = this._buffer.length; this._tryBuffer(); } } close(options) { if (options?.flush) { this.send({ __peerData: { type: "close" } }); return; } this._buffer = []; this._bufferSize = 0; super.close(); } constructor(...args){ super(...args), this._buffer = [], this._bufferSize = 0, this._buffering = false; } } class $9cfea3ad93e740b9$export$f0a5a64d5bb37108 extends (0, $8d5124d0cf36ebe0$export$ff7c9d4c11d94e8b) { close(options) { super.close(options); this._chunkedData = {}; } constructor(peerId, provider, options){ super(peerId, provider, options), this.chunker = new (0, $7ce5389b504cc06c$export$f1c5f4c9cb95390b)(), this.serialization = (0, $1a7e7edd560505fc$export$89f507cf986a947).Binary, this._chunkedData = {}; } // Handles a DataChannel message. _handleDataMessage({ data: data }) { const deserializedData = (0, $2QID2$peerjsjsbinarypack.unpack)(data); // PeerJS specific message const peerData = deserializedData["__peerData"]; if (peerData) { if (peerData.type === "close") { this.close(); return; } // Chunked data -- piece things back together. // @ts-ignore this._handleChunk(deserializedData); return; } this.emit("data", deserializedData); } _handleChunk(data) { const id = data.__peerData; const chunkInfo = this._chunkedData[id] || { data: [], count: 0, total: data.total }; chunkInfo.data[data.n] = new Uint8Array(data.data); chunkInfo.count++; this._chunkedData[id] = chunkInfo; if (chunkInfo.total === chunkInfo.count) { // Clean up before making the recursive call to `_handleDataMessage`. delete this._chunkedData[id]; // We've received all the chunks--time to construct the complete data. // const data = new Blob(chunkInfo.data); const data = (0, $7ce5389b504cc06c$export$52c89ebcdc4f53f2)(chunkInfo.data); this._handleDataMessage({ data: data }); } } _send(data, chunked) { const blob = (0, $2QID2$peerjsjsbinarypack.pack)(data); if (blob instanceof Promise) return this._send_blob(blob); if (!chunked && blob.byteLength > this.chunker.chunkedMTU) { this._sendChunks(blob); return; } this._bufferedSend(blob); } async _send_blob(blobPromise) { const blob = await blobPromise; if (blob.byteLength > this.chunker.chunkedMTU) { this._sendChunks(blob); return; } this._bufferedSend(blob); } _sendChunks(blob) { const blobs = this.chunker.chunk(blob); (0, $df9d8b89ee908b8b$export$2e2bcd8739ae039).log(`DC#${this.connectionId} Try to send ${blobs.length} chunks...`); for (const blob of blobs)this.send(blob, true); } } class $c1c7a35edd5f55d2$export$6f88fe47d32c9c94 extends (0, $8d5124d0cf36ebe0$export$ff7c9d4c11d94e8b) { _handleDataMessage({ data: data }) { super.emit("data", data); } _send(data, _chunked) { this._bufferedSend(data); } constructor(...args){ super(...args), this.serialization = (0, $1a7e7edd560505fc$export$89f507cf986a947).None; } } class $f3415bb65bf67923$export$48880ac635f47186 extends (0, $8d5124d0cf36ebe0$export$ff7c9d4c11d94e8b) { // Handles a DataChannel message. _handleDataMessage({ data: data }) { const deserializedData = this.parse(this.decoder.decode(data)); // PeerJS specific message const peerData = deserializedData["__peerData"]; if (peerData && peerData.type === "close") { this.close(); return; } this.emit("data", deserializedData); } _send(data, _chunked) { const encodedData = this.encoder.encode(this.stringify(data)); if (encodedData.byteLength >= (0, $b83e6a166cc3008f$export$7debb50ef11d5e0b).chunkedMTU) { this.emitError((0, $1a7e7edd560505fc$export$49ae800c114df41d).MessageToBig, "Message too big for JSON channel"); return; } this._bufferedSend(encodedData); } constructor(...args){ super(...args), this.serialization = (0, $1a7e7edd560505fc$export$89f507cf986a947).JSON, this.encoder = new TextEncoder(), this.decoder = new TextDecoder(), this.stringify = JSON.stringify, this.parse = JSON.parse; } } class $2ddecb16305b5a82$var$PeerOptions { } class $2ddecb16305b5a82$export$ecd1fc136c422448 extends (0, $cf62563e7a9fbce5$export$6a678e589c8a4542) { static #_ = this.DEFAULT_KEY = "peerjs"; /** * The brokering ID of this peer * * If no ID was specified in {@apilink Peer | the constructor}, * this will be `undefined` until the {@apilink PeerEvents | `open`} event is emitted. */ get id() { return this._id; } get options() { return this._options; } get open() { return this._open; } /** * @internal */ get socket() { return this._socket; } /** * A hash of all connections associated with thi