Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/fastify/avvio into next
Browse files Browse the repository at this point in the history
  • Loading branch information
gurgunday committed Jun 6, 2024
2 parents 9801fc3 + 2075e4b commit b0b8548
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 12 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ jobs:
with:
lint: true
license-check: true
node-versions: '["18", "20", "21", "22"]'
17 changes: 14 additions & 3 deletions boot.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,16 @@ Boot.prototype._addPlugin = function (pluginFn, opts, isAfter) {
// we always add plugins to load at the current element
const current = this._current[0]

const plugin = new Plugin(fastq(this, this._loadPluginNextTick, 1), pluginFn, opts, isAfter, this._opts.timeout)
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) {
Expand Down Expand Up @@ -470,7 +479,7 @@ function callWithCbOrNextTick (func, cb) {
}

function timeoutCall (func, rootErr, context, cb) {
const name = func.name
const name = func.unwrappedName ?? func.name
debug('setting up ready timeout', name, this._opts.timeout)
let timer = setTimeout(() => {
debug('timed out', name)
Expand Down Expand Up @@ -562,7 +571,9 @@ function encapsulateTwoParam (func, that) {
}

function encapsulateThreeParam (func, that) {
return _encapsulateThreeParam.bind(that)
const wrapped = _encapsulateThreeParam.bind(that)
wrapped.unwrappedName = func.name
return wrapped
function _encapsulateThreeParam (err, cb) {
let res
if (!func) {
Expand Down
2 changes: 2 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ declare namespace avvio {
use?: string;
after?: string;
ready?: string;
close?: string;
onClose?: string;
};
autostart?: boolean;
}
Expand Down
19 changes: 12 additions & 7 deletions lib/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ function Plugin (queue, func, options, isAfter, timeout) {
this.loaded = false

this._promise = null

this.startTime = null
}

inherits(Plugin, EventEmitter)
Expand Down Expand Up @@ -117,6 +119,7 @@ Plugin.prototype.exec = function (server, callback) {
}

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)
Expand Down Expand Up @@ -145,14 +148,16 @@ Plugin.prototype.loadedSoFar = function () {
this._error = afterErr
this.queue.pause()

if (afterErr) {
debug('rejecting promise', this.name, afterErr)
this._promise.reject(afterErr)
} else {
debug('resolving promise', this.name)
this._promise.resolve()
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
}
this._promise = null

process.nextTick(callback, afterErr)
})
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "avvio",
"version": "8.3.0",
"version": "8.3.2",
"description": "Asynchronous bootstrapping of Node applications",
"main": "boot.js",
"type": "commonjs",
Expand Down
33 changes: 33 additions & 0 deletions test/on-ready-timeout-await.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
'use strict'

/* eslint no-prototype-builtins: off */

const { test } = require('tap')
const boot = require('../boot')

test('onReadyTimeout', async (t) => {
const app = boot({}, {
timeout: 10, // 10 ms
autostart: false
})

app.use(function one (innerApp, opts, next) {
t.pass('loaded')
innerApp.ready(function readyNoResolve (err, done) {
t.notOk(err)
t.pass('first ready called')
// Do not call done() to timeout
})
next()
})

await app.start()

try {
await app.ready()
t.fail('should throw')
} catch (err) {
t.equal(err.message, 'Plugin did not start in time: \'readyNoResolve\'. You may have forgotten to call \'done\' function or to resolve a Promise')
// And not Plugin did not start in time: 'bound _encapsulateThreeParam'. You may have forgotten to call 'done' function or to resolve a Promise
}
})
33 changes: 33 additions & 0 deletions test/plugin-timeout-await.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
'use strict'

/* eslint no-prototype-builtins: off */

const { test } = require('tap')
const boot = require('..')

test('do not load', async (t) => {
const app = boot({}, { timeout: 10 })

app.use(first)

async function first (s, opts) {
await s.use(second)
}

async function second (s, opts) {
await s.use(third)
}

function third (s, opts) {
return new Promise((resolve, reject) => {
// no resolve
})
}

try {
await app.start()
t.fail('should throw')
} catch (err) {
t.equal(err.message, 'Plugin did not start in time: \'third\'. You may have forgotten to call \'done\' function or to resolve a Promise')
}
})
21 changes: 21 additions & 0 deletions test/plugin-timeout.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,24 @@ test('timeout without calling next in ready and rethrowing the error', (t) => {

app.start()
})

test('nested timeout do not crash - await', (t) => {
t.plan(4)
const app = boot({}, {
timeout: 10 // 10 ms
})
app.use(one)
async function one (app, opts) {
await app.use(two)
}

function two (app, opts, next) {
// do not call next on purpose
}
app.ready((err) => {
t.ok(err)
t.equal(err.fn, two)
t.equal(err.message, message('two'))
t.equal(err.code, 'AVV_ERR_PLUGIN_EXEC_TIMEOUT')
})
})
2 changes: 1 addition & 1 deletion test/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ import * as avvio from "../../";
const server = { hello: "world" };
const options = {
autostart: false,
expose: { after: "after", ready: "ready", use: "use" }
expose: { after: "after", ready: "ready", use: "use", close: "close", onClose : "onClose" }
};
// avvio with server and options
const app = avvio(server, options);
Expand Down

0 comments on commit b0b8548

Please sign in to comment.