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

multiversum

v0.3.1

Published

An extensible component architecture for JavaScript

Downloads

3

Readme

Multiversum

A toolbox for implementing an "everything's a plugin" architecture in JavaScript.

WARNING: This library hasn't reached version 1.0.0 yet and the API might change at any time. Using the library in production systems is therefore discouraged at this point.

How it works

Multiversum consists of a plugin host, a component gatherer, a bootstrapper and an application module.

A component consists of an implementation file (JavaScript) and a definition file (JSON). The gatherer finds all component definitions in a list of directories and allows you to filter the components according to the meta data in the definition files.

The application manages the lifecycle of components and resolves the dependency graph of the components.

Each component interacts with the rest of the application via the plugin host. The plugin host can be used to broadcast and receive messages. Each component can define one or more interfaces and connect them to the application using the plugin host.

An interface is a set of channels. A channel is an abstraction of a method. Components can connect implementations for a channel, and when the channel is called the result of the first implementation that doesn't throw an error is used, starting with the implementation with the highest priority.

Components can also decorate channels. This is the main way extensibility is realized in Multiversum. Decorators that throw errors are transparently ignored, so that a faulty extension doesn't necessarily break the whole application.

Usage

var createApp = require("multiversum/bootstrap").bootstrap;

var app = createApp([process.cwd()], {
    patterns: ["**/*.com.json"], // A list of file patterns
    onError: console.error.bind(console), // Called when errors occur during the bootstrap phase
    // A function for filtering components using the meta data in the definition files:
    filter: function (component) {
        // Only use components that are made for this app:
        return component.application === "my-application";
    }
});

// Initialize all the components in an order that respects the dependencies of each component:
app.init();

A definition file can look like this:

{
    "name": "myComponent",
    "application": "my-application",
    "applicationVersion": "2.2.1",
    "offers": ["interfaceA", "interfaceB"],
    "requires": ["interfaceC"],
    "file": "./myComponent.js"
}

And a component implementation usually looks like this:

function create(context) {
    
    var api = context.createInterface("interfaceC", {
        doSomething: doSomething,
        doSomethingElse: doSomethingElse
    });
    
    function init() {
        context.connectInterface(api);
    }
    
    function destroy() {
        context.disconnectInterface(api);
    }
    
    function doSomething() {
        api.doSomethingElse();
    }
    
    function doSomethingElse() {
        console.log("Doing something else");
    }
    
    return {
        init: init,
        destroy: destroy
    };
}

module.exports = {
    create: create
};

Components can also communicate using the context as an event bus:

function create(context) {
    
    // Publishing a message with an (optional) payload:
    context.publish("myComponent/foo", {foo: "bar"});
    
    // Subscribing a listener:
    context.subscribe("otherComponent/foo", listener);
    
    // Subscribing a listener that is only called once:
    context.once("otherComponent/foo", listener);
    
    // Removing a listener:
    context.unsubscribe("otherComponent/foo", listener);
    
    function listener(data) {
        console.log("Received data:", JSON.stringify(data, null, 4));
    }
}

If you want to run something once all components have been initialized, subscribe to the app/ready message using the context.

A component can extend other components using decorators:

function create(context) {
    
    // Decorate a channel:
    context.decorate("myComponent/foo", function (fn) {
        return function () {
            console.log("myComponent/foo was called!");
            return fn.apply(null, arguments);
        };
    });
    
    // Decorate all channels:
    context.decorate(function (fn) {
        return function () {
            // ...
            return fn.apply(null, arguments);
        };
    });
    
    // Removing a decorator:
    context.removeDecorator("myComponent/foo", /* the decorator */);
}

Changelog

  • v0.3.1

    • Fix: Decorator value discarded when not calling next
    • Add warning about chaning API to README
  • v0.3.0

    • Change host.channel(name) to use the host bus for errors, not a new one.
  • v0.2.3

    • Add onError callback to app/error event in bootstrapper so that errors thrown during component initialization aren't swallowed silently.
  • v0.2.2

    • Changes ChannelObject to be callable, so that host.channel("foo")() can be used instead of host.channel("foo").call().
  • v0.2.1

    • Changes ComponentDefinition definition to allow [index: string]: any;
  • v0.2.0

    • Changes app and gatherer to use a getModule(name) channel to get their dependencies. This allows applications to be bootstrapped in such a way that they don't use node's file system module directly.