UNPKG

@wordpress/sync

Version:
333 lines (331 loc) 11.1 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // packages/sync/src/providers/webrtc-http-stream-signaling.js var webrtc_http_stream_signaling_exports = {}; __export(webrtc_http_stream_signaling_exports, { HttpSignalingConn: () => HttpSignalingConn, WebrtcProviderWithHttpSignaling: () => WebrtcProviderWithHttpSignaling }); module.exports = __toCommonJS(webrtc_http_stream_signaling_exports); var import_y_webrtc = require("./y-webrtc/y-webrtc"); var cryptoutils = __toESM(require("./y-webrtc/crypto")); var map = __toESM(require("lib0/map")); var import_observable = require("lib0/observable"); var buffer = __toESM(require("lib0/buffer")); var import_url = require("@wordpress/url"); function setupSignalEventHandlers(signalCon, url) { signalCon.on("connect", () => { (0, import_y_webrtc.log)(`connected (${url})`); const topics = Array.from(import_y_webrtc.rooms.keys()); signalCon.send({ type: "subscribe", topics }); import_y_webrtc.rooms.forEach( (room) => (0, import_y_webrtc.publishSignalingMessage)(signalCon, room, { type: "announce", from: room.peerId }) ); }); signalCon.on( "message", (m) => { switch (m.type) { case "publish": { const roomName = m.topic; const room = import_y_webrtc.rooms.get(roomName); if (room === null || typeof roomName !== "string" || room === void 0) { return; } const execMessage = (data) => { const webrtcConns = room.webrtcConns; const peerId = room.peerId; if (data === null || data.from === peerId || data.to !== void 0 && data.to !== peerId || room.bcConns.has(data.from)) { return; } const emitPeerChange = webrtcConns.has(data.from) ? () => { } : () => room.provider.emit("peers", [ { removed: [], added: [data.from], webrtcPeers: Array.from( room.webrtcConns.keys() ), bcPeers: Array.from(room.bcConns) } ]); switch (data.type) { case "announce": if (webrtcConns.size < room.provider.maxConns) { map.setIfUndefined( webrtcConns, data.from, () => new import_y_webrtc.WebrtcConn( signalCon, true, data.from, room ) ); emitPeerChange(); } break; case "signal": if (data.signal.type === "offer") { const existingConn = webrtcConns.get( data.from ); if (existingConn) { const remoteToken = data.token; const localToken = existingConn.glareToken; if (localToken && localToken > remoteToken) { (0, import_y_webrtc.log)( "offer rejected: ", data.from ); return; } existingConn.glareToken = void 0; } } if (data.signal.type === "answer") { (0, import_y_webrtc.log)("offer answered by: ", data.from); const existingConn = webrtcConns.get( data.from ); if (existingConn) { existingConn.glareToken = void 0; } } if (data.to === peerId) { map.setIfUndefined( webrtcConns, data.from, () => new import_y_webrtc.WebrtcConn( signalCon, false, data.from, room ) ).peer.signal(data.signal); emitPeerChange(); } break; } }; if (room.key) { if (typeof m.data === "string") { cryptoutils.decryptJson( buffer.fromBase64(m.data), room.key ).then(execMessage); } } else { execMessage(m.data); } } } } ); signalCon.on("disconnect", () => (0, import_y_webrtc.log)(`disconnect (${url})`)); } function setupHttpSignal(httpClient) { if (httpClient.shouldConnect && httpClient.ws === null) { const subscriberId = Math.floor(1e5 + Math.random() * 9e5); const url = httpClient.url; const eventSource = new window.EventSource( (0, import_url.addQueryArgs)(url, { subscriber_id: subscriberId, action: "gutenberg_signaling_server" }) ); let pingTimeout = null; eventSource.onmessage = (event) => { httpClient.lastMessageReceived = Date.now(); const data = event.data; if (data) { const messages = JSON.parse(data); if (Array.isArray(messages)) { messages.forEach(onSingleMessage); } } }; httpClient.ws = eventSource; httpClient.connecting = true; httpClient.connected = false; const onSingleMessage = (message) => { if (message && message.type === "pong") { clearTimeout(pingTimeout); pingTimeout = setTimeout( sendPing, messageReconnectTimeout / 2 ); } httpClient.emit("message", [message, httpClient]); }; const onclose = (error) => { if (httpClient.ws !== null) { httpClient.ws.close(); httpClient.ws = null; httpClient.connecting = false; if (httpClient.connected) { httpClient.connected = false; httpClient.emit("disconnect", [ { type: "disconnect", error }, httpClient ]); } else { httpClient.unsuccessfulReconnects++; } } clearTimeout(pingTimeout); }; const sendPing = () => { if (httpClient.ws && httpClient.ws.readyState === window.EventSource.OPEN) { httpClient.send({ type: "ping" }); } }; if (httpClient.ws) { httpClient.ws.onclose = () => { onclose(null); }; httpClient.ws.send = function send(message) { window.fetch(url, { body: new URLSearchParams({ subscriber_id: subscriberId.toString(), action: "gutenberg_signaling_server", message }), method: "POST" }).catch(() => { (0, import_y_webrtc.log)( "Error sending to server with message: " + message ); }); }; } eventSource.onerror = () => { }; eventSource.onopen = () => { if (httpClient.connected) { return; } if (eventSource.readyState === window.EventSource.OPEN) { httpClient.lastMessageReceived = Date.now(); httpClient.connecting = false; httpClient.connected = true; httpClient.unsuccessfulReconnects = 0; httpClient.emit("connect", [ { type: "connect" }, httpClient ]); pingTimeout = setTimeout( sendPing, messageReconnectTimeout / 2 ); } }; } } var messageReconnectTimeout = 3e4; var HttpSignalingConn = class extends import_observable.Observable { /** * @param {string} url */ constructor(url) { super(); this.url = url; this.ws = null; this.binaryType = null; this.connected = false; this.connecting = false; this.unsuccessfulReconnects = 0; this.lastMessageReceived = 0; this.shouldConnect = true; this._checkInterval = setInterval(() => { if (this.connected && messageReconnectTimeout < Date.now() - this.lastMessageReceived && this.ws) { this.ws.close(); } }, messageReconnectTimeout / 2); setupHttpSignal(this); this.providers = /* @__PURE__ */ new Set(); setupSignalEventHandlers(this, url); } /** * @param {any} message */ send(message) { if (this.ws) { this.ws.send(JSON.stringify(message)); } } destroy() { clearInterval(this._checkInterval); this.disconnect(); super.destroy(); } disconnect() { this.shouldConnect = false; if (this.ws !== null) { this.ws.close(); } } connect() { this.shouldConnect = true; if (!this.connected && this.ws === null) { setupHttpSignal(this); } } }; var WebrtcProviderWithHttpSignaling = class extends import_y_webrtc.WebrtcProvider { connect() { this.shouldConnect = true; this.signalingUrls.forEach((url) => { const signalingConn = map.setIfUndefined( import_y_webrtc.signalingConns, url, // Only this conditional logic to create a normal websocket connection or // an http signaling connection was added to the constructor when compared // with the base class. url.startsWith("ws://") || url.startsWith("wss://") ? () => new import_y_webrtc.SignalingConn(url) : () => new HttpSignalingConn(url) ); this.signalingConns.push(signalingConn); signalingConn.providers.add(this); }); if (this.room) { this.room.connect(); } } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { HttpSignalingConn, WebrtcProviderWithHttpSignaling }); //# sourceMappingURL=webrtc-http-stream-signaling.js.map