peerjs
Version:
PeerJS client
1,226 lines (1,145 loc) • 72 kB
JavaScript
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