hive-p2p 1.0.31 → 1.0.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  - **.min (unpkg):** [unpkg.com/@hive-p2p/browser@latest/hive-p2p.min.js](https://unpkg.com/@hive-p2p/browser@1.0.19/hive-p2p.min.js)
10
10
  - **.min (cdn):** [cdn.jsdelivr.net/npm/@hive-p2p/browser@latest/hive-p2p.min.js](https://cdn.jsdelivr.net/npm/@hive-p2p/browser@latest/hive-p2p.min.js)
11
11
  - **Stats:** [ghloc.vercel.app/Seigneur-Machiavel/hive-p2p](https://ghloc.vercel.app/Seigneur-Machiavel/hive-p2p?branch=main)
12
- - **DEMO-REPO :** [github.com/Seigneur-Machiavel/hive-p2p-demo](https://github.com/Seigneur-Machiavel/hive-p2p-demo)
12
+ - **demos-repo :** [github.com/Seigneur-Machiavel/hive-p2p-demo](https://github.com/Seigneur-Machiavel/hive-p2p-demo)
13
13
 
14
14
  ## What is HiveP2P?
15
15
 
@@ -45,27 +45,31 @@ npm install @hive-p2p/browser
45
45
  ### Basic Usage
46
46
 
47
47
  ```javascript
48
- import { Node } from 'hive-p2p';
48
+ import HiveP2P from "hive-p2p"; // using full lib (server + client)
49
49
 
50
- // Create a node
51
- const node = new Node();
50
+ // Create a public node
51
+ const publicNode = await HiveP2P.createPublicNode({ domain: 'localhost', port: 12345 });
52
52
 
53
- // Start with bootstrap nodes
54
- await node.start([
55
- { id: '0abc...', publicUrl: 'ws://seed1.example.com:8080' },
56
- { id: '0def...', publicUrl: 'ws://seed2.example.com:8080' }
57
- ]);
53
+ // Use public node as bootstrap by extracting its url
54
+ const bootstraps = [publicNode.publicUrl];
58
55
 
59
- // Send messages
60
- node.on('message', (senderId, data) => {
61
- console.log(`Message from ${senderId}:`, data);
62
- });
56
+ // Create nodes with bootstrap node
57
+ const bee1 = await HiveP2P.createNode({ bootstraps });
58
+ const bee2 = await HiveP2P.createNode({ bootstraps });
59
+
60
+ // Listen for unicast messages (target id)
61
+ for (const node of [publicNode, bee1, bee2])
62
+ node.onMessageData((fromId, message) => console.log(`[${node.id}] from [${fromId}]: ${message}`));
63
63
 
64
- // Broadcast to network
65
- node.gossip.broadcastToAll({ type: 'announcement', value: 'Hello Hive!' });
64
+ // Listen for gossip messages (broadcast to all)
65
+ for (const node of [publicNode, bee1, bee2])
66
+ node.onGossipData((fromId, message) => console.log(`[${node.id}]: from [${fromId}]: ${message}`));
66
67
 
67
- // Direct message
68
- node.messager.sendUnicast(targetId, { type: 'private', content: 'Direct message' });
68
+ while (true) {
69
+ bee1.sendMessage(bee2.id, `Hello bee2!`); // Send direct unicast message
70
+ bee2.broadcast("Hello everyone! I'm bee bee2"); // Broadcast message to all
71
+ await new Promise((resolve) => setTimeout(resolve, 1_000));
72
+ }
69
73
  ```
70
74
 
71
75
  ## How It Works
@@ -110,23 +114,12 @@ Real simulation results with 2000 nodes:
110
114
  ┌──────────────────────────────────────────────────────────┐
111
115
  │ 10 minutes (600s) │
112
116
  ├──────────────────────────────────────────────────────────┤
113
- │ Active nodes: 2002/2002 (1902 established)
117
+ │ Active nodes: 2002/2002 (1902 established)
114
118
  │ Avg neighbors: 4.7 │
115
119
  │ Network coverage: 100% (T: 10571) │
116
120
  │ Avg latency: 169ms │
117
- │ Gossip rate: 0.4 msg/s
118
- │ Bandwidth: 856 bytes/s
119
- └──────────────────────────────────────────────────────────┘
120
-
121
- ┌──────────────────────────────────────────────────────────┐
122
- │ 1 hour (602s - continued sim) │
123
- ├──────────────────────────────────────────────────────────┤
124
- │ Active nodes: 2003/2003 (1903 established) │
125
- │ Avg neighbors: 4.8 │
126
- │ Network coverage: 100% (T: 8225) │
127
- │ Avg latency: 223ms │
128
- │ Gossip rate: 0.2 msg/s │
129
- │ Bandwidth: 396 bytes/s │
121
+ │ Gossip rate: 0.4 msg/s
122
+ │ Bandwidth: 856 bytes/s
130
123
  └──────────────────────────────────────────────────────────┘
131
124
  ```
132
125
 
@@ -284,10 +277,6 @@ Check our [contribution guide](CONTRIBUTING.md) to get started.
284
277
 
285
278
  HiveP2P embodies this principle - simple components (gossip, routing, topology awareness) combine to create a complex, self-organizing system that surpasses traditional approaches.
286
279
 
287
- ## License
288
-
289
- GNU General Public License v3.0 - See [LICENSE](LICENSE) for details.
290
-
291
280
  ---
292
281
 
293
282
  *HiveP2P - Because the internet deserves better than client-server*
@@ -571,10 +560,6 @@ Consultez notre [guide de contribution](CONTRIBUTING.md) pour commencer.
571
560
 
572
561
  HiveP2P incarne ce principe - des composants simples (gossip, routage, conscience topologique) se combinent pour créer un système complexe et auto-organisant qui surpasse les approches traditionnelles.
573
562
 
574
- ## Licence
575
-
576
- GNU General Public License v3.0 - Voir [LICENSE](LICENSE) pour les détails.
577
-
578
563
  ---
579
564
 
580
565
  *HiveP2P - Parce qu'Internet mérite mieux que client-serveur*
@@ -121,11 +121,13 @@ export class NodeServices {
121
121
  static deriveSTUNServers(bootstraps) {
122
122
  /** @type {Array<{urls: string}>} */
123
123
  const stunUrls = [];
124
- for (const b of bootstraps) { // {urls: 'stun:42312:NaN'}
124
+ for (const b of bootstraps) {
125
125
  if (!b.includes(':')) continue;
126
- const url = b.replace('/ws', '/signal'); // in case someone put domain/ws
127
- url.replace('/wss', '/signal'); // in case someone put domain/wss
128
- url.replace('ws://', 'stun:');
126
+ if (b.endsWith('/ws') || b.endsWith('/wss')) continue; // ugly hardcode
127
+ // useless ? => proxy can't serve as stun server
128
+ //const url = b.replace('/ws', '/signal'); // in case someone put domain/ws
129
+ //url.replace('/wss', '/signal'); // in case someone put domain/wss
130
+ const url = b.replace('ws://', 'stun:');
129
131
  url.replace('wss://', 'stun:');
130
132
  if (!url.includes('stun:')) continue;
131
133
  stunUrls.push({ urls: url });
package/core/node.mjs CHANGED
@@ -17,12 +17,13 @@ import { NodeServices } from './node-services.mjs';
17
17
  * @param {number} [options.port] If provided, the node will listen on this port (default: SERVICE.PORT)
18
18
  * @param {number} [options.verbose] Verbosity level for logging (default: NODE.DEFAULT_VERBOSE) */
19
19
  export async function createPublicNode(options) {
20
- await CLOCK.sync(this.verbose);
21
20
  const verbose = options.verbose !== undefined ? options.verbose : NODE.DEFAULT_VERBOSE;
22
21
  const domain = options.domain || undefined;
23
22
  const codex = options.cryptoCodex || new CryptoCodex(undefined, verbose);
23
+ const clockSync = CLOCK.sync(verbose);
24
24
  if (!codex.publicKey) await codex.generate(domain ? true : false);
25
25
 
26
+ await clockSync;
26
27
  const node = new Node(codex, options.bootstraps || [], verbose);
27
28
  if (domain) {
28
29
  node.services = new NodeServices(codex, node.peerStore, undefined, verbose);
@@ -42,8 +43,10 @@ export async function createPublicNode(options) {
42
43
  export async function createNode(options = {}) {
43
44
  const verbose = options.verbose !== undefined ? options.verbose : NODE.DEFAULT_VERBOSE;
44
45
  const codex = options.cryptoCodex || new CryptoCodex(undefined, verbose);
46
+ const clockSync = CLOCK.sync(verbose);
45
47
  if (!codex.publicKey) await codex.generate(false);
46
48
 
49
+ await clockSync;
47
50
  const node = new Node(codex, options.bootstraps || [], verbose);
48
51
  if (options.autoStart !== false) await node.start();
49
52
  return node;
@@ -145,10 +148,13 @@ export class Node {
145
148
  await CLOCK.sync(this.verbose);
146
149
  this.started = true;
147
150
  if (SIMULATION.AVOID_INTERVALS) return true; // SIMULATOR CASE
148
- this.topologist.tryConnectNextBootstrap(); // first shot ASAP
151
+
149
152
  this.arbiterInterval = setInterval(() => this.arbiter.tick(), 1000);
150
- this.enhancerInterval = setInterval(() => this.topologist.tick(), DISCOVERY.LOOP_DELAY);
151
153
  this.peerStoreInterval = setInterval(() => { this.peerStore.cleanupExpired(); this.peerStore.offerManager.tick(); }, 2500);
154
+ if (this.publicUrl) return true;
155
+
156
+ this.enhancerInterval = setInterval(() => this.topologist.tick(), DISCOVERY.LOOP_DELAY);
157
+ this.topologist.tryConnectNextBootstrap(); // first shot ASAP
152
158
  return true;
153
159
  }
154
160
  /** Broadcast a message to all connected peers or to a specified peer
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hive-p2p",
3
- "version": "1.0.31",
3
+ "version": "1.0.32",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -1,5 +1,7 @@
1
1
  import path from 'path';
2
+ import { join } from 'path';
2
3
  import express from 'express';
4
+ import { readFileSync } from 'fs';
3
5
  import { fileURLToPath } from 'url';
4
6
  import { WebSocketServer } from 'ws';
5
7
  import { io } from 'socket.io-client'; // used for twitch events only
@@ -229,13 +231,19 @@ if (SIMULATION.RANDOM_UNICAST_PER_SEC) randomMessagesLoop('U', SIMULATION.RANDOM
229
231
  if (SIMULATION.RANDOM_GOSSIP_PER_SEC) randomMessagesLoop('G', SIMULATION.RANDOM_GOSSIP_PER_SEC);
230
232
 
231
233
  const app = express(); // simple server to serve texts/p2p_simulator.html
232
- app.use('../rendering/visualizer.mjs', (req, res, next) => {
233
- res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' });
234
- next();
234
+ const server = app.listen(3000, () => console.log('%cServer listening on http://localhost:3000', LOG_CSS.SIMULATOR));
235
+ app.get('/core/config.mjs', (req, res) => { // CONFIG PATCH FOR SIMULATOR
236
+ const configPath = join(packageRoot, 'core/config.mjs');
237
+ let content = readFileSync(configPath, 'utf8');
238
+ content = content // Patch the values
239
+ .replace(/ARE_IDS_HEX:\s*true/g, 'ARE_IDS_HEX: false')
240
+ .replace(/PUBLIC_PREFIX:\s*'0'/g, "PUBLIC_PREFIX: 'P_'")
241
+ .replace(/STANDARD_PREFIX:\s*'1'/g, "STANDARD_PREFIX: 'N_'");
242
+
243
+ res.set({ 'Content-Type': 'application/javascript', 'Cache-Control': 'no-cache, no-store, must-revalidate'});
244
+ res.send(content);
235
245
  });
236
-
237
246
  app.use(express.static(packageRoot));
238
- const server = app.listen(3000, () => console.log('%cServer listening on http://localhost:3000', LOG_CSS.SIMULATOR));
239
247
  app.get('/', (req, res) => res.sendFile('rendering/visualizer.html', { root: packageRoot }));
240
248
  app.get('/the-gossip-grail', (req, res) => res.sendFile('resources/the-gossip-grail.html', { root: packageRoot }));
241
249
 
@@ -316,7 +324,7 @@ class TwitchChatCommandInterpreter {
316
324
  .replace(/[^\w-]/g, '_') // remplacer chars spéciaux par _
317
325
 
318
326
  const cryptoCodex = IDENTITY.ARE_IDS_HEX ? new CryptoCodex() : new CryptoCodex(`F_${cleanUser}`);
319
- const peer = await createPublicNode({ bootstraps: pickUpRandomBootstraps(), cryptoCodex, domain: 'localhost', port: 9000 + Object.keys(this.userNodes).length * 2, verbose: 2 });
327
+ const peer = await createNode({ bootstraps: pickUpRandomBootstraps(), cryptoCodex, verbose: 2 });
320
328
  this.userNodes[user] = peer;
321
329
  peers.all[peer.id] = peer;
322
330
  peers.standard.unshift(peer);