Merge pull request #87 from waku-org/web-chat-bump-js-waku
This commit is contained in:
commit
f698d97013
|
@ -4,7 +4,8 @@
|
|||
|
||||
- Group chat
|
||||
- React/TypeScript
|
||||
- Waku Relay
|
||||
- Waku Filter
|
||||
- Waku Light Push
|
||||
- Waku Store
|
||||
|
||||
A ReactJS chat app is provided as a showcase of the library used in the browser.
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
const webpack = require("webpack");
|
||||
|
||||
module.exports = {
|
||||
dev: (config) => {
|
||||
// Override webpack 5 config from react-scripts to load polyfills
|
||||
if (!config.resolve) config.resolve = {};
|
||||
if (!config.resolve.fallback) config.resolve.fallback = {};
|
||||
Object.assign(config.resolve.fallback, {
|
||||
assert: require.resolve("assert"),
|
||||
buffer: require.resolve("buffer"),
|
||||
crypto: false,
|
||||
http: require.resolve("http-browserify"),
|
||||
https: require.resolve("https-browserify"),
|
||||
stream: require.resolve("stream-browserify"),
|
||||
url: require.resolve("url"),
|
||||
zlib: require.resolve("browserify-zlib"),
|
||||
});
|
||||
|
||||
if (!config.plugins) config.plugins = [];
|
||||
config.plugins.push(
|
||||
new webpack.DefinePlugin({
|
||||
"process.env.ENV": JSON.stringify("dev"),
|
||||
})
|
||||
);
|
||||
config.plugins.push(
|
||||
new webpack.ProvidePlugin({
|
||||
process: "process/browser.js",
|
||||
Buffer: ["buffer", "Buffer"],
|
||||
})
|
||||
);
|
||||
|
||||
if (!config.ignoreWarnings) config.ignoreWarnings = [];
|
||||
config.ignoreWarnings.push(/Failed to parse source map/);
|
||||
|
||||
return config;
|
||||
},
|
||||
prod: (config) => {
|
||||
// Override webpack 5 config from react-scripts to load polyfills
|
||||
if (!config.resolve) config.resolve = {};
|
||||
if (!config.resolve.fallback) config.resolve.fallback = {};
|
||||
Object.assign(config.resolve.fallback, {
|
||||
assert: require.resolve("assert"),
|
||||
buffer: require.resolve("buffer"),
|
||||
crypto: false,
|
||||
http: require.resolve("http-browserify"),
|
||||
https: require.resolve("https-browserify"),
|
||||
stream: require.resolve("stream-browserify"),
|
||||
url: require.resolve("url"),
|
||||
zlib: require.resolve("browserify-zlib"),
|
||||
});
|
||||
|
||||
if (!config.plugins) config.plugins = [];
|
||||
config.plugins.push(
|
||||
new webpack.DefinePlugin({
|
||||
"process.env.ENV": JSON.stringify("prod"),
|
||||
})
|
||||
);
|
||||
config.plugins.push(
|
||||
new webpack.ProvidePlugin({
|
||||
process: "process/browser.js",
|
||||
Buffer: ["buffer", "Buffer"],
|
||||
})
|
||||
);
|
||||
|
||||
if (!config.ignoreWarnings) config.ignoreWarnings = [];
|
||||
config.ignoreWarnings.push(/Failed to parse source map/);
|
||||
|
||||
return config;
|
||||
},
|
||||
};
|
|
@ -2,53 +2,44 @@
|
|||
"name": "web-chat",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"homepage": "/examples/web-chat",
|
||||
"homepage": "/web-chat",
|
||||
"dependencies": {
|
||||
"@livechat/ui-kit": "^0.5.0-20",
|
||||
"browserify-zlib": "^0.2.0",
|
||||
"buffer": "^6.0.3",
|
||||
"http-browserify": "^1.7.0",
|
||||
"https-browserify": "^1.0.0",
|
||||
"js-waku": "^0.24.0",
|
||||
"libp2p-interfaces": "^4.0.6",
|
||||
"long": "^5.2.0",
|
||||
"multiaddr": "^10.0.1",
|
||||
"peer-id": "^0.16.0",
|
||||
"@multiformats/multiaddr": "^10.4.0",
|
||||
"js-waku": "0.24.0-f52dd9e",
|
||||
"process": "^0.11.10",
|
||||
"protobufjs": "^7.0.0",
|
||||
"protons-runtime": "^3.1.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"server-name-generator": "^1.0.5",
|
||||
"stream-browserify": "^3.0.0"
|
||||
"uint8arraylist": "^2.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^27.5.0",
|
||||
"@types/node": "^17.0.32",
|
||||
"@types/react": "^17.0.39",
|
||||
"@types/react-dom": "^17.0.11",
|
||||
"assert": "^2.0.0",
|
||||
"cra-webpack-rewired": "^1.0.1",
|
||||
"cspell": "^6.0.0",
|
||||
"gh-pages": "^4.0.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.6.2",
|
||||
"protons": "^5.1.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"typescript": "^4.6.4",
|
||||
"url": "^0.11.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "cra-webpack-rewired start",
|
||||
"build": "cra-webpack-rewired build",
|
||||
"test:unit": "cra-webpack-rewired test",
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test:unit": "exit 0",
|
||||
"fix": "run-s fix:*",
|
||||
"test": "run-s build test:*",
|
||||
"test:lint": "eslint src --ext .ts --ext .tsx",
|
||||
"test:prettier": "prettier \"src/**/*.{ts,tsx}\" \"./*.json\" \"./config/*.js\" --list-different",
|
||||
"test:prettier": "prettier \"src/**/*.{ts,tsx}\" \"./*.json\" --list-different",
|
||||
"test:spelling": "cspell \"{README.md,.github/*.md,src/**/*.{ts,tsx},public/**/*.html}\" -c ../.cspell.json",
|
||||
"fix:prettier": "prettier \"src/**/*.{ts,tsx}\" \"./*.json\" \"./config/*.js\" --write",
|
||||
"fix:prettier": "prettier \"src/**/*.{ts,tsx}\" \"./*.json\" --write",
|
||||
"fix:lint": "eslint src --ext .ts --ext .tsx --fix",
|
||||
"proto": "run-s proto:*",
|
||||
"proto:build": "buf generate",
|
||||
"proto": "protons src/proto/*.proto",
|
||||
"js-waku:build": "cd ../; npm run build",
|
||||
"predeploy": "run-s js-waku:build build",
|
||||
"deploy": "gh-pages -d build"
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,11 +1,14 @@
|
|||
import { useEffect, useReducer, useState } from "react";
|
||||
import "./App.css";
|
||||
import {
|
||||
discovery,
|
||||
getPredefinedBootstrapNodes,
|
||||
PageDirection,
|
||||
Protocols,
|
||||
Waku,
|
||||
WakuFilter,
|
||||
WakuLightPush,
|
||||
WakuMessage,
|
||||
WakuRelay,
|
||||
WakuStore,
|
||||
} from "js-waku";
|
||||
import handleCommand from "./command";
|
||||
import Room from "./Room";
|
||||
|
@ -13,6 +16,14 @@ import { WakuContext } from "./WakuContext";
|
|||
import { ThemeProvider } from "@livechat/ui-kit";
|
||||
import { generate } from "server-name-generator";
|
||||
import { Message } from "./Message";
|
||||
import {
|
||||
Fleet,
|
||||
getPredefinedBootstrapNodes,
|
||||
} from "js-waku/lib/predefined_bootstrap_nodes";
|
||||
import { waitForRemotePeer } from "js-waku/lib/wait_for_remote_peer";
|
||||
import { PeerDiscoveryStaticPeers } from "js-waku/lib/peer_discovery_static_list";
|
||||
import { defaultLibp2p } from "js-waku/lib/create_waku";
|
||||
import process from "process";
|
||||
|
||||
const themes = {
|
||||
AuthorName: {
|
||||
|
@ -110,7 +121,7 @@ export default function App() {
|
|||
// Let's retrieve previous messages before listening to new messages
|
||||
if (!historicalMessagesRetrieved) return;
|
||||
|
||||
const handleRelayMessage = (wakuMsg: WakuMessage) => {
|
||||
const handleIncomingMessage = (wakuMsg: WakuMessage) => {
|
||||
console.log("Message received: ", wakuMsg);
|
||||
const msg = Message.fromWakuMessage(wakuMsg);
|
||||
if (msg) {
|
||||
|
@ -118,10 +129,26 @@ export default function App() {
|
|||
}
|
||||
};
|
||||
|
||||
waku.relay.addObserver(handleRelayMessage, [ChatContentTopic]);
|
||||
let unsubscribe: undefined | (() => Promise<void>);
|
||||
waku.filter.subscribe(handleIncomingMessage, [ChatContentTopic]).then(
|
||||
(_unsubscribe) => {
|
||||
console.log("subscribed to ", ChatContentTopic);
|
||||
unsubscribe = _unsubscribe;
|
||||
},
|
||||
(e) => {
|
||||
console.error("Failed to subscribe", e);
|
||||
}
|
||||
);
|
||||
|
||||
return function cleanUp() {
|
||||
waku?.relay.deleteObserver(handleRelayMessage, [ChatContentTopic]);
|
||||
if (!waku) return;
|
||||
if (typeof unsubscribe === "undefined") return;
|
||||
unsubscribe().then(
|
||||
() => {
|
||||
console.log("unsubscribed to ", ChatContentTopic);
|
||||
},
|
||||
(e) => console.error("Failed to unsubscribe", e)
|
||||
);
|
||||
};
|
||||
}, [waku, historicalMessagesRetrieved]);
|
||||
|
||||
|
@ -130,7 +157,11 @@ export default function App() {
|
|||
if (historicalMessagesRetrieved) return;
|
||||
|
||||
const retrieveMessages = async () => {
|
||||
await waku.waitForRemotePeer();
|
||||
await waitForRemotePeer(waku, [
|
||||
Protocols.Store,
|
||||
Protocols.Filter,
|
||||
Protocols.LightPush,
|
||||
]);
|
||||
console.log(`Retrieving archived messages`);
|
||||
|
||||
try {
|
||||
|
@ -175,19 +206,23 @@ export default function App() {
|
|||
|
||||
async function initWaku(setter: (waku: Waku) => void) {
|
||||
try {
|
||||
const waku = await Waku.create({
|
||||
libp2p: {
|
||||
config: {
|
||||
pubsub: {
|
||||
enabled: true,
|
||||
emitSelf: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
bootstrap: {
|
||||
peers: getPredefinedBootstrapNodes(selectFleetEnv()),
|
||||
},
|
||||
// TODO: Remove this declaration once there are optional in js-waku
|
||||
const wakuRelay = new WakuRelay({ emitSelf: true });
|
||||
|
||||
const libp2p = await defaultLibp2p(wakuRelay, {
|
||||
peerDiscovery: [
|
||||
new PeerDiscoveryStaticPeers(
|
||||
getPredefinedBootstrapNodes(selectFleetEnv())
|
||||
),
|
||||
],
|
||||
});
|
||||
const wakuStore = new WakuStore(libp2p);
|
||||
|
||||
const wakuLightPush = new WakuLightPush(libp2p);
|
||||
const wakuFilter = new WakuFilter(libp2p);
|
||||
|
||||
const waku = new Waku({}, libp2p, wakuStore, wakuLightPush, wakuFilter);
|
||||
await waku.start();
|
||||
|
||||
setter(waku);
|
||||
} catch (e) {
|
||||
|
@ -197,10 +232,11 @@ async function initWaku(setter: (waku: Waku) => void) {
|
|||
|
||||
function selectFleetEnv() {
|
||||
// Works with react-scripts
|
||||
if (process?.env?.NODE_ENV === "development") {
|
||||
return discovery.predefined.Fleet.Test;
|
||||
// TODO: Re-enable the switch once nwaku v0.12 is deployed
|
||||
if (true || process?.env?.NODE_ENV === "development") {
|
||||
return Fleet.Test;
|
||||
} else {
|
||||
return discovery.predefined.Fleet.Prod;
|
||||
return Fleet.Prod;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,12 +16,10 @@ export default function ChatList(props: Props) {
|
|||
const renderedMessages = props.messages.map((message) => (
|
||||
<LiveMessage
|
||||
key={
|
||||
message.sentTimestamp
|
||||
? message.sentTimestamp.valueOf()
|
||||
: "" +
|
||||
message.timestamp.valueOf() +
|
||||
message.nick +
|
||||
message.payloadAsUtf8
|
||||
message.nick +
|
||||
message.payloadAsUtf8 +
|
||||
message.timestamp.valueOf() +
|
||||
message.sentTimestamp?.valueOf()
|
||||
}
|
||||
authorName={message.nick}
|
||||
date={formatDisplayDate(message)}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ChangeEvent, KeyboardEvent, useState } from "react";
|
||||
import { ChangeEvent, KeyboardEvent, useEffect, useState } from "react";
|
||||
import { useWaku } from "./WakuContext";
|
||||
import {
|
||||
TextInput,
|
||||
|
@ -15,6 +15,7 @@ interface Props {
|
|||
|
||||
export default function MessageInput(props: Props) {
|
||||
const [inputText, setInputText] = useState<string>("");
|
||||
const [activeButton, setActiveButton] = useState<boolean>(false);
|
||||
const { waku } = useWaku();
|
||||
|
||||
const sendMessage = async () => {
|
||||
|
@ -39,9 +40,21 @@ export default function MessageInput(props: Props) {
|
|||
}
|
||||
};
|
||||
|
||||
// Enable the button if there are relay peers available or the user is sending a command
|
||||
const activeButton =
|
||||
(waku && waku.relay.getPeers().size !== 0) || inputText.startsWith("/");
|
||||
// Enable the button if there are peers available or the user is sending a command
|
||||
useEffect(() => {
|
||||
if (inputText.startsWith("/")) {
|
||||
setActiveButton(true);
|
||||
} else if (waku) {
|
||||
(async () => {
|
||||
const peers = await waku.lightPush.peers();
|
||||
if (!!peers) {
|
||||
setActiveButton(true);
|
||||
} else {
|
||||
setActiveButton(false);
|
||||
}
|
||||
})();
|
||||
}
|
||||
}, [activeButton, inputText, waku]);
|
||||
|
||||
return (
|
||||
<TextComposer
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { WakuMessage } from "js-waku";
|
||||
import { PushResponse, WakuMessage } from "js-waku";
|
||||
import { ChatContentTopic } from "./App";
|
||||
import ChatList from "./ChatList";
|
||||
import MessageInput from "./MessageInput";
|
||||
|
@ -18,28 +18,22 @@ export default function Room(props: Props) {
|
|||
const { waku } = useWaku();
|
||||
|
||||
const [storePeers, setStorePeers] = useState(0);
|
||||
const [relayPeers, setRelayPeers] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
if (!waku) return;
|
||||
|
||||
// Update relay peer count on heartbeat
|
||||
waku.relay.on("gossipsub:heartbeat", () => {
|
||||
setRelayPeers(waku.relay.getPeers().size);
|
||||
});
|
||||
}, [waku]);
|
||||
const [filterPeers, setFilterPeers] = useState(0);
|
||||
const [lightPushPeers, setLightPushPeers] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
if (!waku) return;
|
||||
|
||||
// Update store peer when new peer connected & identified
|
||||
waku.libp2p.peerStore.on("change:protocols", async () => {
|
||||
let counter = 0;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
for await (const _peer of waku.store.peers) {
|
||||
counter++;
|
||||
}
|
||||
setStorePeers(counter);
|
||||
waku.libp2p.peerStore.addEventListener("change:protocols", async () => {
|
||||
const storePeers = await waku.store.peers();
|
||||
setStorePeers(storePeers.length);
|
||||
|
||||
const filterPeers = await waku.filter.peers();
|
||||
setFilterPeers(filterPeers.length);
|
||||
|
||||
const lightPushPeers = await waku.lightPush.peers();
|
||||
setLightPushPeers(lightPushPeers.length);
|
||||
});
|
||||
}, [waku]);
|
||||
|
||||
|
@ -49,7 +43,9 @@ export default function Room(props: Props) {
|
|||
style={{ height: "98vh", display: "flex", flexDirection: "column" }}
|
||||
>
|
||||
<TitleBar
|
||||
leftIcons={[`Peers: ${relayPeers} relay ${storePeers} store.`]}
|
||||
leftIcons={[
|
||||
`Peers: ${lightPushPeers} light push, ${filterPeers} filter, ${storePeers} store.`,
|
||||
]}
|
||||
title="Waku v2 chat app"
|
||||
/>
|
||||
<ChatList messages={props.messages} />
|
||||
|
@ -61,7 +57,7 @@ export default function Room(props: Props) {
|
|||
messageToSend,
|
||||
props.nick,
|
||||
props.commandHandler,
|
||||
waku.relay.send.bind(waku.relay)
|
||||
waku.lightPush.push.bind(waku.lightPush)
|
||||
);
|
||||
}
|
||||
: undefined
|
||||
|
@ -75,7 +71,7 @@ async function handleMessage(
|
|||
message: string,
|
||||
nick: string,
|
||||
commandHandler: (cmd: string) => void,
|
||||
messageSender: (msg: WakuMessage) => Promise<void>
|
||||
messageSender: (msg: WakuMessage) => Promise<PushResponse | null>
|
||||
) {
|
||||
if (message.startsWith("/")) {
|
||||
commandHandler(message);
|
||||
|
@ -87,6 +83,6 @@ async function handleMessage(
|
|||
ChatContentTopic,
|
||||
{ timestamp }
|
||||
);
|
||||
return messageSender(wakuMsg);
|
||||
await messageSender(wakuMsg);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
import WakuMock, { Message } from "./WakuMock";
|
||||
|
||||
test("Messages are emitted", async () => {
|
||||
const wakuMock = await WakuMock.create();
|
||||
|
||||
let message: Message;
|
||||
wakuMock.on("message", (msg) => {
|
||||
message = msg;
|
||||
});
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
// @ts-ignore
|
||||
expect(message.message).toBeDefined();
|
||||
});
|
||||
|
||||
test("Messages are sent", async () => {
|
||||
const wakuMock = await WakuMock.create();
|
||||
|
||||
const text = "This is a message.";
|
||||
|
||||
let message: Message;
|
||||
wakuMock.on("message", (msg) => {
|
||||
message = msg;
|
||||
});
|
||||
|
||||
await wakuMock.send(text);
|
||||
|
||||
// @ts-ignore
|
||||
expect(message.message).toEqual(text);
|
||||
});
|
|
@ -1,69 +0,0 @@
|
|||
class EventEmitter<T> {
|
||||
public callbacks: { [key: string]: Array<(data: T) => void> };
|
||||
|
||||
constructor() {
|
||||
this.callbacks = {};
|
||||
}
|
||||
|
||||
on(event: string, cb: (data: T) => void) {
|
||||
if (!this.callbacks[event]) this.callbacks[event] = [];
|
||||
this.callbacks[event].push(cb);
|
||||
}
|
||||
|
||||
emit(event: string, data: T) {
|
||||
let cbs = this.callbacks[event];
|
||||
if (cbs) {
|
||||
cbs.forEach((cb) => cb(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface Message {
|
||||
timestamp: Date;
|
||||
handle: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export default class WakuMock extends EventEmitter<Message> {
|
||||
index: number;
|
||||
intervalId?: number | NodeJS.Timeout;
|
||||
|
||||
private constructor() {
|
||||
super();
|
||||
this.index = 0;
|
||||
}
|
||||
|
||||
public static async create(): Promise<WakuMock> {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
|
||||
const wakuMock = new WakuMock();
|
||||
wakuMock.startInterval();
|
||||
return wakuMock;
|
||||
}
|
||||
|
||||
public async send(message: string): Promise<void> {
|
||||
const timestamp = new Date();
|
||||
const handle = "me";
|
||||
this.emit("message", {
|
||||
timestamp,
|
||||
handle,
|
||||
message,
|
||||
});
|
||||
}
|
||||
|
||||
private startInterval() {
|
||||
if (this.intervalId === undefined) {
|
||||
this.intervalId = setInterval(this.emitMessage.bind(this), 1000);
|
||||
}
|
||||
}
|
||||
|
||||
private emitMessage() {
|
||||
const handle = "you";
|
||||
const timestamp = new Date();
|
||||
this.emit("message", {
|
||||
timestamp,
|
||||
handle,
|
||||
message: `This is message #${this.index++}.`,
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
import { Reader } from "protobufjs/minimal";
|
||||
|
||||
import { utils } from "js-waku";
|
||||
import * as proto from "./proto/chat_message";
|
||||
|
||||
/**
|
||||
|
@ -20,8 +19,8 @@ export class ChatMessage {
|
|||
nick: string,
|
||||
text: string
|
||||
): ChatMessage {
|
||||
const timestampNumber = Math.floor(timestamp.valueOf() / 1000);
|
||||
const payload = Buffer.from(text, "utf-8");
|
||||
const timestampNumber = BigInt(Math.floor(timestamp.valueOf() / 1000));
|
||||
const payload = utils.utf8ToBytes(text);
|
||||
|
||||
return new ChatMessage({
|
||||
timestamp: timestampNumber,
|
||||
|
@ -35,7 +34,7 @@ export class ChatMessage {
|
|||
* @param bytes The payload to decode.
|
||||
*/
|
||||
static decode(bytes: Uint8Array): ChatMessage {
|
||||
const protoMsg = proto.ChatMessage.decode(Reader.create(bytes));
|
||||
const protoMsg = proto.ChatMessage.decode(bytes);
|
||||
return new ChatMessage(protoMsg);
|
||||
}
|
||||
|
||||
|
@ -44,11 +43,11 @@ export class ChatMessage {
|
|||
* @returns The encoded payload.
|
||||
*/
|
||||
encode(): Uint8Array {
|
||||
return proto.ChatMessage.encode(this.proto).finish();
|
||||
return proto.ChatMessage.encode(this.proto);
|
||||
}
|
||||
|
||||
get timestamp(): Date {
|
||||
return new Date(this.proto.timestamp * 1000);
|
||||
return new Date(Number(this.proto.timestamp * BigInt(1000)));
|
||||
}
|
||||
|
||||
get nick(): string {
|
||||
|
@ -60,6 +59,6 @@ export class ChatMessage {
|
|||
return "";
|
||||
}
|
||||
|
||||
return Buffer.from(this.proto.payload).toString("utf-8");
|
||||
return utils.bytesToUtf8(this.proto.payload);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { multiaddr } from "multiaddr";
|
||||
import PeerId from "peer-id";
|
||||
import { multiaddr } from "@multiformats/multiaddr";
|
||||
import { Waku } from "js-waku";
|
||||
|
||||
function help(): string[] {
|
||||
|
@ -26,7 +25,7 @@ function info(waku: Waku | undefined): string[] {
|
|||
if (!waku) {
|
||||
return ["Waku node is starting"];
|
||||
}
|
||||
return [`PeerId: ${waku.libp2p.peerId.toB58String()}`];
|
||||
return [`PeerId: ${waku.libp2p.peerId.toString()}`];
|
||||
}
|
||||
|
||||
function connect(peer: string | undefined, waku: Waku | undefined): string[] {
|
||||
|
@ -42,9 +41,7 @@ function connect(peer: string | undefined, waku: Waku | undefined): string[] {
|
|||
if (!peerId) {
|
||||
return ["Peer Id needed to dial"];
|
||||
}
|
||||
waku.addPeerToAddressBook(PeerId.createFromB58String(peerId), [
|
||||
peerMultiaddr,
|
||||
]);
|
||||
waku.addPeerToAddressBook(peerId, [peerMultiaddr]);
|
||||
return [
|
||||
`${peerId}: ${peerMultiaddr.toString()} added to address book, autodial in progress`,
|
||||
];
|
||||
|
@ -58,13 +55,10 @@ async function peers(waku: Waku | undefined): Promise<string[]> {
|
|||
return ["Waku node is starting"];
|
||||
}
|
||||
let response: string[] = [];
|
||||
const peers = [];
|
||||
const peers = await waku.libp2p.peerStore.all();
|
||||
|
||||
for await (const peer of waku.libp2p.peerStore.getPeers()) {
|
||||
peers.push(peer);
|
||||
}
|
||||
Array.from(peers).forEach((peer) => {
|
||||
response.push(peer.id.toB58String() + ":");
|
||||
response.push(peer.id.toString() + ":");
|
||||
let addresses = " addresses: [";
|
||||
peer.addresses.forEach(({ multiaddr }) => {
|
||||
addresses += " " + multiaddr.toString() + ",";
|
||||
|
@ -88,21 +82,14 @@ function connections(waku: Waku | undefined): string[] {
|
|||
return ["Waku node is starting"];
|
||||
}
|
||||
let response: string[] = [];
|
||||
waku.libp2p.connections.forEach(
|
||||
(
|
||||
connections: import("libp2p-interfaces/src/connection/connection")[],
|
||||
peerId
|
||||
) => {
|
||||
response.push(peerId + ":");
|
||||
let strConnections = " connections: [";
|
||||
connections.forEach((connection) => {
|
||||
strConnections += JSON.stringify(connection.stat);
|
||||
strConnections += "; " + JSON.stringify(connection.streams);
|
||||
});
|
||||
strConnections += "]";
|
||||
response.push(strConnections);
|
||||
}
|
||||
);
|
||||
let strConnections = " connections: \n";
|
||||
waku.libp2p.connectionManager.getConnections().forEach((connection) => {
|
||||
strConnections += connection.remotePeer.toString() + ", ";
|
||||
strConnections += JSON.stringify(connection.stat);
|
||||
strConnections += "; " + JSON.stringify(connection.streams);
|
||||
strConnections += "\n";
|
||||
});
|
||||
response.push(strConnections);
|
||||
if (response.length === 0) {
|
||||
response.push("Not connected to any peer.");
|
||||
}
|
||||
|
|
|
@ -1,169 +1,117 @@
|
|||
/* eslint-disable */
|
||||
import Long from "long";
|
||||
import _m0 from "protobufjs/minimal";
|
||||
/* eslint-disable import/export */
|
||||
/* eslint-disable @typescript-eslint/no-namespace */
|
||||
|
||||
export const protobufPackage = "";
|
||||
import { encodeMessage, decodeMessage, message } from "protons-runtime";
|
||||
import type { Uint8ArrayList } from "uint8arraylist";
|
||||
import type { Codec } from "protons-runtime";
|
||||
|
||||
export interface ChatMessage {
|
||||
timestamp: number;
|
||||
timestamp: bigint;
|
||||
nick: string;
|
||||
payload: Uint8Array;
|
||||
}
|
||||
|
||||
function createBaseChatMessage(): ChatMessage {
|
||||
return { timestamp: 0, nick: "", payload: new Uint8Array() };
|
||||
}
|
||||
export namespace ChatMessage {
|
||||
let _codec: Codec<ChatMessage>;
|
||||
|
||||
export const ChatMessage = {
|
||||
encode(
|
||||
message: ChatMessage,
|
||||
writer: _m0.Writer = _m0.Writer.create()
|
||||
): _m0.Writer {
|
||||
if (message.timestamp !== 0) {
|
||||
writer.uint32(8).uint64(message.timestamp);
|
||||
export const codec = (): Codec<ChatMessage> => {
|
||||
if (_codec == null) {
|
||||
_codec = message<ChatMessage>(
|
||||
(obj, writer, opts = {}) => {
|
||||
if (opts.lengthDelimited !== false) {
|
||||
writer.fork();
|
||||
}
|
||||
|
||||
if (obj.timestamp != null) {
|
||||
writer.uint32(8);
|
||||
writer.uint64(obj.timestamp);
|
||||
} else {
|
||||
throw new Error(
|
||||
'Protocol error: required field "timestamp" was not found in object'
|
||||
);
|
||||
}
|
||||
|
||||
if (obj.nick != null) {
|
||||
writer.uint32(18);
|
||||
writer.string(obj.nick);
|
||||
} else {
|
||||
throw new Error(
|
||||
'Protocol error: required field "nick" was not found in object'
|
||||
);
|
||||
}
|
||||
|
||||
if (obj.payload != null) {
|
||||
writer.uint32(26);
|
||||
writer.bytes(obj.payload);
|
||||
} else {
|
||||
throw new Error(
|
||||
'Protocol error: required field "payload" was not found in object'
|
||||
);
|
||||
}
|
||||
|
||||
if (opts.lengthDelimited !== false) {
|
||||
writer.ldelim();
|
||||
}
|
||||
},
|
||||
(reader, length) => {
|
||||
const obj: any = {
|
||||
timestamp: 0n,
|
||||
nick: "",
|
||||
payload: new Uint8Array(0),
|
||||
};
|
||||
|
||||
const end = length == null ? reader.len : reader.pos + length;
|
||||
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
obj.timestamp = reader.uint64();
|
||||
break;
|
||||
case 2:
|
||||
obj.nick = reader.string();
|
||||
break;
|
||||
case 3:
|
||||
obj.payload = reader.bytes();
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.timestamp == null) {
|
||||
throw new Error(
|
||||
'Protocol error: value for required field "timestamp" was not found in protobuf'
|
||||
);
|
||||
}
|
||||
|
||||
if (obj.nick == null) {
|
||||
throw new Error(
|
||||
'Protocol error: value for required field "nick" was not found in protobuf'
|
||||
);
|
||||
}
|
||||
|
||||
if (obj.payload == null) {
|
||||
throw new Error(
|
||||
'Protocol error: value for required field "payload" was not found in protobuf'
|
||||
);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
);
|
||||
}
|
||||
if (message.nick !== "") {
|
||||
writer.uint32(18).string(message.nick);
|
||||
}
|
||||
if (message.payload.length !== 0) {
|
||||
writer.uint32(26).bytes(message.payload);
|
||||
}
|
||||
return writer;
|
||||
},
|
||||
|
||||
decode(input: _m0.Reader | Uint8Array, length?: number): ChatMessage {
|
||||
const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input);
|
||||
let end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseChatMessage();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1:
|
||||
message.timestamp = longToNumber(reader.uint64() as Long);
|
||||
break;
|
||||
case 2:
|
||||
message.nick = reader.string();
|
||||
break;
|
||||
case 3:
|
||||
message.payload = reader.bytes();
|
||||
break;
|
||||
default:
|
||||
reader.skipType(tag & 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
},
|
||||
return _codec;
|
||||
};
|
||||
|
||||
fromJSON(object: any): ChatMessage {
|
||||
const message = createBaseChatMessage();
|
||||
message.timestamp =
|
||||
object.timestamp !== undefined && object.timestamp !== null
|
||||
? Number(object.timestamp)
|
||||
: 0;
|
||||
message.nick =
|
||||
object.nick !== undefined && object.nick !== null
|
||||
? String(object.nick)
|
||||
: "";
|
||||
message.payload =
|
||||
object.payload !== undefined && object.payload !== null
|
||||
? bytesFromBase64(object.payload)
|
||||
: new Uint8Array();
|
||||
return message;
|
||||
},
|
||||
export const encode = (obj: ChatMessage): Uint8Array => {
|
||||
return encodeMessage(obj, ChatMessage.codec());
|
||||
};
|
||||
|
||||
toJSON(message: ChatMessage): unknown {
|
||||
const obj: any = {};
|
||||
message.timestamp !== undefined &&
|
||||
(obj.timestamp = Math.round(message.timestamp));
|
||||
message.nick !== undefined && (obj.nick = message.nick);
|
||||
message.payload !== undefined &&
|
||||
(obj.payload = base64FromBytes(
|
||||
message.payload !== undefined ? message.payload : new Uint8Array()
|
||||
));
|
||||
return obj;
|
||||
},
|
||||
|
||||
fromPartial<I extends Exact<DeepPartial<ChatMessage>, I>>(
|
||||
object: I
|
||||
): ChatMessage {
|
||||
const message = createBaseChatMessage();
|
||||
message.timestamp = object.timestamp ?? 0;
|
||||
message.nick = object.nick ?? "";
|
||||
message.payload = object.payload ?? new Uint8Array();
|
||||
return message;
|
||||
},
|
||||
};
|
||||
|
||||
declare var self: any | undefined;
|
||||
declare var window: any | undefined;
|
||||
declare var global: any | undefined;
|
||||
var globalThis: any = (() => {
|
||||
if (typeof globalThis !== "undefined") return globalThis;
|
||||
if (typeof self !== "undefined") return self;
|
||||
if (typeof window !== "undefined") return window;
|
||||
if (typeof global !== "undefined") return global;
|
||||
throw "Unable to locate global object";
|
||||
})();
|
||||
|
||||
const atob: (b64: string) => string =
|
||||
globalThis.atob ||
|
||||
((b64) => globalThis.Buffer.from(b64, "base64").toString("binary"));
|
||||
function bytesFromBase64(b64: string): Uint8Array {
|
||||
const bin = atob(b64);
|
||||
const arr = new Uint8Array(bin.length);
|
||||
for (let i = 0; i < bin.length; ++i) {
|
||||
arr[i] = bin.charCodeAt(i);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
const btoa: (bin: string) => string =
|
||||
globalThis.btoa ||
|
||||
((bin) => globalThis.Buffer.from(bin, "binary").toString("base64"));
|
||||
function base64FromBytes(arr: Uint8Array): string {
|
||||
const bin: string[] = [];
|
||||
for (const byte of arr) {
|
||||
bin.push(String.fromCharCode(byte));
|
||||
}
|
||||
return btoa(bin.join(""));
|
||||
}
|
||||
|
||||
type Builtin =
|
||||
| Date
|
||||
| Function
|
||||
| Uint8Array
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| undefined;
|
||||
|
||||
export type DeepPartial<T> = T extends Builtin
|
||||
? T
|
||||
: T extends Array<infer U>
|
||||
? Array<DeepPartial<U>>
|
||||
: T extends ReadonlyArray<infer U>
|
||||
? ReadonlyArray<DeepPartial<U>>
|
||||
: T extends {}
|
||||
? { [K in keyof T]?: DeepPartial<T[K]> }
|
||||
: Partial<T>;
|
||||
|
||||
type KeysOfUnion<T> = T extends T ? keyof T : never;
|
||||
export type Exact<P, I extends P> = P extends Builtin
|
||||
? P
|
||||
: P & { [K in keyof P]: Exact<P[K], I[K]> } & Record<
|
||||
Exclude<keyof I, KeysOfUnion<P>>,
|
||||
never
|
||||
>;
|
||||
|
||||
function longToNumber(long: Long): number {
|
||||
if (long.gt(Number.MAX_SAFE_INTEGER)) {
|
||||
throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER");
|
||||
}
|
||||
return long.toNumber();
|
||||
}
|
||||
|
||||
if (_m0.util.Long !== Long) {
|
||||
_m0.util.Long = Long as any;
|
||||
_m0.configure();
|
||||
export const decode = (buf: Uint8Array | Uint8ArrayList): ChatMessage => {
|
||||
return decodeMessage(buf, ChatMessage.codec());
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue