Comparing version 2.2.0 to 3.0.0
19
boot.js
@@ -7,2 +7,3 @@ 'use strict' | ||
const Plugin = require('./plugin') | ||
const debug = require('debug')('avvio') | ||
@@ -89,5 +90,8 @@ function wrap (server, opts, instance) { | ||
this.booted = false | ||
this._readyQ = fastq(this, callWithCbOrNextTick, 1) | ||
this._readyQ.pause() | ||
this._readyQ.drain = () => { | ||
this.booted = true | ||
this.emit('start') | ||
@@ -109,7 +113,9 @@ } | ||
// create the root node upon to hold each subsequent call to use() | ||
// the root node is responsible for emitting 'start' | ||
Boot.prototype._init = function () { | ||
if (this.booted) { | ||
throw new Error('root plugin has already booted') | ||
} | ||
if (this._current.length === 0) { | ||
const main = new Plugin(this, (s, opts, done) => { | ||
const main = new Plugin(this, function root (s, opts, done) { | ||
// we need to wait any call to use() to happen | ||
@@ -119,2 +125,3 @@ process.nextTick(done) | ||
Plugin.loadPlugin.call(this, main, (err) => { | ||
debug('root plugin ready') | ||
if (err) { | ||
@@ -172,4 +179,8 @@ this._error = err | ||
if (current.loaded) { | ||
throw new Error(`Impossible to load "${obj.name}" plugin because the parent "${current.name}" was already loaded`) | ||
} | ||
// we add the plugin to be loaded at the end of the current queue | ||
current.q.push(obj, (err) => { | ||
current.enqueue(obj, (err) => { | ||
if (err) { | ||
@@ -176,0 +187,0 @@ this._error = err |
{ | ||
"name": "avvio", | ||
"version": "2.2.0", | ||
"version": "3.0.0", | ||
"description": "Asynchronous bootstrapping of Node applications", | ||
"main": "boot.js", | ||
"scripts": { | ||
"test": "standard && tap test/*.js" | ||
"test": "standard && tap test/*test.js" | ||
}, | ||
@@ -36,7 +36,9 @@ "precommit": "test", | ||
"standard": "^10.0.3", | ||
"tap": "^10.7.2" | ||
"tap": "^10.7.2", | ||
"then-sleep": "^1.0.1" | ||
}, | ||
"dependencies": { | ||
"debug": "^3.1.0", | ||
"fastq": "^1.5.0" | ||
} | ||
} |
104
plugin.js
'use strict' | ||
const fastq = require('fastq') | ||
const debug = require('debug')('avvio') | ||
@@ -12,5 +13,7 @@ function Plugin (parent, func, opts, callback) { | ||
this.parent = parent | ||
this.name = func.name | ||
this.q = fastq(parent, loadPlugin, 1) | ||
this.q.pause() | ||
this.loaded = false | ||
@@ -21,3 +24,2 @@ // always start the queue in the next tick | ||
// or they will end up at the top of _current | ||
process.nextTick(this.q.resume.bind(this.q)) | ||
} | ||
@@ -27,21 +29,81 @@ | ||
const func = this.func | ||
var completed = false | ||
var name = this.name | ||
this.server = this.parent.override(server, func, this.opts) | ||
// we must defer the loading of the plugin until the | ||
// current execution has ended | ||
process.nextTick(() => { | ||
func(this.server, this.opts, cb) | ||
}) | ||
debug('exec', name) | ||
var promise = func(this.server, this.opts, done) | ||
if (promise && typeof promise.then === 'function') { | ||
debug('resolving promise', name) | ||
promise.then(() => done()).catch(done) | ||
} | ||
function done (err) { | ||
if (completed) { | ||
return | ||
} | ||
debug('exec completed', name) | ||
completed = true | ||
cb(err) | ||
} | ||
} | ||
Plugin.prototype.enqueue = function (obj, cb) { | ||
debug('enqueue', this.name, obj.name) | ||
this.q.push(obj, cb) | ||
} | ||
Plugin.prototype.finish = function (err, cb) { | ||
debug('finish', this.name) | ||
const callback = this.callback | ||
// if 'use' has a callback | ||
if (callback) { | ||
callback(err) | ||
// if 'use' has a callback but does not have parameters | ||
cb(callback.length > 0 ? null : err) | ||
} else { | ||
cb(err) | ||
const done = () => { | ||
if (this.loaded) { | ||
return | ||
} | ||
debug('loaded', this.name) | ||
this.loaded = true | ||
// if 'use' has a callback | ||
if (callback) { | ||
callback(err) | ||
// if 'use' has a callback but does not have parameters | ||
cb(callback.length > 0 ? null : err) | ||
} else { | ||
cb(err) | ||
} | ||
} | ||
if (err) { | ||
done() | ||
return | ||
} | ||
const check = () => { | ||
debug('check', this.name, this.q.length(), this.q.running()) | ||
if (this.q.length() === 0 && this.q.running() === 0) { | ||
done() | ||
} else { | ||
debug('delayed', this.name) | ||
// finish when the queue of nested plugins to load is empty | ||
this.q.drain = () => { | ||
debug('drain', this.name) | ||
this.q.drain = noop | ||
// we defer the check, as a safety net for things | ||
// that might be scheduled in the loading callback | ||
process.nextTick(check) | ||
} | ||
} | ||
} | ||
process.nextTick(check) | ||
// we start loading the dependents plugins only once | ||
// the current level is finished | ||
this.q.resume() | ||
} | ||
@@ -54,18 +116,14 @@ | ||
this._current.unshift(toLoad) | ||
toLoad.exec((last && last.server) || this._server, (err) => { | ||
if (err || !(toLoad.q.length() > 0 || toLoad.q.running() > 0)) { | ||
// finish now, because there is nothing left to do | ||
toLoad.finish(err, (err) => { | ||
this._current.shift() | ||
toLoad.finish(err, cb) | ||
} else { | ||
// finish when the queue of nested plugins to load is empty | ||
toLoad.q.drain = () => { | ||
this._current.shift() | ||
toLoad.finish(null, cb) | ||
} | ||
} | ||
cb(err) | ||
}) | ||
}) | ||
} | ||
function noop () {} | ||
module.exports = Plugin | ||
module.exports.loadPlugin = loadPlugin |
@@ -30,3 +30,3 @@ # avvio | ||
The example below can be found [here][example] and ran using `node example.js`. | ||
The example below can be found [here][example] and ran using `node example.js`. | ||
It demonstrates how to use `avvio` to load functions / plugins in order. | ||
@@ -47,11 +47,4 @@ | ||
avvio.use(third, (err) => { | ||
if (err) { | ||
console.log('something bad happened') | ||
console.log(err) | ||
} | ||
avvio.use(third) | ||
console.log('third plugin loaded') | ||
}) | ||
avvio.ready(function () { | ||
@@ -63,3 +56,4 @@ console.log('application booted!') | ||
console.log('first loaded', opts) | ||
instance.use(second, cb) | ||
instance.use(second) | ||
cb() | ||
} | ||
@@ -72,5 +66,5 @@ | ||
function third (instance, opts, cb) { | ||
// async/await or Promise support | ||
async function third (instance, opts) { | ||
console.log('third loaded') | ||
cb() | ||
} | ||
@@ -96,4 +90,4 @@ ``` | ||
Starts the avvio sequence. | ||
As the name suggest, `instance` is the object representing your application. | ||
Starts the avvio sequence. | ||
As the name suggest, `instance` is the object representing your application. | ||
Avvio will add the functions `use`, `after` and `ready` to the instance. | ||
@@ -110,3 +104,4 @@ | ||
cb() | ||
}, cb) | ||
}) | ||
cb() | ||
}).after(function (err, cb) { | ||
@@ -145,5 +140,5 @@ // after first and second are finished | ||
### app.use(func, [opts], [cb]) | ||
### app.use(func, [opts]) | ||
Loads one or more functions asynchronously. | ||
Loads one or more functions asynchronously. | ||
The function **must** have the signature: `instance, options, done` | ||
@@ -161,2 +156,11 @@ | ||
async/await is also supported: | ||
```js | ||
async function plugin (server, opts) { | ||
await sleep(10) | ||
} | ||
app.use(plugin) | ||
``` | ||
`use` returns the instance on which `use` is called, to support a chainable API. | ||
@@ -166,3 +170,3 @@ | ||
```js | ||
app.use([first, second, third], opts, cb) | ||
app.use([first, second, third], opts) | ||
``` | ||
@@ -173,23 +177,16 @@ The functions will be loaded in the same order as they are inside the array. | ||
#### Error handling | ||
The third argument of the plugin, the `done` function can accept an error parameter, if you pass it, you **must** handle that error. You have two ways to do it: | ||
1. the callback of the use function | ||
In order to handle errors in the loading plugins, you must use the | ||
`.ready()` method, like so: | ||
```js | ||
app.use(function (instance, opts, done) { | ||
done(new Error('error')) | ||
}, opts, function (err) { | ||
if (err) throw err | ||
}) | ||
``` | ||
2. the next `ready` or `after` callback | ||
```js | ||
app.use(function (instance, opts, done) { | ||
done(new Error('error')) | ||
done(new Error('error')) | ||
}, opts) | ||
app.ready(function (err) { | ||
if (err) throw err | ||
if (err) throw err | ||
}) | ||
``` | ||
*Note if you pass a callback to `use` without an error parameter, the error will be automatically passed to the next `ready` or `after` callback.* | ||
------------------------------------------------------- | ||
@@ -201,7 +198,7 @@ <a name="after"></a> | ||
Calls a function after all the previously defined plugins are loaded, including | ||
all their dependencies. The `'start'` event is not emitted yet. | ||
all their dependencies. The `'start'` event is not emitted yet. | ||
The callback changes basing on the parameters your are giving: | ||
1. If one parameter is given to the callback, that parameter will be the `error` object. | ||
2. If two parameters are given to the callback, the first will be the `error` object, the second will be the `done` callback. | ||
1. If one parameter is given to the callback, that parameter will be the `error` object. | ||
2. If two parameters are given to the callback, the first will be the `error` object, the second will be the `done` callback. | ||
3. If three parameters are given to the callback, the first will be the `error` object, the second will be the top level `context` unless you have specified both server and override, in that case the `context` will be what the override returns, and the third the `done` callback. | ||
@@ -240,7 +237,7 @@ | ||
Calls a function after all the plugins and `after` call are completed, but before `'start'` is emitted. `ready` callbacks are executed one at a time. | ||
Calls a function after all the plugins and `after` call are completed, but before `'start'` is emitted. `ready` callbacks are executed one at a time. | ||
The callback changes basing on the parameters your are giving: | ||
1. If one parameter is given to the callback, that parameter will be the `error` object. | ||
2. If two parameters are given to the callback, the first will be the `error` object, the second will be the `done` callback. | ||
1. If one parameter is given to the callback, that parameter will be the `error` object. | ||
2. If two parameters are given to the callback, the first will be the `error` object, the second will be the `done` callback. | ||
3. If three parameters are given to the callback, the first will be the `error` object, the second will be the top level `context` unless you have specified both server and override, in that case the `context` will be what the override returns, and the third the `done` callback. | ||
@@ -296,4 +293,4 @@ | ||
Allows to override the instance of the server for each loading plugin. | ||
It allows the creation of an inheritance chain for the server instances. | ||
Allows to override the instance of the server for each loading plugin. | ||
It allows the creation of an inheritance chain for the server instances. | ||
The first parameter is the server instance and the second is the plugin function while the third is the options object that you give to use. | ||
@@ -322,3 +319,4 @@ | ||
assert(s1.count === 1) | ||
s1.use(second, cb) | ||
s1.use(second) | ||
cb() | ||
@@ -341,4 +339,4 @@ function second (s2, opts, cb) { | ||
The callback changes basing on the parameters your are giving: | ||
1. If one parameter is given to the callback, that parameter will be the `context`. | ||
2. If two parameters are given to the callback, the first will be the top level `context` unless you have specified both server and override, in that case the `context` will be what the override returns, the second will be the `done` callback. | ||
1. If one parameter is given to the callback, that parameter will be the `context`. | ||
2. If two parameters are given to the callback, the first will be the top level `context` unless you have specified both server and override, in that case the `context` will be what the override returns, the second will be the `done` callback. | ||
@@ -371,4 +369,4 @@ ```js | ||
The callback changes basing on the parameters your are giving: | ||
1. If one parameter is given to the callback, that parameter will be the `error` object. | ||
2. If two parameters are given to the callback, the first will be the `error` object, the second will be the `done` callback. | ||
1. If one parameter is given to the callback, that parameter will be the `error` object. | ||
2. If two parameters are given to the callback, the first will be the `error` object, the second will be the `done` callback. | ||
3. If three parameters are given to the callback, the first will be the `error` object, the second will be the top level `context` unless you have specified both server and override, in that case the `context` will be what the override returns, and the third the `done` callback. | ||
@@ -375,0 +373,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
22718
375
0
2
5
394
+ Addeddebug@^3.1.0
+ Addeddebug@3.2.7(transitive)
+ Addedms@2.1.3(transitive)