diff --git a/avvio.js b/avvio.js
new file mode 100644
index 0000000..8d23373
--- /dev/null
+++ b/avvio.js
@@ -0,0 +1,408 @@
+'use strict'
+
+const { Plugin } = require('./lib/plugin')
+const { isPromiseLike, withResolvers } = require('./lib/promise')
+const { kContext, kOptions, kExpose, kAvvio, kPluginRoot, kAddPlugin, kStart, kPluginQueue, kError, kLoadPlugin, kLoadPluginNext, kReadyQueue, kCloseQueue, kOnCloseFunction, kWrappedThen, kTracker, kTrackPlugin } = require('./lib/symbols')
+const { resolveBundledFunction, noop } = require('./lib/utils')
+const EventEmitter = require('node:events').EventEmitter
+const inherits = require('node:util').inherits
+const fastq = require('fastq')
+const { readyWorker, closeWorker } = require('./lib/workers')
+const { AVV_ERR_CALLBACK_NOT_FN } = require('./lib/errors')
+const { Tracker } = require('./lib/tracker')
+
+/**
+ *
+ * @signature `Avvio()`
+ * @signature `Avvio(done)`
+ * @signature `Avvio(instance, done)`
+ * @signature `Avvio(instance, options, done)`
+ * @param {object} [instance] The instance to be exposed with Avvio methods.
+ * @param {*} [options] Options to change the behavior.
+ * @param {Function} [done] Function that called when ready.
+ * @returns {Avvio}
+ *
+ * @example
Boot without any params
+ * const avvio = Avvio()
+ * avvio.ready()
+ *
+ * @example Boot with instance
+ * const server = {}
+ * const avvio = Avvio(server)
+ * avvio.ready()
+ *
+ * @example Boot with callback
+ * const avvio = Avvio(function() {
+ * console.log('ready')
+ * })
+ */
+function Avvio (instance, options, done) {
+ // supports multiple signatures
+ // Avvio(done)
+ if (typeof instance === 'function' && arguments.length === 1) {
+ done = instance
+ options = {}
+ instance = null
+ }
+
+ // Avvio(instance, done)
+ if (typeof options === 'function') {
+ done = options
+ options = {}
+ }
+
+ options ??= {}
+ const o = {
+ autostart: options.autostart !== false,
+ timeout: Number(options.timeout) || 0,
+ expose: options.expose ?? {}
+ }
+
+ // allows to use without new
+ if (!new.target) {
+ return new Avvio(instance, o, done)
+ }
+
+ this[kContext] = instance ?? this
+ this[kOptions] = options
+
+ // override instance when it is suppied
+ if (instance) {
+ this[kExpose]()
+ }
+
+ // prevent memory leak warning
+ this.setMaxListeners(0)
+
+ if (done) {
+ this.once('start', done)
+ }
+
+ this[kReadyQueue] = fastq(this, readyWorker, 1)
+ this[kReadyQueue].pause()
+ this[kReadyQueue].drain = () => {
+ this.emit('start')
+ // prevent emit multiple start event
+ this[kReadyQueue].drain = noop
+ }
+
+ this[kCloseQueue] = fastq(this, closeWorker, 1)
+ this[kCloseQueue].pause()
+ this[kCloseQueue].drain = () => {
+ this.emit('close')
+ // prevent emit multiple close event
+ this[kCloseQueue].drain = noop
+ }
+
+ // status
+ this.started = false // true when called start
+ this.booted = false // true when ready
+ this[kTracker] = new Tracker()
+
+ this[kPluginQueue] = []
+ this[kStart] = null
+ this[kError] = null
+
+ const self = this
+ this[kPluginRoot] = new Plugin(
+ fastq(this, this[kLoadPluginNext], 1),
+ function root (_, options, done) {
+ self[kStart] = done
+ options.autostart && self.start()
+ },
+ options,
+ 0
+ )
+ this[kTrackPlugin](this[kPluginRoot])
+
+ this[kLoadPlugin](this[kPluginRoot], (error) => {
+ try {
+ this.emit('preReady')
+ this[kPluginRoot] = null
+ } catch (preReadyError) {
+ error = error ?? this[kError] ?? preReadyError
+ }
+
+ if (error) {
+ this[kError] = error
+ if (this[kReadyQueue].length() === 0) {
+ throw error
+ }
+ } else {
+ this.booted = true
+ }
+ this[kReadyQueue].resume()
+ })
+}
+
+inherits(Avvio, EventEmitter)
+
+Avvio.prototype.start = function start () {
+ this.started = true
+
+ process.nextTick(this[kStart])
+
+ // allows method chaining
+ return this
+}
+
+Avvio.prototype.ready = function ready (fn) {
+ if (fn) {
+ if (typeof fn !== 'function') {
+ throw AVV_ERR_CALLBACK_NOT_FN('ready', typeof fn)
+ }
+ this[kReadyQueue].push(fn)
+ queueMicrotask(this.start.bind(this))
+ return this
+ } else {
+ const promise = withResolvers()
+ const lastContext = this[kPluginQueue][0].context
+ this[kReadyQueue].push(function ready (error, context, done) {
+ if (error) {
+ promise.reject(error)
+ } else {
+ promise.resolve(lastContext)
+ }
+ process.nextTick(done)
+ })
+ queueMicrotask(this.start.bind(this))
+ return promise.promise
+ }
+}
+
+/**
+ * onClose registered in reverse order.
+ *
+ * @param {Function} fn
+ * @returns
+ */
+Avvio.prototype.onClose = function onClose (fn) {
+ if (typeof fn !== 'function') {
+ throw AVV_ERR_CALLBACK_NOT_FN('onClose', typeof fn)
+ }
+
+ // used to distinguish between onClose and close
+ fn[kOnCloseFunction] = true
+ this[kCloseQueue].unshift(fn, (error) => { error && (this[kError] = error) })
+
+ return this
+}
+
+/**
+ * close registered in sequantial order
+ *
+ * @param {Function} fn
+ * @returns
+ */
+Avvio.prototype.close = function close (fn) {
+ const resolvers = withResolvers()
+
+ if (fn) {
+ if (typeof fn !== 'function') {
+ throw AVV_ERR_CALLBACK_NOT_FN('close', typeof fn)
+ }
+ } else {
+ fn = function close (error) {
+ if (error) {
+ resolvers.reject(error)
+ } else {
+ resolvers.resolve()
+ }
+ }
+ }
+
+ // we need to start and fininalize before closing
+ this.ready(() => {
+ this.emit('preClose')
+ this[kError] = null
+ this[kCloseQueue].push(fn)
+ process.nextTick(this[kCloseQueue].resume.bind(this[kCloseQueue]))
+ })
+
+ return resolvers.promise
+}
+
+Avvio.prototype.use = function use (plugin, options) {
+ this[kAddPlugin](plugin, options)
+ // allows method chaining
+ return this
+}
+
+Avvio.prototype.override = function override (context, fn, options) {
+ return context
+}
+
+Avvio.prototype.prettyPrint = function prettyPrint () {
+ return this[kTracker].prettyPrint()
+}
+
+Avvio.prototype.toJSON = function toJSON () {
+ return this[kTracker].toJSON()
+}
+
+Object.defineProperties(Avvio.prototype, {
+ then: {
+ get () {
+ // when instance is ready, there is nothing
+ // to await.
+ if (this.booted) {
+ return
+ }
+
+ // to prevent recursion of promise resolving
+ // due to resolve(this[kContext]), we need
+ // an indicator to break the recursion.
+ if (this[kWrappedThen]) {
+ this[kWrappedThen] = false
+ return
+ }
+
+ const plugin = this[kPluginQueue][0]
+ const needToStart = !this.started && !this.booted
+
+ // if the root plugin is not loaded, resume
+ if (needToStart) {
+ process.nextTick(() => {
+ this[kPluginRoot].queue.resume()
+ })
+ }
+
+ let promise = null
+
+ if (!plugin) {
+ // when no plugin is registered,
+ // immediately resolve
+ promise = Promise.resolve(this[kContext])
+ } else {
+ // wait until the plugin is fully loaded.
+ promise = plugin.promise()
+ }
+
+ return (resolve, reject) => promise.then(() => {
+ this[kWrappedThen] = true
+ return resolve(this[kContext])
+ }, reject)
+ }
+ }
+})
+
+/**
+ * Used to identify the Avvio instance
+ * and skip some logic when appropriate
+ */
+Avvio.prototype[kAvvio] = true
+
+Avvio.prototype[kExpose] = function expose () {
+ const self = this
+ const instance = this[kContext]
+ const {
+ use: useKey = 'use'
+ } = this[kOptions].expose
+
+ if (instance[useKey]) {
+ throw Error()
+ }
+ instance[useKey] = function (fn, options) {
+ self.use(fn, options)
+ return this
+ }
+
+ instance[kAvvio] = true
+}
+
+Avvio.prototype[kAddPlugin] = function (fn, options) {
+ fn = resolveBundledFunction(fn)
+ Plugin.validate(fn)
+
+ options = options ?? {}
+
+ if (this.booted) {
+ throw Error()
+ }
+
+ const parent = this[kPluginQueue][0]
+
+ let timeout = this[kOptions].timeout
+
+ if (!parent.loaded && parent.timeout > 0) {
+ const delta = Date.now() - parent.startTime
+ // decrease the timeout by 3ms to ensure the child
+ // timeout is triggered earlier than parent
+ timeout = parent.timeout - (delta + 3)
+ }
+
+ const plugin = new Plugin(
+ fastq(this, this[kLoadPluginNext], 1),
+ fn,
+ options,
+ timeout
+ )
+ this[kTrackPlugin](plugin)
+
+ if (parent.loaded) {
+ throw Error(plugin.name, parent.name)
+ }
+
+ parent.enqueue(plugin, (error) => { error && (this[kError] = error) })
+
+ return plugin
+}
+
+Avvio.prototype[kLoadPlugin] = function (plugin, callback) {
+ const self = this
+ if (isPromiseLike(plugin.fn)) {
+ plugin.fn.then((fn) => {
+ fn = resolveBundledFunction(fn)
+ plugin.fn = fn
+ this[kLoadPlugin](plugin, callback)
+ }, callback)
+ return
+ }
+
+ // prev added plugin
+ const prev = self[kPluginQueue][0]
+ self[kPluginQueue].unshift(plugin)
+
+ let context = prev?.context ?? self[kContext]
+ try {
+ context = self.override(context, plugin.fn, plugin.options)
+ } catch (overrideError) {
+ return executeCallback(overrideError)
+ }
+
+ plugin.execute(context, executeCallback)
+
+ function executeCallback (error) {
+ plugin.finish(error, (error) => {
+ self[kPluginQueue].shift()
+ callback(error)
+ })
+ }
+}
+
+Avvio.prototype[kLoadPluginNext] = function (plugin, callback) {
+ process.nextTick(this[kLoadPlugin].bind(this), plugin, callback)
+}
+
+Avvio.prototype[kTrackPlugin] = function (plugin) {
+ const parentName = this[kPluginQueue][0]?.name ?? null
+ plugin.once('start', (contextName, fnName, startTime) => {
+ const nodeId = this[kTracker].start(parentName, fnName, startTime)
+ plugin.once('loaded', (contextName, fnName, endTime) => {
+ this[kTracker].stop(nodeId, endTime)
+ })
+ })
+}
+
+/**
+ * supports the following import
+ * 1. const Avvio = require('avvio')
+ * 2. const { Avvio } = require('avvio')
+ * 3. const { default: Avvio } = require('avvio')
+ * 4. import Avvio from 'avvio'
+ * 5. import { Avvio } from 'avvio'
+ * 6. import { default as Avvio } from 'avvio'
+ */
+module.exports = Avvio
+module.exports.default = Avvio
+module.exports.Avvio = Avvio
diff --git a/boot.js b/boot.js
deleted file mode 100644
index 7897b65..0000000
--- a/boot.js
+++ /dev/null
@@ -1,607 +0,0 @@
-'use strict'
-
-const fastq = require('fastq')
-const EE = require('node:events').EventEmitter
-const inherits = require('node:util').inherits
-const {
- AVV_ERR_EXPOSE_ALREADY_DEFINED,
- AVV_ERR_CALLBACK_NOT_FN,
- AVV_ERR_ROOT_PLG_BOOTED,
- AVV_ERR_READY_TIMEOUT,
- AVV_ERR_ATTRIBUTE_ALREADY_DEFINED
-} = require('./lib/errors')
-const {
- kAvvio,
- kIsOnCloseHandler
-} = require('./lib/symbols')
-const { TimeTree } = require('./lib/time-tree')
-const { Plugin } = require('./lib/plugin')
-const { debug } = require('./lib/debug')
-const { validatePlugin } = require('./lib/validate-plugin')
-const { isBundledOrTypescriptPlugin } = require('./lib/is-bundled-or-typescript-plugin')
-const { isPromiseLike } = require('./lib/is-promise-like')
-const { thenify } = require('./lib/thenify')
-const { executeWithThenable } = require('./lib/execute-with-thenable')
-
-function Boot (server, opts, done) {
- if (typeof server === 'function' && arguments.length === 1) {
- done = server
- opts = {}
- server = null
- }
-
- if (typeof opts === 'function') {
- done = opts
- opts = {}
- }
-
- opts = opts || {}
- opts.autostart = opts.autostart !== false
- opts.timeout = Number(opts.timeout) || 0
- opts.expose = opts.expose || {}
-
- if (!new.target) {
- return new Boot(server, opts, done)
- }
-
- this._server = server || this
- this._opts = opts
-
- if (server) {
- this._expose()
- }
-
- /**
- * @type {Array}
- */
- this._current = []
-
- this._error = null
-
- this._lastUsed = null
-
- this.setMaxListeners(0)
-
- if (done) {
- this.once('start', done)
- }
-
- this.started = false
- this.booted = false
- this.pluginTree = new TimeTree()
-
- this._readyQ = fastq(this, callWithCbOrNextTick, 1)
- this._readyQ.pause()
- this._readyQ.drain = () => {
- this.emit('start')
- // nooping this, we want to emit start only once
- this._readyQ.drain = noop
- }
-
- this._closeQ = fastq(this, closeWithCbOrNextTick, 1)
- this._closeQ.pause()
- this._closeQ.drain = () => {
- this.emit('close')
- // nooping this, we want to emit close only once
- this._closeQ.drain = noop
- }
-
- this._doStart = null
-
- const instance = this
- this._root = new Plugin(fastq(this, this._loadPluginNextTick, 1), function root (server, opts, done) {
- instance._doStart = done
- opts.autostart && instance.start()
- }, opts, false, 0)
-
- this._trackPluginLoading(this._root)
-
- this._loadPlugin(this._root, (err) => {
- debug('root plugin ready')
- try {
- this.emit('preReady')
- this._root = null
- } catch (preReadyError) {
- err = err || this._error || preReadyError
- }
-
- if (err) {
- this._error = err
- if (this._readyQ.length() === 0) {
- throw err
- }
- } else {
- this.booted = true
- }
- this._readyQ.resume()
- })
-}
-
-inherits(Boot, EE)
-
-Boot.prototype.start = function () {
- this.started = true
-
- // we need to wait any call to use() to happen
- process.nextTick(this._doStart)
- return this
-}
-
-// allows to override the instance of a server, given a plugin
-Boot.prototype.override = function (server, func, opts) {
- return server
-}
-
-Boot.prototype[kAvvio] = true
-
-// load a plugin
-Boot.prototype.use = function (plugin, opts) {
- this._lastUsed = this._addPlugin(plugin, opts, false)
- return this
-}
-
-Boot.prototype._loadRegistered = function () {
- const plugin = this._current[0]
- const weNeedToStart = !this.started && !this.booted
-
- // if the root plugin is not loaded, let's resume that
- // so one can use after() before calling ready
- if (weNeedToStart) {
- process.nextTick(() => this._root.queue.resume())
- }
-
- if (!plugin) {
- return Promise.resolve()
- }
-
- return plugin.loadedSoFar()
-}
-
-Object.defineProperty(Boot.prototype, 'then', { get: thenify })
-
-Boot.prototype._addPlugin = function (pluginFn, opts, isAfter) {
- if (isBundledOrTypescriptPlugin(pluginFn)) {
- pluginFn = pluginFn.default
- }
- validatePlugin(pluginFn)
- opts = opts || {}
-
- if (this.booted) {
- throw new AVV_ERR_ROOT_PLG_BOOTED()
- }
-
- // we always add plugins to load at the current element
- const current = this._current[0]
-
- let timeout = this._opts.timeout
-
- if (!current.loaded && current.timeout > 0) {
- const delta = Date.now() - current.startTime
- // We need to decrease it by 3ms to make sure the internal timeout
- // is triggered earlier than the parent
- timeout = current.timeout - (delta + 3)
- }
-
- const plugin = new Plugin(fastq(this, this._loadPluginNextTick, 1), pluginFn, opts, isAfter, timeout)
- this._trackPluginLoading(plugin)
-
- if (current.loaded) {
- throw new Error(plugin.name, current.name)
- }
-
- // we add the plugin to be loaded at the end of the current queue
- current.enqueue(plugin, (err) => { err && (this._error = err) })
-
- return plugin
-}
-
-Boot.prototype._expose = function _expose () {
- const instance = this
- const server = instance._server
- const {
- use: useKey = 'use',
- after: afterKey = 'after',
- ready: readyKey = 'ready',
- onClose: onCloseKey = 'onClose',
- close: closeKey = 'close'
- } = this._opts.expose
-
- if (server[useKey]) {
- throw new AVV_ERR_EXPOSE_ALREADY_DEFINED(useKey, 'use')
- }
- server[useKey] = function (fn, opts) {
- instance.use(fn, opts)
- return this
- }
-
- if (server[afterKey]) {
- throw new AVV_ERR_EXPOSE_ALREADY_DEFINED(afterKey, 'after')
- }
- server[afterKey] = function (func) {
- if (typeof func !== 'function') {
- return instance._loadRegistered()
- }
- instance.after(encapsulateThreeParam(func, this))
- return this
- }
-
- if (server[readyKey]) {
- throw new AVV_ERR_EXPOSE_ALREADY_DEFINED(readyKey, 'ready')
- }
- server[readyKey] = function (func) {
- if (func && typeof func !== 'function') {
- throw new AVV_ERR_CALLBACK_NOT_FN(readyKey, typeof func)
- }
- return instance.ready(func ? encapsulateThreeParam(func, this) : undefined)
- }
-
- if (server[onCloseKey]) {
- throw new AVV_ERR_EXPOSE_ALREADY_DEFINED(onCloseKey, 'onClose')
- }
- server[onCloseKey] = function (func) {
- if (typeof func !== 'function') {
- throw new AVV_ERR_CALLBACK_NOT_FN(onCloseKey, typeof func)
- }
- instance.onClose(encapsulateTwoParam(func, this))
- return this
- }
-
- if (server[closeKey]) {
- throw new AVV_ERR_EXPOSE_ALREADY_DEFINED(closeKey, 'close')
- }
- server[closeKey] = function (func) {
- if (func && typeof func !== 'function') {
- throw new AVV_ERR_CALLBACK_NOT_FN(closeKey, typeof func)
- }
-
- if (func) {
- instance.close(encapsulateThreeParam(func, this))
- return this
- }
-
- // this is a Promise
- return instance.close()
- }
-
- if (server.then) {
- throw new AVV_ERR_ATTRIBUTE_ALREADY_DEFINED('then')
- }
- Object.defineProperty(server, 'then', { get: thenify.bind(instance) })
-
- server[kAvvio] = true
-}
-
-Boot.prototype.after = function (func) {
- if (!func) {
- return this._loadRegistered()
- }
-
- this._addPlugin(_after.bind(this), {}, true)
-
- function _after (s, opts, done) {
- callWithCbOrNextTick.call(this, func, done)
- }
-
- return this
-}
-
-Boot.prototype.onClose = function (func) {
- // this is used to distinguish between onClose and close handlers
- // because they share the same queue but must be called with different signatures
-
- if (typeof func !== 'function') {
- throw new AVV_ERR_CALLBACK_NOT_FN('onClose', typeof func)
- }
-
- func[kIsOnCloseHandler] = true
- this._closeQ.unshift(func, (err) => { err && (this._error = err) })
-
- return this
-}
-
-Boot.prototype.close = function (func) {
- let promise
-
- if (func) {
- if (typeof func !== 'function') {
- throw new AVV_ERR_CALLBACK_NOT_FN('close', typeof func)
- }
- } else {
- promise = new Promise(function (resolve, reject) {
- func = function (err) {
- if (err) {
- return reject(err)
- }
- resolve()
- }
- })
- }
-
- this.ready(() => {
- this._error = null
- this._closeQ.push(func)
- process.nextTick(this._closeQ.resume.bind(this._closeQ))
- })
-
- return promise
-}
-
-Boot.prototype.ready = function (func) {
- if (func) {
- if (typeof func !== 'function') {
- throw new AVV_ERR_CALLBACK_NOT_FN('ready', typeof func)
- }
- this._readyQ.push(func)
- queueMicrotask(this.start.bind(this))
- return
- }
-
- return new Promise((resolve, reject) => {
- this._readyQ.push(readyPromiseCB)
- this.start()
-
- /**
- * The `encapsulateThreeParam` let callback function
- * bind to the right server instance.
- * In promises we need to track the last server
- * instance loaded, the first one in the _current queue.
- */
- const relativeContext = this._current[0].server
-
- function readyPromiseCB (err, context, done) {
- // the context is always binded to the root server
- if (err) {
- reject(err)
- } else {
- resolve(relativeContext)
- }
- process.nextTick(done)
- }
- })
-}
-
-/**
- * @param {Plugin} plugin
- * @returns {void}
- */
-Boot.prototype._trackPluginLoading = function (plugin) {
- const parentName = this._current[0]?.name || null
- plugin.once('start', (serverName, funcName, time) => {
- const nodeId = this.pluginTree.start(parentName || null, funcName, time)
- plugin.once('loaded', (serverName, funcName, time) => {
- this.pluginTree.stop(nodeId, time)
- })
- })
-}
-
-Boot.prototype.prettyPrint = function () {
- return this.pluginTree.prettyPrint()
-}
-
-Boot.prototype.toJSON = function () {
- return this.pluginTree.toJSON()
-}
-
-/**
- * @callback LoadPluginCallback
- * @param {Error} [err]
- */
-
-/**
- * Load a plugin
- *
- * @param {Plugin} plugin
- * @param {LoadPluginCallback} callback
- */
-Boot.prototype._loadPlugin = function (plugin, callback) {
- const instance = this
- if (isPromiseLike(plugin.func)) {
- plugin.func.then((fn) => {
- if (typeof fn.default === 'function') {
- fn = fn.default
- }
- plugin.func = fn
- this._loadPlugin(plugin, callback)
- }, callback)
- return
- }
-
- const last = instance._current[0]
-
- // place the plugin at the top of _current
- instance._current.unshift(plugin)
-
- if (instance._error && !plugin.isAfter) {
- debug('skipping loading of plugin as instance errored and it is not an after', plugin.name)
- process.nextTick(execCallback)
- return
- }
-
- let server = last?.server || instance._server
-
- if (!plugin.isAfter) {
- // Skip override for after
- try {
- server = instance.override(server, plugin.func, plugin.options)
- } catch (overrideErr) {
- debug('override errored', plugin.name)
- return execCallback(overrideErr)
- }
- }
-
- plugin.exec(server, execCallback)
-
- function execCallback (err) {
- plugin.finish(err, (err) => {
- instance._current.shift()
- callback(err)
- })
- }
-}
-
-/**
-* Delays plugin loading until the next tick to ensure any bound `_after` callbacks have a chance
-* to run prior to executing the next plugin
-*/
-Boot.prototype._loadPluginNextTick = function (plugin, callback) {
- process.nextTick(this._loadPlugin.bind(this), plugin, callback)
-}
-
-function noop () { }
-
-function callWithCbOrNextTick (func, cb) {
- const context = this._server
- const err = this._error
-
- // with this the error will appear just in the next after/ready callback
- this._error = null
- if (func.length === 0) {
- this._error = err
- executeWithThenable(func, [], cb)
- } else if (func.length === 1) {
- executeWithThenable(func, [err], cb)
- } else {
- if (this._opts.timeout === 0) {
- const wrapCb = (err) => {
- this._error = err
- cb(this._error)
- }
-
- if (func.length === 2) {
- func(err, wrapCb)
- } else {
- func(err, context, wrapCb)
- }
- } else {
- timeoutCall.call(this, func, err, context, cb)
- }
- }
-}
-
-function timeoutCall (func, rootErr, context, cb) {
- const name = func.unwrappedName ?? func.name
- debug('setting up ready timeout', name, this._opts.timeout)
- let timer = setTimeout(() => {
- debug('timed out', name)
- timer = null
- const toutErr = new AVV_ERR_READY_TIMEOUT(name)
- toutErr.fn = func
- this._error = toutErr
- cb(toutErr)
- }, this._opts.timeout)
-
- if (func.length === 2) {
- func(rootErr, timeoutCb.bind(this))
- } else {
- func(rootErr, context, timeoutCb.bind(this))
- }
-
- function timeoutCb (err) {
- if (timer) {
- clearTimeout(timer)
- this._error = err
- cb(this._error)
- } else {
- // timeout has been triggered
- // can not call cb twice
- }
- }
-}
-
-function closeWithCbOrNextTick (func, cb) {
- const context = this._server
- const isOnCloseHandler = func[kIsOnCloseHandler]
- if (func.length === 0 || func.length === 1) {
- let promise
- if (isOnCloseHandler) {
- promise = func(context)
- } else {
- promise = func(this._error)
- }
- if (promise && typeof promise.then === 'function') {
- debug('resolving close/onClose promise')
- promise.then(
- () => process.nextTick(cb),
- (e) => process.nextTick(cb, e))
- } else {
- process.nextTick(cb)
- }
- } else if (func.length === 2) {
- if (isOnCloseHandler) {
- func(context, cb)
- } else {
- func(this._error, cb)
- }
- } else {
- if (isOnCloseHandler) {
- func(context, cb)
- } else {
- func(this._error, context, cb)
- }
- }
-}
-
-function encapsulateTwoParam (func, that) {
- return _encapsulateTwoParam.bind(that)
- function _encapsulateTwoParam (context, cb) {
- let res
- if (func.length === 0) {
- res = func()
- if (res?.then) {
- res.then(function () {
- process.nextTick(cb)
- }, cb)
- } else {
- process.nextTick(cb)
- }
- } else if (func.length === 1) {
- res = func(this)
-
- if (res?.then) {
- res.then(function () {
- process.nextTick(cb)
- }, cb)
- } else {
- process.nextTick(cb)
- }
- } else {
- func(this, cb)
- }
- }
-}
-
-function encapsulateThreeParam (func, that) {
- const wrapped = _encapsulateThreeParam.bind(that)
- wrapped.unwrappedName = func.name
- return wrapped
- function _encapsulateThreeParam (err, cb) {
- let res
- if (!func) {
- process.nextTick(cb)
- } else if (func.length === 0) {
- res = func()
- if (res?.then) {
- res.then(function () {
- process.nextTick(cb, err)
- }, cb)
- } else {
- process.nextTick(cb, err)
- }
- } else if (func.length === 1) {
- res = func(err)
- if (res?.then) {
- res.then(function () {
- process.nextTick(cb)
- }, cb)
- } else {
- process.nextTick(cb)
- }
- } else if (func.length === 2) {
- func(err, cb)
- } else {
- func(err, this, cb)
- }
- }
-}
-
-module.exports = Boot
diff --git a/examples/example.js b/examples/example.js
index cbba32b..8cf315c 100644
--- a/examples/example.js
+++ b/examples/example.js
@@ -26,21 +26,20 @@ avvio
.use(b)
setTimeout(cb, 42)
})
- .after(function (err, cb) {
- if (err) {
- console.log('something bad happened')
- console.log(err)
- }
+ .then((avvio) => {
console.log('after first and second')
- cb()
- })
- .use(duplicate, { count: 4 })
- .use(third)
- .ready(function (err) {
- if (err) {
- throw err
- }
- console.log('application booted!')
+ avvio
+ .use(duplicate, { count: 4 })
+ .use(third)
+ .ready(function (err) {
+ if (err) {
+ throw err
+ }
+ console.log('application booted!')
+ })
+ }, (error) => {
+ console.log('something bad happened')
+ console.log(error)
})
avvio.on('preReady', () => {
diff --git a/lib/create-promise.js b/lib/create-promise.js
deleted file mode 100644
index 4b10c0b..0000000
--- a/lib/create-promise.js
+++ /dev/null
@@ -1,45 +0,0 @@
-'use strict'
-
-/**
- * @callback PromiseResolve
- * @param {any|PromiseLike} value
- * @returns {void}
- */
-
-/**
- * @callback PromiseReject
- * @param {any} reason
- * @returns {void}
- */
-
-/**
- * @typedef PromiseObject
- * @property {Promise} promise
- * @property {PromiseResolve} resolve
- * @property {PromiseReject} reject
- */
-
-/**
- * @returns {PromiseObject}
- */
-function createPromise () {
- /**
- * @type {PromiseObject}
- */
- const obj = {
- resolve: null,
- reject: null,
- promise: null
- }
-
- obj.promise = new Promise((resolve, reject) => {
- obj.resolve = resolve
- obj.reject = reject
- })
-
- return obj
-}
-
-module.exports = {
- createPromise
-}
diff --git a/lib/debug.js b/lib/debug.js
deleted file mode 100644
index e7cdc6f..0000000
--- a/lib/debug.js
+++ /dev/null
@@ -1,19 +0,0 @@
-'use strict'
-
-const { debuglog } = require('node:util')
-
-/**
- * @callback DebugLogger
- * @param {string} msg
- * @param {...unknown} param
- * @returns {void}
- */
-
-/**
- * @type {DebugLogger}
- */
-const debug = debuglog('avvio')
-
-module.exports = {
- debug
-}
diff --git a/lib/errors.js b/lib/errors.js
index 9aa4c8a..7b3b05c 100644
--- a/lib/errors.js
+++ b/lib/errors.js
@@ -2,37 +2,11 @@
const { createError } = require('@fastify/error')
-module.exports = {
- AVV_ERR_EXPOSE_ALREADY_DEFINED: createError(
- 'AVV_ERR_EXPOSE_ALREADY_DEFINED',
- "'%s' is already defined, specify an expose option for '%s'"
- ),
- AVV_ERR_ATTRIBUTE_ALREADY_DEFINED: createError(
- 'AVV_ERR_ATTRIBUTE_ALREADY_DEFINED',
- "'%s' is already defined"
- ),
- AVV_ERR_CALLBACK_NOT_FN: createError(
- 'AVV_ERR_CALLBACK_NOT_FN',
- "Callback for '%s' hook is not a function. Received: '%s'"
- ),
- AVV_ERR_PLUGIN_NOT_VALID: createError(
- 'AVV_ERR_PLUGIN_NOT_VALID',
- "Plugin must be a function or a promise. Received: '%s'"
- ),
- AVV_ERR_ROOT_PLG_BOOTED: createError(
- 'AVV_ERR_ROOT_PLG_BOOTED',
- 'Root plugin has already booted'
- ),
- AVV_ERR_PARENT_PLG_LOADED: createError(
- 'AVV_ERR_PARENT_PLG_LOADED',
- "Impossible to load '%s' plugin because the parent '%s' was already loaded"
- ),
- AVV_ERR_READY_TIMEOUT: createError(
- 'AVV_ERR_READY_TIMEOUT',
- "Plugin did not start in time: '%s'. You may have forgotten to call 'done' function or to resolve a Promise"
- ),
- AVV_ERR_PLUGIN_EXEC_TIMEOUT: createError(
- 'AVV_ERR_PLUGIN_EXEC_TIMEOUT',
- "Plugin did not start in time: '%s'. You may have forgotten to call 'done' function or to resolve a Promise"
- )
-}
+module.exports.AVV_ERR_EXPOSE_ALREADY_DEFINED = createError(
+ 'AVV_ERR_EXPOSE_ALREADY_DEFINED',
+ "'%s' is already defined, specify an expose option for '%s'"
+)
+module.exports.AVV_ERR_CALLBACK_NOT_FN = createError(
+ 'AVV_ERR_CALLBACK_NOT_FN',
+ "Callback for '%s' hook is not a function. Received: '%s'"
+)
diff --git a/lib/execute-with-thenable.js b/lib/execute-with-thenable.js
deleted file mode 100644
index 6e2c809..0000000
--- a/lib/execute-with-thenable.js
+++ /dev/null
@@ -1,28 +0,0 @@
-'use strict'
-const { isPromiseLike } = require('./is-promise-like')
-const { kAvvio } = require('./symbols')
-
-/**
- * @callback ExecuteWithThenableCallback
- * @param {Error} error
- * @returns {void}
- */
-
-/**
- * @param {Function} func
- * @param {Array} args
- * @param {ExecuteWithThenableCallback} [callback]
- */
-function executeWithThenable (func, args, callback) {
- const result = func.apply(func, args)
- if (isPromiseLike(result) && !result[kAvvio]) {
- // process promise but not avvio mock thenable
- result.then(() => process.nextTick(callback), (error) => process.nextTick(callback, error))
- } else if (callback) {
- process.nextTick(callback)
- }
-}
-
-module.exports = {
- executeWithThenable
-}
diff --git a/lib/get-plugin-name.js b/lib/get-plugin-name.js
deleted file mode 100644
index dad82a9..0000000
--- a/lib/get-plugin-name.js
+++ /dev/null
@@ -1,34 +0,0 @@
-'use strict'
-
-// this symbol is assigned by fastify-plugin
-const { kPluginMeta } = require('./symbols')
-
-/**
- * @param {function} plugin
- * @param {object} [options]
- * @param {string} [options.name]
- * @returns {string}
- */
-function getPluginName (plugin, options) {
- // use explicit function metadata if set
- if (plugin[kPluginMeta]?.name) {
- return plugin[kPluginMeta].name
- }
-
- // use explicit name option if set
- if (options?.name) {
- return options.name
- }
-
- // determine from the function
- if (plugin.name) {
- return plugin.name
- } else {
- // takes the first two lines of the function if nothing else works
- return plugin.toString().split('\n').slice(0, 2).map(s => s.trim()).join(' -- ')
- }
-}
-
-module.exports = {
- getPluginName
-}
diff --git a/lib/is-bundled-or-typescript-plugin.js b/lib/is-bundled-or-typescript-plugin.js
deleted file mode 100644
index 42003dd..0000000
--- a/lib/is-bundled-or-typescript-plugin.js
+++ /dev/null
@@ -1,23 +0,0 @@
-'use strict'
-
-/**
- * bundled or typescript plugin
- * @typedef {object} BundledOrTypescriptPlugin
- * @property {function} default
- */
-
-/**
- * @param {any} maybeBundledOrTypescriptPlugin
- * @returns {plugin is BundledOrTypescriptPlugin}
- */
-function isBundledOrTypescriptPlugin (maybeBundledOrTypescriptPlugin) {
- return (
- maybeBundledOrTypescriptPlugin !== null &&
- typeof maybeBundledOrTypescriptPlugin === 'object' &&
- typeof maybeBundledOrTypescriptPlugin.default === 'function'
- )
-}
-
-module.exports = {
- isBundledOrTypescriptPlugin
-}
diff --git a/lib/is-promise-like.js b/lib/is-promise-like.js
deleted file mode 100644
index 909f8db..0000000
--- a/lib/is-promise-like.js
+++ /dev/null
@@ -1,17 +0,0 @@
-'use strict'
-
-/**
- * @param {any} maybePromiseLike
- * @returns {maybePromiseLike is PromiseLike}
- */
-function isPromiseLike (maybePromiseLike) {
- return (
- maybePromiseLike !== null &&
- typeof maybePromiseLike === 'object' &&
- typeof maybePromiseLike.then === 'function'
- )
-}
-
-module.exports = {
- isPromiseLike
-}
diff --git a/lib/plugin.js b/lib/plugin.js
index a3f248d..8dccfeb 100644
--- a/lib/plugin.js
+++ b/lib/plugin.js
@@ -1,279 +1,166 @@
'use strict'
-const { EventEmitter } = require('node:events')
-const { inherits } = require('node:util')
-const { debug } = require('./debug')
-const { createPromise } = require('./create-promise')
-const { AVV_ERR_PLUGIN_EXEC_TIMEOUT } = require('./errors')
-const { getPluginName } = require('./get-plugin-name')
-const { isPromiseLike } = require('./is-promise-like')
+const EventEmitter = require('node:events').EventEmitter
+const inherits = require('node:util').inherits
+const { withResolvers } = require('./promise')
+const { resolvePluginName, noop, executeFn } = require('./utils')
-/**
- * @param {*} queue
- * @param {*} func
- * @param {*} options
- * @param {boolean} isAfter
- * @param {number} [timeout]
- */
-function Plugin (queue, func, options, isAfter, timeout) {
+// Plugin is used to manage encapsulation levels
+
+function Plugin (queue, fn, options, timeout) {
this.queue = queue
- this.func = func
+ this.fn = fn
this.options = options
-
- /**
- * @type {boolean}
- */
- this.isAfter = isAfter
- /**
- * @type {number}
- */
this.timeout = timeout
- /**
- * @type {boolean}
- */
- this.started = false
- /**
- * @type {string}
- */
- this.name = getPluginName(func, options)
-
this.queue.pause()
- /**
- * @type {Error|null}
- */
- this._error = null
- /**
- * @type {boolean}
- */
- this.loaded = false
-
- this._promise = null
-
+ // status
+ this.context = null
+ this.contextName = null
+ this.name = resolvePluginName(fn, options)
+ this.error = null
+ this.started = false
this.startTime = null
+ this.loaded = false
+ this.resolvers = null
}
inherits(Plugin, EventEmitter)
-/**
- * @callback ExecCallback
- * @param {Error|null} execErr
- * @returns
- */
-
-/**
- *
- * @param {*} server
- * @param {ExecCallback} callback
- * @returns
- */
-Plugin.prototype.exec = function (server, callback) {
- debug('exec', this.name)
+// static function
+Plugin.validate = function validate (maybePlugin) {
+ if (!(maybePlugin && (typeof maybePlugin === 'function' || typeof maybePlugin.then === 'function'))) {
+ if (Array.isArray(maybePlugin)) {
+ throw Error()
+ } else if (maybePlugin === null) {
+ throw Error()
+ } else {
+ throw Error()
+ }
+ }
+}
- this.server = server
- const func = this.func
- const name = this.name
+Plugin.prototype.execute = function execute (context, callback) {
+ this.context = context
+ this.contextName = context ? context.name : null
+ // resolve function options
+ this.options = typeof this.options === 'function' ? this.options(context) : this.options
+
+ const {
+ fn,
+ name,
+ timeout
+ } = this
let completed = false
+ let timerId = null
- this.options = typeof this.options === 'function' ? this.options(this.server) : this.options
-
- let timer = null
-
- /**
- * @param {Error} [execErr]
- */
- const done = (execErr) => {
+ const done = (executeError) => {
+ // prevent execute twice
if (completed) {
- debug('loading complete', name)
return
}
- this._error = execErr
-
- if (execErr) {
- debug('exec errored', name)
- } else {
- debug('exec completed', name)
- }
+ this.error = executeError
completed = true
- if (timer) {
- clearTimeout(timer)
+ if (timerId) {
+ clearTimeout(timerId)
}
- callback(execErr)
+ callback(executeError)
}
- if (this.timeout > 0) {
- debug('setting up timeout', name, this.timeout)
- timer = setTimeout(function () {
- debug('timed out', name)
- timer = null
- const readyTimeoutErr = new AVV_ERR_PLUGIN_EXEC_TIMEOUT(name)
- // TODO Remove reference to function
- readyTimeoutErr.fn = func
- done(readyTimeoutErr)
- }, this.timeout)
+ if (timeout > 0) {
+ timerId = setTimeout(function () {
+ timerId = null
+ done(Error())
+ }, timeout)
}
+ // execute the fn
this.started = true
this.startTime = Date.now()
- this.emit('start', this.server ? this.server.name : null, this.name, Date.now())
-
- const maybePromiseLike = func(this.server, this.options, done)
-
- if (isPromiseLike(maybePromiseLike)) {
- debug('exec: resolving promise', name)
-
- maybePromiseLike.then(
- () => process.nextTick(done),
- (e) => process.nextTick(done, e))
- } else if (func.length < 3) {
- done()
- }
+ this.emit('start', this.contextName, name, this.startTime)
+ executeFn(fn, [context, this.options], done)
}
-/**
- * @returns {Promise}
- */
-Plugin.prototype.loadedSoFar = function () {
- debug('loadedSoFar', this.name)
-
- if (this.loaded) {
- return Promise.resolve()
- }
-
- const setup = () => {
- this.server.after((afterErr, callback) => {
- this._error = afterErr
- this.queue.pause()
-
- if (this._promise) {
- if (afterErr) {
- debug('rejecting promise', this.name, afterErr)
- this._promise.reject(afterErr)
- } else {
- debug('resolving promise', this.name)
- this._promise.resolve()
- }
- this._promise = null
- }
-
- process.nextTick(callback, afterErr)
- })
- this.queue.resume()
- }
-
- let res
-
- if (!this._promise) {
- this._promise = createPromise()
- res = this._promise.promise
-
- if (!this.server) {
- this.on('start', setup)
- } else {
- setup()
- }
- } else {
- res = Promise.resolve()
- }
-
- return res
-}
-
-/**
- * @callback EnqueueCallback
- * @param {Error|null} enqueueErr
- * @param {Plugin} result
- */
-
-/**
- *
- * @param {Plugin} plugin
- * @param {EnqueueCallback} callback
- */
-Plugin.prototype.enqueue = function (plugin, callback) {
- debug('enqueue', this.name, plugin.name)
-
- this.emit('enqueue', this.server ? this.server.name : null, this.name, Date.now())
+Plugin.prototype.enqueue = function enqueue (plugin, callback) {
+ this.emit('enqueue', this.contextName, this.name, Date.now())
this.queue.push(plugin, callback)
}
-/**
- * @callback FinishCallback
- * @param {Error|null} finishErr
- * @returns
- */
-/**
- *
- * @param {Error|null} err
- * @param {FinishCallback} callback
- * @returns
- */
-Plugin.prototype.finish = function (err, callback) {
- debug('finish', this.name, err)
-
+Plugin.prototype.finish = function finish (error, callback) {
const done = () => {
if (this.loaded) {
return
}
- debug('loaded', this.name)
- this.emit('loaded', this.server ? this.server.name : null, this.name, Date.now())
+ this.emit('loaded', this.contextName, this.name, Date.now())
this.loaded = true
- callback(err)
+ callback(error)
}
- if (err) {
- if (this._promise) {
- this._promise.reject(err)
- this._promise = null
- }
+ if (error) {
+ this.reject(error)
done()
return
}
const check = () => {
- debug('check', this.name, this.queue.length(), this.queue.running(), this._promise)
if (this.queue.length() === 0 && this.queue.running() === 0) {
- if (this._promise) {
- const wrap = () => {
- debug('wrap')
+ if (this.resolvers) {
+ function queue () {
queueMicrotask(check)
}
- this._promise.resolve()
- this._promise.promise.then(wrap, wrap)
- this._promise = null
+ this.resolve().then(queue, queue)
} else {
done()
}
} else {
- debug('delayed', this.name)
- // finish when the queue of nested plugins to load is empty
this.queue.drain = () => {
- debug('drain', this.name)
this.queue.drain = noop
-
- // we defer the check, as a safety net for things
- // that might be scheduled in the loading callback
queueMicrotask(check)
}
}
}
queueMicrotask(check)
+ this.queue.resume()
+}
+
+Plugin.prototype.promise = function promise () {
+ if (this.loaded) {
+ return Promise.resolve()
+ }
- // we start loading the dependents plugins only once
- // the current level is finished
this.queue.resume()
+
+ if (!this.resolvers) {
+ this.resolvers = withResolvers()
+ }
+
+ return this.resolvers.promise
}
-function noop () {}
+Plugin.prototype.resolve = function resolve () {
+ if (this.resolvers) {
+ const resolvers = this.resolvers
+ resolvers.resolve()
+ this.resolvers = null
+ return resolvers.promise
+ }
+}
-module.exports = {
- Plugin
+Plugin.prototype.reject = function reject (error) {
+ if (this.resolvers) {
+ const resolvers = this.resolvers
+ resolvers.reject(error)
+ this.resolvers = null
+ return resolvers.promise
+ }
}
+
+module.exports.Plugin = Plugin
diff --git a/lib/promise.js b/lib/promise.js
new file mode 100644
index 0000000..7baf784
--- /dev/null
+++ b/lib/promise.js
@@ -0,0 +1,48 @@
+'use strict'
+
+function withResolvers () {
+ // use native if avaiable
+ if (Promise.withResolvers) return Promise.withResolvers()
+
+ const resolvers = {
+ resolve: null,
+ reject: null,
+ promise: null
+ }
+
+ resolvers.promise = new Promise((resolve, reject) => {
+ resolvers.resolve = resolve
+ resolvers.reject = reject
+ })
+
+ return resolvers
+}
+module.exports.withResolvers = withResolvers
+
+function _try (fn, ...args) {
+ // use native if avaiable
+ if (Promise.try) return Promise.try(fn, ...args)
+
+ const resolvers = withResolvers()
+
+ try {
+ const result = fn.call(fn, ...args)
+ resolvers.resolve(result)
+ } catch (error) {
+ resolvers.reject(error)
+ }
+
+ return resolvers.promise
+}
+
+module.exports.try = _try
+
+function isPromiseLike (maybePromiseLike) {
+ return (
+ maybePromiseLike !== null &&
+ typeof maybePromiseLike === 'object' &&
+ typeof maybePromiseLike.then === 'function'
+ )
+}
+
+module.exports.isPromiseLike = isPromiseLike
diff --git a/lib/symbols.js b/lib/symbols.js
index 62cdf38..6312389 100644
--- a/lib/symbols.js
+++ b/lib/symbols.js
@@ -1,26 +1,28 @@
'use strict'
-// Internal Symbols
-const kAvvio = Symbol('avvio.Boot')
-const kIsOnCloseHandler = Symbol('isOnCloseHandler')
-const kThenifyDoNotWrap = Symbol('avvio.ThenifyDoNotWrap')
-const kUntrackNode = Symbol('avvio.TimeTree.untrackNode')
-const kTrackNode = Symbol('avvio.TimeTree.trackNode')
-const kGetParent = Symbol('avvio.TimeTree.getParent')
-const kGetNode = Symbol('avvio.TimeTree.getNode')
-const kAddNode = Symbol('avvio.TimeTree.addNode')
+module.exports = {
+ // private properties
+ kAvvio: Symbol('Avvio#this'),
+ kError: Symbol('Avvio#error'),
+ kContext: Symbol('Avvio#context'),
+ kOptions: Symbol('Avvio#options'),
+ kPluginRoot: Symbol('Avvio#root'),
+ kPluginQueue: Symbol('Avvio#plugin-queue'),
+ kReadyQueue: Symbol('Avvio#ready-queue'),
+ kCloseQueue: Symbol('Avvio#close-queue'),
+ kResolvers: Symbol('Avvio#resolvers'),
+ kWrappedThen: Symbol('Avvio#wrapped-then'),
+ kTracker: Symbol('Avvio#tracker'),
+ // private functions
+ kExpose: Symbol('Avvio#expose'),
+ kAddPlugin: Symbol('Avvio#add-plugin'),
+ kLoadPlugin: Symbol('Avvio#load-plugin'),
+ kLoadPluginNext: Symbol('Avvio#load-plugin-next'),
+ kStart: Symbol('Avvio#start'),
+ kTrackPlugin: Symbol('Avvio#track-plugin'),
-// Public Symbols
-const kPluginMeta = Symbol.for('plugin-meta')
+ kOnCloseFunction: Symbol('Function#on-close'),
-module.exports = {
- kAvvio,
- kIsOnCloseHandler,
- kThenifyDoNotWrap,
- kUntrackNode,
- kTrackNode,
- kGetParent,
- kGetNode,
- kAddNode,
- kPluginMeta
+ // public properties
+ kPluginMeta: Symbol.for('plugin-meta')
}
diff --git a/lib/thenify.js b/lib/thenify.js
deleted file mode 100644
index e1b614d..0000000
--- a/lib/thenify.js
+++ /dev/null
@@ -1,60 +0,0 @@
-'use strict'
-
-const { debug } = require('./debug')
-const { kThenifyDoNotWrap } = require('./symbols')
-
-/**
- * @callback PromiseConstructorLikeResolve
- * @param {any} value
- * @returns {void}
- */
-
-/**
- * @callback PromiseConstructorLikeReject
- * @param {reason} error
- * @returns {void}
- */
-
-/**
- * @callback PromiseConstructorLike
- * @param {PromiseConstructorLikeResolve} resolve
- * @param {PromiseConstructorLikeReject} reject
- * @returns {void}
- */
-
-/**
- * @returns {PromiseConstructorLike}
- */
-function thenify () {
- // If the instance is ready, then there is
- // nothing to await. This is true during
- // await server.ready() as ready() resolves
- // with the server, end we will end up here
- // because of automatic promise chaining.
- if (this.booted) {
- debug('thenify returning undefined because we are already booted')
- return
- }
-
- // Calling resolve(this._server) would fetch the then
- // property on the server, which will lead it here.
- // If we do not break the recursion, we will loop
- // forever.
- if (this[kThenifyDoNotWrap]) {
- this[kThenifyDoNotWrap] = false
- return
- }
-
- debug('thenify')
- return (resolve, reject) => {
- const p = this._loadRegistered()
- return p.then(() => {
- this[kThenifyDoNotWrap] = true
- return resolve(this._server)
- }, reject)
- }
-}
-
-module.exports = {
- thenify
-}
diff --git a/lib/time-tree.js b/lib/tracker.js
similarity index 50%
rename from lib/time-tree.js
rename to lib/tracker.js
index 5f02d68..f92e01e 100644
--- a/lib/time-tree.js
+++ b/lib/tracker.js
@@ -1,50 +1,13 @@
'use strict'
-const {
- kUntrackNode,
- kTrackNode,
- kGetParent,
- kGetNode,
- kAddNode
-} = require('./symbols')
-
-/**
- * Node of the TimeTree
- * @typedef {object} TimeTreeNode
- * @property {string} id
- * @property {string|null} parent
- * @property {string} label
- * @property {Array} nodes
- * @property {number} start
- * @property {number|undefined} stop
- * @property {number|undefined} diff
- */
-
-class TimeTree {
+class Tracker {
constructor () {
- /**
- * @type {TimeTreeNode|null} root
- * @public
- */
this.root = null
-
- /**
- * @type {Map} tableId
- * @public
- */
this.tableId = new Map()
-
- /**
- * @type {Map>} tableLabel
- * @public
- */
this.tableLabel = new Map()
}
- /**
- * @param {TimeTreeNode} node
- */
- [kTrackNode] (node) {
+ track (node) {
this.tableId.set(node.id, node)
if (this.tableLabel.has(node.label)) {
this.tableLabel.get(node.label).push(node)
@@ -53,10 +16,7 @@ class TimeTree {
}
}
- /**
- * @param {TimeTreeNode} node
- */
- [kUntrackNode] (node) {
+ untrack (node) {
this.tableId.delete(node.id)
const labelNode = this.tableLabel.get(node.label)
@@ -67,38 +27,23 @@ class TimeTree {
}
}
- /**
- * @param {string} parent
- * @returns {TimeTreeNode}
- */
- [kGetParent] (parent) {
- if (parent === null) {
+ findParentByLabel (label) {
+ if (label === null) {
return null
- } else if (this.tableLabel.has(parent)) {
- const parentNode = this.tableLabel.get(parent)
- return parentNode[parentNode.length - 1]
+ } else if (this.tableLabel.has(label)) {
+ const parent = this.tableLabel.get(label)
+ return parent[parent.length - 1]
} else {
return null
}
}
- /**
- *
- * @param {string} nodeId
- * @returns {TimeTreeNode}
- */
- [kGetNode] (nodeId) {
+ findNodeById (nodeId) {
return this.tableId.get(nodeId)
}
- /**
- * @param {string} parent
- * @param {string} label
- * @param {number} start
- * @returns {TimeTreeNode["id"]}
- */
- [kAddNode] (parent, label, start) {
- const parentNode = this[kGetParent](parent)
+ add (parent, label, start) {
+ const parentNode = this.findParentByLabel(parent)
const isRoot = parentNode === null
if (isRoot) {
@@ -111,7 +56,7 @@ class TimeTree {
stop: null,
diff: -1
}
- this[kTrackNode](this.root)
+ this.track(this.root)
return this.root.id
}
@@ -129,53 +74,32 @@ class TimeTree {
diff: -1
}
parentNode.nodes.push(childNode)
- this[kTrackNode](childNode)
+ this.track(childNode)
return nodeId
}
- /**
- * @param {string} parent
- * @param {string} label
- * @param {number|undefined} start
- * @returns {TimeTreeNode["id"]}
- */
start (parent, label, start = Date.now()) {
- return this[kAddNode](parent, label, start)
+ return this.add(parent, label, start)
}
- /**
- * @param {string} nodeId
- * @param {number|undefined} stop
- */
stop (nodeId, stop = Date.now()) {
- const node = this[kGetNode](nodeId)
+ const node = this.findNodeById(nodeId)
if (node) {
node.stop = stop
node.diff = (node.stop - node.start) || 0
- this[kUntrackNode](node)
+ this.untrack(node)
}
}
- /**
- * @returns {TimeTreeNode}
- */
toJSON () {
return Object.assign({}, this.root)
}
- /**
- * @returns {string}
- */
prettyPrint () {
return prettyPrintTimeTree(this.toJSON())
}
}
-/**
- * @param {TimeTreeNode} obj
- * @param {string|undefined} prefix
- * @returns {string}
- */
function prettyPrintTimeTree (obj, prefix = '') {
let result = prefix
@@ -195,6 +119,4 @@ function prettyPrintTimeTree (obj, prefix = '') {
return result
}
-module.exports = {
- TimeTree
-}
+module.exports.Tracker = Tracker
diff --git a/lib/utils.js b/lib/utils.js
new file mode 100644
index 0000000..9150dab
--- /dev/null
+++ b/lib/utils.js
@@ -0,0 +1,58 @@
+'use strict'
+
+const { isPromiseLike } = require('./promise')
+const { kPluginMeta } = require('./symbols')
+const promise = require('./promise')
+
+module.exports.noop = function noop () { }
+
+module.exports.resolvePluginName = function resolvePluginName (plugin, options) {
+ // use explicit function metadata if available
+ if (plugin[kPluginMeta]?.name) {
+ return plugin[kPluginMeta].name
+ }
+
+ // use explicit name option if available
+ if (options?.name) {
+ return options.name
+ }
+
+ // determine from the function
+ if (plugin.name) {
+ return plugin.name
+ } else {
+ // takes the first two lines of the function if nothing else works
+ return plugin.toString().split('\n').slice(0, 2).map(s => s.trim()).join(' -- ')
+ }
+}
+
+module.exports.resolveBundledFunction = function resolveBundledFunction (maybeBundled) {
+ if (
+ maybeBundled !== null &&
+ typeof maybeBundled === 'object' &&
+ typeof maybeBundled.default === 'function'
+ ) {
+ return maybeBundled.default
+ } else {
+ return maybeBundled
+ }
+}
+
+module.exports.executeFn = function executeFn (fn, args, callback) {
+ let thenable = null
+ const length = args.length + 1
+
+ if (fn.length < length) {
+ thenable = promise.try(fn, ...args)
+ } else {
+ const maybePromiseLike = fn.call(fn, ...args, callback)
+ isPromiseLike(maybePromiseLike) && (thenable = maybePromiseLike)
+ }
+
+ if (isPromiseLike(thenable)) {
+ thenable.then(
+ () => process.nextTick(callback),
+ (error) => process.nextTick(callback, error)
+ )
+ }
+}
diff --git a/lib/validate-plugin.js b/lib/validate-plugin.js
deleted file mode 100644
index 374ee56..0000000
--- a/lib/validate-plugin.js
+++ /dev/null
@@ -1,26 +0,0 @@
-'use strict'
-
-const { AVV_ERR_PLUGIN_NOT_VALID } = require('./errors')
-
-/**
- * @param {any} maybePlugin
- * @throws {AVV_ERR_PLUGIN_NOT_VALID}
- *
- * @returns {asserts plugin is Function|PromiseLike}
- */
-function validatePlugin (maybePlugin) {
- // validate if plugin is a function or Promise
- if (!(maybePlugin && (typeof maybePlugin === 'function' || typeof maybePlugin.then === 'function'))) {
- if (Array.isArray(maybePlugin)) {
- throw new AVV_ERR_PLUGIN_NOT_VALID('array')
- } else if (maybePlugin === null) {
- throw new AVV_ERR_PLUGIN_NOT_VALID('null')
- } else {
- throw new AVV_ERR_PLUGIN_NOT_VALID(typeof maybePlugin)
- }
- }
-}
-
-module.exports = {
- validatePlugin
-}
diff --git a/lib/workers.js b/lib/workers.js
new file mode 100644
index 0000000..e9d0b69
--- /dev/null
+++ b/lib/workers.js
@@ -0,0 +1,57 @@
+'use strict'
+
+const { kContext, kError, kOptions, kOnCloseFunction } = require('./symbols')
+const { executeFn } = require('./utils')
+
+/**
+ *
+ * @param {Function} fn ready function
+ * @param {Function} done indicate the current task is done
+ */
+module.exports.readyWorker = function readyWorker (fn, done) {
+ const context = this[kContext]
+ const error = this[kError]
+
+ // prevent the error appear outside of the next ready callback
+ this[kError] = null
+
+ if (this[kOptions].timeout === 0) {
+ executeFn(fn, [error, context], (error) => {
+ this[kError] = error
+ done(this[kError])
+ })
+ } else {
+ let timerId = setTimeout(() => {
+ timerId = null
+ this[kError] = Error()
+ done(this[kError])
+ }, this[kOptions].timeout)
+
+ executeFn(fn, [error, context], (error) => {
+ if (timerId) {
+ clearTimeout(timerId)
+ this[kError] = error
+ done(this[kError])
+ }
+ })
+ }
+}
+
+/**
+ *
+ * @param {Function} fn
+ * @param {Function} done
+ */
+module.exports.closeWorker = function closeWorker (fn, done) {
+ const context = this[kContext]
+ const error = this[kError]
+
+ // close worker is shared between, onClose and close
+ // the arguments accepted by two function is different
+ const isOnCloseFn = fn[kOnCloseFunction]
+ const args = isOnCloseFn
+ ? [context]
+ : [error, context]
+
+ executeFn(fn, args, done)
+}
diff --git a/package.json b/package.json
index f539c2f..b7dc154 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "avvio",
"version": "9.1.0",
"description": "Asynchronous bootstrapping of Node applications",
- "main": "boot.js",
+ "main": "avvio.js",
"type": "commonjs",
"types": "index.d.ts",
"scripts": {
diff --git a/test/after-and-ready.test.js b/test/after-and-ready.test.js
deleted file mode 100644
index ff3d730..0000000
--- a/test/after-and-ready.test.js
+++ /dev/null
@@ -1,864 +0,0 @@
-'use strict'
-
-const { test } = require('tap')
-const boot = require('..')
-
-test('boot a plugin and then execute a call after that', (t) => {
- t.plan(5)
-
- const app = boot()
- let pluginLoaded = false
- let afterCalled = false
-
- app.use(function (s, opts, done) {
- t.notOk(afterCalled, 'after not called')
- pluginLoaded = true
- done()
- })
-
- app.after(function (err, cb) {
- t.error(err)
- t.ok(pluginLoaded, 'afterred!')
- afterCalled = true
- cb()
- })
-
- app.on('start', () => {
- t.ok(afterCalled, 'after called')
- t.ok(pluginLoaded, 'plugin loaded')
- })
-})
-
-test('after without a done callback', (t) => {
- t.plan(5)
-
- const app = boot()
- let pluginLoaded = false
- let afterCalled = false
-
- app.use(function (s, opts, done) {
- t.notOk(afterCalled, 'after not called')
- pluginLoaded = true
- done()
- })
-
- app.after(function (err) {
- t.error(err)
- t.ok(pluginLoaded, 'afterred!')
- afterCalled = true
- })
-
- app.on('start', () => {
- t.ok(afterCalled, 'after called')
- t.ok(pluginLoaded, 'plugin loaded')
- })
-})
-
-test('verify when a afterred call happens', (t) => {
- t.plan(3)
-
- const app = boot()
-
- app.use(function (s, opts, done) {
- done()
- })
-
- app.after(function (err, cb) {
- t.error(err)
- t.pass('afterred finished')
- cb()
- })
-
- app.on('start', () => {
- t.pass('booted')
- })
-})
-
-test('internal after', (t) => {
- t.plan(18)
-
- const app = boot()
- let firstLoaded = false
- let secondLoaded = false
- let thirdLoaded = false
- let afterCalled = false
-
- app.use(first)
- app.use(third)
-
- function first (s, opts, done) {
- t.notOk(firstLoaded, 'first is not loaded')
- t.notOk(secondLoaded, 'second is not loaded')
- t.notOk(thirdLoaded, 'third is not loaded')
- firstLoaded = true
- s.use(second)
- s.after(function (err, cb) {
- t.error(err)
- t.notOk(afterCalled, 'after was not called')
- afterCalled = true
- cb()
- })
- done()
- }
-
- function second (s, opts, done) {
- t.ok(firstLoaded, 'first is loaded')
- t.notOk(secondLoaded, 'second is not loaded')
- t.notOk(thirdLoaded, 'third is not loaded')
- t.notOk(afterCalled, 'after was not called')
- secondLoaded = true
- done()
- }
-
- function third (s, opts, done) {
- t.ok(firstLoaded, 'first is loaded')
- t.ok(secondLoaded, 'second is loaded')
- t.ok(afterCalled, 'after was called')
- t.notOk(thirdLoaded, 'third is not loaded')
- thirdLoaded = true
- done()
- }
-
- app.on('start', () => {
- t.ok(firstLoaded, 'first is loaded')
- t.ok(secondLoaded, 'second is loaded')
- t.ok(thirdLoaded, 'third is loaded')
- t.ok(afterCalled, 'after was called')
- t.pass('booted')
- })
-})
-
-test('ready adds at the end of the queue', (t) => {
- t.plan(14)
-
- const app = boot()
- let pluginLoaded = false
- let afterCalled = false
- let readyCalled = false
-
- app.ready(function (err, cb) {
- t.error(err)
- t.ok(pluginLoaded, 'after the plugin')
- t.ok(afterCalled, 'after after')
- readyCalled = true
- process.nextTick(cb)
- })
-
- app.use(function (s, opts, done) {
- t.notOk(afterCalled, 'after not called')
- t.notOk(readyCalled, 'ready not called')
- pluginLoaded = true
-
- app.ready(function (err) {
- t.error(err)
- t.ok(readyCalled, 'after the first ready')
- t.ok(afterCalled, 'after the after callback')
- })
-
- done()
- })
-
- app.after(function (err, cb) {
- t.error(err)
- t.ok(pluginLoaded, 'executing after!')
- t.notOk(readyCalled, 'ready not called')
- afterCalled = true
- cb()
- })
-
- app.on('start', () => {
- t.ok(afterCalled, 'after called')
- t.ok(pluginLoaded, 'plugin loaded')
- t.ok(readyCalled, 'ready called')
- })
-})
-
-test('if the after/ready callback has two parameters, the first one must be the context', (t) => {
- t.plan(4)
-
- const server = { my: 'server' }
- const app = boot(server)
-
- app.use(function (s, opts, done) {
- done()
- })
-
- app.after(function (err, context, cb) {
- t.error(err)
- t.equal(server, context)
- cb()
- })
-
- app.ready(function (err, context, cb) {
- t.error(err)
- t.equal(server, context)
- cb()
- })
-})
-
-test('if the after/ready async, the returns must be the context generated', (t) => {
- t.plan(3)
-
- const server = { my: 'server', index: 0 }
- const app = boot(server)
- app.override = function (old) {
- return { ...old, index: old.index + 1 }
- }
-
- app.use(function (s, opts, done) {
- s.use(function (s, opts, done) {
- s.ready().then(itself => t.same(itself, s, 'deep deep'))
- done()
- })
- s.ready().then(itself => t.same(itself, s, 'deep'))
- done()
- })
-
- app.ready().then(itself => t.same(itself, server, 'outer'))
-})
-
-test('if the after/ready callback, the returns must be the context generated', (t) => {
- t.plan(3)
-
- const server = { my: 'server', index: 0 }
- const app = boot(server)
- app.override = function (old) {
- return { ...old, index: old.index + 1 }
- }
-
- app.use(function (s, opts, done) {
- s.use(function (s, opts, done) {
- s.ready((_, itself, done) => {
- t.same(itself, s, 'deep deep')
- done()
- })
- done()
- })
- s.ready((_, itself, done) => {
- t.same(itself, s, 'deep')
- done()
- })
- done()
- })
-
- app.ready((_, itself, done) => {
- t.same(itself, server, 'outer')
- done()
- })
-})
-
-test('error should come in the first after - one parameter', (t) => {
- t.plan(3)
-
- const server = { my: 'server' }
- const app = boot(server)
-
- app.use(function (s, opts, done) {
- done(new Error('err'))
- })
-
- app.after(function (err) {
- t.ok(err instanceof Error)
- t.equal(err.message, 'err')
- })
-
- app.ready(function (err) {
- t.error(err)
- })
-})
-
-test('error should come in the first after - two parameters', (t) => {
- t.plan(3)
-
- const server = { my: 'server' }
- const app = boot(server)
-
- app.use(function (s, opts, done) {
- done(new Error('err'))
- })
-
- app.after(function (err, cb) {
- t.ok(err instanceof Error)
- t.equal(err.message, 'err')
- cb()
- })
-
- app.ready(function (err) {
- t.error(err)
- })
-})
-
-test('error should come in the first after - three parameter', (t) => {
- t.plan(4)
-
- const server = { my: 'server' }
- const app = boot(server)
-
- app.use(function (s, opts, done) {
- done(new Error('err'))
- })
-
- app.after(function (err, context, cb) {
- t.ok(err instanceof Error)
- t.equal(err.message, 'err')
- t.equal(context, server)
- cb()
- })
-
- app.ready(function (err) {
- t.error(err)
- })
-})
-
-test('error should come in the first ready - one parameter', (t) => {
- t.plan(2)
-
- const server = { my: 'server' }
- const app = boot(server)
-
- app.use(function (s, opts, done) {
- done(new Error('err'))
- })
-
- app.ready(function (err) {
- t.ok(err instanceof Error)
- t.equal(err.message, 'err')
- })
-})
-
-test('error should come in the first ready - two parameters', (t) => {
- t.plan(2)
-
- const server = { my: 'server' }
- const app = boot(server)
-
- app.use(function (s, opts, done) {
- done(new Error('err'))
- })
-
- app.ready(function (err, cb) {
- t.ok(err instanceof Error)
- t.equal(err.message, 'err')
- cb()
- })
-})
-
-test('error should come in the first ready - three parameters', (t) => {
- t.plan(3)
-
- const server = { my: 'server' }
- const app = boot(server)
-
- app.use(function (s, opts, done) {
- done(new Error('err'))
- })
-
- app.ready(function (err, context, cb) {
- t.ok(err instanceof Error)
- t.equal(err.message, 'err')
- t.equal(context, server)
- cb()
- })
-})
-
-test('if `use` has a callback with more then one parameter, the error must not reach ready', (t) => {
- t.plan(1)
-
- const server = { my: 'server' }
- const app = boot(server)
-
- app.use(function (s, opts, done) {
- done(new Error('err'))
- })
-
- app.ready(function (err) {
- t.ok(err)
- })
-})
-
-test('if `use` has a callback without parameters, the error must reach ready', (t) => {
- t.plan(1)
-
- const server = { my: 'server' }
- const app = boot(server)
-
- app.use(function (s, opts, done) {
- done(new Error('err'))
- }, () => {})
-
- app.ready(function (err) {
- t.ok(err)
- })
-})
-
-test('should pass the errors from after to ready', (t) => {
- t.plan(6)
-
- const server = {}
- const app = boot(server, {})
-
- server.use(function (s, opts, done) {
- t.equal(s, server, 'the first argument is the server')
- t.same(opts, {}, 'no options')
- done()
- }).after((err, done) => {
- t.error(err)
- done(new Error('some error'))
- })
-
- server.onClose(() => {
- t.ok('onClose called')
- })
-
- server.ready(err => {
- t.equal(err.message, 'some error')
- })
-
- app.on('start', () => {
- server.close(() => {
- t.pass('booted')
- })
- })
-})
-
-test('after no encapsulation', t => {
- t.plan(4)
-
- const app = boot()
- app.override = function (s, fn, opts) {
- s = Object.create(s)
- return s
- }
-
- app.use(function (instance, opts, next) {
- instance.test = true
- instance.after(function (err, i, done) {
- t.error(err)
- t.notOk(i.test)
- done()
- })
- next()
- })
-
- app.after(function (err, i, done) {
- t.error(err)
- t.notOk(i.test)
- done()
- })
-})
-
-test('ready no encapsulation', t => {
- t.plan(4)
-
- const app = boot()
- app.override = function (s, fn, opts) {
- s = Object.create(s)
- return s
- }
-
- app.use(function (instance, opts, next) {
- instance.test = true
- instance.ready(function (err, i, done) {
- t.error(err)
- t.notOk(i.test)
- done()
- })
- next()
- })
-
- app.ready(function (err, i, done) {
- t.error(err)
- t.notOk(i.test)
- done()
- })
-})
-
-test('after encapsulation with a server', t => {
- t.plan(4)
-
- const server = { my: 'server' }
- const app = boot(server)
- app.override = function (s, fn, opts) {
- s = Object.create(s)
- return s
- }
-
- app.use(function (instance, opts, next) {
- instance.test = true
- instance.after(function (err, i, done) {
- t.error(err)
- t.ok(i.test)
- done()
- })
- next()
- })
-
- app.after(function (err, i, done) {
- t.error(err)
- t.notOk(i.test)
- done()
- })
-})
-
-test('ready encapsulation with a server', t => {
- t.plan(4)
-
- const server = { my: 'server' }
- const app = boot(server)
- app.override = function (s, fn, opts) {
- s = Object.create(s)
- return s
- }
-
- app.use(function (instance, opts, next) {
- instance.test = true
- instance.ready(function (err, i, done) {
- t.error(err)
- t.ok(i.test)
- done()
- })
- next()
- })
-
- app.ready(function (err, i, done) {
- t.error(err)
- t.notOk(i.test)
- done()
- })
-})
-
-test('after should passthrough the errors', (t) => {
- t.plan(5)
-
- const app = boot()
- let pluginLoaded = false
- let afterCalled = false
-
- app.use(function (s, opts, done) {
- t.notOk(afterCalled, 'after not called')
- pluginLoaded = true
- done(new Error('kaboom'))
- })
-
- app.after(function () {
- t.ok(pluginLoaded, 'afterred!')
- afterCalled = true
- })
-
- app.ready(function (err) {
- t.ok(err)
- t.ok(afterCalled, 'after called')
- t.ok(pluginLoaded, 'plugin loaded')
- })
-})
-
-test('stop loading plugins if it errors', (t) => {
- t.plan(2)
-
- const app = boot()
-
- app.use(function first (server, opts, done) {
- t.pass('first called')
- done(new Error('kaboom'))
- })
-
- app.use(function second (server, opts, done) {
- t.fail('this should never be called')
- })
-
- app.ready((err) => {
- t.equal(err.message, 'kaboom')
- })
-})
-
-test('keep loading if there is an .after', (t) => {
- t.plan(4)
-
- const app = boot()
-
- app.use(function first (server, opts, done) {
- t.pass('first called')
- done(new Error('kaboom'))
- })
-
- app.after(function (err) {
- t.equal(err.message, 'kaboom')
- })
-
- app.use(function second (server, opts, done) {
- t.pass('second called')
- done()
- })
-
- app.ready((err) => {
- t.error(err)
- })
-})
-
-test('do not load nested plugin if parent errors', (t) => {
- t.plan(4)
-
- const app = boot()
-
- app.use(function first (server, opts, done) {
- t.pass('first called')
-
- server.use(function second (_, opts, done) {
- t.fail('this should never be called')
- })
-
- done(new Error('kaboom'))
- })
-
- app.after(function (err) {
- t.equal(err.message, 'kaboom')
- })
-
- app.use(function third (server, opts, done) {
- t.pass('third called')
- done()
- })
-
- app.ready((err) => {
- t.error(err)
- })
-})
-
-test('.after nested', (t) => {
- t.plan(4)
-
- const app = boot()
-
- app.use(function outer (app, opts, done) {
- app.use(function first (app, opts, done) {
- t.pass('first called')
- done(new Error('kaboom'))
- })
-
- app.after(function (err) {
- t.equal(err.message, 'kaboom')
- })
-
- app.use(function second (app, opts, done) {
- t.pass('second called')
- done()
- })
-
- done()
- })
-
- app.ready((err) => {
- t.error(err)
- })
-})
-
-test('nested error', (t) => {
- t.plan(4)
-
- const app = boot()
-
- app.use(function outer (app, opts, done) {
- app.use(function first (app, opts, done) {
- t.pass('first called')
- done(new Error('kaboom'))
- })
-
- app.use(function second (app, opts, done) {
- t.fail('this should never be called')
- })
-
- done()
- })
-
- app.after(function (err) {
- t.equal(err.message, 'kaboom')
- })
-
- app.use(function third (server, opts, done) {
- t.pass('third called')
- done()
- })
-
- app.ready((err) => {
- t.error(err)
- })
-})
-
-test('preReady event', (t) => {
- t.plan(4)
-
- const app = boot()
- const order = [1, 2]
-
- app.use(function first (server, opts, done) {
- t.pass('first called')
- done()
- })
-
- app.use(function second (server, opts, done) {
- t.pass('second called')
- done()
- })
-
- app.on('preReady', () => {
- t.equal(order.shift(), 1)
- })
-
- app.ready(() => {
- t.equal(order.shift(), 2)
- })
-})
-
-test('preReady event (multiple)', (t) => {
- t.plan(6)
-
- const app = boot()
- const order = [1, 2, 3, 4]
-
- app.use(function first (server, opts, done) {
- t.pass('first called')
- done()
- })
-
- app.use(function second (server, opts, done) {
- t.pass('second called')
- done()
- })
-
- app.on('preReady', () => {
- t.equal(order.shift(), 1)
- })
-
- app.on('preReady', () => {
- t.equal(order.shift(), 2)
- })
-
- app.on('preReady', () => {
- t.equal(order.shift(), 3)
- })
-
- app.ready(() => {
- t.equal(order.shift(), 4)
- })
-})
-
-test('preReady event (nested)', (t) => {
- t.plan(6)
-
- const app = boot()
- const order = [1, 2, 3, 4]
-
- app.use(function first (server, opts, done) {
- t.pass('first called')
- done()
- })
-
- app.use(function second (server, opts, done) {
- t.pass('second called')
-
- server.on('preReady', () => {
- t.equal(order.shift(), 3)
- })
-
- done()
- })
-
- app.on('preReady', () => {
- t.equal(order.shift(), 1)
- })
-
- app.on('preReady', () => {
- t.equal(order.shift(), 2)
- })
-
- app.ready(() => {
- t.equal(order.shift(), 4)
- })
-})
-
-test('preReady event (errored)', (t) => {
- t.plan(5)
-
- const app = boot()
- const order = [1, 2, 3]
-
- app.use(function first (server, opts, done) {
- t.pass('first called')
- done(new Error('kaboom'))
- })
-
- app.use(function second (server, opts, done) {
- t.fail('We should not be here')
- })
-
- app.on('preReady', () => {
- t.equal(order.shift(), 1)
- })
-
- app.on('preReady', () => {
- t.equal(order.shift(), 2)
- })
-
- app.ready((err) => {
- t.ok(err)
- t.equal(order.shift(), 3)
- })
-})
-
-test('after return self', (t) => {
- t.plan(6)
-
- const app = boot()
- let pluginLoaded = false
- let afterCalled = false
- let second = false
-
- app.use(function (s, opts, done) {
- t.notOk(afterCalled, 'after not called')
- pluginLoaded = true
- done()
- })
-
- app.after(function () {
- t.ok(pluginLoaded, 'afterred!')
- afterCalled = true
- // happens with after(() => app.use(..))
- return app
- })
-
- app.use(function (s, opts, done) {
- t.ok(afterCalled, 'after called')
- second = true
- done()
- })
-
- app.on('start', () => {
- t.ok(afterCalled, 'after called')
- t.ok(pluginLoaded, 'plugin loaded')
- t.ok(second, 'second plugin loaded')
- })
-})
-
-test('after 1 param swallows errors with server and timeout', (t) => {
- t.plan(3)
-
- const server = {}
- boot(server, { autostart: false, timeout: 1000 })
-
- server.use(function first (server, opts, done) {
- t.pass('first called')
- done(new Error('kaboom'))
- })
-
- server.use(function second (server, opts, done) {
- t.fail('We should not be here')
- })
-
- server.after(function (err) {
- t.ok(err)
- })
-
- server.ready(function (err) {
- t.error(err)
- })
-})
diff --git a/test/after-pass-through.test.js b/test/after-pass-through.test.js
deleted file mode 100644
index 34efeaa..0000000
--- a/test/after-pass-through.test.js
+++ /dev/null
@@ -1,33 +0,0 @@
-'use strict'
-
-const { test } = require('node:test')
-const boot = require('..')
-
-test('proper support for after with a passed async function in wrapped mode', (t, testCompleted) => {
- const app = {}
- boot(app)
-
- t.plan(5)
-
- const e = new Error('kaboom')
-
- app.use(function (f, opts) {
- return Promise.reject(e)
- }).after(function (err, cb) {
- t.assert.deepStrictEqual(err, e)
- cb(err)
- }).after(function () {
- t.assert.ok('this is just called')
- }).after(function (err, cb) {
- t.assert.deepStrictEqual(err, e)
- cb(err)
- })
-
- app.ready().then(() => {
- t.assert.fail('this should not be called')
- }).catch(err => {
- t.assert.ok(err)
- t.assert.strictEqual(err.message, 'kaboom')
- testCompleted()
- })
-})
diff --git a/test/after-self-promise.test.js b/test/after-self-promise.test.js
deleted file mode 100644
index 70791c5..0000000
--- a/test/after-self-promise.test.js
+++ /dev/null
@@ -1,20 +0,0 @@
-'use strict'
-
-const { test } = require('node:test')
-const boot = require('..')
-
-test('after does not await itself', async (t) => {
- t.plan(3)
-
- const app = {}
- boot(app)
-
- app.use(async (app) => {
- t.assert.ok('plugin init')
- })
- app.after(() => app)
- t.assert.ok('reachable')
-
- await app.ready()
- t.assert.ok('reachable')
-})
diff --git a/test/after-throw.test.js b/test/after-throw.test.js
deleted file mode 100644
index c06e725..0000000
--- a/test/after-throw.test.js
+++ /dev/null
@@ -1,24 +0,0 @@
-'use strict'
-
-const { test } = require('tap')
-const boot = require('..')
-
-test('catched error by Promise.reject', (t) => {
- const app = boot()
- t.plan(2)
-
- t.threw = function (err) {
- t.equal(err.message, 'kaboom2')
- }
-
- app.use(function (f, opts) {
- return Promise.reject(new Error('kaboom'))
- }).after(function (err) {
- t.equal(err.message, 'kaboom')
- throw new Error('kaboom2')
- })
-
- app.ready(function () {
- t.fail('the ready callback should never be called')
- })
-})
diff --git a/test/after-use-after.test.js b/test/after-use-after.test.js
deleted file mode 100644
index d0ab596..0000000
--- a/test/after-use-after.test.js
+++ /dev/null
@@ -1,90 +0,0 @@
-'use strict'
-
-const { test } = require('tap')
-const boot = require('..')
-const app = {}
-
-boot(app)
-
-test('multi after', (t) => {
- t.plan(6)
-
- app.use(function (f, opts, cb) {
- cb()
- }).after(() => {
- t.pass('this is just called')
-
- app.use(function (f, opts, cb) {
- t.pass('this is just called')
- cb()
- })
- }).after(function () {
- t.pass('this is just called')
- app.use(function (f, opts, cb) {
- t.pass('this is just called')
- cb()
- })
- }).after(function (err, cb) {
- t.pass('this is just called')
- cb(err)
- })
-
- app.ready().then(() => {
- t.pass('ready')
- }).catch(() => {
- t.fail('this should not be called')
- })
-})
-
-test('after grouping - use called after after called', (t) => {
- t.plan(9)
- const app = {}
- boot(app)
-
- const TEST_VALUE = {}
- const OTHER_TEST_VALUE = {}
- const NEW_TEST_VALUE = {}
-
- const sO = (fn) => {
- fn[Symbol.for('skip-override')] = true
- return fn
- }
-
- app.use(sO(function (f, options, next) {
- f.test = TEST_VALUE
-
- next()
- }))
-
- app.after(function (err, f, done) {
- t.error(err)
- t.equal(f.test, TEST_VALUE)
-
- f.test2 = OTHER_TEST_VALUE
- done()
- })
-
- app.use(sO(function (f, options, next) {
- t.equal(f.test, TEST_VALUE)
- t.equal(f.test2, OTHER_TEST_VALUE)
-
- f.test3 = NEW_TEST_VALUE
-
- next()
- }))
-
- app.after(function (err, f, done) {
- t.error(err)
- t.equal(f.test, TEST_VALUE)
- t.equal(f.test2, OTHER_TEST_VALUE)
- t.equal(f.test3, NEW_TEST_VALUE)
- done()
- })
-
- app.ready().then(() => {
- t.pass('ready')
- }).catch((e) => {
- console.log(e)
- t.fail('this should not be called')
- })
-})
diff --git a/test/async-await.test.js b/test/async-await.test.js
deleted file mode 100644
index fa2386f..0000000
--- a/test/async-await.test.js
+++ /dev/null
@@ -1,323 +0,0 @@
-'use strict'
-
-/* eslint no-prototype-builtins: off */
-
-const { test } = require('node:test')
-const { setTimeout: sleep } = require('node:timers/promises')
-
-const boot = require('..')
-
-test('one level', async (t) => {
- t.plan(14)
-
- const app = boot()
- let firstLoaded = false
- let secondLoaded = false
- let thirdLoaded = false
-
- app.use(first)
- app.use(third)
-
- async function first (s, opts) {
- t.assert.strictEqual(firstLoaded, false, 'first is not loaded')
- t.assert.strictEqual(secondLoaded, false, 'second is not loaded')
- t.assert.strictEqual(thirdLoaded, false, 'third is not loaded')
- firstLoaded = true
- s.use(second)
- }
-
- async function second (s, opts) {
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.strictEqual(secondLoaded, false, 'second is not loaded')
- t.assert.strictEqual(thirdLoaded, false, 'third is not loaded')
- secondLoaded = true
- }
-
- async function third (s, opts) {
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.ok(secondLoaded, 'second is loaded')
- t.assert.strictEqual(thirdLoaded, false, 'third is not loaded')
- thirdLoaded = true
- }
-
- const readyContext = await app.ready()
-
- t.assert.deepStrictEqual(app, readyContext)
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.ok(secondLoaded, 'second is loaded')
- t.assert.ok(thirdLoaded, 'third is loaded')
- t.assert.ok('booted')
-})
-
-test('multiple reentrant plugin loading', async (t) => {
- t.plan(31)
-
- const app = boot()
- let firstLoaded = false
- let secondLoaded = false
- let thirdLoaded = false
- let fourthLoaded = false
- let fifthLoaded = false
-
- app.use(first)
- app.use(fifth)
-
- async function first (s, opts) {
- t.assert.strictEqual(firstLoaded, false, 'first is not loaded')
- t.assert.strictEqual(secondLoaded, false, 'second is not loaded')
- t.assert.strictEqual(thirdLoaded, false, 'third is not loaded')
- t.assert.strictEqual(fourthLoaded, false, 'fourth is not loaded')
- t.assert.strictEqual(fifthLoaded, false, 'fifth is not loaded')
- firstLoaded = true
- s.use(second)
- }
-
- async function second (s, opts) {
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.strictEqual(secondLoaded, false, 'second is not loaded')
- t.assert.strictEqual(thirdLoaded, false, 'third is not loaded')
- t.assert.strictEqual(fourthLoaded, false, 'fourth is not loaded')
- t.assert.strictEqual(fifthLoaded, false, 'fifth is not loaded')
- secondLoaded = true
- s.use(third)
- await sleep(10)
- s.use(fourth)
- }
-
- async function third (s, opts) {
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.ok(secondLoaded, 'second is loaded')
- t.assert.strictEqual(thirdLoaded, false, 'third is not loaded')
- t.assert.strictEqual(fourthLoaded, false, 'fourth is not loaded')
- t.assert.strictEqual(fifthLoaded, false, 'fifth is not loaded')
- thirdLoaded = true
- }
-
- async function fourth (s, opts) {
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.ok(secondLoaded, 'second is loaded')
- t.assert.ok(thirdLoaded, 'third is loaded')
- t.assert.strictEqual(fourthLoaded, false, 'fourth is not loaded')
- t.assert.strictEqual(fifthLoaded, false, 'fifth is not loaded')
- fourthLoaded = true
- }
-
- async function fifth (s, opts) {
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.ok(secondLoaded, 'second is loaded')
- t.assert.ok(thirdLoaded, 'third is loaded')
- t.assert.ok(fourthLoaded, 'fourth is loaded')
- t.assert.strictEqual(fifthLoaded, false, 'fifth is not loaded')
- fifthLoaded = true
- }
-
- await app.ready()
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.ok(secondLoaded, 'second is loaded')
- t.assert.ok(thirdLoaded, 'third is loaded')
- t.assert.ok(fourthLoaded, 'fourth is loaded')
- t.assert.ok(fifthLoaded, 'fifth is loaded')
- t.assert.ok('booted')
-})
-
-test('async ready plugin registration (errored)', async (t) => {
- t.plan(1)
-
- const app = boot()
-
- app.use(async (server, opts) => {
- await sleep(10)
- throw new Error('kaboom')
- })
-
- try {
- await app.ready()
- t.assert.fail('we should not be here')
- } catch (err) {
- t.assert.strictEqual(err.message, 'kaboom')
- }
-})
-
-test('after', async (t) => {
- t.plan(15)
-
- const app = boot()
- let firstLoaded = false
- let secondLoaded = false
- let thirdLoaded = false
-
- app.use(first)
-
- async function first (s, opts) {
- t.assert.strictEqual(firstLoaded, false, 'first is not loaded')
- t.assert.strictEqual(secondLoaded, false, 'second is not loaded')
- t.assert.strictEqual(thirdLoaded, false, 'third is not loaded')
- firstLoaded = true
- s.after(second)
- s.after(third)
- }
-
- async function second (err) {
- t.assert.ifError(err)
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.strictEqual(secondLoaded, false, 'second is not loaded')
- t.assert.strictEqual(thirdLoaded, false, 'third is not loaded')
- await sleep(10)
- secondLoaded = true
- }
-
- async function third () {
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.ok(secondLoaded, 'second is loaded')
- t.assert.strictEqual(thirdLoaded, false, 'third is not loaded')
- await sleep(10)
- thirdLoaded = true
- }
-
- const readyContext = await app.ready()
-
- t.assert.deepStrictEqual(app, readyContext)
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.ok(secondLoaded, 'second is loaded')
- t.assert.ok(thirdLoaded, 'third is loaded')
- t.assert.ok('booted')
-})
-
-test('after wrapped', async (t) => {
- t.plan(15)
-
- const app = {}
- boot(app)
- let firstLoaded = false
- let secondLoaded = false
- let thirdLoaded = false
-
- app.use(first)
-
- async function first (s, opts) {
- t.assert.strictEqual(firstLoaded, false, 'first is not loaded')
- t.assert.strictEqual(secondLoaded, false, 'second is not loaded')
- t.assert.strictEqual(thirdLoaded, false, 'third is not loaded')
- firstLoaded = true
- s.after(second)
- s.after(third)
- }
-
- async function second (err) {
- t.assert.ifError(err)
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.strictEqual(secondLoaded, false, 'second is not loaded')
- t.assert.strictEqual(thirdLoaded, false, 'third is not loaded')
- await sleep(10)
- secondLoaded = true
- }
-
- async function third () {
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.ok(secondLoaded, 'second is loaded')
- t.assert.strictEqual(thirdLoaded, false, 'third is not loaded')
- await sleep(10)
- thirdLoaded = true
- }
-
- const readyContext = await app.ready()
-
- t.assert.deepStrictEqual(app, readyContext)
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.ok(secondLoaded, 'second is loaded')
- t.assert.ok(thirdLoaded, 'third is loaded')
- t.assert.ok('booted')
-})
-
-test('promise plugins', async (t) => {
- t.plan(14)
-
- const app = boot()
- let firstLoaded = false
- let secondLoaded = false
- let thirdLoaded = false
-
- app.use(first())
- app.use(third())
-
- async function first () {
- return async function (s, opts) {
- t.assert.strictEqual(firstLoaded, false, 'first is not loaded')
- t.assert.strictEqual(secondLoaded, false, 'second is not loaded')
- t.assert.strictEqual(thirdLoaded, false, 'third is not loaded')
- firstLoaded = true
- s.use(second())
- }
- }
-
- async function second () {
- return async function (s, opts) {
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.strictEqual(secondLoaded, false, 'second is not loaded')
- t.assert.strictEqual(thirdLoaded, false, 'third is not loaded')
- secondLoaded = true
- }
- }
-
- async function third () {
- return async function (s, opts) {
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.ok(secondLoaded, 'second is loaded')
- t.assert.strictEqual(thirdLoaded, false, 'third is not loaded')
- thirdLoaded = true
- }
- }
-
- const readyContext = await app.ready()
-
- t.assert.deepStrictEqual(app, readyContext)
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.ok(secondLoaded, 'second is loaded')
- t.assert.ok(thirdLoaded, 'third is loaded')
- t.assert.ok('booted')
-})
-
-test('skip override with promise', async (t) => {
- t.plan(3)
-
- const server = { my: 'server' }
- const app = boot(server)
-
- app.override = function (s, func) {
- t.assert.ok('override called')
-
- if (func[Symbol.for('skip-override')]) {
- return s
- }
- return Object.create(s)
- }
-
- app.use(first())
-
- async function first () {
- async function fn (s, opts) {
- t.assert.deepStrictEqual(s, server)
- t.assert.strictEqual(Object.prototype.isPrototypeOf.call(server, s), false)
- }
-
- fn[Symbol.for('skip-override')] = true
-
- return fn
- }
-
- await app.ready()
-})
-
-test('ready queue error', async (t) => {
- const app = boot()
- app.use(first)
-
- async function first (s, opts) {}
-
- app.ready(function (_, worker, done) {
- const error = new Error('kaboom')
- done(error)
- })
-
- await t.assert.rejects(app.ready(), { message: 'kaboom' })
-})
diff --git a/test/avvio-on-close.test.js b/test/avvio-on-close.test.js
new file mode 100644
index 0000000..c105145
--- /dev/null
+++ b/test/avvio-on-close.test.js
@@ -0,0 +1,85 @@
+const test = require('node:test').test
+const Avvio = require('../avvio')
+
+test('Avvio#onClose', async (t) => {
+ const test = t.test.bind(t)
+
+ await test('() => void', (t, done) => {
+ t.plan(1)
+ const avvio = Avvio()
+ avvio.onClose(() => {
+ t.assert.ok(true, 'executed')
+ })
+ avvio.close(() => done())
+ })
+
+ await test('() => Promise', (t, done) => {
+ t.plan(1)
+ const avvio = Avvio()
+ avvio.onClose(() => {
+ t.assert.ok(true, 'executed')
+ return Promise.resolve()
+ })
+ avvio.close(() => done())
+ })
+
+ await test('(context) => void', (t, done) => {
+ t.plan(2)
+ const avvio = Avvio()
+ avvio.onClose((context) => {
+ t.assert.strictEqual(context, avvio)
+ t.assert.ok(true, 'executed')
+ })
+ avvio.close(() => done())
+ })
+
+ await test('(context) => Promise', (t, done) => {
+ t.plan(2)
+ const avvio = Avvio()
+ avvio.onClose((context) => {
+ t.assert.strictEqual(context, avvio)
+ t.assert.ok(true, 'executed')
+ return Promise.resolve()
+ })
+ avvio.close(() => done())
+ })
+
+ await test('(context, done) => void', (t, done) => {
+ t.plan(2)
+ const avvio = Avvio()
+ avvio.onClose((context, done) => {
+ t.assert.strictEqual(context, avvio)
+ t.assert.ok(true, 'executed')
+ setImmediate(done)
+ })
+ avvio.close(() => done())
+ })
+
+ await test('(context, done) => Promise', (t, done) => {
+ t.plan(2)
+ const avvio = Avvio()
+ avvio.onClose((context, done) => {
+ t.assert.strictEqual(context, avvio)
+ t.assert.ok(true, 'executed')
+ return Promise.resolve()
+ })
+ avvio.close(() => done())
+ })
+
+ await test('execution order', (t, done) => {
+ t.plan(3)
+ const counter = 0
+ const avvio = Avvio()
+ avvio.onClose(() => {
+ t.assert.strictEqual(counter, 3)
+ })
+ avvio.onClose(async () => {
+ t.assert.strictEqual(counter, 2)
+ })
+ avvio.onClose(() => {
+ t.assert.strictEqual(counter, 1)
+ return Promise.resolve()
+ })
+ avvio.close(() => done())
+ })
+})
diff --git a/test/avvio-ready.test.js b/test/avvio-ready.test.js
new file mode 100644
index 0000000..1a170dd
--- /dev/null
+++ b/test/avvio-ready.test.js
@@ -0,0 +1,83 @@
+const test = require('node:test').test
+const Avvio = require('../avvio')
+
+test('Avvio#ready', async (t) => {
+ const test = t.test.bind(t)
+ await test('await ready()', async (t) => {
+ t.plan(4)
+ let counter = 0
+ const avvio = Avvio()
+ avvio.use((context, _, done) => {
+ counter++
+ t.assert.strictEqual(counter, 1)
+ context.use((context, _, done) => {
+ counter++
+ t.assert.strictEqual(counter, 2)
+ done()
+ })
+ done()
+ })
+ const context = await avvio.ready()
+ t.assert.strictEqual(counter, 2)
+ t.assert.deepStrictEqual(context, avvio)
+ })
+
+ await test('await ready()', async (t) => {
+ t.plan(4)
+ let counter = 0
+ const avvio = Avvio()
+ avvio.use((context, _, done) => {
+ counter++
+ t.assert.strictEqual(counter, 1)
+ context.use((context, _, done) => {
+ counter++
+ t.assert.strictEqual(counter, 2)
+ setImmediate(done)
+ })
+ setImmediate(done)
+ })
+ const context = await avvio.ready()
+ t.assert.strictEqual(counter, 2)
+ t.assert.deepStrictEqual(context, avvio)
+ })
+
+ await test('ready(callback)', (t, done) => {
+ t.plan(3)
+ let counter = 0
+ const avvio = Avvio()
+ avvio.use((context, _, done) => {
+ counter++
+ t.assert.strictEqual(counter, 1)
+ context.use((context, _, done) => {
+ counter++
+ t.assert.strictEqual(counter, 2)
+ done()
+ })
+ done()
+ })
+ avvio.ready(() => {
+ t.assert.strictEqual(counter, 2)
+ done()
+ })
+ })
+
+ await test('ready(callback)', (t, done) => {
+ t.plan(3)
+ let counter = 0
+ const avvio = Avvio()
+ avvio.use((context, _, done) => {
+ counter++
+ t.assert.strictEqual(counter, 1)
+ context.use((context, _, done) => {
+ counter++
+ t.assert.strictEqual(counter, 2)
+ setImmediate(done)
+ })
+ setImmediate(done)
+ })
+ avvio.ready(() => {
+ t.assert.strictEqual(counter, 2)
+ done()
+ })
+ })
+})
diff --git a/test/await-after.test.js b/test/await-after.test.js
deleted file mode 100644
index b3c3b21..0000000
--- a/test/await-after.test.js
+++ /dev/null
@@ -1,448 +0,0 @@
-'use strict'
-
-const { test } = require('node:test')
-const boot = require('..')
-const { setTimeout: sleep } = require('node:timers/promises')
-const fs = require('node:fs/promises')
-const path = require('node:path')
-
-test('await after - nested plugins with same tick callbacks', async (t) => {
- const app = {}
- boot(app)
-
- let secondLoaded = false
-
- app.use(async (app) => {
- t.assert.ok('plugin init')
- app.use(async () => {
- t.assert.ok('plugin2 init')
- await sleep(1)
- secondLoaded = true
- })
- })
- await app.after()
- t.assert.ok('reachable')
- t.assert.ok(secondLoaded)
-
- await app.ready()
- t.assert.ok('reachable')
-})
-
-test('await after without server', async (t) => {
- const app = boot()
-
- let secondLoaded = false
-
- app.use(async (app) => {
- t.assert.ok('plugin init')
- app.use(async () => {
- t.assert.ok('plugin2 init')
- await sleep(1)
- secondLoaded = true
- })
- })
- await app.after()
- t.assert.ok('reachable')
- t.assert.ok(secondLoaded)
-
- await app.ready()
- t.assert.ok('reachable')
-})
-
-test('await after with cb functions', async (t) => {
- const app = boot()
- let secondLoaded = false
- let record = ''
-
- app.use(async (app) => {
- t.assert.ok('plugin init')
- record += 'plugin|'
- app.use(async () => {
- t.assert.ok('plugin2 init')
- record += 'plugin2|'
- await sleep(1)
- secondLoaded = true
- })
- })
- await app.after(() => {
- record += 'after|'
- })
- t.assert.ok('reachable')
- t.assert.ok(secondLoaded)
- record += 'ready'
- await app.ready()
- t.assert.ok('reachable')
- t.assert.strictEqual(record, 'plugin|plugin2|after|ready')
-})
-
-test('await after - nested plugins with future tick callbacks', async (t) => {
- const app = {}
- boot(app)
-
- t.plan(4)
-
- app.use((f, opts, cb) => {
- t.assert.ok('plugin init')
- app.use((f, opts, cb) => {
- t.assert.ok('plugin2 init')
- setImmediate(cb)
- })
- setImmediate(cb)
- })
- await app.after()
- t.assert.ok('reachable')
-
- await app.ready()
- t.assert.ok('reachable')
-})
-
-test('await after - nested async function plugins', async (t) => {
- const app = {}
- boot(app)
-
- t.plan(5)
-
- app.use(async (f, opts) => {
- t.assert.ok('plugin init')
- await app.use(async (f, opts) => {
- t.assert.ok('plugin2 init')
- })
- t.assert.ok('reachable')
- })
- await app.after()
- t.assert.ok('reachable')
-
- await app.ready()
- t.assert.ok('reachable')
-})
-
-test('await after - promise resolves to undefined', async (t) => {
- const app = {}
- boot(app)
-
- t.plan(4)
-
- app.use(async (f, opts, cb) => {
- app.use((f, opts, cb) => {
- t.assert.ok('plugin init')
- cb()
- })
- const instance = await app.after()
- t.assert.strictEqual(instance, undefined)
- })
- t.assert.ok('reachable')
-
- await app.ready()
- t.assert.ok('reachable')
-})
-
-test('await after - promise returning function plugins + promise chaining', async (t) => {
- const app = {}
- boot(app)
-
- t.plan(6)
-
- app.use((f, opts) => {
- t.assert.ok('plugin init')
- return app.use((f, opts) => {
- t.assert.ok('plugin2 init')
- return Promise.resolve()
- }).then((f2) => {
- t.assert.deepStrictEqual(f2, f)
- return 'test'
- }).then((val) => {
- t.assert.strictEqual(val, 'test')
- })
- })
- await app.after()
- t.assert.ok('reachable')
-
- await app.ready()
- t.assert.ok('reachable')
-})
-
-test('await after - error handling, async throw', async (t) => {
- const app = {}
- boot(app)
-
- t.plan(2)
-
- const e = new Error('kaboom')
-
- app.use(async (f, opts) => {
- throw Error('kaboom')
- })
-
- await t.assert.rejects(app.after(), e)
-
- await t.assert.rejects(() => app.ready(), Error('kaboom'))
-})
-
-test('await after - error handling, async throw, nested', async (t) => {
- const app = {}
- boot(app)
-
- t.plan(2)
-
- const e = new Error('kaboom')
-
- app.use(async (f, opts) => {
- app.use(async (f, opts) => {
- throw e
- })
- })
-
- await t.assert.rejects(app.after())
- await t.assert.rejects(() => app.ready(), e)
-})
-
-test('await after - error handling, same tick cb err', async (t) => {
- const app = {}
- boot(app)
-
- t.plan(2)
-
- app.use((f, opts, cb) => {
- cb(Error('kaboom'))
- })
- await t.assert.rejects(app.after())
- await t.assert.rejects(app.ready(), Error('kaboom'))
-})
-
-test('await after - error handling, same tick cb err, nested', async (t) => {
- const app = {}
- boot(app)
-
- t.plan(2)
-
- app.use((f, opts, cb) => {
- app.use((f, opts, cb) => {
- cb(Error('kaboom'))
- })
- cb()
- })
-
- await t.assert.rejects(app.after())
- await t.assert.rejects(app.ready(), Error('kaboom'))
-})
-
-test('await after - error handling, future tick cb err', async (t) => {
- const app = {}
- boot(app)
-
- t.plan(2)
-
- app.use((f, opts, cb) => {
- setImmediate(() => { cb(Error('kaboom')) })
- })
-
- await t.assert.rejects(app.after())
- await t.assert.rejects(app.ready(), Error('kaboom'))
-})
-
-test('await after - error handling, future tick cb err, nested', async (t) => {
- const app = {}
- boot(app)
-
- t.plan(2)
-
- app.use((f, opts, cb) => {
- app.use((f, opts, cb) => {
- setImmediate(() => { cb(Error('kaboom')) })
- })
- cb()
- })
- await t.assert.rejects(app.after(), Error('kaboom'))
- await t.assert.rejects(app.ready(), Error('kaboom'))
-})
-
-test('await after complex scenario', async (t) => {
- const app = {}
- boot(app)
- t.plan(16)
-
- let firstLoaded = false
- let secondLoaded = false
- let thirdLoaded = false
- let fourthLoaded = false
-
- app.use(first)
- await app.after()
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.strictEqual(secondLoaded, false, 'second is not loaded')
- t.assert.strictEqual(thirdLoaded, false, 'third is not loaded')
- t.assert.strictEqual(fourthLoaded, false, 'fourth is not loaded')
- app.use(second)
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.strictEqual(secondLoaded, false, 'second is not loaded')
- t.assert.strictEqual(thirdLoaded, false, 'third is not loaded')
- t.assert.strictEqual(fourthLoaded, false, 'fourth is not loaded')
- app.use(third)
- await app.after()
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.ok(secondLoaded, 'second is loaded')
- t.assert.ok(thirdLoaded, 'third is loaded')
- t.assert.ok(fourthLoaded, 'fourth is loaded')
- await app.ready()
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.ok(secondLoaded, 'second is loaded')
- t.assert.ok(thirdLoaded, 'third is loaded')
- t.assert.ok(fourthLoaded, 'fourth is loaded')
-
- async function first () {
- firstLoaded = true
- }
-
- async function second () {
- secondLoaded = true
- }
-
- async function third (app) {
- thirdLoaded = true
- app.use(fourth)
- }
-
- async function fourth () {
- fourthLoaded = true
- }
-})
-
-test('without autostart and sync/async plugin mix', async (t) => {
- const app = {}
- boot(app, { autostart: false })
- t.plan(21)
-
- let firstLoaded = false
- let secondLoaded = false
- let thirdLoaded = false
- let fourthLoaded = false
-
- app.use(first)
- await app.after()
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.strictEqual(secondLoaded, false, 'second is not loaded')
- t.assert.strictEqual(thirdLoaded, false, 'third is not loaded')
- t.assert.strictEqual(fourthLoaded, false, 'fourth is not loaded')
-
- app.use(second)
- await app.after()
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.ok(secondLoaded, 'second is loaded')
- t.assert.strictEqual(thirdLoaded, false, 'third is not loaded')
- t.assert.strictEqual(fourthLoaded, false, 'fourth is not loaded')
-
- await sleep(10)
-
- app.use(third)
- await app.after()
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.ok(secondLoaded, 'second is loaded')
- t.assert.ok(thirdLoaded, 'third is loaded')
- t.assert.strictEqual(fourthLoaded, false, 'fourth is not loaded')
-
- app.use(fourth)
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.ok(secondLoaded, 'second is loaded')
- t.assert.ok(thirdLoaded, 'third is loaded')
- t.assert.strictEqual(fourthLoaded, false, 'fourth is not loaded')
-
- await app.after()
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.ok(secondLoaded, 'second is loaded')
- t.assert.ok(thirdLoaded, 'third is loaded')
- t.assert.ok(fourthLoaded, 'fourth is loaded')
-
- await app.ready()
-
- async function first () {
- firstLoaded = true
- }
-
- async function second () {
- const contents = await fs.readFile(path.join(__dirname, 'fixtures', 'dummy.txt'), 'utf-8')
- t.assert.strictEqual(contents, 'hello, world!')
- secondLoaded = true
- }
-
- async function third () {
- await sleep(10)
- thirdLoaded = true
- }
-
- function fourth (server, opts, done) {
- fourthLoaded = true
- done()
- }
-})
-
-test('without autostart', async (t) => {
- const app = {}
- boot(app, { autostart: false })
- let firstLoaded = false
- let secondLoaded = false
- let thirdLoaded = false
-
- app.use(async function first (app) {
- firstLoaded = true
- app.use(async () => {
- await sleep(1)
- secondLoaded = true
- })
- })
-
- await app.after()
- t.assert.ok(firstLoaded)
- t.assert.ok(secondLoaded)
-
- await app.use(async () => {
- thirdLoaded = true
- })
-
- t.assert.ok(thirdLoaded)
-
- await app.ready()
-})
-
-test('without autostart and with override', async (t) => {
- const app = {}
- const _ = boot(app, { autostart: false })
- let count = 0
-
- _.override = function (s) {
- const res = Object.create(s)
- res.count = ++count
-
- return res
- }
-
- app.use(async function first (app) {
- t.assert.strictEqual(app.count, 1)
- app.use(async (app) => {
- t.assert.strictEqual(app.count, 2)
- await app.after()
- })
- })
-
- await app.after()
-
- await app.use(async (app) => {
- t.assert.strictEqual(app.count, 3)
- })
-
- await app.ready()
-})
-
-test('stop processing after errors', async (t) => {
- t.plan(2)
-
- const app = boot()
-
- try {
- await app.use(async function first (app) {
- t.assert.ok('first should be loaded')
- throw new Error('kaboom')
- })
- } catch (e) {
- t.assert.strictEqual(e.message, 'kaboom')
- }
-})
diff --git a/test/await-self.test.js b/test/await-self.test.js
deleted file mode 100644
index 8d8c990..0000000
--- a/test/await-self.test.js
+++ /dev/null
@@ -1,31 +0,0 @@
-'use strict'
-
-const { test } = require('tap')
-const boot = require('..')
-
-test('await self', async (t) => {
- const app = {}
- boot(app)
-
- t.equal(await app, app)
-})
-
-test('await self three times', async (t) => {
- const app = {}
- boot(app)
-
- t.equal(await app, app)
- t.equal(await app, app)
- t.equal(await app, app)
-})
-
-test('await self within plugin', async (t) => {
- const app = {}
- boot(app)
-
- app.use(async (f) => {
- t.equal(await f, f)
- })
-
- t.equal(await app, app)
-})
diff --git a/test/await-use.test.js b/test/await-use.test.js
deleted file mode 100644
index f8adee8..0000000
--- a/test/await-use.test.js
+++ /dev/null
@@ -1,294 +0,0 @@
-'use strict'
-
-const { test } = require('tap')
-const { promisify } = require('node:util')
-const sleep = promisify(setTimeout)
-const boot = require('..')
-
-test('await use - nested plugins with same tick callbacks', async (t) => {
- const app = {}
- boot(app)
-
- t.plan(4)
-
- await app.use((f, opts, cb) => {
- t.pass('plugin init')
- app.use((f, opts, cb) => {
- t.pass('plugin2 init')
- cb()
- })
- cb()
- })
- t.pass('reachable')
-
- await app.ready()
- t.pass('reachable')
-})
-
-test('await use - nested plugins with future tick callbacks', async (t) => {
- const app = {}
- boot(app)
-
- t.plan(4)
-
- await app.use((f, opts, cb) => {
- t.pass('plugin init')
- app.use((f, opts, cb) => {
- t.pass('plugin2 init')
- setImmediate(cb)
- })
- setImmediate(cb)
- })
- t.pass('reachable')
-
- await app.ready()
- t.pass('reachable')
-})
-
-test('await use - nested async function plugins', async (t) => {
- const app = {}
- boot(app)
-
- t.plan(5)
-
- await app.use(async (f, opts) => {
- t.pass('plugin init')
- await app.use(async (f, opts) => {
- t.pass('plugin2 init')
- })
- t.pass('reachable')
- })
- t.pass('reachable')
-
- await app.ready()
- t.pass('reachable')
-})
-
-test('await use - promise returning function plugins + promise chaining', async (t) => {
- const app = {}
- boot(app)
-
- t.plan(6)
-
- await app.use((f, opts) => {
- t.pass('plugin init')
- return app.use((f, opts) => {
- t.pass('plugin2 init')
- return Promise.resolve()
- }).then(() => {
- t.pass('reachable')
- return 'test'
- }).then((val) => {
- t.equal(val, 'test')
- })
- })
- t.pass('reachable')
-
- await app.ready()
- t.pass('reachable')
-})
-
-test('await use - await and use chaining', async (t) => {
- const app = {}
- boot(app)
-
- t.plan(3)
-
- app.use(async (f, opts, cb) => {
- await app.use(async (f, opts) => {
- t.pass('plugin init')
- }).use(async (f, opts) => {
- t.pass('plugin2 init')
- })
- })
-
- await app.ready()
- t.pass('reachable')
-})
-
-function thenableRejects (t, thenable, err, msg) {
- return t.rejects(async () => { await thenable }, err, msg)
-}
-
-test('await use - error handling, async throw', async (t) => {
- const app = {}
- boot(app)
-
- t.plan(2)
-
- await thenableRejects(t, app.use(async (f, opts) => {
- throw Error('kaboom')
- }), Error('kaboom'))
-
- await t.rejects(app.ready(), Error('kaboom'))
-})
-
-test('await use - error handling, async throw, nested', async (t) => {
- const app = {}
- boot(app)
-
- t.plan(2)
-
- await thenableRejects(t, app.use(async function a (f, opts) {
- await app.use(async function b (f, opts) {
- throw Error('kaboom')
- })
- }, Error('kaboom')), 'b')
-
- t.rejects(() => app.ready(), Error('kaboom'))
-})
-
-test('await use - error handling, same tick cb err', async (t) => {
- const app = {}
- boot(app)
-
- t.plan(2)
-
- await thenableRejects(t, app.use((f, opts, cb) => {
- cb(Error('kaboom'))
- }), Error('kaboom'))
-
- t.rejects(() => app.ready(), Error('kaboom'))
-})
-
-test('await use - error handling, same tick cb err, nested', async (t) => {
- const app = {}
- boot(app)
-
- t.plan(2)
-
- await thenableRejects(t, app.use((f, opts, cb) => {
- app.use((f, opts, cb) => {
- cb(Error('kaboom'))
- })
- cb()
- }), Error('kaboom'))
-
- t.rejects(() => app.ready(), Error('kaboom'))
-})
-
-test('await use - error handling, future tick cb err', async (t) => {
- const app = {}
- boot(app)
-
- t.plan(2)
-
- await thenableRejects(t, app.use((f, opts, cb) => {
- setImmediate(() => { cb(Error('kaboom')) })
- }), Error('kaboom'))
-
- t.rejects(() => app.ready(), Error('kaboom'))
-})
-
-test('await use - error handling, future tick cb err, nested', async (t) => {
- const app = {}
- boot(app)
-
- t.plan(2)
-
- await thenableRejects(t, app.use((f, opts, cb) => {
- app.use((f, opts, cb) => {
- setImmediate(() => { cb(Error('kaboom')) })
- })
- cb()
- }), Error('kaboom'))
-
- t.rejects(() => app.ready(), Error('kaboom'))
-})
-
-test('mixed await use and non-awaited use ', async (t) => {
- const app = {}
- boot(app)
- t.plan(16)
-
- let firstLoaded = false
- let secondLoaded = false
- let thirdLoaded = false
- let fourthLoaded = false
-
- await app.use(first)
- t.ok(firstLoaded, 'first is loaded')
- t.notOk(secondLoaded, 'second is not loaded')
- t.notOk(thirdLoaded, 'third is not loaded')
- t.notOk(fourthLoaded, 'fourth is not loaded')
- app.use(second)
- t.ok(firstLoaded, 'first is loaded')
- t.notOk(secondLoaded, 'second is not loaded')
- t.notOk(thirdLoaded, 'third is not loaded')
- t.notOk(fourthLoaded, 'fourth is not loaded')
- await app.use(third)
- t.ok(firstLoaded, 'first is loaded')
- t.ok(secondLoaded, 'second is loaded')
- t.ok(thirdLoaded, 'third is loaded')
- t.ok(fourthLoaded, 'fourth is loaded')
- await app.ready()
- t.ok(firstLoaded, 'first is loaded')
- t.ok(secondLoaded, 'second is loaded')
- t.ok(thirdLoaded, 'third is loaded')
- t.ok(fourthLoaded, 'fourth is loaded')
-
- async function first () {
- firstLoaded = true
- }
-
- async function second () {
- secondLoaded = true
- }
-
- async function third (app) {
- thirdLoaded = true
- app.use(fourth)
- }
-
- async function fourth () {
- fourthLoaded = true
- }
-})
-
-test('await use - mix of same and future tick callbacks', async (t) => {
- const app = {}
- boot(app, { autostart: false })
- let record = ''
-
- t.plan(4)
-
- await app.use(async function plugin0 () {
- t.pass('plugin0 init')
- record += 'plugin0|'
- })
- await app.use(async function plugin1 () {
- t.pass('plugin1 init')
- await sleep(500)
- record += 'plugin1|'
- })
- await sleep(1)
- await app.use(async function plugin2 () {
- t.pass('plugin2 init')
- await sleep(500)
- record += 'plugin2|'
- })
- record += 'ready'
- t.equal(record, 'plugin0|plugin1|plugin2|ready')
-})
-
-test('await use - fork the promise chain', (t) => {
- t.plan(3)
- const app = {}
- boot(app, { autostart: false })
-
- async function setup () {
- let set = false
- await app.use(async function plugin0 () {
- t.pass('plugin0 init')
- await sleep(500)
- set = true
- })
- t.equal(set, true)
- }
- setup()
-
- app.ready((err, done) => {
- t.error(err)
- done()
- })
-})
diff --git a/test/basic.test.js b/test/basic.test.js
index 9a56e4f..d6b3957 100644
--- a/test/basic.test.js
+++ b/test/basic.test.js
@@ -1,459 +1,98 @@
-'use strict'
-
const { test } = require('node:test')
-const boot = require('..')
-
-test('boot an empty app', (t, testCompleted) => {
- t.plan(1)
- const app = boot()
- app.on('start', () => {
- t.assert.ok(true, 'booted')
- testCompleted()
- })
-})
-
-test('start returns app', (t, testCompleted) => {
- t.plan(1)
- const app = boot({}, { autostart: false })
- app
- .start()
- .ready((err) => {
- t.assert.ifError(err)
- testCompleted()
- })
-})
-
-test('boot an app with a plugin', (t, testCompleted) => {
- t.plan(4)
-
- const app = boot()
- let after = false
-
- app.use(function (server, opts, done) {
- t.assert.deepStrictEqual(server, app, 'the first argument is the server')
- t.assert.deepStrictEqual(opts, {}, 'no options')
- t.assert.ok(after, 'delayed execution')
- done()
- })
-
- after = true
-
- app.on('start', () => {
- t.assert.ok(true, 'booted')
- testCompleted()
- })
-})
-
-test('boot an app with a promisified plugin', (t, testCompleted) => {
- t.plan(4)
-
- const app = boot()
- let after = false
-
- app.use(function (server, opts) {
- t.assert.deepStrictEqual(server, app, 'the first argument is the server')
- t.assert.deepStrictEqual(opts, {}, 'no options')
- t.assert.ok(after, 'delayed execution')
- return Promise.resolve()
- })
-
- after = true
-
- app.on('start', () => {
- t.assert.ok(true, 'booted')
- testCompleted()
- })
-})
-
-test('boot an app with a plugin and a callback /1', (t, testCompleted) => {
- t.plan(2)
-
- const app = boot(() => {
- t.assert.ok(true, 'booted')
- })
-
- app.use(function (server, opts, done) {
- t.assert.ok(true, 'plugin loaded')
- done()
- testCompleted()
- })
-})
-
-test('boot an app with a plugin and a callback /2', (t, testCompleted) => {
- t.plan(2)
+const { Avvio } = require('../avvio')
- const app = boot({}, () => {
- t.assert.ok(true, 'booted')
- })
-
- app.use(function (server, opts, done) {
- t.assert.ok(true, 'plugin loaded')
- done()
- testCompleted()
- })
-})
-
-test('boot a plugin with a custom server', (t, testCompleted) => {
- t.plan(4)
-
- const server = {}
- const app = boot(server)
-
- app.use(function (s, opts, done) {
- t.assert.deepStrictEqual(s, server, 'the first argument is the server')
- t.assert.deepStrictEqual(opts, {}, 'no options')
- done()
- })
+test('Avvio#constructor', async (t) => {
+ const test = t.test.bind(t)
+ await test('()', (t, done) => {
+ t.plan(1)
- app.onClose(() => {
- t.assert.ok(true, 'onClose called')
- testCompleted()
- })
-
- app.on('start', () => {
- app.close(() => {
+ const avvio = Avvio()
+ avvio.once('start', () => {
t.assert.ok(true, 'booted')
+ done()
})
})
-})
-
-test('custom instance should inherits avvio methods /1', (t, testCompleted) => {
- t.plan(6)
- const server = {}
- const app = boot(server, {})
+ await test('({ autostart: false })', (t, done) => {
+ t.plan(2)
- server.use(function (s, opts, done) {
- t.assert.deepStrictEqual(s, server, 'the first argument is the server')
- t.assert.deepStrictEqual(opts, {}, 'no options')
- done()
- }).after(() => {
- t.assert.ok(true, 'after called')
- })
-
- server.onClose(() => {
- t.assert.ok(true, 'onClose called')
- testCompleted()
- })
-
- server.ready(() => {
- t.assert.ok(true, 'ready called')
- })
-
- app.on('start', () => {
- server.close(() => {
+ const avvio = Avvio({}, { autostart: false })
+ avvio.once('start', () => {
t.assert.ok(true, 'booted')
+ done()
})
- })
-})
-
-test('custom instance should inherits avvio methods /2', (t, testCompleted) => {
- t.plan(6)
-
- const server = {}
- const app = new boot(server, {}) // eslint-disable-line new-cap
-
- server.use(function (s, opts, done) {
- t.assert.deepStrictEqual(s, server, 'the first argument is the server')
- t.assert.deepStrictEqual(opts, {}, 'no options')
- done()
- }).after(() => {
- t.assert.ok(true, 'after called')
- })
-
- server.onClose(() => {
- t.assert.ok(true, 'onClose called')
- testCompleted()
- })
-
- server.ready(() => {
- t.assert.ok(true, 'ready called')
+ const app = avvio.start()
+ t.assert.deepStrictEqual(avvio, app, 'chainable')
})
- app.on('start', () => {
- server.close(() => {
+ await test('(callback)', (t, done) => {
+ t.plan(3)
+ const avvio = Avvio(() => {
t.assert.ok(true, 'booted')
+ done()
})
- })
-})
-
-test('boot a plugin with options', (t, testCompleted) => {
- t.plan(3)
-
- const server = {}
- const app = boot(server)
- const myOpts = {
- hello: 'world'
- }
-
- app.use(function (s, opts, done) {
- t.assert.deepStrictEqual(s, server, 'the first argument is the server')
- t.assert.deepStrictEqual(opts, myOpts, 'passed options')
- done()
- }, myOpts)
-
- app.on('start', () => {
- t.assert.ok(true, 'booted')
- testCompleted()
- })
-})
-
-test('boot a plugin with a function that returns the options', (t, testCompleted) => {
- t.plan(4)
-
- const server = {}
- const app = boot(server)
- const myOpts = {
- hello: 'world'
- }
- const myOptsAsFunc = parent => {
- t.assert.deepStrictEqual(parent, server)
- return parent.myOpts
- }
-
- app.use(function (s, opts, done) {
- s.myOpts = opts
- done()
- }, myOpts)
-
- app.use(function (s, opts, done) {
- t.assert.deepStrictEqual(s, server, 'the first argument is the server')
- t.assert.deepStrictEqual(opts, myOpts, 'passed options via function accessing parent injected variable')
- done()
- }, myOptsAsFunc)
-
- app.on('start', () => {
- t.assert.ok(true, 'booted')
- testCompleted()
- })
-})
-test('throw on non-function use', (t) => {
- t.plan(1)
- const app = boot()
- t.assert.throws(() => {
- app.use({})
- })
-})
-
-// https://github.com/mcollina/avvio/issues/20
-test('ready and nextTick', (t, testCompleted) => {
- const app = boot()
- process.nextTick(() => {
- app.ready(() => {
- testCompleted()
+ avvio.use(function (context, options, done) {
+ t.assert.deepStrictEqual(avvio, context, 'context is the same as avvio')
+ t.assert.deepStrictEqual(options, {}, 'provides empty options')
+ done()
})
})
-})
-// https://github.com/mcollina/avvio/issues/20
-test('promises and microtask', (t, testCompleted) => {
- const app = boot()
- Promise.resolve()
- .then(() => {
- app.ready(function () {
- testCompleted()
- })
+ await test('(context, callback)', (t, done) => {
+ t.plan(3)
+ const instance = {}
+ const avvio = Avvio(instance, () => {
+ t.assert.ok(true, 'booted')
+ done()
})
-})
-
-test('always loads nested plugins after the current one', (t, testCompleted) => {
- t.plan(2)
-
- const server = {}
- const app = boot(server)
-
- let second = false
- app.use(function (s, opts, done) {
- app.use(function (s, opts, done) {
- second = true
+ avvio.use(function (context, options, done) {
+ t.assert.deepStrictEqual(instance, context, 'context is the same as specified one')
+ t.assert.deepStrictEqual(options, {}, 'provides empty options')
done()
})
- t.assert.ok(!second)
-
- done()
- })
-
- app.on('start', () => {
- t.assert.ok(true, second)
- testCompleted()
- })
-})
-
-test('promise long resolve', (t, testCompleted) => {
- t.plan(2)
-
- const app = boot()
-
- setTimeout(function () {
- t.assert.throws(() => {
- app.use((s, opts, done) => {
- done()
- })
- }, 'root plugin has already booted')
- testCompleted()
- })
-
- app.ready(function (err) {
- t.assert.ok(!err)
- })
-})
-
-test('do not autostart', (t) => {
- const app = boot(null, {
- autostart: false
- })
- app.on('start', () => {
- t.assert.fail()
- })
-})
-
-test('start with ready', (t, testCompleted) => {
- t.plan(2)
-
- const app = boot(null, {
- autostart: false
- })
-
- app.on('start', () => {
- t.assert.ok(true)
- testCompleted()
- })
-
- app.ready(function (err) {
- t.assert.ifError(err)
})
})
-test('load a plugin after start()', (t, testCompleted) => {
- t.plan(1)
+test('automatic start with plugin', (t, done) => {
+ t.plan(4)
- let startCalled = false
- const app = boot(null, {
- autostart: false
- })
+ const avvio = Avvio()
+ let after = false
- app.use((s, opts, done) => {
- t.assert.ok(startCalled)
+ avvio.use(function (context, options, done) {
+ t.assert.deepStrictEqual(avvio, context, 'context is the same as avvio')
+ t.assert.deepStrictEqual(options, {}, 'provides empty options')
+ t.assert.ok(after, 'delayed execution')
done()
- testCompleted()
- })
-
- // we use a timer because
- // it is more reliable than
- // nextTick and setImmediate
- // this almost always will come
- // after those are completed
- setTimeout(() => {
- app.start()
- startCalled = true
- }, 2)
-})
-
-test('booted should be set before ready', (t, testCompleted) => {
- t.plan(2)
-
- const app = boot()
-
- app.ready(function (err) {
- t.assert.ifError(err)
- t.assert.ok(app.booted)
- testCompleted()
})
-})
-test('start should be emitted after ready resolves', (t, testCompleted) => {
- t.plan(1)
-
- const app = boot()
- let ready = false
-
- app.ready().then(function () {
- ready = true
- })
-
- app.on('start', function () {
- t.assert.ok(ready)
- testCompleted()
- })
-})
-
-test('throws correctly if registering after ready', (t, testCompleted) => {
- t.plan(1)
-
- const app = boot()
-
- app.ready(function () {
- t.assert.throws(() => {
- app.use((a, b, done) => done())
- }, 'root plugin has already booted')
- testCompleted()
- })
-})
-
-test('preReady errors must be managed', (t, testCompleted) => {
- t.plan(2)
-
- const app = boot()
-
- app.use((f, opts, cb) => {
- cb()
- })
-
- app.on('preReady', () => {
- throw new Error('boom')
- })
-
- app.ready(err => {
- t.assert.ok(true, 'ready function is called')
- t.assert.equal(err.message, 'boom')
- testCompleted()
- })
-})
-
-test('preReady errors do not override plugin\'s errors', (t, testCompleted) => {
- t.plan(3)
-
- const app = boot()
-
- app.use((f, opts, cb) => {
- cb(new Error('baam'))
- })
-
- app.on('preReady', () => {
- t.assert.ok(true, 'preReady is executed')
- throw new Error('boom')
- })
+ after = true
- app.ready(err => {
- t.assert.ok(true, 'ready function is called')
- t.assert.equal(err.message, 'baam')
- testCompleted()
+ avvio.once('start', () => {
+ t.assert.ok(true, 'booted')
+ done()
})
})
-test('support faux modules', (t, testCompleted) => {
+test('automatic start with promisified plugin', (t, done) => {
t.plan(4)
- const app = boot()
+ const avvio = Avvio()
let after = false
- // Faux modules are modules built with TypeScript
- // or Babel that they export a .default property.
- app.use({
- default: function (server, opts, done) {
- t.assert.deepStrictEqual(server, app, 'the first argument is the server')
- t.assert.deepStrictEqual(opts, {}, 'no options')
- t.assert.ok(true, after, 'delayed execution')
- done()
- }
+ avvio.use(function (context, options) {
+ t.assert.deepStrictEqual(avvio, context, 'context is the same as avvio')
+ t.assert.deepStrictEqual(options, {}, 'provides empty options')
+ t.assert.ok(after, 'delayed execution')
+ return Promise.resolve()
})
after = true
- app.on('start', () => {
+ avvio.once('start', () => {
t.assert.ok(true, 'booted')
- testCompleted()
+ done()
})
})
diff --git a/test/callbacks.test.js b/test/callbacks.test.js
deleted file mode 100644
index 3ae90bd..0000000
--- a/test/callbacks.test.js
+++ /dev/null
@@ -1,116 +0,0 @@
-'use strict'
-
-const { test } = require('node:test')
-const boot = require('..')
-
-test('reentrant', (t, testCompleted) => {
- t.plan(7)
-
- const app = boot()
- let firstLoaded = false
- let secondLoaded = false
-
- app
- .use(first)
- .after(() => {
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.ok(secondLoaded, 'second is loaded')
- t.assert.ok(true)
- testCompleted()
- })
-
- function first (s, opts, done) {
- t.assert.strictEqual(firstLoaded, false, 'first is not loaded')
- t.assert.strictEqual(secondLoaded, false, 'second is not loaded')
- firstLoaded = true
- s.use(second)
- done()
- }
-
- function second (s, opts, done) {
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.strictEqual(secondLoaded, false, 'second is not loaded')
- secondLoaded = true
- done()
- }
-})
-
-test('reentrant with callbacks deferred', (t, testCompleted) => {
- t.plan(11)
-
- const app = boot()
- let firstLoaded = false
- let secondLoaded = false
- let thirdLoaded = false
-
- app.use(first)
-
- function first (s, opts, done) {
- t.assert.strictEqual(firstLoaded, false, 'first is not loaded')
- t.assert.strictEqual(secondLoaded, false, 'second is not loaded')
- t.assert.strictEqual(thirdLoaded, false, 'third is not loaded')
- firstLoaded = true
- s.use(second)
- setTimeout(() => {
- try {
- s.use(third)
- } catch (err) {
- t.assert.strictEqual(err.message, 'Root plugin has already booted')
- }
- testCompleted()
- }, 500)
- done()
- }
-
- function second (s, opts, done) {
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.strictEqual(secondLoaded, false, 'second is not loaded')
- t.assert.strictEqual(thirdLoaded, false, 'third is not loaded')
- secondLoaded = true
- done()
- }
-
- function third (s, opts, done) {
- thirdLoaded = true
- done()
- }
-
- app.on('start', () => {
- t.assert.ok(firstLoaded, 'first is loaded')
- t.assert.ok(secondLoaded, 'second is loaded')
- t.assert.strictEqual(thirdLoaded, false, 'third is not loaded')
- t.assert.ok(true)
- })
-})
-
-test('multiple loading time', (t, testCompleted) => {
- t.plan(1)
- const app = boot()
-
- function a (instance, opts, done) {
- (opts.use || []).forEach(_ => { instance.use(_, { use: opts.subUse || [] }) })
- setTimeout(done, 10)
- }
- const pointer = a
-
- function b (instance, opts, done) {
- (opts.use || []).forEach(_ => { instance.use(_, { use: opts.subUse || [] }) })
- setTimeout(done, 20)
- }
-
- function c (instance, opts, done) {
- (opts.use || []).forEach(_ => { instance.use(_, { use: opts.subUse || [] }) })
- setTimeout(done, 30)
- }
-
- app
- .use(function a (instance, opts, done) {
- instance.use(pointer, { use: [b], subUse: [c] })
- .use(b)
- setTimeout(done, 0)
- })
- .after(() => {
- t.assert.ok(true)
- testCompleted()
- })
-})
diff --git a/test/catch-override-exception.test.js b/test/catch-override-exception.test.js
deleted file mode 100644
index fdcd58a..0000000
--- a/test/catch-override-exception.test.js
+++ /dev/null
@@ -1,26 +0,0 @@
-'use strict'
-
-const { test } = require('tap')
-const boot = require('..')
-
-test('catch exceptions in parent.override', (t) => {
- t.plan(2)
-
- const server = {}
-
- const app = boot(server, {
- autostart: false
- })
- app.override = function () {
- throw Error('catch it')
- }
-
- app
- .use(function () {})
- .start()
-
- app.ready(function (err) {
- t.type(err, Error)
- t.match(err, /catch it/)
- })
-})
diff --git a/test/chainable.test.js b/test/chainable.test.js
deleted file mode 100644
index d07a7ac..0000000
--- a/test/chainable.test.js
+++ /dev/null
@@ -1,67 +0,0 @@
-'use strict'
-
-const { test } = require('tap')
-const boot = require('..')
-
-test('chainable standalone', (t) => {
- t.plan(5)
-
- const readyResult = boot()
- .use(function (ctx, opts, done) {
- t.pass('1st plugin')
- done()
- }).after(function (err, done) {
- t.error(err)
- t.pass('2nd after')
- done()
- }).ready(function () {
- t.pass('we are ready')
- })
- t.equal(readyResult, undefined)
-})
-
-test('chainable automatically binded', (t) => {
- t.plan(5)
-
- const app = {}
- boot(app)
-
- const readyResult = app
- .use(function (ctx, opts, done) {
- t.pass('1st plugin')
- done()
- }).after(function (err, done) {
- t.error(err)
- t.pass('2nd after')
- done()
- }).ready(function () {
- t.pass('we are ready')
- })
- t.equal(readyResult, undefined)
-})
-
-test('chainable standalone with server', (t) => {
- t.plan(6)
-
- const server = {}
- boot(server, {
- expose: {
- use: 'register'
- }
- })
-
- const readyResult = server.register(function (ctx, opts, done) {
- t.pass('1st plugin')
- done()
- }).after(function (err, done) {
- t.error(err)
- t.pass('2nd after')
- done()
- }).register(function (ctx, opts, done) {
- t.pass('3rd plugin')
- done()
- }).ready(function () {
- t.pass('we are ready')
- })
- t.equal(readyResult, undefined)
-})
diff --git a/test/close.test.js b/test/close.test.js
deleted file mode 100644
index a02914b..0000000
--- a/test/close.test.js
+++ /dev/null
@@ -1,544 +0,0 @@
-'use strict'
-
-const { test } = require('tap')
-const boot = require('..')
-const { AVV_ERR_CALLBACK_NOT_FN } = require('../lib/errors')
-
-test('boot an app with a plugin', (t) => {
- t.plan(4)
-
- const app = boot()
- let last = false
-
- app.use(function (server, opts, done) {
- app.onClose(() => {
- t.ok('onClose called')
- t.notOk(last)
- last = true
- })
- done()
- })
-
- app.on('start', () => {
- app.close(() => {
- t.ok(last)
- t.pass('Closed in the correct order')
- })
- })
-})
-
-test('onClose arguments', (t) => {
- t.plan(5)
-
- const app = boot()
-
- app.use(function (server, opts, next) {
- server.onClose((instance, done) => {
- t.ok('called')
- t.equal(server, instance)
- done()
- })
- next()
- })
-
- app.use(function (server, opts, next) {
- server.onClose((instance) => {
- t.ok('called')
- t.equal(server, instance)
- })
- next()
- })
-
- app.on('start', () => {
- app.close(() => {
- t.pass('Closed in the correct order')
- })
- })
-})
-
-test('onClose arguments - fastify encapsulation test case', (t) => {
- t.plan(5)
-
- const server = { my: 'server' }
- const app = boot(server)
-
- app.override = function (s, fn, opts) {
- s = Object.create(s)
- return s
- }
-
- app.use(function (instance, opts, next) {
- instance.test = true
- instance.onClose((i, done) => {
- t.ok(i.test)
- done()
- })
- next()
- })
-
- app.use(function (instance, opts, next) {
- t.notOk(instance.test)
- instance.onClose((i, done) => {
- t.notOk(i.test)
- done()
- })
- next()
- })
-
- app.on('start', () => {
- t.notOk(app.test)
- app.close(() => {
- t.pass('Closed in the correct order')
- })
- })
-})
-
-test('onClose arguments - fastify encapsulation test case / 2', (t) => {
- t.plan(5)
-
- const server = { my: 'server' }
- const app = boot(server)
-
- app.override = function (s, fn, opts) {
- s = Object.create(s)
- return s
- }
-
- server.use(function (instance, opts, next) {
- instance.test = true
- instance.onClose((i, done) => {
- t.ok(i.test)
- done()
- })
- next()
- })
-
- server.use(function (instance, opts, next) {
- t.notOk(instance.test)
- instance.onClose((i, done) => {
- t.notOk(i.test)
- done()
- })
- next()
- })
-
- app.on('start', () => {
- t.notOk(server.test)
- try {
- server.close()
- t.pass()
- } catch (err) {
- t.fail(err)
- }
- })
-})
-
-test('onClose arguments - encapsulation test case no server', (t) => {
- t.plan(5)
-
- const app = boot()
-
- app.override = function (s, fn, opts) {
- s = Object.create(s)
- return s
- }
-
- app.use(function (instance, opts, next) {
- instance.test = true
- instance.onClose((i, done) => {
- t.notOk(i.test)
- done()
- })
- next()
- })
-
- app.use(function (instance, opts, next) {
- t.notOk(instance.test)
- instance.onClose((i) => {
- t.notOk(i.test)
- })
- next()
- })
-
- app.on('start', () => {
- t.notOk(app.test)
- app.close(() => {
- t.pass('Closed in the correct order')
- })
- })
-})
-
-test('onClose should handle errors', (t) => {
- t.plan(3)
-
- const app = boot()
-
- app.use(function (server, opts, done) {
- app.onClose((instance, done) => {
- t.ok('called')
- done(new Error('some error'))
- })
- done()
- })
-
- app.on('start', () => {
- app.close(err => {
- t.equal(err.message, 'some error')
- t.pass('Closed in the correct order')
- })
- })
-})
-
-test('#54 close handlers should receive same parameters when queue is not empty', (t) => {
- t.plan(6)
-
- const context = { test: true }
- const app = boot(context)
-
- app.use(function (server, opts, done) {
- done()
- })
- app.on('start', () => {
- app.close((err, done) => {
- t.equal(err, null)
- t.pass('Closed in the correct order')
- setImmediate(done)
- })
- app.close(err => {
- t.equal(err, null)
- t.pass('Closed in the correct order')
- })
- app.close(err => {
- t.equal(err, null)
- t.pass('Closed in the correct order')
- })
- })
-})
-
-test('onClose should handle errors / 2', (t) => {
- t.plan(4)
-
- const app = boot()
-
- app.onClose((instance, done) => {
- t.ok('called')
- done(new Error('some error'))
- })
-
- app.use(function (server, opts, done) {
- app.onClose((instance, done) => {
- t.ok('called')
- done()
- })
- done()
- })
-
- app.on('start', () => {
- app.close(err => {
- t.equal(err.message, 'some error')
- t.pass('Closed in the correct order')
- })
- })
-})
-
-test('close arguments', (t) => {
- t.plan(4)
-
- const app = boot()
-
- app.use(function (server, opts, done) {
- app.onClose((instance, done) => {
- t.ok('called')
- done()
- })
- done()
- })
-
- app.on('start', () => {
- app.close((err, instance, done) => {
- t.error(err)
- t.equal(instance, app)
- done()
- t.pass('Closed in the correct order')
- })
- })
-})
-
-test('close event', (t) => {
- t.plan(3)
-
- const app = boot()
- let last = false
-
- app.on('start', () => {
- app.close(() => {
- t.notOk(last)
- last = true
- })
- })
-
- app.on('close', () => {
- t.ok(last)
- t.pass('event fired')
- })
-})
-
-test('close order', (t) => {
- t.plan(5)
-
- const app = boot()
- const order = [1, 2, 3, 4]
-
- app.use(function (server, opts, done) {
- app.onClose(() => {
- t.equal(order.shift(), 3)
- })
-
- app.use(function (server, opts, done) {
- app.onClose(() => {
- t.equal(order.shift(), 2)
- })
- done()
- })
- done()
- })
-
- app.use(function (server, opts, done) {
- app.onClose(() => {
- t.equal(order.shift(), 1)
- })
- done()
- })
-
- app.on('start', () => {
- app.close(() => {
- t.equal(order.shift(), 4)
- t.pass('Closed in the correct order')
- })
- })
-})
-
-test('close without a cb', (t) => {
- t.plan(1)
-
- const app = boot()
-
- app.onClose((instance, done) => {
- t.ok('called')
- done()
- })
-
- app.close()
-})
-
-test('onClose with 0 parameters', (t) => {
- t.plan(4)
-
- const server = { my: 'server' }
- const app = boot(server)
-
- app.use(function (instance, opts, next) {
- instance.onClose(function () {
- t.ok('called')
- t.equal(arguments.length, 0)
- })
- next()
- })
-
- app.close(err => {
- t.error(err)
- t.pass('Closed')
- })
-})
-
-test('onClose with 1 parameter', (t) => {
- t.plan(3)
-
- const server = { my: 'server' }
- const app = boot(server)
-
- app.use(function (instance, opts, next) {
- instance.onClose(function (context) {
- t.equal(arguments.length, 1)
- })
- next()
- })
-
- app.close(err => {
- t.error(err)
- t.pass('Closed')
- })
-})
-
-test('close passing not a function', (t) => {
- t.plan(1)
-
- const app = boot()
-
- app.onClose((instance, done) => {
- t.ok('called')
- done()
- })
-
- t.throws(() => app.close({}), { message: 'not a function' })
-})
-
-test('close passing not a function', (t) => {
- t.plan(1)
-
- const app = boot()
-
- app.onClose((instance, done) => {
- t.ok('called')
- done()
- })
-
- t.throws(() => app.close({}), { message: 'not a function' })
-})
-
-test('close passing not a function when wrapping', (t) => {
- t.plan(1)
-
- const app = {}
- boot(app)
-
- app.onClose((instance, done) => {
- t.ok('called')
- done()
- })
-
- t.throws(() => app.close({}), { message: 'not a function' })
-})
-
-test('close should trigger ready()', (t) => {
- t.plan(2)
-
- const app = boot(null, {
- autostart: false
- })
-
- app.on('start', () => {
- // this will be emitted after the
- // callback in close() is fired
- t.pass('started')
- })
-
- app.close(() => {
- t.pass('closed')
- })
-})
-
-test('close without a cb returns a promise', (t) => {
- t.plan(1)
-
- const app = boot()
- app.close().then(() => {
- t.pass('promise resolves')
- })
-})
-
-test('close without a cb returns a promise when attaching to a server', (t) => {
- t.plan(1)
-
- const server = {}
- boot(server)
- server.close().then(() => {
- t.pass('promise resolves')
- })
-})
-
-test('close with async onClose handlers', t => {
- t.plan(7)
-
- const app = boot()
- const order = [1, 2, 3, 4, 5, 6]
-
- app.onClose(() => {
- return new Promise(resolve => setTimeout(resolve, 500)).then(() => {
- t.equal(order.shift(), 5)
- })
- })
-
- app.onClose(() => {
- t.equal(order.shift(), 4)
- })
-
- app.onClose(instance => {
- return new Promise(resolve => setTimeout(resolve, 500)).then(() => {
- t.equal(order.shift(), 3)
- })
- })
-
- app.onClose(async instance => {
- return new Promise(resolve => setTimeout(resolve, 500)).then(() => {
- t.equal(order.shift(), 2)
- })
- })
-
- app.onClose(async () => {
- return new Promise(resolve => setTimeout(resolve, 500)).then(() => {
- t.equal(order.shift(), 1)
- })
- })
-
- app.on('start', () => {
- app.close(() => {
- t.equal(order.shift(), 6)
- t.pass('Closed in the correct order')
- })
- })
-})
-
-test('onClose callback must be a function', (t) => {
- t.plan(1)
-
- const app = boot()
-
- app.use(function (server, opts, done) {
- t.throws(() => app.onClose({}), new AVV_ERR_CALLBACK_NOT_FN('onClose', 'object'))
- done()
- })
-})
-
-test('close custom server with async onClose handlers', t => {
- t.plan(7)
-
- const server = {}
- const app = boot(server)
- const order = [1, 2, 3, 4, 5, 6]
-
- server.onClose(() => {
- return new Promise(resolve => setTimeout(resolve, 500)).then(() => {
- t.equal(order.shift(), 5)
- })
- })
-
- server.onClose(() => {
- t.equal(order.shift(), 4)
- })
-
- server.onClose(instance => {
- return new Promise(resolve => setTimeout(resolve, 500)).then(() => {
- t.equal(order.shift(), 3)
- })
- })
-
- server.onClose(async instance => {
- return new Promise(resolve => setTimeout(resolve, 500)).then(() => {
- t.equal(order.shift(), 2)
- })
- })
-
- server.onClose(async () => {
- return new Promise(resolve => setTimeout(resolve, 500)).then(() => {
- t.equal(order.shift(), 1)
- })
- })
-
- app.on('start', () => {
- app.close(() => {
- t.equal(order.shift(), 6)
- t.pass('Closed in the correct order')
- })
- })
-})
diff --git a/test/errors.test.js b/test/errors.test.js
deleted file mode 100644
index 44c0787..0000000
--- a/test/errors.test.js
+++ /dev/null
@@ -1,26 +0,0 @@
-'use strict'
-
-const { test } = require('node:test')
-const errors = require('../lib/errors')
-
-test('Correct codes of AvvioErrors', t => {
- const testcases = [
- 'AVV_ERR_EXPOSE_ALREADY_DEFINED',
- 'AVV_ERR_ATTRIBUTE_ALREADY_DEFINED',
- 'AVV_ERR_CALLBACK_NOT_FN',
- 'AVV_ERR_PLUGIN_NOT_VALID',
- 'AVV_ERR_ROOT_PLG_BOOTED',
- 'AVV_ERR_PARENT_PLG_LOADED',
- 'AVV_ERR_READY_TIMEOUT',
- 'AVV_ERR_PLUGIN_EXEC_TIMEOUT'
- ]
-
- t.plan(testcases.length + 1)
- // errors.js exposes errors and the createError fn
- t.assert.strictEqual(testcases.length, Object.keys(errors).length)
-
- for (const testcase of testcases) {
- const error = new errors[testcase]()
- t.assert.strictEqual(error.code, testcase)
- }
-})
diff --git a/test/esm.mjs b/test/esm.mjs
deleted file mode 100644
index df53d64..0000000
--- a/test/esm.mjs
+++ /dev/null
@@ -1,12 +0,0 @@
-import { test } from 'tap'
-import boot from '../boot.js'
-
-test('support import', async (t) => {
- const app = boot()
-
- app.use(import('./fixtures/esm.mjs'))
-
- await app.ready()
-
- t.equal(app.loaded, true)
-})
diff --git a/test/esm.test.js b/test/esm.test.js
deleted file mode 100644
index d930881..0000000
--- a/test/esm.test.js
+++ /dev/null
@@ -1,14 +0,0 @@
-'use strict'
-
-const { test } = require('tap')
-
-test('support esm import', (t) => {
- import('./esm.mjs').then(() => {
- t.pass('esm is supported')
- t.end()
- }).catch((err) => {
- process.nextTick(() => {
- throw err
- })
- })
-})
diff --git a/test/events-listeners.test.js b/test/events-listeners.test.js
deleted file mode 100644
index 556ea67..0000000
--- a/test/events-listeners.test.js
+++ /dev/null
@@ -1,23 +0,0 @@
-'use strict'
-
-const { test } = require('node:test')
-const boot = require('..')
-const noop = () => {}
-
-test('boot a plugin and then execute a call after that', (t, testDone) => {
- t.plan(1)
-
- process.on('warning', (warning) => {
- t.assert.fail('we should not get a warning')
- })
-
- const app = boot()
- for (let i = 0; i < 12; i++) {
- app.on('preReady', noop)
- }
-
- setTimeout(() => {
- t.assert.ok('Everything ok')
- testDone()
- }, 500)
-})
diff --git a/test/expose.test.js b/test/expose.test.js
deleted file mode 100644
index 422524e..0000000
--- a/test/expose.test.js
+++ /dev/null
@@ -1,80 +0,0 @@
-'use strict'
-
-const { test } = require('tap')
-const boot = require('..')
-const { AVV_ERR_EXPOSE_ALREADY_DEFINED, AVV_ERR_ATTRIBUTE_ALREADY_DEFINED } = require('../lib/errors')
-const { kAvvio } = require('../lib/symbols')
-
-for (const key of ['use', 'after', 'ready', 'onClose', 'close']) {
- test('throws if ' + key + ' is by default already there', (t) => {
- t.plan(1)
-
- const app = {}
- app[key] = () => { }
-
- t.throws(() => boot(app), new AVV_ERR_EXPOSE_ALREADY_DEFINED(key, key))
- })
-
- test('throws if ' + key + ' is already there', (t) => {
- t.plan(1)
-
- const app = {}
- app['cust' + key] = () => { }
-
- t.throws(() => boot(app, { expose: { [key]: 'cust' + key } }), new AVV_ERR_EXPOSE_ALREADY_DEFINED('cust' + key, key))
- })
-
- test('support expose for ' + key, (t) => {
- const app = {}
- app[key] = () => { }
-
- const expose = {}
- expose[key] = 'muahah'
-
- boot(app, {
- expose
- })
-
- t.end()
- })
-}
-
-test('set the kAvvio to true on the server', (t) => {
- t.plan(1)
-
- const server = {}
- boot(server)
-
- t.ok(server[kAvvio])
-})
-
-test('.then()', t => {
- t.plan(3)
-
- t.test('.then() can not be overwritten', (t) => {
- t.plan(1)
-
- const server = {
- then: () => {}
- }
- t.throws(() => boot(server), AVV_ERR_ATTRIBUTE_ALREADY_DEFINED('then'))
- })
-
- t.test('.then() is a function', (t) => {
- t.plan(1)
-
- const server = {}
- boot(server)
-
- t.type(server.then, 'function')
- })
-
- t.test('.then() can not be overwritten', (t) => {
- t.plan(1)
-
- const server = {}
- boot(server)
-
- t.throws(() => { server.then = 'invalid' }, TypeError('Cannot set property then of #