hyperswarm-web/dist/index.js

191 lines
5.9 KiB
JavaScript
Raw Normal View History

2022-07-20 06:06:55 +00:00
// @ts-ignore
2022-07-20 05:59:16 +00:00
import DhtNode from "@hyperswarm/dht-relay";
2022-07-20 06:06:55 +00:00
// @ts-ignore
2022-06-27 22:22:53 +00:00
import Stream from "@hyperswarm/dht-relay/ws";
2023-01-31 10:10:34 +00:00
import { createClient } from "@lumeweb/kernel-peer-discovery-client";
import { load } from "@lumeweb/libkernel-universal";
2023-02-01 10:01:12 +00:00
// @ts-ignore
import Hyperswarm from "hyperswarm";
2022-07-26 23:28:53 +00:00
import randomNumber from "random-number-csprng";
2023-01-31 10:43:58 +00:00
import EventEmitter from "eventemitter2";
2023-02-01 13:15:36 +00:00
import { Mutex } from "async-mutex";
2023-01-31 10:10:34 +00:00
export default class HyperswarmWeb extends EventEmitter {
2022-07-20 06:06:55 +00:00
_options;
2023-01-31 10:10:34 +00:00
_discovery;
2023-01-31 11:58:39 +00:00
_queuedEmActions = [];
2023-02-01 13:15:36 +00:00
_connectionMutex = new Mutex();
2022-07-20 06:06:55 +00:00
constructor(opts = {}) {
2023-01-31 10:10:34 +00:00
super();
2022-07-21 18:58:32 +00:00
opts.custodial = false;
2022-07-20 06:06:55 +00:00
this._options = opts;
2023-01-31 10:10:34 +00:00
this._discovery = createClient();
2022-06-27 22:22:53 +00:00
}
2023-02-17 13:04:33 +00:00
_relays = new Set();
get relays() {
return [...this._relays.values()];
}
_activeRelay;
2023-02-01 13:36:58 +00:00
get activeRelay() {
return this._activeRelay;
}
2023-02-17 13:04:33 +00:00
_ready = false;
get ready() {
return this._ready;
}
2023-02-01 13:22:47 +00:00
init() {
2023-01-31 10:10:34 +00:00
return this.ensureConnection();
2022-06-27 22:22:53 +00:00
}
2023-02-17 13:04:33 +00:00
async connect(pubkey, options = {}) {
if (!this._activeRelay) {
await this.ensureConnection();
}
return this._activeRelay.connect(pubkey, options);
}
async addRelay(pubkey) {
this._relays.add(pubkey);
}
removeRelay(pubkey) {
if (!this._relays.has(pubkey)) {
return false;
}
this._relays.delete(pubkey);
return true;
}
clearRelays() {
this._relays.clear();
}
on(eventName, listener) {
return this._processOrQueueAction("on", ...arguments);
}
2023-02-18 03:23:06 +00:00
onSelf(eventName, listener, options) {
return super.on(eventName, listener, options);
}
2023-02-17 13:04:33 +00:00
addListener(eventName, listener) {
return this.on(eventName, listener);
}
off(eventName, listener) {
return this._processOrQueueAction("off", ...arguments);
}
2023-03-19 17:23:20 +00:00
offSelf(eventName, listener) {
return super.off(eventName, listener);
}
2023-02-17 13:04:33 +00:00
removeListener(eventName, listener) {
return this.off(eventName, listener);
}
emit(eventName, ...args) {
return this._processOrQueueAction("emit", ...arguments);
}
2023-02-18 03:23:06 +00:00
emitSelf(eventName, ...args) {
return super.emit(eventName, ...args);
}
2023-02-17 13:04:33 +00:00
once(eventName, listener) {
return this._processOrQueueAction("once", ...arguments);
}
2023-03-19 17:23:20 +00:00
onceSelf(eventName, listener) {
return this.once(eventName, listener);
}
2023-02-17 13:04:33 +00:00
join(topic, opts = {}) {
return this._processOrQueueAction("join", ...arguments);
}
joinPeer(publicKey) {
return this._processOrQueueAction("joinPeer", ...arguments);
}
leave(topic) {
return this._processOrQueueAction("leave", ...arguments);
}
leavePeer(publicKey) {
return this._processOrQueueAction("leavePeer", ...arguments);
}
status(publicKey) {
return this._activeRelay?.status(publicKey);
}
topics() {
return this._activeRelay?.topics();
}
async flush() {
return this._activeRelay?.flush();
}
async clear() {
return this._activeRelay?.clear();
}
2023-01-31 10:10:34 +00:00
async ensureConnection() {
const logErr = (await load()).logErr;
2023-02-01 13:15:36 +00:00
await this._connectionMutex.waitForUnlock();
this._connectionMutex.acquire();
2023-01-31 10:10:34 +00:00
if (this._activeRelay) {
2023-03-29 18:50:42 +00:00
this._connectionMutex.release();
2023-01-31 10:10:34 +00:00
return;
2022-07-20 06:06:55 +00:00
}
2023-01-31 10:10:34 +00:00
const relays = this.relays;
2023-02-01 13:15:36 +00:00
if (relays.length > 0) {
do {
const index = relays.length > 1 ? await randomNumber(0, relays.length - 1) : 0;
const relay = relays[index];
let ret;
try {
ret = await this._discovery.discover(relay);
}
catch (e) {
logErr(e);
relays.splice(index, 1);
continue;
}
if (!ret) {
relays.splice(index, 1);
continue;
}
ret = ret;
const connection = `wss://${ret.host}:${ret.port}`;
if (!(await this.isServerAvailable(connection))) {
relays.splice(index, 1);
continue;
}
this._activeRelay = new Hyperswarm({
dht: new DhtNode(new Stream(true, new WebSocket(connection)), this._options),
keyPair: this._options.keyPair,
});
2023-02-17 21:32:55 +00:00
this._activeRelay.dht._protocol._stream.once("close", () => {
2023-02-01 13:15:36 +00:00
this._activeRelay = undefined;
2023-02-17 13:04:33 +00:00
this._ready = false;
2023-03-19 16:00:17 +00:00
this.emitSelf("close");
2023-02-01 13:15:36 +00:00
});
} while (relays.length > 0 && !this._activeRelay);
}
2023-01-31 10:10:34 +00:00
if (!this._activeRelay) {
2023-02-01 13:15:36 +00:00
this._connectionMutex.release();
2023-01-31 10:10:34 +00:00
throw new Error("Failed to find an available relay");
2022-07-26 23:28:53 +00:00
}
2023-02-18 03:23:06 +00:00
this.emitSelf("init");
2023-01-31 11:58:39 +00:00
this._processQueuedActions();
2023-01-31 10:10:34 +00:00
await this._activeRelay.dht.ready();
2023-02-01 13:15:36 +00:00
this._connectionMutex.release();
2023-02-17 13:04:33 +00:00
this._ready = true;
2023-02-01 13:31:06 +00:00
this.emit("ready");
2022-07-20 06:06:55 +00:00
}
async isServerAvailable(connection) {
return new Promise((resolve) => {
const ws = new WebSocket(connection);
ws.addEventListener("open", () => {
ws.close();
resolve(true);
});
ws.addEventListener("error", () => {
resolve(false);
});
});
}
2023-01-31 11:58:39 +00:00
_processOrQueueAction(method, ...args) {
if (this._activeRelay) {
return this._activeRelay[method](...args);
}
this._queuedEmActions.push([method, args]);
return this;
}
_processQueuedActions() {
for (const action of this._queuedEmActions) {
this._activeRelay[action[0]](...action[1]);
}
this._queuedEmActions = [];
2022-07-20 06:06:55 +00:00
}
2022-06-27 22:22:53 +00:00
}