Giter Club home page Giter Club logo

avvio's Introduction

CI Package Manager CI Web SIte neostandard javascript style CII Best Practices

NPM version NPM downloads Security Responsible Disclosure Discord Contribute with Gitpod Open Collective backers and sponsors


An efficient server implies a lower cost of the infrastructure, a better responsiveness under load and happy users. How can you efficiently handle the resources of your server, knowing that you are serving the highest number of requests as possible, without sacrificing security validations and handy development?

Enter Fastify. Fastify is a web framework highly focused on providing the best developer experience with the least overhead and a powerful plugin architecture. It is inspired by Hapi and Express and as far as we know, it is one of the fastest web frameworks in town.

The main branch refers to the Fastify v5 release, which is not released/LTS yet. Check out the 4.x branch for v4.

Table of Contents

Quick start

Create a folder and make it your current working directory:

mkdir my-app
cd my-app

Generate a fastify project with npm init:

npm init fastify

Install dependencies:

npm i

To start the app in dev mode:

npm run dev

For production mode:

npm start

Under the hood npm init downloads and runs Fastify Create, which in turn uses the generate functionality of Fastify CLI.

Install

To install Fastify in an existing project as a dependency:

Install with npm:

npm i fastify

Install with yarn:

yarn add fastify

Example

// Require the framework and instantiate it

// ESM
import Fastify from 'fastify'

const fastify = Fastify({
  logger: true
})
// CommonJs
const fastify = require('fastify')({
  logger: true
})

// Declare a route
fastify.get('/', (request, reply) => {
  reply.send({ hello: 'world' })
})

// Run the server!
fastify.listen({ port: 3000 }, (err, address) => {
  if (err) throw err
  // Server is now listening on ${address}
})

with async-await:

// ESM
import Fastify from 'fastify'

const fastify = Fastify({
  logger: true
})
// CommonJs
const fastify = require('fastify')({
  logger: true
})

fastify.get('/', async (request, reply) => {
  reply.type('application/json').code(200)
  return { hello: 'world' }
})

fastify.listen({ port: 3000 }, (err, address) => {
  if (err) throw err
  // Server is now listening on ${address}
})

Do you want to know more? Head to the Getting Started.

Note

.listen binds to the local host, localhost, interface by default (127.0.0.1 or ::1, depending on the operating system configuration). If you are running Fastify in a container (Docker, GCP, etc.), you may need to bind to 0.0.0.0. Be careful when deciding to listen on all interfaces; it comes with inherent security risks. See the documentation for more information.

Core features

  • Highly performant: as far as we know, Fastify is one of the fastest web frameworks in town, depending on the code complexity we can serve up to 76+ thousand requests per second.
  • Extensible: Fastify is fully extensible via its hooks, plugins and decorators.
  • Schema based: even if it is not mandatory we recommend to use JSON Schema to validate your routes and serialize your outputs, internally Fastify compiles the schema in a highly performant function.
  • Logging: logs are extremely important but are costly; we chose the best logger to almost remove this cost, Pino!
  • Developer friendly: the framework is built to be very expressive and help the developer in their daily use, without sacrificing performance and security.

Benchmarks

Machine: EX41S-SSD, Intel Core i7, 4Ghz, 64GB RAM, 4C/8T, SSD.

Method:: autocannon -c 100 -d 40 -p 10 localhost:3000 * 2, taking the second average

Framework Version Router? Requests/sec
Express 4.17.3 14,200
hapi 20.2.1 42,284
Restify 8.6.1 50,363
Koa 2.13.0 54,272
Fastify 4.0.0 77,193
-
http.Server 16.14.2 74,513

Benchmarks taken using https://github.com/fastify/benchmarks. This is a synthetic, "hello world" benchmark that aims to evaluate the framework overhead. The overhead that each framework has on your application depends on your application, you should always benchmark if performance matters to you.

Documentation

中文文档地址

Ecosystem

  • Core - Core plugins maintained by the Fastify team.
  • Community - Community supported plugins.
  • Live Examples - Multirepo with a broad set of real working examples.
  • Discord - Join our discord server and chat with the maintainers.

Support

Please visit Fastify help to view prior support issues and to ask new support questions.

Contributing

Whether reporting bugs, discussing improvements and new ideas or writing code, we welcome contributions from anyone and everyone. Please read the CONTRIBUTING guidelines before submitting pull requests.

Team

Fastify is the result of the work of a great community. Team members are listed in alphabetical order.

Lead Maintainers:

Fastify Core team

Fastify Plugins team

Great Contributors

Great contributors on a specific area in the Fastify ecosystem will be invited to join this group by Lead Maintainers.

Past Collaborators

Hosted by

We are a At-Large Project in the OpenJS Foundation.

Sponsors

Support this project by becoming a SPONSOR! Fastify has an Open Collective page where we accept and manage financial contributions.

Acknowledgements

This project is kindly sponsored by:

Past Sponsors:

This list includes all companies that support one or more of the team members in the maintenance of this project.

License

Licensed under MIT.

For your convenience, here is a list of all the licenses of our production dependencies:

  • MIT
  • ISC
  • BSD-3-Clause
  • BSD-2-Clause

avvio's People

Contributors

0xflotus avatar airhorns avatar alex-ppg avatar big-kahuna-burger avatar cemremengu avatar chrskrchr avatar coreyfarrell avatar davidmarkclements avatar delvedor avatar dependabot-preview[bot] avatar dependabot[bot] avatar eomm avatar erfanium avatar fdawgs avatar gyszalai avatar jsumners avatar marcbachmann avatar marcopiraccini avatar matteogranziera avatar mbelsky avatar mcollina avatar metcoder95 avatar remidewitte avatar rstagi avatar shogunpanda avatar starptech avatar tellnes avatar uzlopak avatar wtgtybhertgeghgtwtg avatar xtx1130 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

avvio's Issues

After hides the error generated by use

As titled, if you uncomment the after in the following example, the error will never arrive inside ready.

const server = {}
require('avvio')(server)

server.use((instance, opts, next) => {
  next(new Error('error'))
})

// server.after((instance, opts, next) => {
//   console.log('after')
// })

server.ready((err, instance, done) => {
  if (err) throw err
})

Are calls to override wanted?

Reading the docs (master&next) of the afterfunction:

If three parameters are given to the callback, the first will be the error object, the second will be the top level context unless you have specified both server and override, in that case the context will be what the override returns, and the third the done callback.

My understanding is that the .after interfaces with one or two params don't call the override
Instead override is always called:

'use strict'

const server = { count: 0 }
const avvio = require('.')(server)

avvio.override = function (s, fn, opts) {
  console.log('override', s.count)
  const res = Object.create(s)
  res.count = res.count + 1
  return res
}

avvio
  .use(function hello (server, opts, done) {
    console.log('hello server', server.count)
    done()
  })
  .after(function (err, done) {
    if (err) throw err
    done()
  })
  .after(function (err, context, done) {
    if (err) throw err
    console.log('context', context.count)
    done()
  })

avvio.ready(() => { console.log('ready') })

Show:

override 0
hello server 1
override 0
override 0
context 0
ready
start

is this wanted/needed?
Does not this behaviour add unuseful overhead?

With this (fast) code on plugin.js all our tests are green and also on fastify.next all is green:

if (!this.isAfter || func.length === 3 || (func.length === 2 && func.then)) {
  this.server = this.parent.override(server, func, this.opts)
}

Doubt born by fastify/fastify#2166

`start()` is not exported on instance

🐛 Bug Report

.start() is not exported on instance, however, .ready() is.

To Reproduce

Steps to reproduce the behavior:

const app = {};

const avvioOptions = {
    expose: {
        use: 'load'
    }
};

require('avvio')(app, avvioOptions);

console.log(app.load === undefined); // false
console.log(app.ready === undefined); // false

console.log(app.start === undefined); // true

Expected behavior

I expect that start is exported either automatically or via the expose configuration option in boot.js at line 18: startKey = expose.start || 'start'

const app = {};

const avvioOptions = {
    expose: {
        use: 'load'
    }
};

require('avvio')(app, avvioOptions);

console.log(app.load === undefined); // false
console.log(app.ready === undefined); // false
console.log(app.start === undefined); // false

// --------- and/or ------ 

const app = {};

const avvioOptions = {
    expose: {
        start: 'blastOff'
    }
};

require('avvio')(app, avvioOptions);

console.log(app.blastOff === undefined); // false

Your Environment

  • node version: 12
  • fastify version: N/A avvio = 7.0.3
  • os: Mac

Use inside use doesn't work anymore

Using fastify, this code works correctly in 0.28 (avvio version 2):

function pluginOk (fastify, opts, next) { next() }

t.test('ok', t => {
  t.plan(2)
  const fastify = Fastify()

  fastify.register(require('./index'), err => {
    t.error(err)
    fastify.register(pluginOk, err => {
      t.error(err)
    })
  })
})

In 0.30.2 (avvio version 3) the second error has this message: Impossible to load "pluginOk" plugin because the parent "" was already loaded

This behaviour is introduced by #29 on avvio version 3.

How can I rewrite the previous code in new avvio version?

Ready is not called

As discussed here fastify/fastify#168 and here fastify/fastify#165, I'm reporting the bug.

The code:

'use strict'

const avvio = require('.')()

Promise.resolve()
  .then(() => {
    avvio
      .ready(function () {
        console.log('application booted!')
      })
  })

doesn't print nothing

Return avvio inside after breaks avvio

If you use avvio#next, there is a rather funny bug, if you return avvio inside an after function without callback (I guess it will happens the same inside a use), avvio will go crazy and stop its execution.

'use strict'

const avvio = require('.')()

avvio.use((instance, opts, next) => {
  console.log('plugin 1')
  next()
})

avvio.after(() => {
  console.log('after plugin 1')
  return avvio // <=================== LOOK HERE
})

avvio.use((instance, opts, next) => {
  console.log('plugin 2')
  next()
})

avvio.ready(err => {
  console.log(err || 'ready')
})

/*

Actual log:

plugin 1
after plugin 1



Expected log:

plugin 1
after plugin 1
plugin 2
ready

*/

This is very likely caused by how we are handling promises and the fact that avvio itself is a thenable.
https://github.com/mcollina/avvio/blob/5d36884e9f2c4483a23466668a41a4edb9e9ff4c/boot.js#L434-L451

cc @davidmarkclements

Error are not passed from after to ready

I don't think this should happen.
Could be related to this?

Example:

test('shold 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.deepEqual(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.is(err.message, 'some error') // this will fail
  })

  app.on('start', () => {
    server.close(() => {
      t.pass('booted')
    })
  })
})

Error propagation

What's the reason to prevent the error propagation here?

The user is not able to catch the error in the ready event after a ready or after callback was registered.

const boot = require('./')

function Server () {}
const app = boot(new Server())

function plugin (server, opts, done) {
  done(new Error('test'))
}

app.use(plugin)
.after((err) => console.error('Plugin: ' + err))

app.ready(function (err) {
  console.error('Ready: ' + err)
})

// Plugin: Error: test
// Ready: null

Awaiting on app.close() resolves before registered onClose functions resolved

🐛 Bug Report

Awaiting on app.close() resolves before async onClose handlers are resolved

To Reproduce

Steps to reproduce the behavior:

const avvio = require('avvio')
const server = {}
const app = avvio(server, { autostart: false })
async function start() {
  await server.use(async instance => {
    console.log('plugin 1 starting')
    await new Promise(resolve => setTimeout(resolve, 1000))
    instance.onClose(async () => {
      console.log('plugin 1 stopping')
      await new Promise(resolve => setTimeout(resolve, 2000))
      console.log('plugin 1 stopped')
    })
    console.log('plugin 1 started')
  }, {})

  await server.ready()
  console.log('app started')
}

start()

setTimeout(async () => {
  console.log('app closing')
  await app.close()
  console.log('app closed')
}, 5000)

Expected behavior

The await app.close() call should not resolve until the registered plugin's onClose function is resolved

The output is:

plugin 1 starting
plugin 1 started
app started
app closing
plugin 1 stopping
app closed
plugin 1 stopped

whereas the expected output is:

plugin 1 starting
plugin 1 started
app started
app closing
plugin 1 stopping
plugin 1 stopped
app closed

Your Environment

  • node version: 14.16.0
  • os: Mac

How to use

I am wondering what is the best way to use boot-in-the-arse. Currently there are two options:

a. subclassing
b. passing another server through the constructor

Case a. works as expected, but it fills the object with the boot-in-the-arse properties. It would probably need to be more documented.

Case b. uses the passed object as the context and this in the plugin loading. However it does not expose use, the 'start' event and the upcoming ready()  and after() #1.

I think there should be a blessed way on using this.
What should we do?

I proposed we auto-bind use, ready and after in case b), so that they work as expected.

cc @mcdonnelldean @lucamaraschi

Avvio throws 'Error: root plugin has already booted' on first `instance.use` call

🐛 Bug Report

If there is an async call between the creation of the avvio instance and the instance.use(...) call, avvio throws an error with the root plugin has already booted message.

To Reproduce

Run the following code:

const avvio = require('avvio')
const app = avvio({ autostart: false })
async function start() {
  console.log('initializing async resource')
  await new Promise(resolve => setTimeout(resolve, 100))
  console.log('async resource initialized')
  // plugin 1
  await app.use(async () => {
    console.log('plugin 1 starting')
    await new Promise(resolve => setTimeout(resolve, 1000))
    console.log('plugin 1 started')
  }, {})

  await app.ready()
  console.log('app started')
}

start()

Expected behavior

Avvio should register the given plugin and start the application gracefully when awaiting on app.ready():

initializing async resource
async resource initialized
plugin 1 starting
plugin 1 started
app started

but instead it throws an error:

initializing async resource
async resource initialized
(node:90235) UnhandledPromiseRejectionWarning: Error: root plugin has already booted
    at Boot._addPlugin (XXXXXXXXXXX/node_modules/avvio/boot.js:237:11)
    at Boot.use (XXXXXXXXXXX/node_modules/avvio/boot.js:209:25)
    at start (XXXXXXXXXXX/avvio_sample.js:8:13)
...

Your Environment

  • node version: 14.16.0
  • fastify version: -
  • os: Mac

'Close' should shutdown application even when there are plugins being processed

The close implementation should be simplified.

  • close should not start the queue.
  • close should shutdown the application anyway in its current state
'use strict'

const avvio = require('.')()

avvio.onClose(() => console.log('onClose!'))
avvio
  .use(first, { hello: 'world' })
  .ready(function () {
    console.log('application booted!')
  })

function first (instance, opts, cb) {
  console.log('first loaded', opts)
  instance.close(() => console.log('closed!'))
  // cb()
}

/**
first loaded { hello: 'world' }
*/

Proposal

A plugin should be able to close the application with close() regardless in which phase the application is in. This is very important to ensure a graceful shutdown.

When close() is called only the close queue should proceed and an error will pass when the ready queue readyQ.idle() returns false.

'use strict'

const avvio = require('.')()

avvio.onClose(() => console.log('onClose!'))
avvio
  .use(first, { hello: 'world' })
  .ready(function () {
    console.log('application booted!')
  })

function first (instance, opts, cb) {
  console.log('first loaded', opts)
  instance.close((err) => {
   console.log(err.message)
   console.log('closed!')
  })
  // cb()
}

/**
first loaded { hello: 'world' }
onClose!
Plugin queue is still in bootstrapping!
closed!
*/

Make Fastify plugins interoperable with Avvio

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the feature has not already been requested

🚀 Feature Proposal

My proposal is to make Fastify plugins interoperable with Avvio for cases when I need to share the same resources (plugins) with another process without need to handle HTTP requests.

Motivation

A common pattern for developing backend applications is to have 2 distinct processes one for handle http requests and another to do background tasks, a worker process.

Fastity is really great to handle HTTP requests and more than that, the plugin ecosystem is awesome and starting a robust application is a nice experience.

But the thing gets complicated when need to share plugins (resources) with another process for example to accomplish the pattern above described for background job processing.

Once the plugin system is very opinionated and tied to Fastify HTTP lifecycle it turns very difficult to share those resources.

Example

For example today I have an application with 2 entry points:

  1. api.js instantiate a Fastity instance and start listening for http requests
  2. worker.js instantiate a Fastity instance too but instead of listening for http requests it is waiting for consume amqp messages.

Both processes lives on the same project and my intention is both share the same resource/plugins.

The code of worker.js looks like this:

const Fastify = require('fastify')
const Autoload = require('fastify-autoload')
const closeWithGrace = require('close-with-grace')

const { join } = require('path')
const amqp = require('amqplib')

const server = Fastify({ logger: true })

;(async function () {
  closeWithGrace({ delay: 500 }, async function ({ err }) {
    if (err) {
      server.log.error(err)
    }
  })

  server.register(Autoload, {
    dir: join(__dirname, 'plugins')
  })

  await server.after()
  
  server.register(Autoload, {
    dir: join(__dirname, 'tasks')
  })

  await server.after()
  
  server.register(async function (server, opts) {
    const conn = await amqp.connect(server.config.RABBITMQ_CONNECTION_STRING)
    const channel = await conn.createChannel()
  
    await channel.assertExchange(server.config.PORTAL_EXCHANGE, 'topic')
    await channel.assertQueue(server.config.PORTAL_QUEUE)

    await channel.bindQueue(server.config.PORTAL_QUEUE, server.config.PORTAL_EXCHANGE, '*.*')

    async function onMessage (message) {
      const { fields: { routingKey }, content } = message
      
      let param

      try {
        param = JSON.parse(content)
      } catch (err) {
        param = content.toString()
      }

      const taskFn = server[routingKey]

      if (!taskFn) {
        server.log.error(`Task not found: ${routingKey}`)
        return
      }
      
      try {
        await taskFn(param)
      } catch (err) {
        channel.nack(message)
        server.log.error({ err }, `Unable to process task: ${routingKey}`)
      }
    }

    channel.consume(server.config.PORTAL_QUEUE, onMessage, { noAck: true })

    conn.on('close', function () {
      server.log.error(`Connection lost with ${server.config.RABBITMQ_CONNECTION_STRING}`)
      process.exit(1)
    })

    conn.on('error', function (err) {
      server.log.error(err)
      process.exit(1)
    })
  })

  await server.ready()

  server.log.info('Worker is ready and waiting for messages.')
})()

It does the job, but as highlighted from @jsumners doesn´t make sense to have a HTTP server without listening to HTTP requests and I have to agree with that.

Updating for v5

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the issue has not already been raised

Issue

I'm looking to update this repo for v5. fastify/fastify#5116

Changes Needed (Please let me know if anything might be missing)

  • Update workflows to use v4
  • Update dependencies

Typescript definitions

Hello,

Is it planned to add TypeScript definitions to this module? I think it would be appreciated by the developers. I unfortunately do not have enough experience writing good definitions of my own.

Anyway, good work with avvio ^^

Improve documentation

At the moment the documentation is almost complex as the actual code, we should fix that.

Sync loading?

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the feature has not already been requested

🚀 Feature Proposal

Is there any way that I could propose a way to sync load plugins?
detecting the number of parameters of the pluign function (3 === callbackPlugin) or checking if the function is async (like I implemented it here should be a good enough to determine that an non async function with 2 parameters is a sync function?!

Maybe add symbol to check if the method is a sync Function so when we load the plugin we check if syncSymbolKey is true and load it sync instead of async?

Motivation

I want to load some specific plugins sync. now i have to await it to ensure it is loaded when I want to add the next plugin

Example

No response

TypeError: Cannot read properties of undefined (reading 'id')

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Fastify version

3.20.1

Plugin version

7.2.2

Node.js version

16.9

Operating system

Windows

Operating system version (i.e. 20.04, 11.3, 10)

10

Description

I tried adding CSRF protection as described here: https://docs.nestjs.com/security/csrf#use-with-fastify

Then I run: npm run start:dev

Before accessing a route, I got the following errors:

C:\Users\Patrick\dev\nastyville-testing\node_modules\avvio\time-tree.js:40
if (parentNode.id) {

TypeError: Cannot read properties of undefined (reading 'id')
at TimeTree.getParent (C:\Users\Patrick\dev\nastyville-testing\node_modules\avvio\time-tree.js:40:18)
at TimeTree.add (C:\Users\Patrick\dev\nastyville-testing\node_modules\avvio\time-tree.js:63:27)
at TimeTree.start (C:\Users\Patrick\dev\nastyville-testing\node_modules\avvio\time-tree.js:78:15)
at Plugin. (C:\Users\Patrick\dev\nastyville-testing\node_modules\avvio\boot.js:245:36)
at Object.onceWrapper (node:events:514:26)
at Plugin.emit (node:events:394:28)

Steps to Reproduce

See description.

Expected Behavior

No error

Regression: cannot prettyPrint

Below sample can reproduce this problem with [email protected]:

const app = require('avvio')()
app.use(function first(instance, opts, cb) {
  console.log('plugins:', instance.prettyPrint())
  cb()
})

Output:

plugins: bound root undefined ms
└── first undefined ms

/Users/xuxucode/projects/avvio-sample/node_modules/avvio/time-tree.js:31
    if (labelNode.id) {
                  ^

TypeError: Cannot read properties of undefined (reading 'id')
    at [avvio.TimeTree.untrackNode] (/Users/xuxucode/projects/avvio-sample/node_modules/avvio/time-tree.js:31:19)
    at TimeTree.stop (/Users/xuxucode/projects/avvio-sample/node_modules/avvio/time-tree.js:94:25)
    at Plugin.<anonymous> (/Users/xuxucode/projects/avvio-sample/node_modules/avvio/boot.js:254:23)
    at Object.onceWrapper (node:events:626:26)
    at Plugin.emit (node:events:511:28)
    at done (/Users/xuxucode/projects/avvio-sample/node_modules/avvio/plugin.js:197:10)
    at check (/Users/xuxucode/projects/avvio-sample/node_modules/avvio/plugin.js:224:9)
    at node:internal/process/task_queues:140:7
    at AsyncResource.runInAsyncScope (node:async_hooks:206:9)
    at AsyncResource.runMicrotask (node:internal/process/task_queues:137:8)

Node.js v20.1.0

Originally posted by @xuxucode in #156 (comment)

Fix backport event order in master

I'm merging master in next, and the alpha-2 showed this issue

This script:

const avvio = require('.')()

avvio.ready().then(() => {
  console.log('ready')
})

// avvio.ready(() => {
//   console.log('ready')
// })

avvio.on('start', () => {
  console.log('start')
})

Produce:

Branch Promise Callback
next ready then start ready then start
master start then ready ready then start

Because of the change, this test is failing:
https://github.com/fastify/fastify/blob/9b533f5062356e27d997eeee076375deba37cb69/test/decorator.test.js#L712

Because the fastify's server is flaged as started in the start event here:
https://github.com/fastify/fastify/blob/9b533f5062356e27d997eeee076375deba37cb69/fastify.js#L287

I think ready->start is correct execution order, but if I think in all my application's tests, await server.ready() is quite the standard 😭

TypeError: Cannot read property 'nodes' of undefined

Don't know what kind of error it is. I am using fastify 2.1.0 not this plugin directly.

\node_modules\avvio\time-tree.js:54
  parentNode.nodes.push(childNode)
             ^

TypeError: Cannot read property 'nodes' of undefined
    at TimeTree.add (\node_modules\avvio\time-tree.js:54:14)
    at TimeTree.start (\node_modules\avvio\time-tree.js:60:15)
    at Plugin.obj.once (\node_modules\avvio\boot.js:209:38)
    at Object.onceWrapper (events.js:285:20)
    at Plugin.emit (events.js:197:13)
    at Plugin.exec (\node_modules\avvio\plugin.js:88:8)
    at Boot.loadPlugin (\node_modules\avvio\plugin.js:175:10)
    at release (\node_modules\fastq\queue.js:127:16)
    at Object.resume (\node_modules\fastq\queue.js:61:7)
    at Plugin.finish (\node_modules\avvio\plugin.js:166:10)
    at toLoad.exec (\node_modules\avvio\plugin.js:176:12)
    at done (\node_modules\avvio\plugin.js:115:5)
    at processTicksAndRejections (internal/process/next_tick.js:76:17)
[nodemon] app crashed - waiting for file changes before starting...

Ready ignore timeout

The ready queue doesn't apply the timeout parameter.

Is this wanted?

const boot = require('..')

const app = boot({ timeout: 1000 })

// ready with two parameter
app.ready(function (_, done) {
  console.log('ready called')
  setInterval(() => {}, 100)
  // done() // not call done
})

app.on('start', () => {
  // never called
  console.log('start')
})

app.start()

Pass context to ready and after

This allows these functions to be written in other modules. consider the following.

// some setup function that handles post plugin
// loading for some icky plugin.
module.exports = function (context, done) {
  // I can use the context without issue now
  context.dispatch('comms:open')
  done()
}

Support await for `after` method

Hi!
currently the ready method support the promises. So you can write

await avvioInstance.ready()
console.log('UP!')

It can be useful for after method too.

await avvioInstance.use(...).after()
await Promise.all([
  avvioInstance.use(...).after(),
  avvioInstance.use(...).after()
])
...
await avvioInstance.ready()
console.log('UP!')

Move to Fastify org?

Should this repo be moved to the Fastify org? It provides a significant portion of Fastify's core and routinely has to be updated to resolve issues in Fastify. Also, was it not written specifically to make Fastify possible?

ChainAlert: npm package release (7.2.3) has no matching tag in this repo

Dear avvio maintainers,
Thank you for your contribution to the open-source community.

This issue was automatically created to inform you a new version (7.2.3) of avvio was published without a matching tag in this repo.

As part of our efforts to fight software supply chain attacks, we would like to verify this release is known and intended, and not a result of an unauthorized activity.

If you find this behavior legitimate, kindly close and ignore this issue. Read more

badge

Rewrite avvio from scratch

Avvio is a fantastic library and the hearth of the Fastify plugin system. In the first days of Fastify (4 years ago!!) @mcollina and I spent months working on this library, trying to figure out all the moving pieces, and I'm very happy about the outcome.
Avvio is predictable, works well with good enough performances, and has a comprehensive set of features.
Unfortunately, in the past two years, we have focused more on adding features and less on refactoring the library. I guess we can all agree it is something that should be done in the near future, as the internals has become rather complex both to understand and maintain.

This is a long term goal, but I would love to see a complete rewrite of the library by Fastify v4.
The first thing we should do, in my opinion, is to define which features are essential and which are no longer useful or too hard to maintain.
For example, register (use here) being awaitable but also returning Fastify itself is nice but complex to maintain and with its fair amount of edge cases (see #107, #114, and #116). Or .after, which was very useful in the past, but today it's just a difficult component to understand for users with its edge cases as well(see #93 and fastify/fastify-express#3).

Once we have defined which features we should keep, we should write a Fastify application that uses them all, so when we'll start the refactoring we'll make sure that all of them will still work.
We'll use this issue for tracking the effort, let's keep here the discussion about which feature should be kept and then open a separate pr for each one.
It would be nice also to ask the community how are they using the plugin system, so we are sure not to break the most used parts.

cc @fastify/core

this._init is not a function

Hi, I get this error while trying to use boot-in-the-arse.
What am I doing wrong?

Here the code.

Error:

.../node_modules/boot-in-the-arse/boot.js:119
  this._init()
       ^

TypeError: this._init is not a function
    at Function.Boot.use (.../node_modules/boot-in-the-arse/boot.js:119:8)
    at Object.<anonymous> (.../example.js:27:9)
    at Module._compile (module.js:556:32)
    at Object.Module._extensions..js (module.js:565:10)
    at Module.load (module.js:473:32)
    at tryModuleLoad (module.js:432:12)
    at Function.Module._load (module.js:424:3)
    at Module.runMain (module.js:590:10)
    at run (bootstrap_node.js:394:7)
    at startup (bootstrap_node.js:149:9)

Invalid typescript interface for the Use method

I'm trying to use the avvio.Use interface (see below) in a fastify type def.

https://github.com/mcollina/avvio/blob/e2b5bd2d6e04c135249ac956e918c34a9a8aa731/index.d.ts#L67-L69

I don't understand how to invoke the O generic. Can someone either show me how to properly implement this interface or identify this as a bug so I we can fix it.

FWIW, I created a simpler version of it in TypeScript playground and I can't seem to reach that same generic value: playground

Pretty Print Plugin Tree

Hi @mcollina, @smartiniOnGitHub has a neat idea to illustrate the plugin tree for diagnostic purposes as in find-my-way WDYT?

e.g

console.log(avvio.prettyPrint())
// └── /
//   ├── root (fastify v1.0.0) 44ms
//   │   └── /fastify-auth (v0.0.4) 2ms
//   │   └── /fastify-swagger (v0.4.4) 23ms
//   │   └── /fastify-custom (v1.0.0) 19ms
//   │   	└── /fastify-a (v1.0.0) 9ms
//   │   	└── /fastify-b (v1.0.0) 10ms

The tree would illustrate the loading order and other useful informations like plugin name, version, loading-time. Dependencies across different plugins could be marked in different colors. There are many options 😄.

avvio.prettyPrint([updates=false])

Should be able to call it before everything is bootsrapped. The cli can update itself so it's possible to see which plugin has issues to bootstrap.

Release alpha 3

@mcollina Wrongly I pushed a merge from master to next 😅

In order to merge fastify's master to next I need this fix #96 : some tests are failing cause of this.
Could you (or may I) release an alpha-3 version?

Do not load any more plugins with .after() when the previous errors

From Fastify https://github.com/fastify/fastify/pull/2093/files:

test('awaitable after error handling', async t => {
   const fastify = Fastify()
   let first = false
   let second = false
   let third = false

   // Note that this does not throw in case of error,
   // all errors are currently collected to ready() because it's
   // not really possible to do anything about them.
   await fastify.register(async (instance, opts, next) => {
     first = true
     throw new Error('kaboom')
   })
   t.is(first, true)

   fastify.register(async (instance, opts, next) => {
     second = true
   })

   await fastify.after()
   // TODO this is a bug, it should be false
   t.is(second, true)

   fastify.register(async (instance, opts, next) => {
     third = true
   })

   await t.rejects(fastify.ready())
   t.is(third, false)
 })

cc @davidmarkclements

async function as onClose handler

If I register an asynchronous onClose handler as a two argument (context, done) function:

async function myPlugin (instance, opts) {
  instance.onClose((context, done) => {
    setTimeout(done, 3000)
  })
}

when app.close() is called, the next onClose handler does not start until the done() callback is not called on the previous one.

But if I register an asynchronous onClose handler as an async function:

async function myPlugin (instance, opts) {
  instance.onClose(async () => {
    await new Promise(resolve => setTimeout(resolve, 3000))
  })
}

when app.close() is called, the next onClose handler does not wait until the async function finishes, i.e. the promise resolves.

Is it intentional? It would be much easier to write asynchronous onClose handlers with async functions.

override function not throw errors

🐛 Bug Report

override callback function not throw errors

To Reproduce

Steps to reproduce the behavior:

invoke the below code, not throw errors

// Paste your code here
const boot = require('avvio');

const server = {my: 'server'};

const app = boot(server);

app.override = function (s, fn) {
    error;
    return s;
};

app.use(first);

function first(s, opts, cb) {
    cb();
}

Expected behavior

A clear and concise description of what you expected to happen.

should throw error

ReferenceError: error is not defined

Your Environment

  • node version: v12.14.0
  • fastify version: >=2.0.0
  • os: Mac
  • any other relevant information

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.