@mothepro/fancy-p2p
Version:
A quick and efficient way to form p2p groups in the browser
114 lines • 4.78 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MockPeer = void 0;
const fancy_emitter_1 = require("fancy-emitter");
/** Simple class that can be used as a local feedback peer. */
class MockPeer {
constructor(name) {
this.name = name;
this.isYou = true;
this.message = new fancy_emitter_1.Emitter;
// Convert ArrayBufferView's to their raw buffer to match how it is over the wire.
this.send = (data) => this.message.activate(
// @ts-ignore Type 'ArrayBuffer' **is** assignable to type 'Exclude<T, ArrayBufferView>' since T mustin include Buffers and their views together
ArrayBuffer.isView(data)
? data.buffer
: data);
this.close = this.message.cancel;
this.ready = Promise.resolve(true);
}
}
exports.MockPeer = MockPeer;
class default_1 {
constructor(stuns, client, retries = 1, timeout = -1, fallback) {
this.fallback = fallback;
this.isYou = false;
this.message = new fancy_emitter_1.Emitter;
this.send = (data) => {
if (this.rtc)
this.rtc.send(data); // this is fine since browser handles casting
else if (this.fallback) {
let val;
if (data instanceof ArrayBuffer)
val = data;
else if (ArrayBuffer.isView(data))
val = data.buffer;
// TODO transform strings into buffers
else
throw Error('Only buffers can be used when sending data thru fallback server');
this.fallback.sendFallback(this.fallbackId, val);
}
else
throw Error('Unable to send data to peer directly nor thru server');
};
// TODO closing all peers should close the fallback as well
this.close = () => {
if (this.rtc)
this.rtc.destroy();
this.message.cancel();
};
this.name = client.name;
this.fallbackId = client.id;
this.ready = this.makeRtc(stuns, client, retries, timeout)
.then(() => {
this.rtc.once('close', this.message.cancel);
this.rtc.once('error', this.message.deactivate);
this.rtc.on('data', data => this.message.activate(ArrayBuffer.isView(data) ? data.buffer : data));
// TODO get rid of this.fallback?
return true;
}).catch((reason) => {
delete this.rtc;
// Switch to fallback if the direct connection still isn't made
if (this.fallback) {
this.fallback.fallbackMessage
// @ts-ignore support all T in fallback messages
.on(({ from, data }) => from == this.fallbackId && this.message.activate(data))
.then(this.close);
return false;
}
else {
// Cancel early since no events will ever occur.
this.message.cancel();
throw reason;
}
});
}
async makeRtc(stuns, client, retries, timeout) {
if (retries < 0)
throw Error('Not attempting to create a p2p connection');
// @ts-ignore from the `import 'simple-peer'`
this.rtc = new SimplePeer({
initiator: client.isOpener,
config: { iceServers: [{ urls: stuns }] },
trickle: false,
offerConstraints: {
iceRestart: true,
offerToReceiveAudio: false,
offerToReceiveVideo: false,
voiceActivityDetection: false,
},
answerConstraints: {
iceRestart: true,
offerToReceiveAudio: false,
offerToReceiveVideo: false,
voiceActivityDetection: false,
},
});
// Exchange the SDPs
// If using trickle, listen to more than just 1 event from each
this.rtc.once('signal', client.creator.activate);
client.acceptor.next.then(sdp => this.rtc.signal(sdp));
// This looks weird, but is how to handle optional chaining a set number of times (retries)
return new Promise((resolve, reject) => {
if (timeout > 0)
setTimeout(() => reject(Error(`Connection with "${this.name}" didn't become ready in ${timeout}ms`)), timeout);
this.rtc
.once('connect', resolve)
.once('error', reject);
}).catch(reason => retries > 0
? this.makeRtc(stuns, client, retries - 1, timeout)
: Promise.reject(reason));
}
}
exports.default = default_1;
//# sourceMappingURL=Peer.js.map