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

nomade-kernel

v0.1.3

Published

Nomade framework kernel

Downloads

6

Readme

Nomade kernel Build Status

The Nomade kernel provides a simple SOLID architecture using a service & configuration container and a flexible extension system. Your application is empty by default, functionalities only are provided by extensions.

Quick start

How to install?

npm install nomade-kernel

How to test?

cd node_modules/nomade-kernel
npm test

How to use?

// creates kernel and run its server service
require('nomade-kernel')
    .create({ config_dir: __dirname + '/app/config' })
    .register(require('nomade-core'))
    .register(require('nomade-server'))
    .register(require('./app/extension'))
    .get('server')
    .run();

Container and services

Nomade framework is entirely built around a simple principle: the service container. Each functionality is provided by a service accessible through the container.

Services container

The services container role is to provide services and configuration values (and help services to communicate) using a very simple API:

  • container.read('repository.default.database', 'nomade'): reads a config value by its path, and provides an optional fallback value
  • container.has('repository'): tests a service existence, returns a boolean
  • container.get('repository'): returns a service by its ID, throws an Error if service does not exist
  • container.create('repository.query', { ... }): creates an object using a registered factory
  • container.emit('channel', { ... }): will emit an event on the given channel
  • container.promise(): instantiates a promise (for convenience, avoid requiring the class file)

Service definition

A service is a stateless object (the same instance is provided each time a service is accessed) which can own following attributes types:

  • a method:
    • always return a Promise instance
    • can be decorated
  • an object:
    • is considered as service itself
    • can be accessed directly through the container
    • can be accessed by parent service through this
    • can access parent service through this._owner
    • can be extended by decorators
  • an array:
    • is considered as a registry
    • can be accessed by parent service through this
    • can access parent service through this._owner
    • can be extended by decorators

Other attribute types are simply omitted in order to avoid design errors. A service is just a functions container and must be stateless.

Service method

A service method always returns a Promise instance (read more about promise system below). But there is a special case, when a sync function directly returns a value, the framework will return a promise for you.

var sample = {
    // calling this method will return a promise
    sayHello: function (name) { return 'hello ' + name; }
};

Service registry

A registry is a simple array of objects which can be used by services. It permits implementation of flexible designs.

var repository = { // the `repository` service definition
    engines: { // `engines` child service
        mysql: function (config) { /* returns mysql repository engine */ }
    },
    adapters: [ // `adapters` registry
        { supports: function(entity) { return true; }, engine: repository.engines.mysql }
    ],
    update: function (entity, values) { // `update` method
        for (var index in this.adapters)
            if (this.adapters[index].supports(entity)
                return this.adapters[index].engine.update(entity, value);
    }
};

Decorating service

Each extension is able to decorate existing services.

Promises system

  • A promise is a way to handle method results by unifying sync & async calls.
  • A service method always returns promise.

Promise creation

To create a promise, you must do like this:

mysql.update = function (entity, data) {
    var promise = container.promise();
    this.engine.execute('update ...', function (error, result) {
        if (error) promise.dispatch(Promise.ERROR, error);
        else promise.dispatch(Promise.DONE, result);
    );
    return promise;
}

We can simplify this because:

  • promise can create handler function using the right strategy and the default strategy, named errorAsFirstArgument is doing exactly the same thing as above;
  • the promise constructor can take a function which will be executed bound (this) to the promise itself.
mysql.update = function (entity, data) {
    var engine = this.engine;
    return container.promise().call(function () { engine.execute('update ...', this.handle()); );
}

Promise usage

Promises are simple to use, you just have to register function called when job is done or throws an error:

container
    .get('repository')
    .update(user, { firtName: 'John', lastName: 'Doe' })
    .done(function (user) {
        /* user is stored, you can continue to work with it */ 
    })
    .done(function (user) { 
        /* you can register many listeners for the same event */ 
    })
    .error(function (error) { 
        /* an error occurred, you should do something to handle it */ 
    })
;

Promise interception

When it comes to decorate a function, catch result of the parent function and return a new result based on it is a real pain because of the promises. To handle this, you can use intercept method like that:

var decorator = {
    getName: function (parent) {
        return parent
            .getName()
            .intercept(function (name) { return name + ' decorated'; });
    }
};

Extension system

An extension is a function taking a Builder instance as argument.

// exports your extension for node
module.exports = function (builder) {
    builder
        .configure(/* config loader */)
        .substitute('extension.directory', __dirname)
        .construct('my_service', /* service constructor */)
        .factor('my_factory', /* object factory */)
        .listen('channel', /* event listener */)
        .decorate('decorated_service', /* service decorator */);
        .validate(/* container validator */)
    ;
};

Config loading

A config loader is a function

  • that takes current config (a Config instance) as argument
  • who must return an object which is deeply merged to current config
builder.load(function (config) {
    return { param: 'value' }; 
});

Service construction

A service constructor is a function

  • that takes current config (a Config instance) as argument
  • who must return an object
builder.construct('service_id', function (config) {
    return { method: function () {} }; 
});

Object factoring

An object factory is a function

  • that takes
    • the service container as 1st argument
    • the arguments passed to the factory as other arguments
  • who must return a promise
builder.factor('user', function (container, username, password) {
    return new User(username, password);
});

Event listening

A event listener is a function

  • that takes an event as argument
  • who doesn't return anything
  • you can specify a priority as 3rd argument
builder.listen('error', function (event) {
    event.container.get('logger').log('error', event.error.message);
}, 0);

Service decoration

A service decorator is a function

  • that takes
    • the decorated service (an object) as 1st argument
    • the container (a Container instance) as 2nd argument
  • who must return an object
  • you can specify a priority as 3rd argument
builder.decorate('service_id', function (parent, container) {
    return { method: function () {
        // do something more
        return parent.method();
    }; } 
}, 0);

Container validation

A container validator is a function

  • that takes
    • the container (a Container instance) as 1st argument
    • the violations (a Violations instance) as 2nd argument
  • who doesn't return anything
builder.validate(function (container, violations) {
    if (!container.has('server')) {
        violations.add('The `server` service is missing, its required by the `api` extension');
    }
});

Factory system

A factory repository for patterns and processes that are common in Nomade's land.

var factory = require('nomade-factory').get('package.module');
var result = factory(/* arguments */);
// or using the create function
var result = require('nomade-factory').create('package.module', /* arguments */);

Service adapater

Used to define services using adapter pattern.

var definition = require('nomade-factory').create(
    'service.adapter', // the factory name
    container,         // a child container for this service
    {/* adapters */},  // service adapters definitions, indexed by ID
    {/* methods */}    // service methods definitions, indexed by name
);

Adapters definitions should look like this:

var adapters = {
    'adapter-id': {
        // construct method is an optional constructor returning an engine
        construct: function () { return engine; },
        // each service methods must be defined, this represent the engine (if defined)
        method1: function (parameter) { return this.doSomething(parameter); }
    }
};

Methods definitions should look like this:

var methods = {
    // all service methods are defined here
    'method-name': {
        // the minimal definition is the name of the method
        method1: {},
        // you can define a resolve method to intercept and resolve parameters
        method2: { resolve: function (param1, param2) { return [param1, param2 || 'default']; }
    }
};