Socket
Socket
Sign inDemoInstall

fastify

Package Overview
Dependencies
65
Maintainers
4
Versions
280
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 4.25.2 to 4.26.0

.vscode/settings.json

4

build/build-validation.js

@@ -44,3 +44,4 @@ 'use strict'

http2SessionTimeout: 72000, // 72 seconds
exposeHeadRoutes: true
exposeHeadRoutes: true,
useSemicolonDelimiter: true
}

@@ -105,2 +106,3 @@

exposeHeadRoutes: { type: 'boolean', default: defaultInitOptions.exposeHeadRoutes },
useSemicolonDelimiter: { type: 'boolean', default: defaultInitOptions.useSemicolonDelimiter },
// deprecated style of passing the versioning constraint

@@ -107,0 +109,0 @@ versioning: {

@@ -16,3 +16,3 @@ <h1 align="center">Fastify</h1>

- [`@fastify/any-schema`](https://github.com/fastify/any-schema-you-like) Save
multiple schemas and decide which one to use to serialize the payload
multiple schemas and decide which one to use to serialize the payload.
- [`@fastify/auth`](https://github.com/fastify/fastify-auth) Run multiple auth

@@ -57,2 +57,4 @@ functions in Fastify.

generate ETags for HTTP responses.
- [`@fastify/express`](https://github.com/fastify/fastify-express) Express
compatibility layer for Fastify.
- [`@fastify/flash`](https://github.com/fastify/fastify-flash) Set and get flash

@@ -74,2 +76,4 @@ messages using the session.

Fastify, internally uses [fast-jwt](https://github.com/nearform/fast-jwt).
- [`@fastify/kafka`](https://github.com/fastify/fastify-kafka) Plugin to interact
with Apache Kafka.
- [`@fastify/leveldb`](https://github.com/fastify/fastify-leveldb) Plugin to

@@ -84,2 +88,4 @@ share a common LevelDB connection across Fastify.

support for Fastify.
- [`@fastify/mysql`](https://github.com/fastify/fastify-mysql) Fastify MySQL
connection plugin.
- [`@fastify/nextjs`](https://github.com/fastify/fastify-nextjs) React

@@ -92,2 +98,4 @@ server-side rendering support for Fastify with

Fastify's logs into a nice one-line message.
- [`@fastify/passport`](https://github.com/fastify/fastify-passport) Use Passport
strategies to authenticate requests and protect route.
- [`@fastify/postgres`](https://github.com/fastify/fastify-postgres) Fastify

@@ -112,2 +120,4 @@ PostgreSQL connection plugin, with this you can share the same PostgreSQL

provides a `Map` of routes.
- [`@fastify/routes-stats`](https://github.com/fastify/fastify-routes-stats)
Provide stats for routes using `node:perf_hooks`.
- [`@fastify/schedule`](https://github.com/fastify/fastify-schedule) Plugin for

@@ -130,2 +140,4 @@ scheduling periodic jobs, based on

generation.
- [`@fastify/swagger-ui`](https://github.com/fastify/fastify-swagger-ui) Plugin
for serving Swagger UI.
- [`@fastify/throttle`](https://github.com/fastify/fastify-throttle) Plugin for

@@ -135,7 +147,7 @@ throttling the download speed of a request.

Fastify
[type provider](https://www.fastify.dev/docs/latest/Reference/Type-Providers/)
[type provider](https://fastify.dev/docs/latest/Reference/Type-Providers/)
for [json-schema-to-ts](https://github.com/ThomasAribart/json-schema-to-ts).
- [`@fastify/type-provider-typebox`](https://github.com/fastify/fastify-type-provider-typebox)
Fastify
[type provider](https://www.fastify.dev/docs/latest/Reference/Type-Providers/)
[type provider](https://fastify.dev/docs/latest/Reference/Type-Providers/)
for [Typebox](https://github.com/sinclairzx81/typebox).

@@ -153,2 +165,4 @@ - [`@fastify/under-pressure`](https://github.com/fastify/under-pressure) Measure

support for Fastify. Built upon [ws](https://github.com/websockets/ws).
- [`@fastify/zipkin`](https://github.com/fastify/fastify-zipkin) Plugin
for Zipkin distributed tracing system.

@@ -218,5 +232,2 @@ #### [Community](#community)

API key management solution.
- [`apollo-server-fastify`](https://github.com/apollographql/apollo-server/tree/master/packages/apollo-server-fastify)
Run an [Apollo Server](https://github.com/apollographql/apollo-server) to
serve GraphQL with Fastify.
- [`arecibo`](https://github.com/nucleode/arecibo) Fastify ping responder for

@@ -397,2 +408,7 @@ Kubernetes Liveness and Readiness Probes.

[nats.io](https://nats.io/).
- [`fastify-hl7`](https://github.com/Bugs5382/fastify-hl7) A Fastify Plugin to
create a server, build, and send HL7 formatted Hl7 messages. Using
[node-hl7-client](https://github.com/Bugs5382/node-hl7-client) and
[node-hl7-server](https://github.com/Bugs5382/node-hl7-server) as the
underlining technology to do this.
- [`fastify-http-client`](https://github.com/kenuyx/fastify-http-client) Plugin

@@ -551,3 +567,3 @@ to send HTTP(s) requests. Built upon [urllib](https://github.com/node-modules/urllib).

RabbitMQ plugin that uses
[node-amqp-connection-manager](https://github.com/jwalton/node-amqp-connection-manager)
[node-rabbitmq-client](https://github.com/cody-greene/node-rabbitmq-client)
plugin as a wrapper.

@@ -615,2 +631,4 @@ - [`fastify-racing`](https://github.com/metcoder95/fastify-racing) Fastify's

application to a sqlite3 database.
- [`fastify-sqlite-typed`](https://github.com/yoav0gal/fastify-sqlite-typed) connects
your application to a SQLite database with full Typescript support.
- [`fastify-sse`](https://github.com/lolo32/fastify-sse) to provide Server-Sent

@@ -641,7 +659,7 @@ Events with `reply.sse( … )` to Fastify.

Fastify
[type provider](https://www.fastify.dev/docs/latest/Reference/Type-Providers/)
[type provider](https://fastify.dev/docs/latest/Reference/Type-Providers/)
for [@effect/schema](https://github.com/effect-ts/schema).
- [`fastify-type-provider-zod`](https://github.com/turkerdev/fastify-type-provider-zod)
Fastify
[type provider](https://www.fastify.dev/docs/latest/Reference/Type-Providers/)
[type provider](https://fastify.dev/docs/latest/Reference/Type-Providers/)
for [zod](https://github.com/colinhacks/zod).

@@ -648,0 +666,0 @@ - [`fastify-typeorm-plugin`](https://github.com/inthepocket/fastify-typeorm-plugin)

@@ -58,2 +58,10 @@ <h1 align="center">Fastify</h1>

> If you are using ECMAScript Modules (ESM) in your project, be sure to
> include "type": "module" in your package.json.
>```js
>{
> "type": "module"
>}
>```
Do you prefer to use `async/await`? Fastify supports it out-of-the-box.

@@ -176,2 +184,3 @@

```js

@@ -183,3 +192,3 @@ // our-first-route.js

* @param {FastifyInstance} fastify Encapsulated Fastify Instance
* @param {Object} options plugin options, refer to https://www.fastify.dev/docs/latest/Reference/Plugins/#plugin-options
* @param {Object} options plugin options, refer to https://fastify.dev/docs/latest/Reference/Plugins/#plugin-options
*/

@@ -192,2 +201,6 @@ async function routes (fastify, options) {

//ESM
export default routes;
// CommonJs
module.exports = routes

@@ -300,3 +313,3 @@ ```

* @param {FastifyInstance} fastify Encapsulated Fastify Instance
* @param {Object} options plugin options, refer to https://www.fastify.dev/docs/latest/Reference/Plugins/#plugin-options
* @param {Object} options plugin options, refer to https://fastify.dev/docs/latest/Reference/Plugins/#plugin-options
*/

@@ -320,3 +333,3 @@ async function dbConnector (fastify, options) {

* @param {FastifyInstance} fastify encapsulated fastify instance
* @param {Object} options plugin options, refer to https://www.fastify.dev/docs/latest/Reference/Plugins/#plugin-options
* @param {Object} options plugin options, refer to https://fastify.dev/docs/latest/Reference/Plugins/#plugin-options
*/

@@ -323,0 +336,0 @@ async function routes (fastify, options) {

@@ -16,3 +16,3 @@ # Fastify Style Guide

Visit the [contribute](https://www.fastify.dev/contribute) page on our website or
Visit the [contribute](https://fastify.dev/contribute) page on our website or
read the

@@ -74,3 +74,3 @@ [CONTRIBUTING.md](https://github.com/fastify/fastify/blob/main/CONTRIBUTING.md)

```
To learn more about hooks, see [Fastify hooks](https://www.fastify.dev/docs/latest/Reference/Hooks/).
To learn more about hooks, see [Fastify hooks](https://fastify.dev/docs/latest/Reference/Hooks/).
```

@@ -80,3 +80,3 @@

>To learn more about hooks, see [Fastify
>hooks](https://www.fastify.dev/docs/latest/Reference/Hooks/).
>hooks](https://fastify.dev/docs/latest/Reference/Hooks/).

@@ -230,3 +230,3 @@

// Add clear & brief description
[Fastify Plugins] (https://www.fastify.dev/docs/latest/Plugins/)
[Fastify Plugins] (https://fastify.dev/docs/latest/Plugins/)

@@ -236,9 +236,9 @@ <!--Less like this -->

// incomplete description
[Fastify] (https://www.fastify.dev/docs/latest/Plugins/)
[Fastify] (https://fastify.dev/docs/latest/Plugins/)
// Adding title in link brackets
[](https://www.fastify.dev/docs/latest/Plugins/ "fastify plugin")
[](https://fastify.dev/docs/latest/Plugins/ "fastify plugin")
// Empty title
[](https://www.fastify.dev/docs/latest/Plugins/)
[](https://fastify.dev/docs/latest/Plugins/)

@@ -245,0 +245,0 @@ // Adding links localhost URLs instead of using code strings (``)

@@ -104,3 +104,3 @@ <h1 align="center">Fastify</h1>

The decorated [Fastify server](./Server.md) is bound to `this` in
route [route](./Routes.md) handlers:
[route](./Routes.md) handlers:

@@ -107,0 +107,0 @@ ```js

@@ -48,2 +48,3 @@ <h1 align="center">Fastify</h1>

- [FST_ERR_REP_INVALID_PAYLOAD_TYPE](#fst_err_rep_invalid_payload_type)
- [FST_ERR_REP_RESPONSE_BODY_CONSUMED](#fst_err_rep_response_body_consumed)
- [FST_ERR_REP_ALREADY_SENT](#fst_err_rep_already_sent)

@@ -173,2 +174,61 @@ - [FST_ERR_REP_SENT_VALUE](#fst_err_rep_sent_value)

When utilizing Fastify's custom error handling through [`setErrorHandler`](./Server.md#seterrorhandler),
you should be aware of how errors are propagated between custom and default
error handlers.
If a plugin's error handler re-throws an error, and the error is not an
instance of [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)
(as seen in the `/bad` route in the following example), it will not propagate
to the parent context error handler. Instead, it will be caught by the default
error handler.
To ensure consistent error handling, it is recommended to throw instances of
`Error`. For instance, in the following example, replacing `throw 'foo'` with
`throw new Error('foo')` in the `/bad` route ensures that errors propagate through
the custom error handling chain as intended. This practice helps avoid potential
pitfalls when working with custom error handling in Fastify.
For example:
```js
const Fastify = require('fastify')
// Instantiate the framework
const fastify = Fastify({
logger: true
})
// Register parent error handler
fastify.setErrorHandler((error, request, reply) => {
reply.status(500).send({ ok: false })
})
fastify.register((app, options, next) => {
// Register child error handler
fastify.setErrorHandler((error, request, reply) => {
throw error
})
fastify.get('/bad', async () => {
// Throws a non-Error type, 'bar'
throw 'foo'
})
fastify.get('/good', async () => {
// Throws an Error instance, 'bar'
throw new Error('bar')
})
next()
})
// Run the server
fastify.listen({ port: 3000 }, function (err, address) {
if (err) {
fastify.log.error(err)
process.exit(1)
}
// Server is listening at ${address}
})
```
### Fastify Error Codes

@@ -188,3 +248,3 @@ <a id="fastify-error-codes"></a>

```js
const Fastify = require('./fastify')
const Fastify = require('fastify')

@@ -259,2 +319,4 @@ // Instantiate the framework

| <a id="fst_err_rep_invalid_payload_type">FST_ERR_REP_INVALID_PAYLOAD_TYPE</a> | Reply payload can be either a `string` or a `Buffer`. | Use a `string` or a `Buffer` for the payload. | [#1168](https://github.com/fastify/fastify/pull/1168) |
| <a id="fst_err_rep_response_body_consumed">FST_ERR_REP_RESPONSE_BODY_CONSUMED</a> | Using `Response` as reply payload
but the body is being consumed. | Make sure you don't consume the `Response.body` | [#5286](https://github.com/fastify/fastify/pull/5286) |
| <a id="fst_err_rep_already_sent">FST_ERR_REP_ALREADY_SENT</a> | A response was already sent. | - | [#1336](https://github.com/fastify/fastify/pull/1336) |

@@ -261,0 +323,0 @@ | <a id="fst_err_rep_sent_value">FST_ERR_REP_SENT_VALUE</a> | The only possible value for `reply.sent` is `true`. | - | [#1336](https://github.com/fastify/fastify/pull/1336) |

@@ -235,3 +235,3 @@ <h1 align="center">Fastify</h1>

Note: If you change the payload, you may only change it to a `string`, a
`Buffer`, a `stream`, or `null`.
`Buffer`, a `stream`, a `ReadableStream`, a `Response`, or `null`.

@@ -238,0 +238,0 @@

@@ -64,5 +64,5 @@ <h1 align="center">Fastify</h1>

If you want to pass some options to the logger, just pass them to Fastify. You
can find all available options in the [Pino
documentation](https://github.com/pinojs/pino/blob/master/docs/api.md#pinooptions-stream).
If you want to pass some options to the logger, just pass them to Fastify.
You can find all available options in the
[Pino documentation](https://github.com/pinojs/pino/blob/master/docs/api.md#options).
If you want to specify a file destination, use:

@@ -69,0 +69,0 @@

@@ -7,2 +7,3 @@ <h1 align="center">Fastify</h1>

- [.code(statusCode)](#codestatuscode)
- [.elapsedTime](#elapsedtime)
- [.statusCode](#statuscode)

@@ -36,2 +37,4 @@ - [.server](#server)

- [Buffers](#buffers)
- [ReadableStream](#send-readablestream)
- [Response](#send-response)
- [Errors](#errors)

@@ -51,2 +54,4 @@ - [Type of the final payload](#type-of-the-final-payload)

- `.statusCode` - Read and set the HTTP status code.
- `.elapsedTime` - Returns the amount of time passed
since the request was received by Fastify.
- `.server` - A reference to the fastify instance object.

@@ -91,2 +96,4 @@ - `.header(name, value)` - Sets a response header.

- `.request` - The incoming request.
- `.getResponseTime()` - Deprecated, returns the amount of time passed
since the request was received by Fastify.
- `.context` - Deprecated, access the [Request's context](./Request.md) property.

@@ -117,2 +124,15 @@

### .elapsedTime
<a id="elapsedTime"></a>
Invokes the custom response time getter to calculate the amount of time passed
since the request was received by Fastify.
Note that unless this function is called in the [`onResponse`
hook](./Hooks.md#onresponse) it will always return `0`.
```js
const milliseconds = reply.elapsedTime
```
### .statusCode

@@ -335,3 +355,3 @@ <a id="statusCode"></a>

Invokes the custom response time getter to calculate the amount of time passed
since the request was started.
since the request was received by Fastify.

@@ -345,2 +365,5 @@ Note that unless this function is called in the [`onResponse`

*Note: This method is deprecated and will be removed in `fastify@5`.
Use the [.elapsedTime](#elapsedtime) property instead.*
### .type(contentType)

@@ -745,2 +768,48 @@ <a id="type"></a>

#### ReadableStream
<a id="send-readablestream"></a>
`ReadableStream` will be treated as a node stream mentioned above,
the content is considered to be pre-serialized, so they will be
sent unmodified without response validation.
```js
const fs = require('node:fs')
const { ReadableStream } = require('node:stream/web')
fastify.get('/streams', function (request, reply) {
const stream = fs.createReadStream('some-file')
reply.header('Content-Type', 'application/octet-stream')
reply.send(ReadableStream.from(stream))
})
```
#### Response
<a id="send-response"></a>
`Response` allows to manage the reply payload, status code and
headers in one place. The payload provided inside `Response` is
considered to be pre-serialized, so they will be sent unmodified
without response validation.
Plese be aware when using `Response`, the status code and headers
will not directly reflect to `reply.statusCode` and `reply.getHeaders()`.
Such behavior is based on `Response` only allow `readonly` status
code and headers. The data is not allow to be bi-direction editing,
and may confuse when checking the `payload` in `onSend` hooks.
```js
const fs = require('node:fs')
const { ReadableStream } = require('node:stream/web')
fastify.get('/streams', function (request, reply) {
const stream = fs.createReadStream('some-file')
const readableStream = ReadableStream.from(stream)
const response = new Response(readableStream, {
status: 200,
headers: { 'content-type': 'application/octet-stream' }
})
reply.send(response)
})
```
#### Errors

@@ -747,0 +816,0 @@ <a id="errors"></a>

@@ -26,2 +26,3 @@

- [FSTDEP019](#FSTDEP019)
- [FSTDEP020](#FSTDEP020)

@@ -79,1 +80,2 @@

| <a id="FSTDEP019">FSTDEP019</a> | You are accessing the deprecated `reply.context` property. | Use `reply.routeOptions.config` or `reply.routeOptions.schema`. | [#5032](https://github.com/fastify/fastify/pull/5032) [#5084](https://github.com/fastify/fastify/pull/5084) |
| <a id="FSTDEP020">FSTDEP020</a> | You are using the deprecated `reply.getReponseTime()` method. | Use the `reply.elapsedTime` property instead. | [#5263](https://github.com/fastify/fastify/pull/5263) |

@@ -17,3 +17,3 @@ import * as http from 'http'

import { FastifyListenOptions, FastifyInstance, PrintRoutesOptions } from './types/instance'
import { FastifyBaseLogger, FastifyLoggerInstance, FastifyLoggerOptions, PinoLoggerOptions, FastifyLogFn, LogLevel, Bindings, ChildLoggerOptions } from './types/logger'
import { FastifyBaseLogger, FastifyLoggerInstance, FastifyLoggerOptions, PinoLoggerOptions, FastifyLogFn, LogLevel } from './types/logger'
import { FastifyPluginCallback, FastifyPluginAsync, FastifyPluginOptions, FastifyPlugin } from './types/plugin'

@@ -113,2 +113,3 @@ import { FastifyRegister, FastifyRegisterOptions, RegisterOptions } from './types/register'

requestIdLogLabel?: string;
useSemicolonDelimiter?: boolean,
jsonShorthand?: boolean;

@@ -243,2 +244,2 @@ genReqId?: (req: RawRequestDefaultExpression<RawServer>) => string,

// const fastify = require('fastify')
export = fastify
export = fastify
'use strict'
const VERSION = '4.25.2'
const VERSION = '4.26.0'

@@ -31,3 +31,4 @@ const Avvio = require('avvio')

kKeepAliveConnections,
kChildLoggerFactory
kChildLoggerFactory,
kGenReqId
} = require('./lib/symbols.js')

@@ -46,3 +47,3 @@

const pluginUtils = require('./lib/pluginUtils')
const { reqIdGenFactory } = require('./lib/reqIdGenFactory')
const { getGenReqId, reqIdGenFactory } = require('./lib/reqIdGenFactory')
const { buildRouting, validateBodyLimitOption } = require('./lib/route')

@@ -143,3 +144,2 @@ const build404 = require('./lib/fourOhFour')

options.logger = logger
options.genReqId = genReqId
options.requestIdHeader = requestIdHeader

@@ -187,3 +187,4 @@ options.requestIdLogLabel = requestIdLogLabel

buildPrettyMeta: defaultBuildPrettyMeta,
querystringParser: options.querystringParser
querystringParser: options.querystringParser,
useSemicolonDelimiter: options.useSemicolonDelimiter ?? defaultInitOptions.useSemicolonDelimiter
}

@@ -254,2 +255,3 @@ })

[kAvvioBoot]: null,
[kGenReqId]: genReqId,
// routing method

@@ -293,2 +295,5 @@ routing: httpHandler,

},
findRoute: function _findRoute (options) {
return router.findRoute(options)
},
// expose logger instance

@@ -309,2 +314,4 @@ log: logger,

setSchemaErrorFormatter,
// set generated request id
setGenReqId,
// custom parsers

@@ -406,2 +413,6 @@ addContentTypeParser: ContentTypeParser.helpers.addContentTypeParser,

}
},
genReqId: {
configurable: true,
get () { return this[kGenReqId] }
}

@@ -750,3 +761,3 @@ })

if (frameworkErrors) {
const id = genReqId(req)
const id = getGenReqId(onBadUrlContext.server, req)
const childLogger = createChildLogger(onBadUrlContext, logger, req, id)

@@ -776,3 +787,3 @@

if (frameworkErrors) {
const id = genReqId(req)
const id = getGenReqId(onBadUrlContext.server, req)
const childLogger = createChildLogger(onBadUrlContext, logger, req, id)

@@ -881,2 +892,9 @@

}
function setGenReqId (func) {
throwIfAlreadyStarted('Cannot call "setGenReqId"!')
this[kGenReqId] = reqIdGenFactory(this[kOptions].requestIdHeader, func)
return this
}
}

@@ -883,0 +901,0 @@

# Fastify Project Governance
<!-- TOC -->
* [Lead Maintainers](#lead-maintainers)
* [Collaborators](#collaborators)
* [Collaborator activities](#collaborator-activities)
* [Great Contributors](#great-contributors)
* [Collaborator nominations](#collaborator-maintainers-nominations)
* [Lead Maintainers nominations](#lead-maintainers-nominations)
* [Consensus seeking process](#consensus-seeking-process)
<!-- /TOC -->
## Lead Maintainers
Fastify Lead Maintainers are the organization owners.
They are the only members of the `@fastify/leads` team. The Lead
Maintainers are the curator of the Fastify project and their key responsibility
is to issue releases of Fastify and its dependencies.
## Collaborators
Fastify Collaborators maintain the projects of the Fastify organization.
They are split into the following teams:
| Team | Responsibility | Repository |
|---|---|---|
| `@fastify/leads` | Fastify Lead Maintainers | GitHub organization owners |
| `@fastify/core` | Fastify Core development | `fastify`, `fast-json-stringify`, `light-my-request`, `fastify-plugin`, `middie` |
| `@fastify/plugins` | Build, maintain and release Fastify plugins | All plugins repositories |
| `@fastify/benchmarks` | Build and maintain our benchmarks suite | `benchmarks` |
| `@fastify/docs-chinese` | Translate the Fastify documentation in Chinese | `docs-chinese` |
Every member of the org is also part of `@fastify/fastify`.
Collaborators have:
* Commit access to the projects repository of the team they belong
* Grant to release new versions of the project
Both Collaborators and non-Collaborators may propose changes to the source code
of the projects of the organization. The mechanism to propose such a change is a
GitHub pull request. Collaborators review and merge (_land_) pull requests
following the [CONTRIBUTING](CONTRIBUTING.md#rules) guidelines.
### Collaborator activities
* Helping users and novice contributors
* Contributing code and documentation changes that improve the project
* Reviewing and commenting on issues and pull requests
* Participation in working groups
* Merging pull requests
* Release plugins
The Lead Maintainers can remove inactive Collaborators or provide them with
_Past Collaborators_ status. Past Collaborators may request that the Lead
Maintainers restore them to active status.
## Great Contributors
Great contributors on a specific area in the Fastify ecosystem will be invited
to join this group by Lead Maintainers. This group has the same permissions of a
contributor.
## Collaborator nominations
Individuals making significant and valuable contributions to the project may be
a candidate to join the Fastify organization.
A Collaborator needs to open a private team discussion on GitHub and list the
candidates they want to sponsor with a link to the user's contributions. For
example:
* Activities in the Fastify organization
`[USERNAME](https://github.com/search?q=author:USERNAME+org:fastify)`
Otherwise, a Contributor may self-apply if they believe they meet the above
criteria by reaching out to a Lead Maintainer privately with the links to their
valuable contributions. The Lead Maintainers will reply to the Contributor and
will decide if candidate it to be made a collaborator.
The consensus to grant a new candidate Collaborator status is reached when:
- at least one of the Lead Maintainers approve
- at least two of the Team Members approve
After these conditions are satisfied, the [onboarding
process](CONTRIBUTING.md#onboarding-collaborators) may start.
## Lead Maintainers nominations
A Team Member may be promoted to a Lead Maintainers only through nomination by a
Lead maintainer and with agreement from the rest of Lead Maintainers.
## Consensus seeking process
The Fastify organization follows a [Consensus Seeking][] decision-making model.
[Consensus Seeking]:
https://en.wikipedia.org/wiki/Consensus-seeking_decision-making
Please see Fastify's [organization-wide governance
](https://github.com/fastify/.github/blob/main/GOVERNANCE.md) document.

@@ -6,3 +6,3 @@ // This file is autogenerated by build/build-validation.js, do not edit

module.exports.default = validate10;
const schema11 = {"type":"object","additionalProperties":false,"properties":{"connectionTimeout":{"type":"integer","default":0},"keepAliveTimeout":{"type":"integer","default":72000},"forceCloseConnections":{"oneOf":[{"type":"string","pattern":"idle"},{"type":"boolean"}]},"maxRequestsPerSocket":{"type":"integer","default":0,"nullable":true},"requestTimeout":{"type":"integer","default":0},"bodyLimit":{"type":"integer","default":1048576},"caseSensitive":{"type":"boolean","default":true},"allowUnsafeRegex":{"type":"boolean","default":false},"http2":{"type":"boolean"},"https":{"if":{"not":{"oneOf":[{"type":"boolean"},{"type":"null"},{"type":"object","additionalProperties":false,"required":["allowHTTP1"],"properties":{"allowHTTP1":{"type":"boolean"}}}]}},"then":{"setDefaultValue":true}},"ignoreTrailingSlash":{"type":"boolean","default":false},"ignoreDuplicateSlashes":{"type":"boolean","default":false},"disableRequestLogging":{"type":"boolean","default":false},"jsonShorthand":{"type":"boolean","default":true},"maxParamLength":{"type":"integer","default":100},"onProtoPoisoning":{"type":"string","default":"error"},"onConstructorPoisoning":{"type":"string","default":"error"},"pluginTimeout":{"type":"integer","default":10000},"requestIdHeader":{"anyOf":[{"enum":[false]},{"type":"string"}],"default":"request-id"},"requestIdLogLabel":{"type":"string","default":"reqId"},"http2SessionTimeout":{"type":"integer","default":72000},"exposeHeadRoutes":{"type":"boolean","default":true},"versioning":{"type":"object","additionalProperties":true,"required":["storage","deriveVersion"],"properties":{"storage":{},"deriveVersion":{}}},"constraints":{"type":"object","additionalProperties":{"type":"object","required":["name","storage","validate","deriveConstraint"],"additionalProperties":true,"properties":{"name":{"type":"string"},"storage":{},"validate":{},"deriveConstraint":{}}}}}};
const schema11 = {"type":"object","additionalProperties":false,"properties":{"connectionTimeout":{"type":"integer","default":0},"keepAliveTimeout":{"type":"integer","default":72000},"forceCloseConnections":{"oneOf":[{"type":"string","pattern":"idle"},{"type":"boolean"}]},"maxRequestsPerSocket":{"type":"integer","default":0,"nullable":true},"requestTimeout":{"type":"integer","default":0},"bodyLimit":{"type":"integer","default":1048576},"caseSensitive":{"type":"boolean","default":true},"allowUnsafeRegex":{"type":"boolean","default":false},"http2":{"type":"boolean"},"https":{"if":{"not":{"oneOf":[{"type":"boolean"},{"type":"null"},{"type":"object","additionalProperties":false,"required":["allowHTTP1"],"properties":{"allowHTTP1":{"type":"boolean"}}}]}},"then":{"setDefaultValue":true}},"ignoreTrailingSlash":{"type":"boolean","default":false},"ignoreDuplicateSlashes":{"type":"boolean","default":false},"disableRequestLogging":{"type":"boolean","default":false},"jsonShorthand":{"type":"boolean","default":true},"maxParamLength":{"type":"integer","default":100},"onProtoPoisoning":{"type":"string","default":"error"},"onConstructorPoisoning":{"type":"string","default":"error"},"pluginTimeout":{"type":"integer","default":10000},"requestIdHeader":{"anyOf":[{"enum":[false]},{"type":"string"}],"default":"request-id"},"requestIdLogLabel":{"type":"string","default":"reqId"},"http2SessionTimeout":{"type":"integer","default":72000},"exposeHeadRoutes":{"type":"boolean","default":true},"useSemicolonDelimiter":{"type":"boolean","default":true},"versioning":{"type":"object","additionalProperties":true,"required":["storage","deriveVersion"],"properties":{"storage":{},"deriveVersion":{}}},"constraints":{"type":"object","additionalProperties":{"type":"object","required":["name","storage","validate","deriveConstraint"],"additionalProperties":true,"properties":{"name":{"type":"string"},"storage":{},"validate":{},"deriveConstraint":{}}}}}};
const func2 = Object.prototype.hasOwnProperty;

@@ -73,2 +73,5 @@ const pattern0 = new RegExp("idle", "u");

}
if(data.useSemicolonDelimiter === undefined){
data.useSemicolonDelimiter = true;
}
const _errs1 = errors;

@@ -990,9 +993,34 @@ for(const key0 in data){

if(valid0){
let data23 = data.useSemicolonDelimiter;
const _errs68 = errors;
if(typeof data23 !== "boolean"){
let coerced25 = undefined;
if(!(coerced25 !== undefined)){
if(data23 === "false" || data23 === 0 || data23 === null){
coerced25 = false;
}
else if(data23 === "true" || data23 === 1){
coerced25 = true;
}
else {
validate10.errors = [{instancePath:instancePath+"/useSemicolonDelimiter",schemaPath:"#/properties/useSemicolonDelimiter/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"}];
return false;
}
}
if(coerced25 !== undefined){
data23 = coerced25;
if(data !== undefined){
data["useSemicolonDelimiter"] = coerced25;
}
}
}
var valid0 = _errs68 === errors;
if(valid0){
if(data.versioning !== undefined){
let data23 = data.versioning;
const _errs68 = errors;
if(errors === _errs68){
if(data23 && typeof data23 == "object" && !Array.isArray(data23)){
let data24 = data.versioning;
const _errs70 = errors;
if(errors === _errs70){
if(data24 && typeof data24 == "object" && !Array.isArray(data24)){
let missing1;
if(((data23.storage === undefined) && (missing1 = "storage")) || ((data23.deriveVersion === undefined) && (missing1 = "deriveVersion"))){
if(((data24.storage === undefined) && (missing1 = "storage")) || ((data24.deriveVersion === undefined) && (missing1 = "deriveVersion"))){
validate10.errors = [{instancePath:instancePath+"/versioning",schemaPath:"#/properties/versioning/required",keyword:"required",params:{missingProperty: missing1},message:"must have required property '"+missing1+"'"}];

@@ -1007,3 +1035,3 @@ return false;

}
var valid0 = _errs68 === errors;
var valid0 = _errs70 === errors;
}

@@ -1015,13 +1043,13 @@ else {

if(data.constraints !== undefined){
let data24 = data.constraints;
const _errs71 = errors;
if(errors === _errs71){
if(data24 && typeof data24 == "object" && !Array.isArray(data24)){
for(const key2 in data24){
let data25 = data24[key2];
const _errs74 = errors;
if(errors === _errs74){
let data25 = data.constraints;
const _errs73 = errors;
if(errors === _errs73){
if(data25 && typeof data25 == "object" && !Array.isArray(data25)){
for(const key2 in data25){
let data26 = data25[key2];
const _errs76 = errors;
if(errors === _errs76){
if(data26 && typeof data26 == "object" && !Array.isArray(data26)){
let missing2;
if(((((data25.name === undefined) && (missing2 = "name")) || ((data25.storage === undefined) && (missing2 = "storage"))) || ((data25.validate === undefined) && (missing2 = "validate"))) || ((data25.deriveConstraint === undefined) && (missing2 = "deriveConstraint"))){
if(((((data26.name === undefined) && (missing2 = "name")) || ((data26.storage === undefined) && (missing2 = "storage"))) || ((data26.validate === undefined) && (missing2 = "validate"))) || ((data26.deriveConstraint === undefined) && (missing2 = "deriveConstraint"))){
validate10.errors = [{instancePath:instancePath+"/constraints/" + key2.replace(/~/g, "~0").replace(/\//g, "~1"),schemaPath:"#/properties/constraints/additionalProperties/required",keyword:"required",params:{missingProperty: missing2},message:"must have required property '"+missing2+"'"}];

@@ -1031,13 +1059,13 @@ return false;

else {
if(data25.name !== undefined){
let data26 = data25.name;
if(typeof data26 !== "string"){
let dataType25 = typeof data26;
let coerced25 = undefined;
if(!(coerced25 !== undefined)){
if(dataType25 == "number" || dataType25 == "boolean"){
coerced25 = "" + data26;
if(data26.name !== undefined){
let data27 = data26.name;
if(typeof data27 !== "string"){
let dataType26 = typeof data27;
let coerced26 = undefined;
if(!(coerced26 !== undefined)){
if(dataType26 == "number" || dataType26 == "boolean"){
coerced26 = "" + data27;
}
else if(data26 === null){
coerced25 = "";
else if(data27 === null){
coerced26 = "";
}

@@ -1049,6 +1077,6 @@ else {

}
if(coerced25 !== undefined){
data26 = coerced25;
if(data25 !== undefined){
data25["name"] = coerced25;
if(coerced26 !== undefined){
data27 = coerced26;
if(data26 !== undefined){
data26["name"] = coerced26;
}

@@ -1065,3 +1093,3 @@ }

}
var valid7 = _errs74 === errors;
var valid7 = _errs76 === errors;
if(!valid7){

@@ -1077,3 +1105,3 @@ break;

}
var valid0 = _errs71 === errors;
var valid0 = _errs73 === errors;
}

@@ -1108,2 +1136,3 @@ else {

}
}
else {

@@ -1119,2 +1148,2 @@ validate10.errors = [{instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"}];

module.exports.defaultInitOptions = {"connectionTimeout":0,"keepAliveTimeout":72000,"maxRequestsPerSocket":0,"requestTimeout":0,"bodyLimit":1048576,"caseSensitive":true,"allowUnsafeRegex":false,"disableRequestLogging":false,"jsonShorthand":true,"ignoreTrailingSlash":false,"ignoreDuplicateSlashes":false,"maxParamLength":100,"onProtoPoisoning":"error","onConstructorPoisoning":"error","pluginTimeout":10000,"requestIdHeader":"request-id","requestIdLogLabel":"reqId","http2SessionTimeout":72000,"exposeHeadRoutes":true}
module.exports.defaultInitOptions = {"connectionTimeout":0,"keepAliveTimeout":72000,"maxRequestsPerSocket":0,"requestTimeout":0,"bodyLimit":1048576,"caseSensitive":true,"allowUnsafeRegex":false,"disableRequestLogging":false,"jsonShorthand":true,"ignoreTrailingSlash":false,"ignoreDuplicateSlashes":false,"maxParamLength":100,"onProtoPoisoning":"error","onConstructorPoisoning":"error","pluginTimeout":10000,"requestIdHeader":"request-id","requestIdLogLabel":"reqId","http2SessionTimeout":72000,"exposeHeadRoutes":true,"useSemicolonDelimiter":true}

@@ -160,3 +160,2 @@ 'use strict'

const parser = this.getParser(contentType)
const resource = new AsyncResource('content-type-parser:run', request)

@@ -169,3 +168,10 @@ if (parser === undefined) {

}
} else if (parser.asString === true || parser.asBuffer === true) {
// Early return to avoid allocating an AsyncResource if it's not needed
return
}
const resource = new AsyncResource('content-type-parser:run', request)
if (parser.asString === true || parser.asBuffer === true) {
rawBody(

@@ -189,2 +195,3 @@ request,

resource.runInAsyncScope(() => {
resource.emitDestroy()
if (error) {

@@ -191,0 +198,0 @@ reply[kReplyIsError] = true

@@ -98,2 +98,3 @@ 'use strict'

const statusCode = reply.statusCode
reply[kReplyHeaders]['content-type'] = reply[kReplyHeaders]['content-type'] ?? 'application/json; charset=utf-8'
let payload

@@ -125,3 +126,2 @@ try {

reply[kReplyHeaders]['content-type'] = 'application/json; charset=utf-8'
reply[kReplyHeaders]['content-length'] = '' + Buffer.byteLength(payload)

@@ -128,0 +128,0 @@

@@ -26,2 +26,4 @@ // This file is autogenerated by build/build-error-serializer.js, do not edit

if (obj === null) return '{}'

@@ -28,0 +30,0 @@ let addComma = false

@@ -215,2 +215,6 @@ 'use strict'

),
FST_ERR_REP_RESPONSE_BODY_CONSUMED: createError(
'FST_ERR_REP_RESPONSE_BODY_CONSUMED',
'Response.body is already consumed.'
),
FST_ERR_REP_ALREADY_SENT: createError(

@@ -217,0 +221,0 @@ 'FST_ERR_REP_ALREADY_SENT',

@@ -22,2 +22,3 @@ 'use strict'

const { createChildLogger } = require('./logger')
const { getGenReqId } = require('./reqIdGenFactory.js')

@@ -32,3 +33,3 @@ /**

function fourOhFour (options) {
const { logger, genReqId } = options
const { logger } = options

@@ -63,4 +64,4 @@ // 404 router, used for handling encapsulated 404 handlers

return function onBadUrl (path, req, res) {
const id = genReqId(req)
const fourOhFourContext = this[kFourOhFourLevelInstance][kFourOhFourContext]
const id = getGenReqId(fourOhFourContext.server, req)
const childLogger = createChildLogger(fourOhFourContext, logger, req, id)

@@ -172,4 +173,4 @@ const request = new Request(id, null, req, null, childLogger, fourOhFourContext)

// we can
const id = genReqId(req)
const fourOhFourContext = this[kFourOhFourLevelInstance][kFourOhFourContext]
const id = getGenReqId(fourOhFourContext.server, req)
const childLogger = createChildLogger(fourOhFourContext, logger, req, id)

@@ -176,0 +177,0 @@

@@ -184,6 +184,2 @@ 'use strict'

if (hooksLen === 0) {
return
}
let i = 0

@@ -200,3 +196,3 @@ let c = 0

) {
if (c < server[kChildren].length) {
while (c < server[kChildren].length) {
const child = server[kChildren][c++]

@@ -203,0 +199,0 @@ onListenHookRunner(child)

'use strict'
const eos = require('node:stream').finished
const Readable = require('node:stream').Readable

@@ -47,2 +48,3 @@ const {

FST_ERR_REP_INVALID_PAYLOAD_TYPE,
FST_ERR_REP_RESPONSE_BODY_CONSUMED,
FST_ERR_REP_ALREADY_SENT,

@@ -57,4 +59,6 @@ FST_ERR_REP_SENT_VALUE,

} = require('./errors')
const { FSTDEP010, FSTDEP013, FSTDEP019 } = require('./warnings')
const { FSTDEP010, FSTDEP013, FSTDEP019, FSTDEP020 } = require('./warnings')
const toString = Object.prototype.toString
function Reply (res, request, log) {

@@ -89,2 +93,10 @@ this.raw = res

},
elapsedTime: {
get () {
if (this[kReplyStartTime] === undefined) {
return 0
}
return (this[kReplyEndTime] || now()) - this[kReplyStartTime]
}
},
server: {

@@ -161,3 +173,10 @@ get () {

if (payload !== null) {
if (typeof payload.pipe === 'function') {
if (
// node:stream
typeof payload.pipe === 'function' ||
// node:stream/web
typeof payload.getReader === 'function' ||
// Response
toString.call(payload) === '[object Response]'
) {
onSendHook(this, payload)

@@ -459,10 +478,7 @@ return this

// TODO: should be removed in fastify@5
Reply.prototype.getResponseTime = function () {
let responseTime = 0
FSTDEP020()
if (this[kReplyStartTime] !== undefined) {
responseTime = (this[kReplyEndTime] || now()) - this[kReplyStartTime]
}
return responseTime
return this.elapsedTime
}

@@ -573,3 +589,2 @@

const req = reply.request
const statusCode = res.statusCode

@@ -590,2 +605,13 @@ // we check if we need to update the trailers header and set it

// since Response contain status code, we need to update before
// any action that used statusCode
const isResponse = toString.call(payload) === '[object Response]'
if (isResponse) {
// https://developer.mozilla.org/en-US/docs/Web/API/Response/status
if (typeof payload.status === 'number') {
reply.code(payload.status)
}
}
const statusCode = res.statusCode
if (payload === undefined || payload === null) {

@@ -622,2 +648,3 @@ // according to https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2

// node:stream
if (typeof payload.pipe === 'function') {

@@ -628,2 +655,28 @@ sendStream(payload, res, reply)

// node:stream/web
if (typeof payload.getReader === 'function') {
sendWebStream(payload, res, reply)
return
}
// Response
if (isResponse) {
// https://developer.mozilla.org/en-US/docs/Web/API/Response/headers
if (typeof payload.headers === 'object' && typeof payload.headers.forEach === 'function') {
for (const [headerName, headerValue] of payload.headers) {
reply.header(headerName, headerValue)
}
}
// https://developer.mozilla.org/en-US/docs/Web/API/Response/body
if (payload.body != null) {
if (payload.bodyUsed) {
throw new FST_ERR_REP_RESPONSE_BODY_CONSUMED()
}
// Response.body always a ReadableStream
sendWebStream(payload.body, res, reply)
}
return
}
if (typeof payload !== 'string' && !Buffer.isBuffer(payload)) {

@@ -661,2 +714,7 @@ throw new FST_ERR_REP_INVALID_PAYLOAD_TYPE(typeof payload)

function sendWebStream (payload, res, reply) {
const nodeStream = Readable.fromWeb(payload)
sendStream(nodeStream, res, reply)
}
function sendStream (payload, res, reply) {

@@ -826,3 +884,3 @@ let sourceOpen = true

const responseTime = reply.getResponseTime()
const responseTime = reply.elapsedTime

@@ -829,0 +887,0 @@ if (err != null) {

@@ -24,2 +24,6 @@ 'use strict'

function getGenReqId (contextServer, req) {
return contextServer.genReqId(req)
}
function buildDefaultGenReqId () {

@@ -47,3 +51,4 @@ // 2,147,483,647 (2^31 − 1) stands for max SMI value (an internal optimization of V8).

module.exports = {
getGenReqId,
reqIdGenFactory
}

@@ -59,2 +59,3 @@ 'use strict'

const { createChildLogger } = require('./logger')
const { getGenReqId } = require('./reqIdGenFactory.js')

@@ -70,3 +71,2 @@ function buildRouting (options) {

let throwIfAlreadyStarted
let genReqId
let disableRequestLogging

@@ -97,3 +97,2 @@ let ignoreTrailingSlash

globalExposeHeadRoutes = options.exposeHeadRoutes
genReqId = options.genReqId
disableRequestLogging = options.disableRequestLogging

@@ -126,3 +125,4 @@ ignoreTrailingSlash = options.ignoreTrailingSlash

hasConstraintStrategy,
isAsyncConstraint
isAsyncConstraint,
findRoute
}

@@ -175,7 +175,23 @@

function hasRoute ({ options }) {
return router.find(
return findRoute(options) !== null
}
function findRoute (options) {
const route = router.find(
options.method,
options.url || '',
options.constraints
) !== null
)
if (route) {
// we must reduce the expose surface, otherwise
// we provide the ability for the user to modify
// all the route and server information in runtime
return {
handler: route.handler,
params: route.params,
searchParams: route.searchParams
}
} else {
return null
}
}

@@ -436,3 +452,3 @@

function routeHandler (req, res, params, context, query) {
const id = genReqId(req)
const id = getGenReqId(context.server, req)

@@ -439,0 +455,0 @@ const loggerOpts = {

@@ -42,8 +42,6 @@ 'use strict'

constructor (parent, options) {
this.opts = options || (parent && parent.opts)
this.opts = options || parent?.opts
this.addedSchemas = false
this.compilersFactory = this.opts.compilersFactory
this.isCustomValidatorCompiler = this.opts.isCustomValidatorCompiler || false
this.isCustomSerializerCompiler = this.opts.isCustomSerializerCompiler || false

@@ -59,2 +57,4 @@ if (parent) {

this.schemaBucket = this.opts.bucket()
this.isCustomValidatorCompiler = this.opts.isCustomValidatorCompiler || false
this.isCustomSerializerCompiler = this.opts.isCustomSerializerCompiler || false
}

@@ -77,4 +77,20 @@ }

// Schema Controller compilers holder
setValidatorCompiler (validatorCompiler) {
// Set up as if the fixed validator compiler had been provided
// by a custom 'options.compilersFactory.buildValidator' that
// always returns the same compiler object. This is required because:
//
// - setValidatorCompiler must immediately install a compiler to preserve
// legacy behavior
// - setupValidator will recreate compilers from builders in some
// circumstances, so we have to install this adapter to make it
// behave the same if the legacy API is used
//
// The cloning of the compilersFactory object is necessary because
// we are aliasing the parent compilersFactory if none was provided
// to us (see constructor.)
this.compilersFactory = Object.assign(
{},
this.compilersFactory,
{ buildValidator: () => validatorCompiler })
this.validatorCompiler = validatorCompiler

@@ -85,2 +101,19 @@ this.isCustomValidatorCompiler = true

setSerializerCompiler (serializerCompiler) {
// Set up as if the fixed serializer compiler had been provided
// by a custom 'options.compilersFactory.buildSerializer' that
// always returns the same compiler object. This is required because:
//
// - setSerializerCompiler must immediately install a compiler to preserve
// legacy behavior
// - setupSerializer will recreate compilers from builders in some
// circumstances, so we have to install this adapter to make it
// behave the same if the legacy API is used
//
// The cloning of the compilersFactory object is necessary because
// we are aliasing the parent compilersFactory if none was provided
// to us (see constructor.)
this.compilersFactory = Object.assign(
{},
this.compilersFactory,
{ buildSerializer: () => serializerCompiler })
this.serializerCompiler = serializerCompiler

@@ -87,0 +120,0 @@ this.isCustomSerializerCompiler = true

@@ -19,2 +19,3 @@ 'use strict'

kPublicRouteContext: Symbol('fastify.routeOptions'),
kGenReqId: Symbol('fastify.genReqId'),
// Schema

@@ -21,0 +22,0 @@ kSchemaController: Symbol('fastify.schemaController'),

@@ -80,2 +80,7 @@ 'use strict'

const FSTDEP020 = createDeprecation({
code: 'FSTDEP020',
message: 'You are using the deprecated "reply.getResponseTime()"" method. Use the "request.elapsedTime" property instead. Method "reply.getResponseTime()" will be removed in `fastify@5`.'
})
const FSTWRN001 = createWarning({

@@ -111,4 +116,5 @@ name: 'FastifyWarning',

FSTDEP019,
FSTDEP020,
FSTWRN001,
FSTWRN002
}
{
"name": "fastify",
"version": "4.25.2",
"version": "4.26.0",
"description": "Fast and low overhead web framework, for Node.js",

@@ -10,4 +10,4 @@ "main": "fastify.js",

"bench": "branchcmp -r 2 -g -s \"npm run benchmark\"",
"benchmark": "npx concurrently -k -s first \"node ./examples/benchmark/simple.js\" \"npx autocannon -c 100 -d 30 -p 10 localhost:3000/\"",
"benchmark:parser": "npx concurrently -k -s first \"node ./examples/benchmark/parser.js\" \"npx autocannon -c 100 -d 30 -p 10 -i ./examples/benchmark/body.json -H \"content-type:application/jsoff\" -m POST localhost:3000/\"",
"benchmark": "concurrently -k -s first \"node ./examples/benchmark/simple.js\" \"autocannon -c 100 -d 30 -p 10 localhost:3000/\"",
"benchmark:parser": "concurrently -k -s first \"node ./examples/benchmark/parser.js\" \"autocannon -c 100 -d 30 -p 10 -i ./examples/benchmark/body.json -H \"content-type:application/jsoff\" -m POST localhost:3000/\"",
"build:validation": "node build/build-error-serializer.js && node build/build-validation.js",

@@ -145,3 +145,13 @@ "coverage": "npm run unit -- --coverage-report=html",

},
"homepage": "https://www.fastify.dev/",
"homepage": "https://fastify.dev/",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fastify"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fastify"
}
],
"devDependencies": {

@@ -159,4 +169,6 @@ "@fastify/pre-commit": "^2.0.2",

"ajv-merge-patch": "^5.0.1",
"autocannon": "^7.14.0",
"branch-comparer": "^1.1.0",
"c8": "^8.0.1",
"concurrently": "^8.2.2",
"cross-env": "^7.0.3",

@@ -201,3 +213,3 @@ "eslint": "^8.51.0",

"fast-json-stringify": "^5.8.0",
"find-my-way": "^7.7.0",
"find-my-way": "^8.0.0",
"light-my-request": "^5.11.0",

@@ -204,0 +216,0 @@ "pino": "^8.17.0",

@@ -32,2 +32,3 @@ <div align="center"> <a href="https://fastify.dev/">

[![Contribute with Gitpod](https://img.shields.io/badge/Contribute%20with-Gitpod-908a85?logo=gitpod&color=blue)](https://gitpod.io/#https://github.com/fastify/fastify)
![Open Collective backers and sponsors](https://img.shields.io/opencollective/all/fastify)

@@ -175,11 +176,2 @@ </div>

### Fastify v1.x and v2.x
Code for Fastify's **v1.x** is in [**`branch
1.x`**](https://github.com/fastify/fastify/tree/1.x), so all Fastify 1.x related
changes should be based on **`branch 1.x`**. In a similar way, all Fastify
**v2.x** related changes should be based on [**`branch
2.x`**](https://github.com/fastify/fastify/tree/2.x).
> ## Note

@@ -280,2 +272,8 @@ > `.listen` binds to the local host, `localhost`, interface by default

## Contributing
Whether reporting bugs, discussing improvements and new ideas or writing code,
we welcome contributions from anyone and everyone. Please read the [CONTRIBUTING](./CONTRIBUTING.md)
guidelines before submitting pull requests.
## Team

@@ -389,2 +387,8 @@

## Sponsors
Support this project by becoming a [SPONSOR](./SPONSORS.md)!
Fastify has an [Open Collective](https://opencollective.com/fastify)
page where we accept and manage financial contributions.
## Acknowledgements

@@ -391,0 +395,0 @@

# Security Policy
This document describes the management of vulnerabilities for the Fastify
project and its official plugins.
## Reporting vulnerabilities
Individuals who find potential vulnerabilities in Fastify are invited to
complete a vulnerability report via the dedicated HackerOne page:
[https://hackerone.com/fastify](https://hackerone.com/fastify).
### Strict measures when reporting vulnerabilities
It is of the utmost importance that you read carefully and follow these
guidelines to ensure the ecosystem as a whole isn't disrupted due to improperly
reported vulnerabilities:
* Avoid creating new "informative" reports on HackerOne. Only create new
HackerOne reports on a vulnerability if you are absolutely sure this should be
tagged as an actual vulnerability. Third-party vendors and individuals are
tracking any new vulnerabilities reported in HackerOne and will flag them as
such for their customers (think about snyk, npm audit, ...).
* HackerOne reports should never be created and triaged by the same person. If
you are creating a HackerOne report for a vulnerability that you found, or on
behalf of someone else, there should always be a 2nd Security Team member who
triages it. If in doubt, invite more Fastify Collaborators to help triage the
validity of the report. In any case, the report should follow the same process
as outlined below of inviting the maintainers to review and accept the
vulnerability.
* ***Do not*** attempt to show CI/CD vulnerabilities by creating new pull
requests to any of the Fastify organization's repositories. Doing so will
result in a [content report][cr] to GitHub as an unsolicited exploit.
The proper way to provide such reports is by creating a new repository,
configured in the same manner as the repository you would like to submit
a report about, and with a pull request to your own repository showing
the proof of concept.
[cr]: https://docs.github.com/en/communities/maintaining-your-safety-on-github/reporting-abuse-or-spam#reporting-an-issue-or-pull-request
### Vulnerabilities found outside this process
⚠ The Fastify project does not support any reporting outside the HackerOne
process.
## Handling vulnerability reports
When a potential vulnerability is reported, the following actions are taken:
### Triage
**Delay:** 4 business days
Within 4 business days, a member of the security team provides a first answer to
the individual who submitted the potential vulnerability. The possible responses
can be:
* Acceptance: what was reported is considered as a new vulnerability
* Rejection: what was reported is not considered as a new vulnerability
* Need more information: the security team needs more information in order to
evaluate what was reported.
Triaging should include updating issue fields:
* Asset - set/create the module affected by the report
* Severity - TBD, currently left empty
Reference: [HackerOne: Submitting
Reports](https://docs.hackerone.com/hackers/submitting-reports.html)
### Correction follow-up
**Delay:** 90 days
When a vulnerability is confirmed, a member of the security team volunteers to
follow up on this report.
With the help of the individual who reported the vulnerability, they contact the
maintainers of the vulnerable package to make them aware of the vulnerability.
The maintainers can be invited as participants to the reported issue.
With the package maintainer, they define a release date for the publication of
the vulnerability. Ideally, this release date should not happen before the
package has been patched.
The report's vulnerable versions upper limit should be set to:
* `*` if there is no fixed version available by the time of publishing the
report.
* the last vulnerable version. For example: `<=1.2.3` if a fix exists in `1.2.4`
### Publication
**Delay:** 90 days
Within 90 days after the triage date, the vulnerability must be made public.
**Severity**: Vulnerability severity is assessed using [CVSS
v.3](https://www.first.org/cvss/user-guide). More information can be found on
[HackerOne documentation](https://docs.hackerone.com/hackers/severity.html)
If the package maintainer is actively developing a patch, an additional delay
can be added with the approval of the security team and the individual who
reported the vulnerability.
At this point, a CVE should be requested through the HackerOne platform through
the UI, which should include the Report ID and a summary.
Within HackerOne, this is handled through a "public disclosure request".
Reference: [HackerOne:
Disclosure](https://docs.hackerone.com/hackers/disclosure.html)
## The Fastify Security team
The core team is responsible for the management of HackerOne program and this
policy and process.
Members of this team are expected to keep all information that they have
privileged access to by being on the team completely private to the team. This
includes agreeing to not notify anyone outside the team of issues that have not
yet been disclosed publicly, including the existence of issues, expectations of
upcoming releases, and patching of any issues other than in the process of their
work as a member of the Fastify Core team.
### Members
* [__Matteo Collina__](https://github.com/mcollina),
<https://twitter.com/matteocollina>, <https://www.npmjs.com/~matteo.collina>
* [__Tomas Della Vedova__](https://github.com/delvedor),
<https://twitter.com/delvedor>, <https://www.npmjs.com/~delvedor>
* [__Vincent Le Goff__](https://github.com/zekth)
* [__KaKa Ng__](https://github.com/climba03003)
* [__James Sumners__](https://github.com/jsumners),
<https://twitter.com/jsumners79>, <https://www.npmjs.com/~jsumners>
## OpenSSF CII Best Practices
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/7585/badge)](https://bestpractices.coreinfrastructure.org/projects/7585)
There are three “tiers”: passing, silver, and gold.
### Passing
We meet 100% of the “passing” criteria.
### Silver
We meet 87% of the “silver” criteria. The gaps are as follows:
- we do not have a DCO or a CLA process for contributions.
- we do not currently document
“what the user can and cannot expect in terms of security” for our project.
- we do not currently document ”the architecture (aka high-level design)”
for our project.
### Gold
We meet 70% of the “gold” criteria. The gaps are as follows:
- we do not yet have the “silver” badge; see all the gaps above.
- We do not include a copyright or license statement in each source file.
Efforts are underway to change this archaic practice into a
suggestion instead of a hard requirement.
- There are a few unanswered questions around cryptography that are
waiting for clarification.
Please see Fastify's [organization-wide security policy
](https://github.com/fastify/.github/blob/main/SECURITY.md).

@@ -5,2 +5,3 @@ 'use strict'

const { test } = require('tap')
const fp = require('fastify-plugin')
const Fastify = require('..')

@@ -76,1 +77,392 @@

})
test('Should accept option to set genReqId with setGenReqId option', t => {
t.plan(9)
const fastify = Fastify({
genReqId: function (req) {
return 'base'
}
})
fastify.register(function (instance, opts, next) {
instance.setGenReqId(function (req) {
return 'foo'
})
instance.get('/', (req, reply) => {
t.ok(req.id)
reply.send({ id: req.id })
})
next()
}, { prefix: 'foo' })
fastify.register(function (instance, opts, next) {
instance.setGenReqId(function (req) {
return 'bar'
})
instance.get('/', (req, reply) => {
t.ok(req.id)
reply.send({ id: req.id })
})
next()
}, { prefix: 'bar' })
fastify.get('/', (req, reply) => {
t.ok(req.id)
reply.send({ id: req.id })
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'base')
fastify.close()
})
fastify.inject({
method: 'GET',
url: '/foo'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'foo')
fastify.close()
})
fastify.inject({
method: 'GET',
url: '/bar'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'bar')
fastify.close()
})
})
test('Should encapsulate setGenReqId', t => {
t.plan(12)
const fastify = Fastify({
genReqId: function (req) {
return 'base'
}
})
const bazInstance = function (instance, opts, next) {
instance.register(barInstance, { prefix: 'baz' })
instance.setGenReqId(function (req) {
return 'baz'
})
instance.get('/', (req, reply) => {
t.ok(req.id)
reply.send({ id: req.id })
})
next()
}
const barInstance = function (instance, opts, next) {
instance.setGenReqId(function (req) {
return 'bar'
})
instance.get('/', (req, reply) => {
t.ok(req.id)
reply.send({ id: req.id })
})
next()
}
const fooInstance = function (instance, opts, next) {
instance.register(bazInstance, { prefix: 'baz' })
instance.register(barInstance, { prefix: 'bar' })
instance.setGenReqId(function (req) {
return 'foo'
})
instance.get('/', (req, reply) => {
t.ok(req.id)
reply.send({ id: req.id })
})
next()
}
fastify.register(fooInstance, { prefix: 'foo' })
fastify.get('/', (req, reply) => {
t.ok(req.id)
reply.send({ id: req.id })
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'base')
fastify.close()
})
fastify.inject({
method: 'GET',
url: '/foo'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'foo')
fastify.close()
})
fastify.inject({
method: 'GET',
url: '/foo/bar'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'bar')
fastify.close()
})
fastify.inject({
method: 'GET',
url: '/foo/baz'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'baz')
fastify.close()
})
})
test('Should encapsulate setGenReqId', t => {
t.plan(12)
const fastify = Fastify({
genReqId: function (req) {
return 'base'
}
})
const bazInstance = function (instance, opts, next) {
instance.register(barInstance, { prefix: 'baz' })
instance.setGenReqId(function (req) {
return 'baz'
})
instance.get('/', (req, reply) => {
t.ok(req.id)
reply.send({ id: req.id })
})
next()
}
const barInstance = function (instance, opts, next) {
instance.setGenReqId(function (req) {
return 'bar'
})
instance.get('/', (req, reply) => {
t.ok(req.id)
reply.send({ id: req.id })
})
next()
}
const fooInstance = function (instance, opts, next) {
instance.register(bazInstance, { prefix: 'baz' })
instance.register(barInstance, { prefix: 'bar' })
instance.setGenReqId(function (req) {
return 'foo'
})
instance.get('/', (req, reply) => {
t.ok(req.id)
reply.send({ id: req.id })
})
next()
}
fastify.register(fooInstance, { prefix: 'foo' })
fastify.get('/', (req, reply) => {
t.ok(req.id)
reply.send({ id: req.id })
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'base')
fastify.close()
})
fastify.inject({
method: 'GET',
url: '/foo'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'foo')
fastify.close()
})
fastify.inject({
method: 'GET',
url: '/foo/bar'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'bar')
fastify.close()
})
fastify.inject({
method: 'GET',
url: '/foo/baz'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'baz')
fastify.close()
})
})
test('Should not alter parent of genReqId', t => {
t.plan(6)
const fastify = Fastify()
const fooInstance = function (instance, opts, next) {
instance.setGenReqId(function (req) {
return 'foo'
})
instance.get('/', (req, reply) => {
t.ok(req.id)
reply.send({ id: req.id })
})
next()
}
fastify.register(fooInstance, { prefix: 'foo' })
fastify.get('/', (req, reply) => {
t.ok(req.id)
reply.send({ id: req.id })
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'req-1')
fastify.close()
})
fastify.inject({
method: 'GET',
url: '/foo'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'foo')
fastify.close()
})
})
test('Should have child instance user parent genReqId', t => {
t.plan(6)
const fastify = Fastify({
genReqId: function (req) {
return 'foo'
}
})
const fooInstance = function (instance, opts, next) {
instance.get('/', (req, reply) => {
t.ok(req.id)
reply.send({ id: req.id })
})
next()
}
fastify.register(fooInstance, { prefix: 'foo' })
fastify.get('/', (req, reply) => {
t.ok(req.id)
reply.send({ id: req.id })
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'foo')
fastify.close()
})
fastify.inject({
method: 'GET',
url: '/foo'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'foo')
fastify.close()
})
})
test('genReqId set on root scope when using fastify-plugin', t => {
t.plan(6)
const fastify = Fastify()
fastify.register(fp(function (fastify, options, done) {
fastify.setGenReqId(function (req) {
return 'not-encapsulated'
})
fastify.get('/not-encapsulated-1', (req, reply) => {
t.ok(req.id)
reply.send({ id: req.id })
})
done()
}))
fastify.get('/not-encapsulated-2', (req, reply) => {
t.ok(req.id)
reply.send({ id: req.id })
})
fastify.inject({
method: 'GET',
url: '/not-encapsulated-1'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'not-encapsulated')
fastify.close()
})
fastify.inject({
method: 'GET',
url: '/not-encapsulated-2'
}, (err, res) => {
t.error(err)
const payload = JSON.parse(res.payload)
t.equal(payload.id, 'not-encapsulated')
fastify.close()
})
})

@@ -8,6 +8,4 @@ 'use strict'

const helper = require('./helper')
const { kState } = require('../lib/symbols')
// fix citgm @aix72-ppc64
const LISTEN_READYNESS = process.env.CITGM ? 250 : 50
let localhost

@@ -278,4 +276,4 @@ before(async function () {

test('localhost onListen encapsulation should be called in order', t => {
t.plan(6)
test('localhost onListen encapsulation should be called in order', async t => {
t.plan(8)
const fastify = Fastify()

@@ -292,3 +290,3 @@ t.teardown(fastify.close.bind(fastify))

fastify.register(async (childOne, o) => {
await fastify.register(async (childOne, o) => {
childOne.addHook('onListen', function (done) {

@@ -299,3 +297,4 @@ t.equal(++order, 2, 'called in childOne')

})
childOne.register(async (childTwo, o) => {
await childOne.register(async (childTwo, o) => {
childTwo.addHook('onListen', async function () {

@@ -306,4 +305,11 @@ t.equal(++order, 3, 'called in childTwo')

})
await childOne.register(async (childTwoPeer, o) => {
childTwoPeer.addHook('onListen', async function () {
t.equal(++order, 4, 'called second in childTwo')
t.equal(this.pluginName, childTwoPeer.pluginName, 'the this binding is the right instance')
})
})
})
fastify.listen({
await fastify.listen({
host: 'localhost',

@@ -314,2 +320,49 @@ port: 0

test('localhost onListen encapsulation with only nested hook', async t => {
t.plan(1)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
await fastify.register(async (child) => {
await child.register(async (child2) => {
child2.addHook('onListen', function (done) {
t.pass()
done()
})
})
})
await fastify.listen({
host: 'localhost',
port: 0
})
})
test('localhost onListen peer encapsulations with only nested hooks', async t => {
t.plan(2)
const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
await fastify.register(async (child) => {
await child.register(async (child2) => {
child2.addHook('onListen', function (done) {
t.pass()
done()
})
})
await child.register(async (child2) => {
child2.addHook('onListen', function (done) {
t.pass()
done()
})
})
})
await fastify.listen({
host: 'localhost',
port: 0
})
})
test('localhost onListen encapsulation should be called in order and should log errors as warnings and continue', t => {

@@ -1065,2 +1118,3 @@ t.plan(7)

t.plan(2)
const fastify = Fastify()

@@ -1070,6 +1124,6 @@ t.teardown(fastify.close.bind(fastify))

fastify.addHook('onListen', function (done) {
setTimeout(done, 500)
t.equal(fastify[kState].listening, true)
done()
})
const startDate = new Date()
fastify.listen({

@@ -1080,3 +1134,2 @@ host: 'localhost',

t.error(err)
t.ok(new Date() - startDate < LISTEN_READYNESS)
})

@@ -1087,2 +1140,3 @@ })

t.plan(1)
const fastify = Fastify()

@@ -1092,6 +1146,5 @@ t.teardown(fastify.close.bind(fastify))

fastify.addHook('onListen', async function () {
await new Promise(resolve => setTimeout(resolve, 500))
t.equal(fastify[kState].listening, true)
})
const startDate = new Date()
await fastify.listen({

@@ -1101,3 +1154,2 @@ host: 'localhost',

})
t.ok(new Date() - startDate < LISTEN_READYNESS)
})

@@ -8,3 +8,3 @@ 'use strict'

test('should expose 78 errors', t => {
test('should expose 79 errors', t => {
t.plan(1)

@@ -18,7 +18,7 @@ const exportedKeys = Object.keys(errors)

}
t.equal(counter, 78)
t.equal(counter, 79)
})
test('ensure name and codes of Errors are identical', t => {
t.plan(78)
t.plan(79)
const exportedKeys = Object.keys(errors)

@@ -342,2 +342,12 @@ for (const key of exportedKeys) {

test('FST_ERR_REP_RESPONSE_BODY_CONSUMED', t => {
t.plan(5)
const error = new errors.FST_ERR_REP_RESPONSE_BODY_CONSUMED()
t.equal(error.name, 'FastifyError')
t.equal(error.code, 'FST_ERR_REP_RESPONSE_BODY_CONSUMED')
t.equal(error.message, 'Response.body is already consumed.')
t.equal(error.statusCode, 500)
t.ok(error instanceof Error)
})
test('FST_ERR_REP_ALREADY_SENT', t => {

@@ -824,3 +834,3 @@ t.plan(5)

test('Ensure that all errors are in Errors.md TOC', t => {
t.plan(78)
t.plan(79)
const errorsMd = readFileSync(resolve(__dirname, '../../docs/Reference/Errors.md'), 'utf8')

@@ -837,3 +847,3 @@

test('Ensure that non-existing errors are not in Errors.md TOC', t => {
t.plan(78)
t.plan(79)
const errorsMd = readFileSync(resolve(__dirname, '../../docs/Reference/Errors.md'), 'utf8')

@@ -851,3 +861,3 @@

test('Ensure that all errors are in Errors.md documented', t => {
t.plan(78)
t.plan(79)
const errorsMd = readFileSync(resolve(__dirname, '../../docs/Reference/Errors.md'), 'utf8')

@@ -864,3 +874,3 @@

test('Ensure that non-existing errors are not in Errors.md documented', t => {
t.plan(78)
t.plan(79)
const errorsMd = readFileSync(resolve(__dirname, '../../docs/Reference/Errors.md'), 'utf8')

@@ -867,0 +877,0 @@

@@ -51,3 +51,4 @@ 'use strict'

http2SessionTimeout: 72000,
exposeHeadRoutes: true
exposeHeadRoutes: true,
useSemicolonDelimiter: true
}

@@ -59,3 +60,3 @@

test('Fastify.initialConfig should expose all options', t => {
t.plan(21)
t.plan(22)

@@ -104,2 +105,3 @@ const serverFactory = (handler, opts) => {

pluginTimeout: 20000,
useSemicolonDelimiter: false,
querystringParser: str => str,

@@ -129,2 +131,3 @@ genReqId: function (req) {

t.equal(fastify.initialConfig.caseSensitive, true)
t.equal(fastify.initialConfig.useSemicolonDelimiter, false)
t.equal(fastify.initialConfig.allowUnsafeRegex, false)

@@ -292,3 +295,4 @@ t.equal(fastify.initialConfig.requestIdHeader, 'request-id-alt')

http2SessionTimeout: 72000,
exposeHeadRoutes: true
exposeHeadRoutes: true,
useSemicolonDelimiter: true
})

@@ -295,0 +299,0 @@ } catch (error) {

@@ -22,3 +22,3 @@ 'use strict'

const path = require('node:path')
const { FSTDEP019, FSTDEP010 } = require('../../lib/warnings')
const { FSTDEP010, FSTDEP019, FSTDEP020 } = require('../../lib/warnings')

@@ -1602,3 +1602,36 @@ const agent = new http.Agent({ keepAlive: false })

test('reply.getResponseTime() should return the time since a request started while inflight', t => {
test('should emit deprecation warning when trying to use reply.getResponseTime() and should return the time since a request started while inflight', t => {
t.plan(5)
const fastify = Fastify()
fastify.route({
method: 'GET',
url: '/',
handler: (req, reply) => {
reply.send('hello world')
}
})
process.removeAllListeners('warning')
process.on('warning', onWarning)
function onWarning (warning) {
t.equal(warning.name, 'DeprecationWarning')
t.equal(warning.code, FSTDEP020.code)
}
fastify.addHook('preValidation', (req, reply, done) => {
t.equal(reply.getResponseTime(), reply.getResponseTime())
done()
})
fastify.inject({ method: 'GET', url: '/' }, (err, res) => {
t.error(err)
t.pass()
process.removeListener('warning', onWarning)
})
FSTDEP020.emitted = false
})
test('reply.getResponseTime() should return the same value after a request is finished', t => {
t.plan(1)

@@ -1614,4 +1647,45 @@ const fastify = Fastify()

fastify.addHook('onResponse', (req, reply) => {
t.equal(reply.getResponseTime(), reply.getResponseTime())
t.end()
})
fastify.inject({ method: 'GET', url: '/' })
})
test('reply.elapsedTime should return a number greater than 0 after the timer is initialised on the reply by setting up response listeners', t => {
t.plan(1)
const fastify = Fastify()
fastify.route({
method: 'GET',
url: '/',
handler: (req, reply) => {
reply.send('hello world')
}
})
fastify.addHook('onResponse', (req, reply) => {
t.ok(reply.elapsedTime > 0)
t.end()
})
fastify.inject({ method: 'GET', url: '/' })
})
test('reply.elapsedTime should return the time since a request started while inflight', t => {
t.plan(1)
const fastify = Fastify()
fastify.route({
method: 'GET',
url: '/',
handler: (req, reply) => {
reply.send('hello world')
}
})
let preValidationElapsedTime
fastify.addHook('preValidation', (req, reply, done) => {
t.not(reply.getResponseTime(), reply.getResponseTime())
preValidationElapsedTime = reply.elapsedTime
done()

@@ -1621,2 +1695,3 @@ })

fastify.addHook('onResponse', (req, reply) => {
t.ok(reply.elapsedTime > preValidationElapsedTime)
t.end()

@@ -1628,3 +1703,3 @@ })

test('reply.getResponseTime() should return the same value after a request is finished', t => {
test('reply.elapsedTime should return the same value after a request is finished', t => {
t.plan(1)

@@ -1641,3 +1716,3 @@ const fastify = Fastify()

fastify.addHook('onResponse', (req, reply) => {
t.equal(reply.getResponseTime(), reply.getResponseTime())
t.equal(reply.elapsedTime, reply.elapsedTime)
t.end()

@@ -1644,0 +1719,0 @@ })

@@ -596,2 +596,43 @@ 'use strict'

test('Custom setSerializerCompiler with addSchema', t => {
t.plan(6)
const fastify = Fastify({ exposeHeadRoutes: false })
const outSchema = {
$id: 'test',
type: 'object',
whatever: 'need to be parsed by the custom serializer'
}
fastify.setSerializerCompiler(({ schema, method, url, httpStatus }) => {
t.equal(method, 'GET')
t.equal(url, '/foo/:id')
t.equal(httpStatus, '200')
t.same(schema, outSchema)
return _data => JSON.stringify({ id: 2 })
})
// provoke re-creation of serialization compiler in setupSerializer
fastify.addSchema({ $id: 'dummy', type: 'object' })
fastify.get('/foo/:id', {
handler (_req, reply) {
reply.send({ id: 1 })
},
schema: {
response: {
200: outSchema
}
}
})
fastify.inject({
method: 'GET',
url: '/foo/123'
}, (err, res) => {
t.error(err)
t.equal(res.payload, JSON.stringify({ id: 2 }))
})
})
test('Custom serializer per route', async t => {

@@ -598,0 +639,0 @@ const fastify = Fastify()

@@ -109,3 +109,3 @@ 'use strict'

test('External AJV instance', t => {
t.plan(4)
t.plan(5)

@@ -122,2 +122,3 @@ const fastify = Fastify()

fastify.setValidatorCompiler(({ schema, method, url, httpPart }) => {
t.pass('custom validator compiler called')
return ajv.compile(schema)

@@ -156,3 +157,3 @@ })

test('Encapsulation', t => {
t.plan(19)
t.plan(21)

@@ -170,2 +171,3 @@ const fastify = Fastify()

const validator = ({ schema, method, url, httpPart }) => {
t.pass('custom validator compiler called')
return ajv.compile(schema)

@@ -278,3 +280,3 @@ }

test('Triple $ref with a simple $id', t => {
t.plan(6)
t.plan(7)

@@ -293,2 +295,3 @@ const fastify = Fastify()

fastify.setValidatorCompiler(({ schema, method, url, httpPart }) => {
t.pass('custom validator compiler called')
return ajv.compile(schema)

@@ -1033,3 +1036,3 @@ })

t.plan(2)
class Headers {}
class Headers { }
const fastify = Fastify()

@@ -1039,3 +1042,3 @@

t.type(schema, Headers)
return () => {}
return () => { }
})

@@ -1047,5 +1050,111 @@

}
}, () => {})
}, () => { })
await fastify.ready()
})
test('Custom validator builder override by custom validator compiler', async t => {
t.plan(3)
const ajvDefaults = {
removeAdditional: true,
coerceTypes: true,
allErrors: true
}
const ajv1 = new AJV(ajvDefaults).addKeyword({ keyword: 'extended_one', type: 'object', validator: () => true })
const ajv2 = new AJV(ajvDefaults).addKeyword({ keyword: 'extended_two', type: 'object', validator: () => true })
const fastify = Fastify({ schemaController: { compilersFactory: { buildValidator: () => (routeSchemaDef) => ajv1.compile(routeSchemaDef.schema) } } })
fastify.setValidatorCompiler((routeSchemaDef) => ajv2.compile(routeSchemaDef.schema))
fastify.post('/two/:id', {
schema: {
params: {
type: 'object',
extended_two: true,
properties: {
id: { type: 'number' }
},
required: ['id']
}
},
handler: (req, _reply) => {
t.same(typeof req.params.id, 'number')
t.same(req.params.id, 43)
return 'ok'
}
})
await fastify.ready()
const two = await fastify.inject({
method: 'POST',
url: '/two/43'
})
t.equal(two.statusCode, 200)
})
test('Custom validator builder override by custom validator compiler in child instance', async t => {
t.plan(6)
const ajvDefaults = {
removeAdditional: true,
coerceTypes: true,
allErrors: true
}
const ajv1 = new AJV(ajvDefaults).addKeyword({ keyword: 'extended_one', type: 'object', validator: () => true })
const ajv2 = new AJV(ajvDefaults).addKeyword({ keyword: 'extended_two', type: 'object', validator: () => true })
const fastify = Fastify({ schemaController: { compilersFactory: { buildValidator: () => (routeSchemaDef) => ajv1.compile(routeSchemaDef.schema) } } })
fastify.register((embedded, _opts, done) => {
embedded.setValidatorCompiler((routeSchemaDef) => ajv2.compile(routeSchemaDef.schema))
embedded.post('/two/:id', {
schema: {
params: {
type: 'object',
extended_two: true,
properties: {
id: { type: 'number' }
},
required: ['id']
}
},
handler: (req, _reply) => {
t.same(typeof req.params.id, 'number')
t.same(req.params.id, 43)
return 'ok'
}
})
done()
})
fastify.post('/one/:id', {
schema: {
params: {
type: 'object',
extended_one: true,
properties: {
id: { type: 'number' }
},
required: ['id']
}
},
handler: (req, _reply) => {
t.same(typeof req.params.id, 'number')
t.same(req.params.id, 42)
return 'ok'
}
})
await fastify.ready()
const one = await fastify.inject({
method: 'POST',
url: '/one/42'
})
t.equal(one.statusCode, 200)
const two = await fastify.inject({
method: 'POST',
url: '/two/43'
})
t.equal(two.statusCode, 200)
})

@@ -54,2 +54,14 @@ import { expectAssignable, expectDeprecated, expectError, expectNotDeprecated, expectType } from 'tsd'

expectAssignable<FastifyInstance>(
server.setGenReqId(function (req) {
expectType<RawRequestDefaultExpression>(req)
return 'foo'
})
)
function fastifySetGenReqId (req: RawRequestDefaultExpression) {
return 'foo'
}
server.setGenReqId(fastifySetGenReqId)
function fastifyErrorHandler (this: FastifyInstance, error: FastifyError) {}

@@ -334,3 +346,4 @@ server.setErrorHandler(fastifyErrorHandler)

requestIdLogLabel?: string,
http2SessionTimeout?: number
http2SessionTimeout?: number,
useSemicolonDelimiter?: boolean
}>

@@ -337,0 +350,0 @@

import { Buffer } from 'buffer'
import { expectAssignable, expectError, expectType } from 'tsd'
import { expectAssignable, expectDeprecated, expectError, expectType } from 'tsd'
import fastify, { FastifyReplyContext, FastifyReply, FastifyRequest, FastifySchema, FastifySchemaCompiler, FastifyTypeProviderDefault, RawRequestDefaultExpression, RouteHandler, RouteHandlerMethod } from '../../fastify'

@@ -22,2 +22,3 @@ import { FastifyInstance } from '../../types/instance'

expectType<(payload?: unknown) => FastifyReply>(reply.code(100 as number).send)
expectType<number>(reply.elapsedTime)
expectType<number>(reply.statusCode)

@@ -35,3 +36,4 @@ expectType<boolean>(reply.sent)

expectType<() => void>(reply.callNotFound)
expectType<() => number>(reply.getResponseTime)
// Test reply.getResponseTime() deprecation
expectDeprecated(reply.getResponseTime)
expectType<(contentType: string) => FastifyReply>(reply.type)

@@ -38,0 +40,0 @@ expectType<(fn: (payload: any) => string) => FastifyReply>(reply.serializer)

@@ -6,3 +6,4 @@ import { FastifyError } from '@fastify/error'

import { RequestPayload } from '../../types/hooks'
import { HTTPMethods } from '../../types/utils'
import { FindMyWayFindResult } from '../../types/instance'
import { HTTPMethods, RawServerDefault } from '../../types/utils'

@@ -457,2 +458,15 @@ /*

expectType<Omit<FindMyWayFindResult<RawServerDefault>, 'store'>>(
fastify().findRoute({
url: '/',
method: 'get'
})
)
// we should not expose store
expectError(fastify().findRoute({
url: '/',
method: 'get'
}).store)
expectType<FastifyInstance>(fastify().route({

@@ -459,0 +473,0 @@ url: '/',

import { FastifyError } from '@fastify/error'
import { ConstraintStrategy, HTTPVersion } from 'find-my-way'
import { ConstraintStrategy, FindResult, HTTPVersion } from 'find-my-way'
import * as http from 'http'
import { CallbackFunc as LightMyRequestCallback, Chain as LightMyRequestChain, InjectOptions, Response as LightMyRequestResponse } from 'light-my-request'
import { AddContentTypeParser, ConstructorAction, FastifyBodyParser, getDefaultJsonParser, hasContentTypeParser, ProtoAction, removeAllContentTypeParsers, removeContentTypeParser } from './content-type-parser'
import { onCloseAsyncHookHandler, onCloseHookHandler, onErrorAsyncHookHandler, onErrorHookHandler, onReadyAsyncHookHandler, onReadyHookHandler, onListenAsyncHookHandler, onListenHookHandler, onRegisterHookHandler, onRequestAsyncHookHandler, onRequestHookHandler, onRequestAbortAsyncHookHandler, onRequestAbortHookHandler, onResponseAsyncHookHandler, onResponseHookHandler, onRouteHookHandler, onSendAsyncHookHandler, onSendHookHandler, onTimeoutAsyncHookHandler, onTimeoutHookHandler, preHandlerAsyncHookHandler, preHandlerHookHandler, preParsingAsyncHookHandler, preParsingHookHandler, preSerializationAsyncHookHandler, preSerializationHookHandler, preValidationAsyncHookHandler, preValidationHookHandler, preCloseHookHandler, preCloseAsyncHookHandler, LifecycleHook, ApplicationHook, HookAsyncLookup, HookLookup } from './hooks'
import { InjectOptions, CallbackFunc as LightMyRequestCallback, Chain as LightMyRequestChain, Response as LightMyRequestResponse } from 'light-my-request'
import { AddressInfo } from 'net'
import { AddContentTypeParser, ConstructorAction, FastifyBodyParser, ProtoAction, getDefaultJsonParser, hasContentTypeParser, removeAllContentTypeParsers, removeContentTypeParser } from './content-type-parser'
import { ApplicationHook, HookAsyncLookup, HookLookup, LifecycleHook, onCloseAsyncHookHandler, onCloseHookHandler, onErrorAsyncHookHandler, onErrorHookHandler, onListenAsyncHookHandler, onListenHookHandler, onReadyAsyncHookHandler, onReadyHookHandler, onRegisterHookHandler, onRequestAbortAsyncHookHandler, onRequestAbortHookHandler, onRequestAsyncHookHandler, onRequestHookHandler, onResponseAsyncHookHandler, onResponseHookHandler, onRouteHookHandler, onSendAsyncHookHandler, onSendHookHandler, onTimeoutAsyncHookHandler, onTimeoutHookHandler, preCloseAsyncHookHandler, preCloseHookHandler, preHandlerAsyncHookHandler, preHandlerHookHandler, preParsingAsyncHookHandler, preParsingHookHandler, preSerializationAsyncHookHandler, preSerializationHookHandler, preValidationAsyncHookHandler, preValidationHookHandler } from './hooks'
import { FastifyBaseLogger, FastifyChildLoggerFactory } from './logger'

@@ -11,3 +12,3 @@ import { FastifyRegister } from './register'

import { FastifyRequest } from './request'
import { DefaultRoute, RouteGenericInterface, RouteOptions, RouteShorthandMethod, RouteHandlerMethod } from './route'
import { DefaultRoute, RouteGenericInterface, RouteHandlerMethod, RouteOptions, RouteShorthandMethod } from './route'
import {

@@ -24,4 +25,3 @@ FastifySchema,

} from './type-provider'
import { HTTPMethods, ContextConfigDefault, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerBase, RawServerDefault } from './utils'
import { AddressInfo } from 'net'
import { ContextConfigDefault, HTTPMethods, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerBase, RawServerDefault } from './utils'

@@ -91,2 +91,3 @@ export interface PrintRoutesOptions {

type FindMyWayVersion<RawServer extends RawServerBase> = RawServer extends http.Server ? HTTPVersion.V1 : HTTPVersion.V2
type FindMyWayFindResult<RawServer extends RawServerBase> = FindResult<FindMyWayVersion<RawServer>>

@@ -225,2 +226,8 @@ type GetterSetter<This, T> = T | {

findRoute<
RouteGeneric extends RouteGenericInterface = RouteGenericInterface,
ContextConfig = ContextConfigDefault,
SchemaCompiler extends FastifySchema = FastifySchema,
>(opts: Pick<RouteOptions<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider>, 'method' | 'url' | 'constraints'>): Omit<FindMyWayFindResult<RawServer>, 'store'>;
// addHook: overloads

@@ -465,4 +472,4 @@

opts: {
preValidation?: preValidationHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider> | preValidationHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider>[];
preHandler?: preHandlerHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider> | preHandlerHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider>[];
preValidation?: preValidationHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider> | preValidationAsyncHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider> | preValidationHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider>[] | preValidationAsyncHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider>[];
preHandler?: preHandlerHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider> | preHandlerAsyncHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider> | preHandlerHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider>[] | preHandlerAsyncHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider>[];
},

@@ -485,2 +492,7 @@ handler: RouteHandlerMethod<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider, Logger>

/**
* Set a function that will generate a request-ids
*/
setGenReqId(fn: (req: RawRequestDefaultExpression<RawServer>) => string): FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>;
/**
* Hook function that is called when creating a child logger instance for each request

@@ -598,4 +610,5 @@ * which allows for modifying or adding child logger bindings and logger options, or

requestIdLogLabel?: string,
http2SessionTimeout?: number
http2SessionTimeout?: number,
useSemicolonDelimiter?: boolean,
}>
}

@@ -43,2 +43,3 @@ import { Buffer } from 'buffer'

context: FastifyReplyContext<ContextConfig>;
elapsedTime: number;
log: FastifyBaseLogger;

@@ -63,2 +64,5 @@ request: FastifyRequest<RouteGeneric, RawServer, RawRequest, SchemaCompiler, TypeProvider>;

callNotFound(): void;
/**
* @deprecated Use the Reply#elapsedTime property instead
*/
getResponseTime(): number;

@@ -65,0 +69,0 @@ type(contentType: string): FastifyReply<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider>;

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc