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

reactive-graph

v0.2.2

Published

Convert Graphlib graphs to observable networks

Downloads

6

Readme

reactive-graph

reactive-graph converts a Graphlib graph into a network of observables / streams (e.g RxJS observables, Kefir observables, bacon.js streams).

Why is this useful?

  • Easy serialization and deserialization of reactive networks to / from JSON
  • Visualization of reactive networks (dependency graphs) using graph libraries like Cytoscape.js (see image below) or visualization libraries like d3.js
  • Live-visualization of dataflows
  • Creation of reactive networks and pipelines using a visual graph editor

Rx example

example graph

Above, you see a simple network (dependency graph) of Rx observables that we want to re-create in this example. Here is a representation of this network as a Graphlib graph:

import graphlib from 'graphlib';

const graph = new graphlib.Graph();
graph.setNode( 'A', ['Observable.interval', 500] );
graph.setNode( 'B', ['Observable.interval', 1000] );
graph.setNode( 'A_map', ['map', x => 'A' + x] );
graph.setNode( 'A_take5', ['take', 5] );
graph.setNode( 'B_map', ['map', x => 'B' + x] );
graph.setNode( 'AB_concat', ['Observable.concat'] );
graph.setNode( 'subscribe', ['subscribeOnNext', function(x) { console.log(x) } ] );

graph.setEdge( 'A', 'A_map' );
graph.setEdge( 'A_map', 'A_take5' );
graph.setEdge( 'B', 'B_map' );

// setting indexes for sorting
graph.setEdge( 'A_take5', 'AB_concat', 0 );
graph.setEdge( 'B_map', 'AB_concat', 1 );

graph.setEdge( 'AB_concat', 'subscribe' );

reactive-graph tries to be as flexible as possible. Thus you can attach whatever data you want to the graph nodes (in this case an array with an operator name as the first argument, followed by additional arguments that will be passed to the operator). It is not necessary to attach data to edges, you can however set an index that will be used for sorting the incoming sources, in case that is necessary (f.ex. for Rx.Observable.concat).

Due to this flexibility, reactive-graph does not know how to insert these operators into the network. Thus we need to provide an insertOperator function to take care of this. This function will be called for every node. It receives the node's data and an array of source observables (sorted by the edge indexes) and must return a new observable. The graph's nodes will have been topologically sorted first to ensure that all source observables exist before trying to create an operator.

import reactiveGraph from 'reactive-graph';
import Rx from 'rx';

// super simple Rx inserter
function insertOperator( id, operatorConfig, sources ) {
  let operatorName = operatorConfig[0];
  const args = operatorConfig.splice( 1 );

  // static operators
  if( operatorName.startsWith( 'Observable.' ) ) {
    operatorName = operatorName.substr( 11 );
    return Rx.Observable[ operatorName ]( ...sources, ...args );

  // instance operators
  } else {
    // splitting sources - this universal approach will terribly fail, when
    // an operator like `map` has multiple ingoing sources...
    const source = sources[0];
    const restSources = sources.splice( 1 );
    return source[ operatorName ]( ...restSources, ...args );
  }

}

// running the graph; we'll get back a dictionary of the created
// observables (or disposables)
const observables = reactiveGraph.run( graph, insertOperator );

Inserter functions can be far more complex than this, possibly providing shared state, custom operators, etc. That is up to the developer.

API

# run( graph, insertOperator )

Takes a Graphlib graph and connects it to an observable network. The seond argument, insertOperator, is responsible creating the operator. It receives the user-set operator configuration and an array of source observables.

run is equivilant to the following code:

const topsortedNodes = reactiveGraph.getTopsortedNodes( graph );
const observables = reactiveGraph.connectOperators( topsortedNodes, insertOperator );

The operation can thus be split into two steps (useful if the same graph should be run multiple times).

# getTopsortedNodes( graph )

Takes a Graphlib graph and returns an array of topsorted graph nodes in the following format:

{
  id,              // id of graph node (string)
  operatorConfig,  // user-set node data (*)
  sources          // ID's of sources (string[])
}

# connectOperators( topsortedNodes, insertOperator )

Takes an array of topsorted nodes and connects those nodes by calling the provided insertOperator function.

Development

# after cloning, run once
npm run build

# there's also a watcher
npm run watch:build