UNPKG

@mothepro/fancy-p2p

Version:

A quick and efficient way to form p2p groups in the browser

159 lines (158 loc) 6.84 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; Object.defineProperty(exports, "__esModule", { value: true }); const lit_element_1 = require("lit-element"); require("lit-log"); const decoder = new TextDecoder, encoder = new TextEncoder, orderTestLimit = 5e3; let default_1 = class default_1 extends lit_element_1.LitElement { constructor() { super(...arguments); this.data = ''; this.replies = 0; this.orderedMessages = []; this.log = (...detail) => this.dispatchEvent(new CustomEvent('log', { detail, bubbles: true, composed: true })) && this.requestUpdate(); this.render = () => lit_element_1.html ` <lit-log open id="log" .entry=${this.chat}> <span slot="summary">Chat</span> Peers <ul> ${[...this.peers].map(peer => lit_element_1.html ` <li @click=${this.sendDirect(peer)}> ${peer.name} ${peer.isYou ? '🌟' : ''} </li>`)} </ul> <form @submit=${this.sendData}> <input required type="text" name="data" autocomplete="off" placeholder="Message" .value=${this.data} @change=${({ target: { value } }) => this.data = value} /> <input type="submit" value="Broadcast"> </form> <button @click=${this.sendRtt}>Latency Check</button> <button @click=${this.sendRandom}>Generate Random Number</button> <button @click=${this.orderTest} title=${`Sends ${orderTestLimit} packets and peers are expected to receive them all in order.`} >Order check</button> </lit-log>`; this.sendData = (event) => { event.preventDefault(); this.dispatchEvent(new CustomEvent('broadcast', { detail: encoder.encode(this.data), bubbles: true })); this.data = ''; }; this.sendDirect = ({ name, send }) => (event) => { try { event.preventDefault(); send(encoder.encode(this.data)); this.log(`Sending ${name} "${this.data}"`); this.data = ''; } catch (err) { this.log(err); } }; this.sendRandom = (event) => { event.preventDefault(); this.dispatchEvent(new CustomEvent('broadcast', { detail: new Uint8Array([2 /* GENERATE_RANDOM */]), bubbles: true })); }; this.sendRtt = (event) => { event.preventDefault(); this.initRtt = this.elapsedTime; this.dispatchEvent(new CustomEvent('broadcast', { detail: new Uint8Array([1 /* RTT */]), bubbles: true })); }; this.orderTest = (event) => { event.preventDefault(); const detail = new DataView(new ArrayBuffer(1 + 4)); detail.setInt8(0, 0 /* CHECK */); for (let i = 0; i < orderTestLimit; i++) { detail.setUint32(1, i, true); this.dispatchEvent(new CustomEvent('broadcast', { detail, bubbles: true })); } }; } /** * Number of microseconds have passed since the page has opened. * Could be innaccurate due to https://developer.mozilla.org/en-US/docs/Web/API/Performance/now#Reduced_time_precision */ get elapsedTime() { return Math.trunc(1000 * performance.now()); } firstUpdated() { for (const peer of this.peers) this.bindMessage(peer); } async bindMessage({ message, send, name }) { try { for await (const data of message) { if (!(data instanceof ArrayBuffer)) throw Error(`${name} sent unexpected data: ${data}`); const view = new DataView(data); switch (view.getInt8(0)) { case 0 /* CHECK */: this.orderedMessages.push(view.getUint32(1, true)); if (this.orderedMessages.length == orderTestLimit) { for (let i = 0; i < this.orderedMessages.length - 1; i++) { if (this.orderedMessages[i] > this.orderedMessages[i + 1]) this.log(this.orderedMessages[i], 'should have come before', this.orderedMessages[i + 1]); } this.chat = `Finished checking order of ${orderTestLimit} messages`; this.orderedMessages.length = 0; } break; case 2 /* GENERATE_RANDOM */: this.chat = `${name} shared the random integer ${this.nextRandom} for us`; this.dispatchEvent(new CustomEvent('requestRNG', { bubbles: true })); break; case 1 /* RTT */: if (this.initRtt) { this.chat = `Round Trip Time with ${name} is ${this.elapsedTime - this.initRtt}μs`; this.replies++; } else send(new Uint8Array([1 /* RTT */])); // All living peers responded if (this.replies == this.peers.length) { delete this.initRtt; this.replies = 0; } break; default: this.chat = `${name} says "${decoder.decode(data)}"`; } } } catch (err) { this.log(err); } this.log(`Connection with ${name} closed`); } }; __decorate([ lit_element_1.internalProperty() ], default_1.prototype, "data", void 0); __decorate([ lit_element_1.internalProperty() ], default_1.prototype, "chat", void 0); __decorate([ lit_element_1.property({ type: Number, attribute: 'next-random' }) ], default_1.prototype, "nextRandom", void 0); __decorate([ lit_element_1.property({ attribute: false }) ], default_1.prototype, "peers", void 0); default_1 = __decorate([ lit_element_1.customElement('lit-ready') ], default_1); exports.default = default_1; //# sourceMappingURL=ready.js.map