diff --git a/examples/dogfooding/index.html b/examples/dogfooding/index.html
index 4ca2627..3e9337a 100644
--- a/examples/dogfooding/index.html
+++ b/examples/dogfooding/index.html
@@ -1,51 +1,62 @@
-
+
-
-
+
+
+
+
+
+
+
+
+ Message Statistics
+
+
+ 0
+ Sent by Me
+
+
+ 0
+ Received (Mine)
+
+
+ 0
+ Received (Others)
+
+
+
+
+
+
+
+
+
+
-
+
\ No newline at end of file
diff --git a/examples/dogfooding/package-lock.json b/examples/dogfooding/package-lock.json
index a7515c3..04bd2c2 100644
--- a/examples/dogfooding/package-lock.json
+++ b/examples/dogfooding/package-lock.json
@@ -10,7 +10,7 @@
"dependencies": {
"@libp2p/crypto": "^5.0.5",
"@multiformats/multiaddr": "^12.3.1",
- "@waku/sdk": "0.0.31-3038c48.0",
+ "@waku/sdk": "0.0.32-16328a3.0",
"libp2p": "^2.1.10",
"protobufjs": "^7.3.0",
"uint8arrays": "^5.1.0"
@@ -72,6 +72,23 @@
"wherearewe": "^2.0.1"
}
},
+ "node_modules/@chainsafe/libp2p-noise/node_modules/it-length-prefixed": {
+ "version": "9.1.1",
+ "resolved": "https://registry.npmjs.org/it-length-prefixed/-/it-length-prefixed-9.1.1.tgz",
+ "integrity": "sha512-O88nBweT6M9ozsmok68/auKH7ik/slNM4pYbM9lrfy2z5QnpokW5SlrepHZDKtN71llhG2sZvd6uY4SAl+lAQg==",
+ "license": "Apache-2.0 OR MIT",
+ "dependencies": {
+ "it-reader": "^6.0.1",
+ "it-stream-types": "^2.0.1",
+ "uint8-varint": "^2.0.1",
+ "uint8arraylist": "^2.0.0",
+ "uint8arrays": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=16.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
"node_modules/@chainsafe/netmask": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@chainsafe/netmask/-/netmask-2.0.0.tgz",
@@ -1586,16 +1603,16 @@
]
},
"node_modules/@waku/discovery": {
- "version": "0.0.8-3038c48.0",
- "resolved": "https://registry.npmjs.org/@waku/discovery/-/discovery-0.0.8-3038c48.0.tgz",
- "integrity": "sha512-+FnEs0nWkYtCGU4rfAmXQwwxH09WgzTLkM6t8tgplkjNNQ5TLyqptA1yt9gQKXL5ZEWVMbR8Ydzv86jS15Hizw==",
+ "version": "0.0.9-16328a3.0",
+ "resolved": "https://registry.npmjs.org/@waku/discovery/-/discovery-0.0.9-16328a3.0.tgz",
+ "integrity": "sha512-s3tQpF4t0G/Fa5BCwO+92vf9gPczaz3YIGhrctdU1SYoS5CHhQ0JZ44HKHv/nL+eIzRKwimiGAk3RjLW7Z9pIw==",
"license": "MIT OR Apache-2.0",
"dependencies": {
- "@waku/core": "0.0.35-3038c48.0",
- "@waku/enr": "0.0.29-3038c48.0",
- "@waku/interfaces": "0.0.30-3038c48.0",
- "@waku/proto": "0.0.10-3038c48.0",
- "@waku/utils": "0.0.23-3038c48.0",
+ "@waku/core": "0.0.36-16328a3.0",
+ "@waku/enr": "0.0.30-16328a3.0",
+ "@waku/interfaces": "0.0.31-16328a3.0",
+ "@waku/proto": "0.0.11-16328a3.0",
+ "@waku/utils": "0.0.24-16328a3.0",
"debug": "^4.3.4",
"dns-over-http-resolver": "^3.0.8",
"hi-base32": "^0.5.1",
@@ -1606,16 +1623,16 @@
}
},
"node_modules/@waku/discovery/node_modules/@waku/core": {
- "version": "0.0.35-3038c48.0",
- "resolved": "https://registry.npmjs.org/@waku/core/-/core-0.0.35-3038c48.0.tgz",
- "integrity": "sha512-N2YTKAL4ovl0pbiDr/FqVGtM41Lm+a5+Obk+FKUws/QlCHQK0RG8Qiders7UO5kWpuStm7Cynp8ngPp91U2nBQ==",
+ "version": "0.0.36-16328a3.0",
+ "resolved": "https://registry.npmjs.org/@waku/core/-/core-0.0.36-16328a3.0.tgz",
+ "integrity": "sha512-+5GNNcy3FYnPKaL0RuJ01oo3+wMqkVhHPKdpRc3T0Rodi6IZsFCFdT1ob3A5rpFo5IURXqNjElUVBItIGrtupw==",
"license": "MIT OR Apache-2.0",
"dependencies": {
"@libp2p/ping": "2.0.1",
- "@waku/enr": "0.0.29-3038c48.0",
- "@waku/interfaces": "0.0.30-3038c48.0",
- "@waku/proto": "0.0.10-3038c48.0",
- "@waku/utils": "0.0.23-3038c48.0",
+ "@waku/enr": "0.0.30-16328a3.0",
+ "@waku/interfaces": "0.0.31-16328a3.0",
+ "@waku/proto": "0.0.11-16328a3.0",
+ "@waku/utils": "0.0.24-16328a3.0",
"debug": "^4.3.4",
"it-all": "^3.0.4",
"it-length-prefixed": "^9.0.4",
@@ -1689,9 +1706,9 @@
}
},
"node_modules/@waku/enr": {
- "version": "0.0.29-3038c48.0",
- "resolved": "https://registry.npmjs.org/@waku/enr/-/enr-0.0.29-3038c48.0.tgz",
- "integrity": "sha512-ZT+8Lh5hmeCDuQ3gQQ9ve5pPkR/sZs1/g7IM8hQTPxbP663/GVrJ6d6jfHCAWmEFauP4FfVdsDl3UfphU7RD+g==",
+ "version": "0.0.30-16328a3.0",
+ "resolved": "https://registry.npmjs.org/@waku/enr/-/enr-0.0.30-16328a3.0.tgz",
+ "integrity": "sha512-9D4WVJcuL/ncVsHb7KyeIkkDJia6UVdNK9Kf1u9BhvJBtzioPGgXrmp1O7CsXpIKVu4f8IVCu1Lc6hUVG84Chw==",
"license": "MIT OR Apache-2.0",
"dependencies": {
"@ethersproject/rlp": "^5.7.0",
@@ -1699,7 +1716,7 @@
"@libp2p/peer-id": "^5.0.1",
"@multiformats/multiaddr": "^12.0.0",
"@noble/secp256k1": "^1.7.1",
- "@waku/utils": "0.0.23-3038c48.0",
+ "@waku/utils": "0.0.24-16328a3.0",
"debug": "^4.3.4",
"js-sha3": "^0.9.2"
},
@@ -1716,34 +1733,34 @@
}
},
"node_modules/@waku/interfaces": {
- "version": "0.0.30-3038c48.0",
- "resolved": "https://registry.npmjs.org/@waku/interfaces/-/interfaces-0.0.30-3038c48.0.tgz",
- "integrity": "sha512-QsXga9S6cUh8culnWomZcRBRZZdkPDT5PNinKGcjqQAMpueyuXqeSCVVH41JUkuTKel4w7fXOP9qcr+KLn+I4Q==",
+ "version": "0.0.31-16328a3.0",
+ "resolved": "https://registry.npmjs.org/@waku/interfaces/-/interfaces-0.0.31-16328a3.0.tgz",
+ "integrity": "sha512-2jZCwBSBKd7PX12xJadMmxMpIKGR9SrQTnTqUfDD4aT72GQ9SpaqQDY5h+svMt/qjriZMBSUpITQQF04w0GIaw==",
"license": "MIT OR Apache-2.0",
"dependencies": {
- "@waku/proto": "0.0.10-3038c48.0"
+ "@waku/proto": "0.0.11-16328a3.0"
},
"engines": {
"node": ">=20"
}
},
"node_modules/@waku/message-hash": {
- "version": "0.1.19-3038c48.0",
- "resolved": "https://registry.npmjs.org/@waku/message-hash/-/message-hash-0.1.19-3038c48.0.tgz",
- "integrity": "sha512-u3THyufk2yLnLNyfLbyh3iGls2EgR3TdpQ17rZz4lK73qYyMbT9Rw/bwFL+W+FVg8eKCeBfgXjou2NuqNPLvdA==",
+ "version": "0.1.20-16328a3.0",
+ "resolved": "https://registry.npmjs.org/@waku/message-hash/-/message-hash-0.1.20-16328a3.0.tgz",
+ "integrity": "sha512-j1FHSgeJeKnc9Tet3/NcrNjOV1gKa6U3WXNe/uBRZsvn2P30Qg6a1Am2eMfxpmO3ggXz25ywe2WDNA/K4vk/RQ==",
"license": "MIT OR Apache-2.0",
"dependencies": {
"@noble/hashes": "^1.3.2",
- "@waku/utils": "0.0.23-3038c48.0"
+ "@waku/utils": "0.0.24-16328a3.0"
},
"engines": {
"node": ">=20"
}
},
"node_modules/@waku/proto": {
- "version": "0.0.10-3038c48.0",
- "resolved": "https://registry.npmjs.org/@waku/proto/-/proto-0.0.10-3038c48.0.tgz",
- "integrity": "sha512-xwStT7V2BsP9i4y0+sV5jCqBAuiuqPgHIz2vO4bGClVFwe5HCMjHbkWNZp0MVj4OWI3EqXmxakcirtBzT3gbOw==",
+ "version": "0.0.11-16328a3.0",
+ "resolved": "https://registry.npmjs.org/@waku/proto/-/proto-0.0.11-16328a3.0.tgz",
+ "integrity": "sha512-yFGW6UaQC0MrffZV25uVDYC8sUxI6OpnizTfSyebLg84eihYHJyxTed7pD/EyjSl8vL9GFX8nDuksDbkIQ2OQw==",
"license": "MIT OR Apache-2.0",
"dependencies": {
"protons-runtime": "^5.4.0"
@@ -1753,9 +1770,9 @@
}
},
"node_modules/@waku/sdk": {
- "version": "0.0.31-3038c48.0",
- "resolved": "https://registry.npmjs.org/@waku/sdk/-/sdk-0.0.31-3038c48.0.tgz",
- "integrity": "sha512-+ZkejfevYPQpIyxiUGeGxOYZHnl0VgwvWzJ/SQiBXP4YusLTRZJgulFwTcWzFlL8iBwdEptg9A2Ju/BC6IxVQA==",
+ "version": "0.0.32-16328a3.0",
+ "resolved": "https://registry.npmjs.org/@waku/sdk/-/sdk-0.0.32-16328a3.0.tgz",
+ "integrity": "sha512-tHS2+lf7NMQekuxfrZOGsMWya35W8saxjMjPnIRE6FOq0Mlv1ADwso05W7iy9ExNJoFD3+4XVemDvc5RfIM75w==",
"license": "MIT OR Apache-2.0",
"dependencies": {
"@chainsafe/libp2p-noise": "16.0.0",
@@ -1765,12 +1782,12 @@
"@libp2p/ping": "2.0.1",
"@libp2p/websockets": "^9.0.1",
"@noble/hashes": "^1.3.3",
- "@waku/core": "0.0.35-3038c48.0",
- "@waku/discovery": "0.0.8-3038c48.0",
- "@waku/interfaces": "0.0.30-3038c48.0",
- "@waku/message-hash": "0.1.19-3038c48.0",
- "@waku/proto": "0.0.10-3038c48.0",
- "@waku/utils": "0.0.23-3038c48.0",
+ "@waku/core": "0.0.36-16328a3.0",
+ "@waku/discovery": "0.0.9-16328a3.0",
+ "@waku/interfaces": "0.0.31-16328a3.0",
+ "@waku/message-hash": "0.1.20-16328a3.0",
+ "@waku/proto": "0.0.11-16328a3.0",
+ "@waku/utils": "0.0.24-16328a3.0",
"libp2p": "2.1.8"
},
"engines": {
@@ -1778,16 +1795,16 @@
}
},
"node_modules/@waku/sdk/node_modules/@waku/core": {
- "version": "0.0.35-3038c48.0",
- "resolved": "https://registry.npmjs.org/@waku/core/-/core-0.0.35-3038c48.0.tgz",
- "integrity": "sha512-N2YTKAL4ovl0pbiDr/FqVGtM41Lm+a5+Obk+FKUws/QlCHQK0RG8Qiders7UO5kWpuStm7Cynp8ngPp91U2nBQ==",
+ "version": "0.0.36-16328a3.0",
+ "resolved": "https://registry.npmjs.org/@waku/core/-/core-0.0.36-16328a3.0.tgz",
+ "integrity": "sha512-+5GNNcy3FYnPKaL0RuJ01oo3+wMqkVhHPKdpRc3T0Rodi6IZsFCFdT1ob3A5rpFo5IURXqNjElUVBItIGrtupw==",
"license": "MIT OR Apache-2.0",
"dependencies": {
"@libp2p/ping": "2.0.1",
- "@waku/enr": "0.0.29-3038c48.0",
- "@waku/interfaces": "0.0.30-3038c48.0",
- "@waku/proto": "0.0.10-3038c48.0",
- "@waku/utils": "0.0.23-3038c48.0",
+ "@waku/enr": "0.0.30-16328a3.0",
+ "@waku/interfaces": "0.0.31-16328a3.0",
+ "@waku/proto": "0.0.11-16328a3.0",
+ "@waku/utils": "0.0.24-16328a3.0",
"debug": "^4.3.4",
"it-all": "^3.0.4",
"it-length-prefixed": "^9.0.4",
@@ -1859,13 +1876,13 @@
}
},
"node_modules/@waku/utils": {
- "version": "0.0.23-3038c48.0",
- "resolved": "https://registry.npmjs.org/@waku/utils/-/utils-0.0.23-3038c48.0.tgz",
- "integrity": "sha512-vLoMRH6/wOx+Jx4522D6zeuf4v7CRzaOxhZYFv+lxJFEdTZr7C4ruuHVSdo5VdMx/u6Ad9oNtfqhygppcWyxpw==",
+ "version": "0.0.24-16328a3.0",
+ "resolved": "https://registry.npmjs.org/@waku/utils/-/utils-0.0.24-16328a3.0.tgz",
+ "integrity": "sha512-zYNeWtRAYGJLVvE/8xnKmO5ctxuPD6/k9NAS/g3ES0o++kBrXNRFy3d1V1nKlnerwJuwYmB0RhfwFmz7ehtwWA==",
"license": "MIT OR Apache-2.0",
"dependencies": {
"@noble/hashes": "^1.3.2",
- "@waku/interfaces": "0.0.30-3038c48.0",
+ "@waku/interfaces": "0.0.31-16328a3.0",
"chai": "^4.3.10",
"debug": "^4.3.4",
"uint8arrays": "^5.0.1"
@@ -3393,9 +3410,9 @@
}
},
"node_modules/dns-over-http-resolver": {
- "version": "3.0.10",
- "resolved": "https://registry.npmjs.org/dns-over-http-resolver/-/dns-over-http-resolver-3.0.10.tgz",
- "integrity": "sha512-l2kMOLxK6f9ll+5sf2Ndl8WS/2eXhOf9ZSXZMPnTVyHsv1ktN1WX3FwcyYklMq3ORv2N1nhf0TsGKWBjhgn0ug==",
+ "version": "3.0.15",
+ "resolved": "https://registry.npmjs.org/dns-over-http-resolver/-/dns-over-http-resolver-3.0.15.tgz",
+ "integrity": "sha512-h2Ldu6b8LjW725Q5zjjv7T5s1K3dPjlU3DWvcEFqB3Ksb3QmqC4dHhPKlGlBS/1P47D4T5arZMiE4dD4OIfO6A==",
"license": "Apache-2.0 OR MIT",
"dependencies": {
"quick-lru": "^7.0.0",
@@ -6022,9 +6039,9 @@
}
},
"node_modules/it-first": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/it-first/-/it-first-3.0.7.tgz",
- "integrity": "sha512-e2dVSlOP+pAxPYPVJBF4fX7au8cvGfvLhIrGCMc5aWDnCvwgOo94xHbi3Da6eXQ2jPL5FGEM8sJMn5uE8Seu+g==",
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/it-first/-/it-first-3.0.8.tgz",
+ "integrity": "sha512-neaRRwOMCmMKkXJVZ4bvUDVlde+Xh0aTWr7hFaOZeDXzbctGVV/WHmPVqBqy3RjlsP7eRM0vcqNtlM8hivcmGw==",
"license": "Apache-2.0 OR MIT"
},
"node_modules/it-foreach": {
diff --git a/examples/dogfooding/package.json b/examples/dogfooding/package.json
index 248cf23..e30e9f7 100644
--- a/examples/dogfooding/package.json
+++ b/examples/dogfooding/package.json
@@ -9,7 +9,7 @@
"dependencies": {
"@libp2p/crypto": "^5.0.5",
"@multiformats/multiaddr": "^12.3.1",
- "@waku/sdk": "0.0.31-3038c48.0",
+ "@waku/sdk": "0.0.32-16328a3.0",
"libp2p": "^2.1.10",
"protobufjs": "^7.3.0",
"uint8arrays": "^5.1.0"
diff --git a/examples/dogfooding/public/style.css b/examples/dogfooding/public/style.css
new file mode 100644
index 0000000..d10d7e8
--- /dev/null
+++ b/examples/dogfooding/public/style.css
@@ -0,0 +1,187 @@
+/* General Styles */
+body {
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+ margin: 0;
+ background-color: #f4f7f9;
+ color: #333;
+ line-height: 1.6;
+}
+
+.app-container {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 20px;
+}
+
+header {
+ background-color: #2c3e50;
+ color: #ecf0f1;
+ padding: 20px;
+ border-radius: 8px;
+ margin-bottom: 20px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+header h1 {
+ margin: 0;
+ font-size: 1.8em;
+}
+
+.connection-status span {
+ font-size: 0.9em;
+}
+
+main {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 20px;
+}
+
+section {
+ background-color: #fff;
+ padding: 20px;
+ border-radius: 8px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+}
+
+h2 {
+ margin-top: 0;
+ color: #3498db;
+ border-bottom: 2px solid #ecf0f1;
+ padding-bottom: 10px;
+ margin-bottom: 15px;
+}
+
+/* Message Statistics */
+.stats-counters {
+ display: flex;
+ justify-content: space-around;
+ text-align: center;
+}
+
+.counter-value {
+ display: block;
+ font-size: 2em;
+ font-weight: bold;
+ color: #2980b9;
+}
+
+.counter-label {
+ font-size: 0.9em;
+ color: #555;
+}
+
+/* Message Controls */
+.btn {
+ padding: 10px 15px;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
+ font-size: 1em;
+ transition: background-color 0.3s ease;
+}
+
+.btn-primary {
+ background-color: #3498db;
+ color: white;
+}
+
+.btn-primary:hover {
+ background-color: #2980b9;
+}
+
+.search-container {
+ margin-top: 15px;
+ display: flex;
+ gap: 10px;
+}
+
+#searchInput {
+ flex-grow: 1;
+ padding: 10px;
+ border: 1px solid #ddd;
+ border-radius: 5px;
+ font-size: 0.9em;
+}
+
+/* Message Display */
+.message-list {
+ max-height: 400px;
+ overflow-y: auto;
+ border: 1px solid #ecf0f1;
+ border-radius: 5px;
+ padding: 10px;
+}
+
+.message-item {
+ padding: 10px;
+ margin-bottom: 8px;
+ border-radius: 4px;
+ background-color: #ecf0f1;
+ font-size: 0.9em;
+}
+
+.message-item.sent {
+ background-color: #eafaf1; /* Light green for sent */
+}
+
+.message-item.received-mine {
+ background-color: #e8f6fd; /* Light blue for received (mine) */
+}
+
+.message-item.received-other {
+ background-color: #fdf3e8; /* Light orange for received (other) */
+}
+
+.message-item p {
+ margin: 5px 0;
+}
+
+.message-item .timestamp {
+ font-size: 0.8em;
+ color: #7f8c8d;
+ text-align: right;
+}
+
+.message-item .content {
+ font-weight: 500;
+}
+
+.message-item .sender-info {
+ font-size: 0.8em;
+ color: #34495e;
+}
+
+/* Added style for Message ID */
+.message-item .message-id {
+ font-size: 0.75em;
+ color: #888;
+ margin-bottom: 3px;
+ word-break: break-all; /* Ensure long IDs don't break layout */
+}
+
+footer {
+ text-align: center;
+ margin-top: 30px;
+ padding-top: 15px;
+ border-top: 1px solid #ecf0f1;
+ font-size: 0.9em;
+ color: #7f8c8d;
+}
+
+/* Responsive Design */
+@media (min-width: 768px) {
+ main {
+ grid-template-columns: repeat(2, 1fr);
+ }
+
+ .message-stats, .message-controls {
+ grid-column: span 1;
+ }
+
+ .message-display {
+ grid-column: span 2;
+ }
+}
diff --git a/examples/dogfooding/src/index.ts b/examples/dogfooding/src/index.ts
index 3677558..e850495 100644
--- a/examples/dogfooding/src/index.ts
+++ b/examples/dogfooding/src/index.ts
@@ -1,161 +1,139 @@
import {
- createLightNode,
- DecodedMessage,
- LightNode,
-} from "@waku/sdk";
-import { generateKeyPairFromSeed } from "@libp2p/crypto/keys";
-import { fromString } from "uint8arrays";
-
-import { Type, Field } from "protobufjs";
+ getWakuNode,
+ createWakuEncoder,
+ createWakuDecoder,
+ getPeerId
+} from "./waku-service";
+import { DecodedMessage } from "@waku/sdk";
import {
- generateRandomNumber,
- sha256,
-} from "./util";
+ encodeMessage,
+ decodeMessage,
+ ChatMessage,
+ ProtoChatMessage,
+} from "./message-service";
+import {
+ updatePeerIdDisplay,
+ incrementSentByMe,
+ incrementReceivedMine,
+ incrementReceivedOthers,
+ addMessageToLog,
+ renderMessages,
+ getSearchTerm,
+} from "./ui-manager";
-const DEFAULT_CONTENT_TOPIC = "/js-waku-examples/1/message-ratio/utf8";
+const NUM_MESSAGES_PER_BATCH = 5;
+let batchCounter = 0;
-const ProtoSequencedMessage = new Type("SequencedMessage")
- .add(new Field("hash", 1, "string"))
- .add(new Field("total", 2, "uint64"))
- .add(new Field("index", 3, "uint64"))
- .add(new Field("sender", 4, "string"));
+async function initializeApp() {
+ try {
+ console.log("Initializing Waku node...");
+ const node = await getWakuNode();
+ const currentPeerId = getPeerId();
+ console.log("Waku node initialized. Peer ID:", currentPeerId);
-const sequenceCompletedEvent = new CustomEvent("sequenceCompleted");
-const messageReceivedEvent = new CustomEvent("messageReceived");
+ if (currentPeerId) {
+ updatePeerIdDisplay(currentPeerId);
+ }
-async function wakuNode(): Promise
{
- let seed = localStorage.getItem("seed");
+ const sendMessageBatch = async () => {
+ const encoder = createWakuEncoder();
+ batchCounter++;
+ console.log(`Sending batch C${batchCounter} of ${NUM_MESSAGES_PER_BATCH} messages...`);
+ for (let i = 0; i < NUM_MESSAGES_PER_BATCH; i++) {
+ const messageContent = `Batch ${batchCounter} - Msg ${i + 1} @ ${new Date().toLocaleTimeString()}`;
+ const payload = encodeMessage(messageContent);
- if (!seed) {
- seed = (await sha256(generateRandomNumber())).slice(0, 32);
- localStorage.setItem("seed", seed);
- }
+ const tempDecodedMessage = ProtoChatMessage.decode(payload);
+ const messageId = (tempDecodedMessage as any).id || `temp-id-${Date.now()}`;
- const privateKey = await generateKeyPairFromSeed("Ed25519", fromString(seed));
+ const chatMessage: ChatMessage = {
+ id: messageId,
+ timestamp: Date.now(),
+ senderPeerId: currentPeerId || "unknown",
+ content: messageContent
+ };
- const node = await createLightNode({
- defaultBootstrap: false,
- numPeersToUse: 2,
- networkConfig: {
- clusterId: 42,
- shards: [0]
- },
- libp2p: {
- privateKey,
- },
- });
-
- (window as any).waku = node;
-
- await node.dial("/dns4/waku-test.bloxy.one/tcp/8095/wss/p2p/16Uiu2HAmSZbDB7CusdRhgkD81VssRjQV5ZH13FbzCGcdnbbh6VwZ");
- await node.dial("/dns4/vps-aaa00d52.vps.ovh.ca/tcp/8000/wss/p2p/16Uiu2HAm9PftGgHZwWE3wzdMde4m3kT2eYJFXLZfGoSED3gysofk");
- await node.dial("/dns4/waku.fryorcraken.xyz/tcp/8000/wss/p2p/16Uiu2HAmMRvhDHrtiHft1FTUYnn6cVA8AWVrTyLUayJJ3MWpUZDB");
-
- return node;
-}
-
-export async function app() {
- const node = await wakuNode();
-
- console.log("DEBUG: your peer ID is:", node.libp2p.peerId.toString());
-
- await node.start();
- await node.waitForPeers();
-
- const peerId = node.libp2p.peerId.toString();
- const encoder = node.createEncoder({
- contentTopic: DEFAULT_CONTENT_TOPIC
- });
-
- const startLightPushSequence = async (
- numMessages: number,
- period: number = 3000
- ) => {
- const sequenceHash = await sha256(generateRandomNumber());
- const sequenceTotal = numMessages;
- let sequenceIndex = 0;
-
- const sendMessage = async () => {
- try {
- const messageHash = await sha256(
- `${sequenceHash}-${sequenceIndex}-${sequenceTotal}`
- );
-
- const timestamp = Math.floor(new Date().getTime() / 1000);
- const message = ProtoSequencedMessage.create({
- hash: messageHash,
- total: sequenceTotal,
- index: sequenceIndex,
- sender: peerId,
- });
- const payload = ProtoSequencedMessage.encode(message).finish();
-
- const result = await node.lightPush.send(
- encoder,
- {
+ try {
+ const result = await node.lightPush.send(encoder, {
payload,
- timestamp: new Date(),
- },
- { autoRetry: true }
- );
+ timestamp: new Date(chatMessage.timestamp),
+ }, { autoRetry: true });
- console.log("DEBUG: light push successes: ", result.successes.length);
- console.log(
- "DEBUG: light push failures: ",
- result.failures.length
- );
-
- // Increment sequence
- sequenceIndex++;
-
- if (sequenceIndex < sequenceTotal) {
- setTimeout(sendMessage, period); // Schedule the next send
- } else {
- document.dispatchEvent(sequenceCompletedEvent);
+ if (result.successes.length > 0) {
+ console.log(`Message ${i + 1} (ID: ${chatMessage.id}) sent successfully.`);
+ incrementSentByMe();
+ addMessageToLog(chatMessage, 'sent');
+ } else {
+ console.warn(`Failed to send message ${i + 1} (ID: ${chatMessage.id}):`, result.failures);
+ }
+ } catch (error) {
+ console.error(`Error sending message ${i + 1} (ID: ${chatMessage.id}):`, error);
}
- } catch (error) {
- console.error("DEBUG: Error sending message", error);
+ await new Promise(resolve => setTimeout(resolve, 100));
}
+ console.log("Message batch sending complete.");
};
- sendMessage(); // Start the recursive sending
- };
+ const subscribeToMessages = async () => {
+ const decoder = createWakuDecoder();
+ console.log("Subscribing to messages...");
+ await node.filter.subscribe(decoder, (wakuMessage: DecodedMessage) => {
+ console.log("Raw Waku message received, payload length:", wakuMessage.payload.length);
+ const chatMessage = decodeMessage(wakuMessage.payload);
- const startFilterSubscription = async () => {
- const decoder = node.createDecoder({ contentTopic: DEFAULT_CONTENT_TOPIC });
-
- const subscriptionCallback = async (message: DecodedMessage) => {
- const decodedMessage: any = ProtoSequencedMessage.decode(
- message.payload
- );
-
- if (decodedMessage.sender === peerId) {
- return;
- }
-
- const messageElement = document.createElement("div");
- messageElement.textContent = `Message: ${decodedMessage.hash}`;
- document.dispatchEvent(messageReceivedEvent);
+ if (chatMessage) {
+ console.log("Decoded chat message:", chatMessage);
+ if (chatMessage.senderPeerId === currentPeerId) {
+ incrementReceivedMine();
+ console.log("Received own message (loopback):", chatMessage.id);
+ } else {
+ incrementReceivedOthers();
+ addMessageToLog(chatMessage, 'received-other');
+ console.log("Received message from other peer:", chatMessage.id);
+ }
+ } else {
+ console.warn("Could not decode received Waku message. Payload might be malformed or not a ChatMessage.");
+ }
+ });
+ console.log("Subscription active.");
};
- await node.filter.subscribe(decoder, subscriptionCallback);
- };
+ const sendMessageButton = document.getElementById("sendMessageButton");
+ if (sendMessageButton) {
+ sendMessageButton.addEventListener("click", () => {
+ console.log("Send Message Button clicked");
+ sendMessageBatch();
+ });
+ }
- return {
- node,
- startLightPushSequence,
- startFilterSubscription,
- };
+ const searchButton = document.getElementById("searchButton");
+ if (searchButton) {
+ searchButton.addEventListener("click", () => {
+ console.log("Search button clicked");
+ renderMessages(getSearchTerm());
+ });
+ }
+
+ const searchInput = document.getElementById("searchInput");
+ if(searchInput) {
+ searchInput.addEventListener("input", () => {
+ console.log("Search input changed");
+ renderMessages(getSearchTerm());
+ });
+ }
+
+ await subscribeToMessages();
+
+ console.log("Application setup complete. Click 'Send New Message Batch' to send messages.");
+
+ } catch (error) {
+ console.error("Critical error during app initialization:", error);
+ const peerIdDisplayEl = document.getElementById("peerIdDisplay");
+ if(peerIdDisplayEl) peerIdDisplayEl.textContent = "Error connecting to Waku Network.";
+ }
}
-(async () => {
- const { startLightPushSequence, startFilterSubscription } = await app();
-
- startFilterSubscription();
-
- document.addEventListener(sequenceCompletedEvent.type, () =>
- startLightPushSequence(10, 3000)
- );
-
- startLightPushSequence(10, 3000);
-})();
\ No newline at end of file
+document.addEventListener("DOMContentLoaded", () => {
+ console.log("DOM fully loaded and parsed. Starting app initialization.");
+ initializeApp();
+});
diff --git a/examples/dogfooding/src/message-service.ts b/examples/dogfooding/src/message-service.ts
new file mode 100644
index 0000000..bc1539e
--- /dev/null
+++ b/examples/dogfooding/src/message-service.ts
@@ -0,0 +1,42 @@
+import { Type, Field } from "protobufjs";
+import { getPeerId } from "./waku-service";
+
+// New message structure with a unique ID and content for searchability
+export const ProtoChatMessage = new Type("ChatMessage")
+ .add(new Field("id", 1, "string")) // Unique message ID (e.g., UUID or timestamp-based)
+ .add(new Field("timestamp", 2, "uint64"))
+ .add(new Field("senderPeerId", 3, "string"))
+ .add(new Field("content", 4, "string")); // Actual message content
+
+export interface ChatMessage {
+ id: string;
+ timestamp: number;
+ senderPeerId: string;
+ content: string;
+}
+
+export function encodeMessage(content: string): Uint8Array {
+ const id = `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
+ const message = ProtoChatMessage.create({
+ id,
+ timestamp: Date.now(),
+ senderPeerId: getPeerId() || "unknown",
+ content,
+ });
+ return ProtoChatMessage.encode(message).finish();
+}
+
+export function decodeMessage(payload: Uint8Array): ChatMessage | null {
+ try {
+ const decoded = ProtoChatMessage.decode(payload) as any;
+ return {
+ id: decoded.id,
+ timestamp: Number(decoded.timestamp),
+ senderPeerId: decoded.senderPeerId,
+ content: decoded.content,
+ };
+ } catch (error) {
+ console.error("Failed to decode message:", error);
+ return null;
+ }
+}
diff --git a/examples/dogfooding/src/ui-manager.ts b/examples/dogfooding/src/ui-manager.ts
new file mode 100644
index 0000000..4c0dcc0
--- /dev/null
+++ b/examples/dogfooding/src/ui-manager.ts
@@ -0,0 +1,105 @@
+import { ChatMessage } from "./message-service";
+
+const sentByMeCountEl = document.getElementById("sentByMeCount") as HTMLSpanElement;
+const receivedMineCountEl = document.getElementById("receivedMineCount") as HTMLSpanElement;
+const receivedOthersCountEl = document.getElementById("receivedOthersCount") as HTMLSpanElement;
+const peerIdDisplayEl = document.getElementById("peerIdDisplay") as HTMLSpanElement;
+const messageListEl = document.getElementById("messageList") as HTMLDivElement;
+const searchInputEl = document.getElementById("searchInput") as HTMLInputElement;
+
+let sentByMe = 0;
+let receivedMine = 0;
+let receivedOthers = 0;
+
+let currentMessages: ChatMessage[] = [];
+let currentPeerId: string | undefined;
+
+export function updatePeerIdDisplay(peerId: string) {
+ currentPeerId = peerId;
+ if (peerIdDisplayEl) {
+ peerIdDisplayEl.textContent = peerId;
+ }
+}
+
+export function incrementSentByMe() {
+ sentByMe++;
+ if (sentByMeCountEl) sentByMeCountEl.textContent = sentByMe.toString();
+}
+
+export function incrementReceivedMine() {
+ receivedMine++;
+ if (receivedMineCountEl) receivedMineCountEl.textContent = receivedMine.toString();
+}
+
+export function incrementReceivedOthers() {
+ receivedOthers++;
+ if (receivedOthersCountEl) receivedOthersCountEl.textContent = receivedOthers.toString();
+}
+
+export function addMessageToLog(message: ChatMessage, type: 'sent' | 'received-mine' | 'received-other') {
+ currentMessages.push(message);
+ renderMessages();
+}
+
+export function renderMessages(filterText?: string) {
+ if (!messageListEl) return;
+
+ messageListEl.innerHTML = "";
+
+ const messagesToRender = filterText
+ ? currentMessages.filter(msg => {
+ const searchTerm = filterText.toLowerCase();
+ return (
+ msg.content.toLowerCase().includes(searchTerm) ||
+ msg.id.toLowerCase().includes(searchTerm) ||
+ msg.senderPeerId.toLowerCase().includes(searchTerm)
+ );
+ })
+ : currentMessages;
+
+ messagesToRender.sort((a, b) => a.timestamp - b.timestamp);
+
+ messagesToRender.forEach(message => {
+ const item = document.createElement("div");
+ item.classList.add("message-item");
+
+ let typeClass = '';
+ let senderPrefix = '';
+
+ if (message.senderPeerId === currentPeerId) {
+ typeClass = 'sent';
+ senderPrefix = 'Me';
+ } else {
+ typeClass = 'received-other';
+ senderPrefix = `Other (${message.senderPeerId.substring(0, 6)}...)`;
+ }
+
+ item.classList.add(typeClass);
+
+ const idText = document.createElement("p");
+ idText.classList.add("message-id");
+ idText.textContent = `ID: ${message.id}`;
+
+ const contentP = document.createElement("p");
+ contentP.classList.add("content");
+ contentP.textContent = message.content;
+
+ const senderInfoP = document.createElement("p");
+ senderInfoP.classList.add("sender-info");
+ senderInfoP.textContent = `From: ${senderPrefix}`;
+
+ const timestampP = document.createElement("p");
+ timestampP.classList.add("timestamp");
+ timestampP.textContent = new Date(message.timestamp).toLocaleTimeString();
+
+ item.appendChild(idText);
+ item.appendChild(senderInfoP);
+ item.appendChild(contentP);
+ item.appendChild(timestampP);
+ messageListEl.appendChild(item);
+ });
+}
+
+export function getSearchTerm(): string {
+ return searchInputEl ? searchInputEl.value : "";
+}
diff --git a/examples/dogfooding/src/util.ts b/examples/dogfooding/src/util.ts
index ed17548..de5580c 100644
--- a/examples/dogfooding/src/util.ts
+++ b/examples/dogfooding/src/util.ts
@@ -3,10 +3,10 @@ export const generateRandomNumber = (): number => {
};
export const sha256 = async (number: number | string ): Promise => {
- const encoder = new TextEncoder();
- const data = encoder.encode(number.toString());
- const buffer = await crypto.subtle.digest("SHA-256", data);
- return Array.from(new Uint8Array(buffer))
- .map((b) => b.toString(16).padStart(2, "0"))
- .join("");
-};
\ No newline at end of file
+ const encoder = new TextEncoder();
+ const data = encoder.encode(number.toString());
+ const buffer = await crypto.subtle.digest("SHA-256", data);
+ return Array.from(new Uint8Array(buffer))
+ .map((b) => b.toString(16).padStart(2, "0"))
+ .join("");
+ };
diff --git a/examples/dogfooding/src/utils.ts b/examples/dogfooding/src/utils.ts
new file mode 100644
index 0000000..58c3e91
--- /dev/null
+++ b/examples/dogfooding/src/utils.ts
@@ -0,0 +1,12 @@
+export async function sha256(text: string): Promise {
+ const encoder = new TextEncoder();
+ const data = encoder.encode(text);
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
+ const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
+ return hashHex;
+}
+
+export function generateRandomNumber(): string {
+ return Math.random().toString();
+}
diff --git a/examples/dogfooding/src/waku-service.ts b/examples/dogfooding/src/waku-service.ts
new file mode 100644
index 0000000..f2bf83a
--- /dev/null
+++ b/examples/dogfooding/src/waku-service.ts
@@ -0,0 +1,64 @@
+import { LightNode, createLightNode, createEncoder, createDecoder } from "@waku/sdk";
+import { generateKeyPairFromSeed } from "@libp2p/crypto/keys";
+import { fromString } from "uint8arrays";
+import { sha256, generateRandomNumber } from "./utils";
+
+export const DEFAULT_CONTENT_TOPIC = "/js-waku-examples/1/message-ratio/utf8";
+
+let wakuNodeInstance: LightNode | null = null;
+
+export async function getWakuNode(): Promise {
+ if (wakuNodeInstance) {
+ return wakuNodeInstance;
+ }
+
+ let seed = localStorage.getItem("seed");
+ if (!seed) {
+ seed = (await sha256(generateRandomNumber())).slice(0, 32);
+ localStorage.setItem("seed", seed);
+ }
+
+ const privateKey = await generateKeyPairFromSeed("Ed25519", fromString(seed));
+
+ const node = await createLightNode({
+ defaultBootstrap: false,
+ networkConfig: {
+ clusterId: 42,
+ shards: [0]
+ },
+ numPeersToUse: 2,
+ libp2p: {
+ privateKey,
+ },
+ });
+
+ await Promise.allSettled([
+ node.dial("/dns4/waku-test.bloxy.one/tcp/8095/wss/p2p/16Uiu2HAmSZbDB7CusdRhgkD81VssRjQV5ZH13FbzCGcdnbbh6VwZ"),
+ node.dial("/dns4/vps-aaa00d52.vps.ovh.ca/tcp/8000/wss/p2p/16Uiu2HAm9PftGgHZwWE3wzdMde4m3kT2eYJFXLZfGoSED3gysofk")
+ ]);
+
+ await node.start();
+ await node.waitForPeers();
+
+ wakuNodeInstance = node;
+ (window as any).waku = node;
+ return node;
+}
+
+export function getPeerId(): string | undefined {
+ return wakuNodeInstance?.libp2p.peerId.toString();
+}
+
+export function createWakuEncoder() {
+ return createEncoder({
+ contentTopic: DEFAULT_CONTENT_TOPIC,
+ pubsubTopicShardInfo: {
+ clusterId: 42,
+ shard: 0,
+ }
+ });
+}
+
+export function createWakuDecoder() {
+ return createDecoder(DEFAULT_CONTENT_TOPIC, { clusterId: 42, shard: 0 });
+}
diff --git a/examples/dogfooding/webpack.config.js b/examples/dogfooding/webpack.config.js
index b5fe261..6a4d646 100644
--- a/examples/dogfooding/webpack.config.js
+++ b/examples/dogfooding/webpack.config.js
@@ -1,25 +1,22 @@
-const path = require("path");
-const webpack = require("webpack");
const CopyWebpackPlugin = require("copy-webpack-plugin");
-const HtmlWebpackPlugin = require('html-webpack-plugin');
+const path = require("path");
module.exports = {
- entry: "./src/index.ts", // Changed from index.js to index.ts
+ entry: "./src/index.ts",
output: {
path: path.resolve(__dirname, "build"),
filename: "index.js",
- publicPath: process.env.NODE_ENV === 'production' ? '/dogfooding/' : '/'
},
experiments: {
asyncWebAssembly: true,
},
resolve: {
- extensions: ['.ts', '.js'], // Add .ts to the extensions
+ extensions: ['.ts', '.js'],
},
module: {
rules: [
{
- test: /\.ts$/, // Add a rule for TypeScript files
+ test: /\.ts$/,
use: 'ts-loader',
exclude: /node_modules/,
},
@@ -32,11 +29,13 @@ module.exports = {
},
plugins: [
new CopyWebpackPlugin({
- patterns: ["favicon.ico", "favicon.png", "manifest.json"],
+ patterns: [
+ { from: "index.html", to: "index.html" },
+ { from: "public/style.css", to: "style.css" },
+ { from: "manifest.json", to: "manifest.json" },
+ { from: "favicon.ico", to: "favicon.ico" },
+ { from: "favicon.png", to: "favicon.png" },
+ ],
}),
- new HtmlWebpackPlugin({
- template: 'index.html',
- base: process.env.NODE_ENV === 'production' ? '/dogfooding/' : '/',
- })
],
};
\ No newline at end of file