fastify
Advanced tools
Comparing version 4.25.2 to 4.26.0
@@ -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 @@ |
159
SECURITY.md
# 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
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
2416645
331
58025
412
2
45
6
+ Addedfind-my-way@8.1.0(transitive)
- Removedfind-my-way@7.7.0(transitive)
Updatedfind-my-way@^8.0.0