Compare commits
96 Commits
v0.0.1
...
v0.0.2-dev
Author | SHA1 | Date |
---|---|---|
semantic-release-bot | 14e3df654a | |
Derrick Hammer | 743e76bcf1 | |
Derrick Hammer | 6a6f460967 | |
Derrick Hammer | e95b366ea4 | |
Derrick Hammer | 276da74eb5 | |
Derrick Hammer | b6dda4dd5e | |
Derrick Hammer | 4b6ab61da5 | |
Derrick Hammer | 9102cc77d5 | |
Derrick Hammer | f5d93fe03d | |
Derrick Hammer | b3e0722364 | |
Derrick Hammer | e48440710e | |
Derrick Hammer | 42f0c1b60d | |
Derrick Hammer | 8427cbdff6 | |
Derrick Hammer | e8a44315a8 | |
Derrick Hammer | 411582ef05 | |
Derrick Hammer | 439bd9f022 | |
Derrick Hammer | 343fda1172 | |
Derrick Hammer | 701a3190ae | |
Derrick Hammer | b4e406b468 | |
Derrick Hammer | 5bdbf77971 | |
Derrick Hammer | 511185e921 | |
Derrick Hammer | 2a4806b0b3 | |
Derrick Hammer | fb13e6e323 | |
Derrick Hammer | 400d768fe8 | |
Derrick Hammer | c7fd7f294b | |
Derrick Hammer | 50aa507410 | |
Derrick Hammer | 249311c522 | |
Derrick Hammer | a1afba565e | |
Derrick Hammer | 9d14579ae9 | |
Derrick Hammer | 5a49d74b77 | |
Derrick Hammer | af76d85983 | |
Derrick Hammer | 17ca564f43 | |
Derrick Hammer | 9ae03e12e2 | |
Derrick Hammer | 5472397958 | |
Derrick Hammer | 6a843447b1 | |
Derrick Hammer | cc7c7c8edf | |
Derrick Hammer | 21fb795488 | |
Derrick Hammer | 9b815444e1 | |
Derrick Hammer | 2b836207ab | |
Derrick Hammer | 47ab88f56f | |
Derrick Hammer | f20aafe52e | |
Derrick Hammer | f597e56285 | |
Derrick Hammer | ce8edbdaa0 | |
Derrick Hammer | 5073788529 | |
Derrick Hammer | 929b64cc1d | |
Derrick Hammer | e61bd06454 | |
Derrick Hammer | 281121b7cc | |
Derrick Hammer | bd82046a94 | |
Derrick Hammer | e2ab0b8e91 | |
Derrick Hammer | a41e162b1d | |
Derrick Hammer | 35e394fec4 | |
Derrick Hammer | f8b5bcab64 | |
Derrick Hammer | 5276de4363 | |
Derrick Hammer | 8c30190a78 | |
Derrick Hammer | f6bcc8ecdb | |
Derrick Hammer | 0120b67c59 | |
Derrick Hammer | c8e320d798 | |
Derrick Hammer | 21bcebb689 | |
Derrick Hammer | 7633e000e6 | |
Derrick Hammer | 3f843b5266 | |
Derrick Hammer | ad062df383 | |
Derrick Hammer | 3864465ba2 | |
Derrick Hammer | 3cf6738850 | |
Derrick Hammer | 1090b203dc | |
Derrick Hammer | 7a12d7e4cd | |
Derrick Hammer | cb139131e1 | |
Derrick Hammer | 00b0fe29f3 | |
Derrick Hammer | 5f37b74baf | |
Derrick Hammer | cb16b87b73 | |
Derrick Hammer | 58ba247740 | |
Derrick Hammer | 8ebaa2a5a5 | |
Derrick Hammer | 7cb32b716a | |
Derrick Hammer | 8ee4df8d1b | |
Derrick Hammer | ce575b623c | |
Derrick Hammer | 007b9a09d7 | |
Derrick Hammer | b4962e994b | |
Derrick Hammer | 8d4be6ec4a | |
Derrick Hammer | 83692b8225 | |
Derrick Hammer | b52bdcdeb2 | |
Derrick Hammer | fe09e2a5d9 | |
Derrick Hammer | ec291909c4 | |
Derrick Hammer | 2e71082a59 | |
Derrick Hammer | c7b64064ad | |
Derrick Hammer | 377b9548cf | |
Derrick Hammer | 35eed2a157 | |
Derrick Hammer | a0ecea1dc9 | |
Derrick Hammer | 685bc59894 | |
Derrick Hammer | 918c49f146 | |
Derrick Hammer | b2f2c7e9f0 | |
Derrick Hammer | f2e6e36783 | |
Derrick Hammer | 83a7dcd64f | |
Derrick Hammer | 0325d4488f | |
Derrick Hammer | 615b3fcc0c | |
Derrick Hammer | 2c197cdeef | |
Derrick Hammer | 428e8b3160 | |
Derrick Hammer | 915d15a9dc |
|
@ -0,0 +1,13 @@
|
|||
name: Build/Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- develop-*
|
||||
|
||||
jobs:
|
||||
main:
|
||||
uses: lumeweb/github-node-deploy-workflow/.github/workflows/main.yml@master
|
||||
secrets: inherit
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"preset": ["@lumeweb/node-library-preset"]
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
## [0.0.2-develop.1](https://git.lumeweb.com/LumeWeb/hyperswarm-web/compare/v0.0.1...v0.0.2-develop.1) (2023-07-01)
|
||||
|
||||
|
||||
### Reverts
|
||||
|
||||
* Revert "*Store copy of activeRelay size to prevent infinite loop" ([cb16b87](https://git.lumeweb.com/LumeWeb/hyperswarm-web/commit/cb16b87b73e104aa2e6ad3dad1b827cb0a85243a))
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2022 Lume Web
|
||||
Copyright (c) 2022 Hammer Technologies LLC
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"name": "@lumeweb/hyperswarm-web",
|
||||
"type": "module",
|
||||
"version": "0.0.2-develop.1",
|
||||
"main": "lib/index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "gitea@git.lumeweb.com:LumeWeb/hyperswarm-web.git"
|
||||
},
|
||||
"scripts": {
|
||||
"prepare": "presetter bootstrap",
|
||||
"build": "run build",
|
||||
"semantic-release": "semantic-release"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@lumeweb/node-library-preset": "^0.2.5",
|
||||
"@types/random-number-csprng": "^1.0.0",
|
||||
"@types/ws": "^8.5.5",
|
||||
"async-mutex": "^0.4.0",
|
||||
"esbuild": "^0.14.54",
|
||||
"eventemitter2": "^6.4.9",
|
||||
"hyperswarm": "^4.5.1",
|
||||
"presetter": "*",
|
||||
"prettier": "^2.8.8",
|
||||
"rimraf": "^3.0.2",
|
||||
"typescript": "^4.9.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hyperswarm/dht-relay": "^0.4.1",
|
||||
"@lumeweb/kernel-peer-discovery-client": "^0.0.2-develop.1",
|
||||
"@lumeweb/libkernel": "^0.1.0-develop.6",
|
||||
"async-mutex": "^0.4.0",
|
||||
"eventemitter2": "^6.4.9",
|
||||
"hyperswarm": "^4.5.1"
|
||||
},
|
||||
"readme": "ERROR: No README data found!",
|
||||
"files": [
|
||||
"lib/**"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,271 @@
|
|||
// @ts-ignore
|
||||
import DhtNode from "@hyperswarm/dht-relay";
|
||||
// @ts-ignore
|
||||
import Stream from "@hyperswarm/dht-relay/ws";
|
||||
import { createClient } from "@lumeweb/kernel-peer-discovery-client";
|
||||
import type {
|
||||
PeerDiscoveryClient,
|
||||
Peer,
|
||||
} from "@lumeweb/kernel-peer-discovery-client";
|
||||
// @ts-ignore
|
||||
import Hyperswarm from "hyperswarm";
|
||||
import randomNumber from "random-number-csprng";
|
||||
import EventEmitter, { OnOptions } from "eventemitter2";
|
||||
import { Mutex } from "async-mutex";
|
||||
import { logErr } from "@lumeweb/libkernel";
|
||||
|
||||
export default class HyperswarmWeb extends EventEmitter.default {
|
||||
private _options: any;
|
||||
private _discovery: PeerDiscoveryClient;
|
||||
private _queuedEmActions: [string, any][] = [];
|
||||
private _connectionMutex: Mutex = new Mutex();
|
||||
|
||||
constructor(opts: any = {}) {
|
||||
super();
|
||||
opts.custodial = false;
|
||||
this._options = opts;
|
||||
this._discovery = createClient();
|
||||
}
|
||||
|
||||
private _relays: Set<string> = new Set();
|
||||
|
||||
get relays(): string[] {
|
||||
return [...this._relays.values()];
|
||||
}
|
||||
|
||||
private _activeRelay: Hyperswarm;
|
||||
|
||||
get activeRelay(): Hyperswarm {
|
||||
return this._activeRelay;
|
||||
}
|
||||
|
||||
private _ready = false;
|
||||
|
||||
get ready(): boolean {
|
||||
return this._ready;
|
||||
}
|
||||
|
||||
init(): Promise<void> {
|
||||
return this.ensureConnection();
|
||||
}
|
||||
|
||||
async connect(pubkey: string, options = {}): Promise<DhtNode> {
|
||||
if (!this._activeRelay) {
|
||||
await this.ensureConnection();
|
||||
}
|
||||
|
||||
return this._activeRelay.connect(pubkey, options);
|
||||
}
|
||||
|
||||
public async addRelay(pubkey: string): Promise<void> {
|
||||
this._relays.add(pubkey);
|
||||
}
|
||||
|
||||
public removeRelay(pubkey: string): boolean {
|
||||
if (!this._relays.has(pubkey)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this._relays.delete(pubkey);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public clearRelays(): void {
|
||||
this._relays.clear();
|
||||
}
|
||||
|
||||
on(
|
||||
eventName: string | symbol,
|
||||
listener: (...args: any[]) => void,
|
||||
): Hyperswarm {
|
||||
return this._processOrQueueAction("on", ...arguments);
|
||||
}
|
||||
|
||||
onSelf(
|
||||
eventName: string | symbol,
|
||||
listener: (...args: any[]) => void,
|
||||
options?: boolean | OnOptions,
|
||||
): Hyperswarm {
|
||||
return super.on(eventName, listener, options);
|
||||
}
|
||||
|
||||
addListener(
|
||||
eventName: string | symbol,
|
||||
listener: (...args: any[]) => void,
|
||||
): this {
|
||||
return this.on(eventName, listener);
|
||||
}
|
||||
|
||||
off(
|
||||
eventName: string | symbol,
|
||||
listener: (...args: any[]) => void,
|
||||
): Hyperswarm {
|
||||
return this._processOrQueueAction("off", ...arguments);
|
||||
}
|
||||
|
||||
offSelf(
|
||||
eventName: string | symbol,
|
||||
listener: (...args: any[]) => void,
|
||||
): Hyperswarm {
|
||||
return super.off(eventName, listener);
|
||||
}
|
||||
|
||||
removeListener(
|
||||
eventName: string | symbol,
|
||||
listener: (...args: any[]) => void,
|
||||
): this {
|
||||
return this.off(eventName, listener);
|
||||
}
|
||||
|
||||
emit(eventName: string | symbol, ...args: any[]): boolean {
|
||||
return this._processOrQueueAction("emit", ...arguments);
|
||||
}
|
||||
|
||||
emitSelf(eventName: string | symbol, ...args: any[]): boolean {
|
||||
return super.emit(eventName, ...args);
|
||||
}
|
||||
|
||||
once(eventName: string | symbol, listener: (...args: any[]) => void): this {
|
||||
return this._processOrQueueAction("once", ...arguments);
|
||||
}
|
||||
|
||||
onceSelf(
|
||||
eventName: string | symbol,
|
||||
listener: (...args: any[]) => void,
|
||||
): this {
|
||||
return this.once(eventName, listener);
|
||||
}
|
||||
|
||||
public join(topic: Uint8Array, opts = {}): void {
|
||||
return this._processOrQueueAction("join", ...arguments);
|
||||
}
|
||||
|
||||
public joinPeer(publicKey: Uint8Array): void {
|
||||
return this._processOrQueueAction("joinPeer", ...arguments);
|
||||
}
|
||||
|
||||
public leave(topic: Uint8Array): void {
|
||||
return this._processOrQueueAction("leave", ...arguments);
|
||||
}
|
||||
|
||||
public leavePeer(publicKey: Uint8Array): void {
|
||||
return this._processOrQueueAction("leavePeer", ...arguments);
|
||||
}
|
||||
|
||||
public status(publicKey: Uint8Array) {
|
||||
return this._activeRelay?.status(publicKey);
|
||||
}
|
||||
|
||||
public topics(): string[] {
|
||||
return this._activeRelay?.topics();
|
||||
}
|
||||
|
||||
public async flush(): Promise<any> {
|
||||
return this._activeRelay?.flush();
|
||||
}
|
||||
|
||||
public async clear(): Promise<any> {
|
||||
return this._activeRelay?.clear();
|
||||
}
|
||||
|
||||
private async ensureConnection(): Promise<any> {
|
||||
await this._connectionMutex.acquire();
|
||||
|
||||
if (this._activeRelay) {
|
||||
this._connectionMutex.release();
|
||||
return;
|
||||
}
|
||||
|
||||
const relays = this.relays;
|
||||
|
||||
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 as Peer;
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
this._activeRelay.dht._protocol._stream.once("close", () => {
|
||||
this._activeRelay = undefined;
|
||||
this._ready = false;
|
||||
this.emitSelf("close");
|
||||
});
|
||||
} while (relays.length > 0 && !this._activeRelay);
|
||||
}
|
||||
|
||||
if (!this._activeRelay) {
|
||||
this._connectionMutex.release();
|
||||
throw new Error("Failed to find an available relay");
|
||||
}
|
||||
|
||||
this.emitSelf("init");
|
||||
|
||||
this._processQueuedActions();
|
||||
await this._activeRelay.dht.ready();
|
||||
this._connectionMutex.release();
|
||||
|
||||
this._ready = true;
|
||||
this.emit("ready");
|
||||
}
|
||||
|
||||
private async isServerAvailable(connection: string): Promise<boolean> {
|
||||
return new Promise<boolean>((resolve) => {
|
||||
const ws = new WebSocket(connection);
|
||||
ws.addEventListener("open", () => {
|
||||
ws.close();
|
||||
resolve(true);
|
||||
});
|
||||
ws.addEventListener("error", () => {
|
||||
resolve(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private _processOrQueueAction(method: string, ...args: any[]) {
|
||||
if (this._activeRelay) {
|
||||
return this._activeRelay[method](...args);
|
||||
}
|
||||
|
||||
this._queuedEmActions.push([method, args]);
|
||||
return this;
|
||||
}
|
||||
|
||||
private _processQueuedActions(): void {
|
||||
for (const action of this._queuedEmActions) {
|
||||
this._activeRelay[action[0]](...action[1]);
|
||||
}
|
||||
|
||||
this._queuedEmActions = [];
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue