npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

p2p-manager

v0.1.0

Published

Create a peer-to-peer relationship

Downloads

7

Readme

P2P Manager

Manage a network of peers in a peer-to-peer network.

Messages

peerConnect, peerEnd, peerError

Emitted when an active Peer emits a connect, end or error message respectively, and the data payload is the same:

{
  peer: Peer
}

peerMessage

Emitted when an active Peer sends a message event.

{
  peer: Peer,
  command: String,
  data: Raw payload as binary data
}

commandMessage

An alternate version of the peerMessage event; in these events, the command of the message is used as the event name (i.e. command 'foo' would cause a fooMessage event).

{
  peer: Peer,
  data: Raw payload as binary data
}

error

Error message from the PeerManager. The severity attribute is one of 'info', 'notice', 'warning', or 'error' (in increasing severity).

{
  severity: String,
  message: String
}

PeerManager.send()

The PeerManager allows sending messages to a collection of Peers at once, based on certain criteria. The most common criteria is the "state" of the Peer. Each Peer object has a state property which is set to 'new' when first created, 'connecting' when first opened, and 'connected' when first connect event is heard (before handleConnect is called, so that can be overwritten, if desired). Any other states are up to your PeerManager instance to implement.

  • number: How many peers to send to? Once filtered down, should all or a sub-set be messaged? Pass a Number to send to that many Peers (picked at random). Passing a zero or negative number for this argument sends to all matched Peers.
  • prop: (String) Which property of the Peers should be examined?
  • value: Value of the property the Peers need to match to be included in the set. Matching is done in a case-insensitive way for strings. Passing an array for this argument requires Peers match any one of the array's values.
  • cmd: (String) Message name to be sent to the Peers
  • payload: (Buffer) Binary data to be sent to the Peers
  • answerCmd: (String) Message name of answering message. If null/false, defaults to all message events.
  • callback: (function) If provided, is bound to the "answerCmd" event of the Peers contacted, to await their reply
PeerManager.send('all', 'state', 'connected', 'hi', new Buffer([1,2,3,4,5])); // Send a message to all connected clients
PeerManager.send(2, 'state', 'lonely', 'matchmaker', new Buffer([1,2,3,4,5])); // Send a message to a random two Peers who have state=='lonely'
PeerManager.send(5, 'myProp', [1,5,42,false], 'hi', new Buffer([1,2,3,4,5])); // Send a message to a random five Peers who have myProp equal to either 1, 5 ,42, or false

The function returns an dictionary of Peers the message was sent to, stored by their UUID.

If you're expecting a specific answer to your message, there's a few ways you can listen in:

Listen to only answer messages, from just the filtered peers

Probably the most common, and the most specific listening type:

var m = new PeerManager();
var waitForAnswer = function(d) {
  console.log(d.peer.getUUID()+': has answered', d.data.toString('hex'));
  d.peer.removeListener('message', this); // This listener doesn't care about further messages
  delete peers[d.peer.getUUID()]; // Remove this peer from the list of peers who haven't answered yet
};

var peers = m.send(2, 'state', 'waiting', 'knock', new Buffer([1,2,3]), 'answer', waitForAnswer);

setTimeout(function() {
  // Ten seconds have passed; give up on those who haven't answered
  for (var uuid in peers) {
    if (peers.hasOwnProperty(uuid)) {
      console.log(uuid+' never answered...');
      peers[uuid].removeListener('message', waitForAnswer);
    }
  }
}, 10000);

Listen to all messages, from just the filtered Peers

A good method if you expect the answer you seek to be in the next few messages from those Peers, and the answer might be one of several messages ('answer' or 'error'):

var m = new PeerManager();
var waitForAnswer = function(d) {
  if (d.command !== 'answer') return; // Wait for next message...
  
  console.log(d.peer.getUUID()+': has answered', d.data.toString('hex'));
  d.peer.removeListener('message', this); // This listener doesn't care about further messages
  delete peers[d.peer.getUUID()]; // Remove this peer from the list of peers who haven't answered yet
};

var peers = m.send(2, 'state', 'waiting', 'knock', new Buffer([1,2,3]), false, waitForAnswer);

setTimeout(function() {
  // Ten seconds have passed; give up on those who haven't answered
  for (var uuid in peers) {
    if (peers.hasOwnProperty(uuid)) {
      console.log(uuid+' never answered...');
      peers[uuid].removeListener('message', waitForAnswer);
    }
  }
}, 10000);

Listen to only answer messages, from all Peers

If there are lots of other messages being sent around, but very few of the particular answer messages you're looking for, this works well, or if there's a possibility another peer will answer on behalf of the peer you requested from:

var m = new PeerManager();
var waitForAnswer = function(d) {
  // Look through our list of peers and see if d.peer is one of them
  if (peers[d.peer.getUUID()] !== d.peer) return;
  
  console.log(d.peer.getUUID()+': has answered', d.data.toString('hex'));
  delete peers[d.peer.getUUID()]; // Remove this peer from the list of peers who haven't answered yet
};
var peers = m.send(2, 'state', 'waiting', 'knock', new Buffer([1,2,3]));
m.on('answer', waitForAnswer); // Listen in on all 'answer' messages from all peers

setTimeout(function() {
  // Ten seconds have passed; give up on those who haven't answered
  for (var uuid in peers) {
    if (peers.hasOwnProperty(uuid)) {
      console.log(uuid+' never answered...');
    }
  }
  m.removeListener('answer', waitForAnswer);
}, 10000);

Peers

The PeerManager keeps a list of known Peers to connect to (the "pool"), as well as a list of those currently connected ("active").

As your implementation discovers new Peers, use the PeerManager.addPool(hosts) method to tell the PeerManager about them. If the number of active Peers is currently below the minimum (options.minPeers), a connection will be attempted immediately. Otherwise, they will just be added to the pool. PeerManager.addActive(hosts) adds a new peer and attempts to connect to it immediately, regardless if the number of active Peers is above the threshhold.

Peer lists

The seedPeers argument of launch(), and the hosts argument of addPool() and addActive() are lists of hosts, expressed one of the following ways:

  • If it's a String, it's assumed to be the hostname/ip of one host to use; make sure to include a port number as part of the hostname (colon-delimited).
  • If it's an Array, it's looped through and each element inspected.
    • If it's a String, it's assumed to be a hostname/ip of a host to use.
    • If it's an Array, index zero is used as host and index one is used as port.
    • If it's an Object, the host property is used as host, and the port property is used as port.