greenium

0.1.1 • Public • Published

greenium Build Status Code coverage

This package makes your Node.js app evergreen by becoming Continuous Deployment capable.

It uses a very simple API and has only few limitations for your code. The main goal of writing this package was to avoid the demanding micro-services stuff in early stages of application life cycle, while still having Continuous Deployment on from the day one.

Unlike some other similar packages, greenium does not modify any of Node.js original behavior.

Usage

Of course, it should be installed:

yarn add greenium   # npm i -S greenium

...before it can be used in your code:

const express = require('express')
const app = express()
// Commented out:   const shopAPI = require('./shop')
const verdant = require('greenium')(['./shop']) 
const shopAPI = verdant.attach(app.locals)
process.on('SIGPIPE', () => verdant.reload())     //  Admin pressing the "red button".
// The rest of the application code remains unchanged.

app.use('/shop/hello', shopAPI.hello)
...

In the above example, a new namespace for an e-shop API is created and every time the app receives the SIGPIPE, it re-loads its (hopefully fixed) source code again - w/o breaking anything 😎!

The bad news is, that instead of having just line #3, you ended up adding two extra lines 😖.

NOTE: the greenium package does not depend on express.js or on any particular protocol, except the Node.js module API.

API

NOTE: The asynchronous attach / reload will probably not supported in next releases. The real-world experience has shown that those fancy features create more problems than they can solve.

Package exports

  1. Verdant: the object class / constructor - useful for derived classes;
  2. verdant: the factory function, also the default export.

Verdant class

Verdant( [ options : object | string[] ] ) constructor.
The valid options object keys are:

  • async : boolean - if set, forces either fully async or fully sync operation;
  • attacher : string - attach hook name, def: 'attach';
  • detacher : string - detach hook name, def: 'detach';
  • dirPath : string - def: __dirpath of the parent module;
  • paths : string[] - paths for initial loadables;
  • strict : boolean - enables strict error checking (true in production mode).

If the argument is an array of strings, then it is assumed to present the paths option.

add(path: string) : Verdant instance method.
Loads the module or package by the path. It resolves the path the same way as require() does, except that it uses the dirPath option value instead of Node.js __dirpath. An alternative way is to use the paths option.

api : Proxy<Object> r/o instance property.
Exposes the (aggregated) API from modules loaded by this instance. This value remains the same.

attach([context : object]) : Proxy | Promise<Proxy> instance method.
Attaches (initializes) all loaded loadables, not initialized yet and returns the API proxy. Returns a promise, if at least one detach hook was asynchronous, of if the asyncAttach option is on.
The returned / resolved value is always the api instance property value and does not change.

Repeated calls with the same context and w/o anything new being loaded in between, have no effect.

reload([filter : string|RegExp]) : Promise<Verdant> instance method.
Reloads all matching loadables, detaching their old instances and repeating the recently applied attach sequence to each of them. The optional filter argument applies to loadables paths and may be:

  • a string: must be an exact match or if it contains meta symbols, then it will be turned to a RegExp instance;
  • a RegExp: only the loadables with matching paths will be processed.

If there were no matches, an error will be thrown, if strict option is on. Repeated calls while reloading in progress, will have no effect.

reloadSync([filter : string|RegExp]) : Verdant instance method.
The same as above, but will throw an exception, if any of attach hooks returns a promise or if the asyncAttach option is on.

revoke() instance method.
This is analogous to Javascript Proxy.prototype.revoke(), except that it calls detach hooks of all loaded modules, before rendering the Verdant instance and its exposed API inactive. Call this method before exiting the application. Calling this method will interrupt any reloading or attaching in progress.

Factory function

verdant( dirPath : string [, options : object] ) : Verdant
is just a wrapper around the constructor call. This is also the default export of the package.

Concept

Here: life cycle of a loadable module and what verdant does.

An application may contain a set of modules (loadables), each providing its own sub-API. Often, a sub-API needs a special initialization with a proper context (e.g. services or shared namespaces), in order to become functional.

A Verdant instance represents an aggregated API from its loadables, sharing the same initialization context. For example, common utility functions may need no context at all, or may depend on some command-line options; whereas business logic might need ready-to-use services running, and lower-level API-s ready to be used.

A Verdant instance can fetch a loadable at any time, but it would be unwise to do this from HTTP request handler, for example. Because of this, initial loading of loadables, their initialization and possible further re-loading are separate operations.

Life cycle of a loadable

Attaching

If loadable exports a function, it is called with the context as its argument and its return value is used as a sub-API. Otherwise, the exported object is a sub-API. Next, if the sub-API contains a specific attach hook function, it is called with the context. The optional attach hook can be synchronous or asynchronous function with meaningless return value.

Operation

Leaving few restrictions aside, a loadable can do anything it needs to.

Detaching

On reloading a loadable, the old instance may need to be detached first. This being a case, its sub-API must have a specific detach hook function. This optional function is called w/o arguments, and it must be synchronous. Typically, this function removes event listeners or object references from the outside world, so the instance can be garbage-collected. Do not trash any resources needed for servicing a possibly pending asynchronous request, however!

After all the requests targeting this instance, are gone from Node.js event queue, the garbage collector should kick in automatically.

What greenium does

After loading a source code module, Verdant instance exposes the module API via its api instance property, which is actually a proxy. This way, the rest of application code will not see actual data references - so when the module needs to be reloaded, its old instance can be garbage collected.

The loadable code may run asynchronous operations, though - but this will not spoil the party here. The old instance will be kept in memory, until all its asynchronous ops are finished. After this, it will be likely garbage collected. Even if this fails (for some sort of memory leak bug), the old instance will still be un-accessible from the moment the new one gets loaded.

Beyond the API proxying and require.cache manipulations, there is not much left for a Verdant instance to take care of.

Restrictions

The loadable modules code must be stateless - everything, except database connections, event handlers and similar (being set up by attach hook and released by detach hook), must live in database records, Node.js event loop or HTTP request/response instances. This is how the most of the server code should be written anyway.

The loadable main module should not expose a class (constructor) via its default export. The default export may be a synchronous wrapper function only.

All parts of loadable modules exported API will be read-only and mutating any part of it would likely result in an exception thrown. For altering any options, use the wrapper- or attach hook function instead.

Names in loadable API-s should not overlap - if any part of API collides with an item from some other module, an exception will be thrown.

Compatibility

This package requires Node.js v10.15.1 or higher.

The package may fail, if Node.js require subsystem is modified in any way. For this particular reason, good 'ol mocha, not the magnificent jest, was used for testing here.

Dependents (0)

Package Sidebar

Install

npm i greenium

Weekly Downloads

0

Version

0.1.1

License

MIT

Unpacked Size

18.9 kB

Total Files

5

Last publish

Collaborators

  • valango