// The byte pipe to a smolmaild server. Browsers cannot open TCP sockets, so // bytes travel through the local bridge's WebSocket relay unchanged; the // Noise session lives in this page either way. const BRIDGE = `${location.protocol === "https:" ? "wss" : "ws"}://${location.host}`; export function connectStream(host, port, timeoutMs = 0) { return new Promise((resolve, reject) => { const socket = new WebSocket(`${BRIDGE}/tcp/${host}/${port}`); socket.binaryType = "arraybuffer"; const stream = { send: bytes => socket.send(bytes), close: () => socket.close(), onData: null, onClose: null, }; // Settings' timeout only governs this; the WebSocket may otherwise never // fire open, error, or close if something between here and the bridge // just drops packets. const timer = timeoutMs ? setTimeout(() => { socket.close(); reject(new Error(`could not reach ${host}:${port} within ${timeoutMs / 1000}s`)); }, timeoutMs) : null; socket.onopen = () => { clearTimeout(timer); resolve(stream); }; socket.onerror = () => { clearTimeout(timer); reject(new Error(`cannot reach the bridge at ${BRIDGE}`)); }; socket.onmessage = event => stream.onData?.(new Uint8Array(event.data)); socket.onclose = () => stream.onClose?.(); }); }