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
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
greenium package does not depend on express.js or on any particular
protocol, except the
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.
Verdant: the object class / constructor - useful for derived classes;
verdant: the factory function, also the default export.
( [ options : object | string ] ) constructor.
options object keys are:
: boolean- if set, forces either fully async or fully sync operation;
: string- attach hook name, def:
: string- detach hook name, def:
: string- def:
__dirpathof the parent module;
: string- paths for initial loadables;
: boolean- enables strict error checking (
truein production mode).
If the argument is an array of strings, then it is assumed to present the
(path: string) : Verdant instance method.
Loads the module or package by the path. It resolves the path the same way as
does, except that it uses the
dirPath option value instead of Node.js
An alternative way is to use the
: Proxy<Object> r/o instance property.
Exposes the (aggregated) API from modules loaded by this instance. This value remains the same.
([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.
([filter : string|RegExp]) : Promise<Verdant>
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.
([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.
() instance method.
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.
( dirPath : string [, options : object] ) : Verdant
is just a wrapper around the constructor call. This is also the default export of the package.
Here: life cycle of a loadable module and
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.
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.
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
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.
Leaving few restrictions aside, a loadable can do anything it needs to.
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
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.
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.
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.