fastify 4.23.2 → 4.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +1 -1
  2. package/docs/Guides/Ecosystem.md +6 -0
  3. package/docs/Reference/Hooks.md +1 -0
  4. package/docs/Reference/Plugins.md +1 -1
  5. package/docs/Reference/Reply.md +4 -3
  6. package/docs/Reference/Request.md +3 -2
  7. package/docs/Reference/Server.md +31 -3
  8. package/docs/Reference/Type-Providers.md +2 -2
  9. package/docs/Reference/TypeScript.md +21 -7
  10. package/fastify.d.ts +2 -2
  11. package/fastify.js +8 -1
  12. package/lib/contentTypeParser.js +1 -1
  13. package/lib/reply.js +20 -3
  14. package/lib/reqIdGenFactory.js +15 -9
  15. package/lib/request.js +1 -1
  16. package/lib/route.js +18 -3
  17. package/lib/schemas.js +3 -3
  18. package/lib/warnings.js +3 -1
  19. package/package.json +2 -2
  20. package/test/async-dispose.test.js +21 -0
  21. package/test/constrained-routes.test.js +127 -3
  22. package/test/hooks-async.test.js +160 -10
  23. package/test/input-validation.js +3 -3
  24. package/test/internals/reply.test.js +33 -4
  25. package/test/logger/instantiation.test.js +338 -0
  26. package/test/logger/logger-test-utils.js +47 -0
  27. package/test/logger/logging.test.js +406 -0
  28. package/test/logger/options.test.js +500 -0
  29. package/test/logger/request.test.js +292 -0
  30. package/test/logger/response.test.js +184 -0
  31. package/test/reply-code.test.js +64 -0
  32. package/test/types/reply.test-d.ts +3 -3
  33. package/test/types/request.test-d.ts +9 -9
  34. package/test/types/type-provider.test-d.ts +89 -0
  35. package/test/types/using.test-d.ts +14 -0
  36. package/types/context.d.ts +9 -2
  37. package/types/instance.d.ts +4 -1
  38. package/types/plugin.d.ts +2 -1
  39. package/types/reply.d.ts +2 -2
  40. package/types/request.d.ts +3 -3
  41. package/types/route.d.ts +5 -5
  42. package/test/serial/logger.0.test.js +0 -866
  43. package/test/serial/logger.1.test.js +0 -862
  44. /package/test/{serial → logger}/tap-parallel-not-ok +0 -0
package/README.md CHANGED
@@ -388,7 +388,7 @@ Past Sponsors:
388
388
  - [LetzDoIt](https://www.letzdoitapp.com/)
389
389
 
390
390
  This list includes all companies that support one or more of the team members
391
- in the maintainance of this project.
391
+ in the maintenance of this project.
392
392
 
393
393
  ## License
394
394
 
@@ -181,6 +181,8 @@ section.
181
181
  to go!
182
182
  A plugin to implement [Lyra](https://github.com/nearform/lyra) search engine
183
183
  on Fastify
184
+ - [`@joggr/fastify-prisma`](https://github.com/joggrdocs/fastify-prisma)
185
+ A plugin for accessing an instantiated PrismaClient on your server.
184
186
  - [`@mgcrea/fastify-graceful-exit`](https://github.com/mgcrea/fastify-graceful-exit)
185
187
  A plugin to close the server gracefully
186
188
  - [`@mgcrea/fastify-request-logger`](https://github.com/mgcrea/fastify-request-logger)
@@ -279,6 +281,8 @@ section.
279
281
  - [`fastify-cloudevents`](https://github.com/smartiniOnGitHub/fastify-cloudevents)
280
282
  Fastify plugin to generate and forward Fastify events in the Cloudevents
281
283
  format.
284
+ - [`fastify-cloudflare-turnstile`](https://github.com/112RG/fastify-cloudflare-turnstile)
285
+ Fastify plugin for CloudFlare Turnstile.
282
286
  - [`fastify-cloudinary`](https://github.com/Vanilla-IceCream/fastify-cloudinary)
283
287
  The Cloudinary Fastify SDK allows you to quickly and easily integrate your
284
288
  application with Cloudinary. Effortlessly optimize and transform your cloud's
@@ -317,6 +321,8 @@ section.
317
321
  - [`fastify-esso`](https://github.com/patrickpissurno/fastify-esso) The easiest
318
322
  authentication plugin for Fastify, with built-in support for Single sign-on
319
323
  (and great documentation).
324
+ - [`fastify-event-bus`](https://github.com/Shiva127/fastify-event-bus) Event bus
325
+ support for Fastify. Built upon [js-event-bus](https://github.com/bcerati/js-event-bus).
320
326
  - [`fastify-evervault`](https://github.com/Briscoooe/fastify-evervault/) Fastify
321
327
  plugin for instantiating and encapsulating the
322
328
  [Evervault](https://evervault.com/) client.
@@ -445,6 +445,7 @@ fastify.addHook('onListen', async function () {
445
445
 
446
446
  > **Note**
447
447
  > This hook will not run when the server is started using `fastify.inject()` or `fastify.ready()`
448
+
448
449
  ### onClose
449
450
  <a id="on-close"></a>
450
451
 
@@ -134,7 +134,7 @@ fastify.listen({ port: 3000 }, (err, address) => {
134
134
  <a id="async-await"></a>
135
135
 
136
136
  *async/await* is supported by `after`, `ready`, and `listen`, as well as
137
- `fastify` being a [Thenable](https://promisesaplus.com/).
137
+ `fastify` being a Thenable.
138
138
 
139
139
  ```js
140
140
  await fastify.register(require('my-plugin'))
@@ -85,7 +85,8 @@ object that exposes the following functions and properties:
85
85
  from Node core.
86
86
  - `.log` - The logger instance of the incoming request.
87
87
  - `.request` - The incoming request.
88
- - `.context` - Access the [Request's context](./Request.md) property.
88
+ - `.context` - Deprecated, access the [Request's context](./Request.md) property.
89
+ - `.routeOptions` - Access the [Request's routeOptions](./Request.md) property.
89
90
 
90
91
  ```js
91
92
  fastify.get('/', options, function (request, reply) {
@@ -605,8 +606,9 @@ low-level request and response. Moreover, hooks will not be invoked.
605
606
  *Modifying the `.sent` property directly is deprecated. Please use the
606
607
  aforementioned `.hijack()` method to achieve the same effect.*
607
608
 
608
- <a name="hijack"></a>
609
609
  ### .hijack()
610
+ <a name="hijack"></a>
611
+
610
612
  Sometimes you might need to halt the execution of the normal request lifecycle
611
613
  and handle sending the response manually.
612
614
 
@@ -892,6 +894,5 @@ For more details, see:
892
894
 
893
895
  - https://github.com/fastify/fastify/issues/1864 for the discussion about this
894
896
  feature
895
- - https://promisesaplus.com/ for the definition of thenables
896
897
  - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
897
898
  for the signature
@@ -37,8 +37,9 @@ Request is a core Fastify object containing the following fields:
37
37
  - `connection` - Deprecated, use `socket` instead. The underlying connection of
38
38
  the incoming request.
39
39
  - `socket` - the underlying connection of the incoming request
40
- - `context` - A Fastify internal object. You should not use it directly or
41
- modify it. It is useful to access one special key:
40
+ - `context` - Deprecated, use `request.routeOptions.config` instead.
41
+ A Fastify internal object. You should not use
42
+ it directly or modify it. It is useful to access one special key:
42
43
  - `context.config` - The route [`config`](./Routes.md#routes-config) object.
43
44
  - `routeSchema` - Deprecated, use `request.routeOptions.schema` instead. The
44
45
  scheme definition set for the router that is handling the request
@@ -50,20 +50,21 @@ describes the properties available in that options object.
50
50
  - [after](#after)
51
51
  - [ready](#ready)
52
52
  - [listen](#listen)
53
+ - [`listenTextResolver`](#listentextresolver)
53
54
  - [addresses](#addresses)
54
55
  - [getDefaultRoute](#getdefaultroute)
55
56
  - [setDefaultRoute](#setdefaultroute)
56
57
  - [routing](#routing)
57
58
  - [route](#route)
58
- - [hasRoute](#hasRoute)
59
+ - [hasRoute](#hasroute)
59
60
  - [close](#close)
60
- - [decorate*](#decorate)
61
+ - [decorate\*](#decorate)
61
62
  - [register](#register)
62
63
  - [addHook](#addhook)
63
64
  - [prefix](#prefix)
64
65
  - [pluginName](#pluginname)
65
66
  - [hasPlugin](#hasplugin)
66
- - [listeningOrigin](#listeningOrigin)
67
+ - [listeningOrigin](#listeningorigin)
67
68
  - [log](#log)
68
69
  - [version](#version)
69
70
  - [inject](#inject)
@@ -93,6 +94,7 @@ describes the properties available in that options object.
93
94
  - [defaultTextParser](#defaulttextparser)
94
95
  - [errorHandler](#errorhandler)
95
96
  - [childLoggerFactory](#childloggerfactory)
97
+ - [Symbol.asyncDispose](#symbolasyncdispose)
96
98
  - [initialConfig](#initialconfig)
97
99
 
98
100
  ### `http`
@@ -1866,6 +1868,32 @@ fastify.get('/', {
1866
1868
  Fastify instance. See the [`childLoggerFactory` config option](#setchildloggerfactory)
1867
1869
  for more info.
1868
1870
 
1871
+ #### Symbol.asyncDispose
1872
+ <a id="symbolAsyncDispose"></a>
1873
+
1874
+ `fastify[Symbol.asyncDispose]` is a symbol that can be used to define an
1875
+ asynchronous function that will be called when the Fastify instance is closed.
1876
+
1877
+ It's commonly used alongside the `using` TypeScript keyword to ensure that
1878
+ resources are cleaned up when the Fastify instance is closed.
1879
+
1880
+ This combines perfectly inside short lived processes or unit tests, where you must
1881
+ close all Fastify resources after returning from inside the function.
1882
+
1883
+ ```ts
1884
+ test('Uses app and closes it afterwards', async () => {
1885
+ await using app = fastify();
1886
+ // do something with app.
1887
+ })
1888
+ ```
1889
+
1890
+ In the above example, Fastify is closed automatically after the test finishes.
1891
+
1892
+ Read more about the
1893
+ [ECMAScript Explicit Resource Management](https://tc39.es/proposal-explicit-resource-management)
1894
+ and the [using keyword](https://devblogs.microsoft.com/typescript/announcing-typescript-5-2/)
1895
+ introduced in TypeScript 5.2.
1896
+
1869
1897
  #### initialConfig
1870
1898
  <a id="initial-config"></a>
1871
1899
 
@@ -47,7 +47,7 @@ server.get('/route', {
47
47
  },
48
48
  required: ['foo', 'bar']
49
49
  }
50
- } as const // don't forget to use const !
50
+ }
51
51
 
52
52
  }, (request, reply) => {
53
53
 
@@ -141,7 +141,7 @@ function pluginWithJsonSchema(fastify: FastifyInstance, _opts, done): void {
141
141
  y: { type: 'number' },
142
142
  z: { type: 'boolean' }
143
143
  },
144
- } as const
144
+ }
145
145
  }
146
146
  }, (req) => {
147
147
  const { x, y, z } = req.body // type safe
@@ -606,7 +606,7 @@ your plugin.
606
606
  ```
607
607
  5. Open `index.d.ts` and add the following code:
608
608
  ```typescript
609
- import { FastifyPlugin } from 'fastify'
609
+ import { FastifyPluginCallback } from 'fastify'
610
610
 
611
611
  interface PluginOptions {
612
612
  //...
@@ -627,7 +627,7 @@ your plugin.
627
627
 
628
628
  // fastify-plugin automatically adds named export, so be sure to add also this type
629
629
  // the variable name is derived from `options.name` property if `module.exports.myPlugin` is missing
630
- export const myPlugin: FastifyPlugin<PluginOptions>
630
+ export const myPlugin: FastifyPluginCallback<PluginOptions>
631
631
 
632
632
  // fastify-plugin automatically adds `.default` property to the exported plugin. See the note below
633
633
  export default myPlugin
@@ -1233,15 +1233,16 @@ a function signature with an underlying generic `Options` which is defaulted to
1233
1233
  FastifyPlugin parameter when calling this function so there is no need to
1234
1234
  specify the underlying generic. The options parameter is the intersection of the
1235
1235
  plugin's options and two additional optional properties: `prefix: string` and
1236
- `logLevel`: [LogLevel][LogLevel].
1236
+ `logLevel`: [LogLevel][LogLevel]. `FastifyPlugin` is deprecated use
1237
+ `FastifyPluginCallback` and `FastifyPluginAsync` instead.
1237
1238
 
1238
1239
  Below is an example of the options inference in action:
1239
1240
 
1240
1241
  ```typescript
1241
1242
  const server = fastify()
1242
1243
 
1243
- const plugin: FastifyPlugin<{
1244
- option1: string;
1244
+ const plugin: FastifyPluginCallback<{
1245
+ : option1: string;
1245
1246
  option2: boolean;
1246
1247
  }> = function (instance, opts, done) { }
1247
1248
 
@@ -1296,9 +1297,22 @@ Union type of: `'info' | 'error' | 'debug' | 'fatal' | 'warn' | 'trace'`
1296
1297
  The context type definition is similar to the other highly dynamic pieces of the
1297
1298
  type system. Route context is available in the route handler method.
1298
1299
 
1299
- ##### fastify.FastifyContext
1300
+ ##### fastify.FastifyRequestContext
1300
1301
 
1301
- [src](https://github.com/fastify/fastify/blob/main/types/context.d.ts#L6)
1302
+ [src](https://github.com/fastify/fastify/blob/main/types/context.d.ts#L11)
1303
+
1304
+ An interface with a single required property `config` that is set by default to
1305
+ `unknown`. Can be specified either using a generic or an overload.
1306
+
1307
+ This type definition is potentially incomplete. If you are using it and can
1308
+ provide more details on how to improve the definition, we strongly encourage you
1309
+ to open an issue in the main
1310
+ [fastify/fastify](https://github.com/fastify/fastify) repository. Thank you in
1311
+ advanced!
1312
+
1313
+ ##### fastify.FastifyReplyContext
1314
+
1315
+ [src](https://github.com/fastify/fastify/blob/main/types/context.d.ts#L11)
1302
1316
 
1303
1317
  An interface with a single required property `config` that is set by default to
1304
1318
  `unknown`. Can be specified either using a generic or an overload.
package/fastify.d.ts CHANGED
@@ -10,7 +10,7 @@ import { ConstraintStrategy, HTTPVersion } from 'find-my-way'
10
10
  import { Chain as LightMyRequestChain, InjectOptions, Response as LightMyRequestResponse, CallbackFunc as LightMyRequestCallback } from 'light-my-request'
11
11
 
12
12
  import { FastifyBodyParser, FastifyContentTypeParser, AddContentTypeParser, hasContentTypeParser, getDefaultJsonParser, ProtoAction, ConstructorAction } from './types/content-type-parser'
13
- import { FastifyContext, FastifyContextConfig } from './types/context'
13
+ import { FastifyRequestContext, FastifyContextConfig, FastifyReplyContext } from './types/context'
14
14
  import { FastifyErrorCodes } from './types/errors'
15
15
  import { DoneFuncWithErrOrRes, HookHandlerDoneFunction, RequestPayload, onCloseAsyncHookHandler, onCloseHookHandler, onErrorAsyncHookHandler, onErrorHookHandler, onReadyAsyncHookHandler, onReadyHookHandler, onListenAsyncHookHandler, onListenHookHandler, onRegisterHookHandler, onRequestAsyncHookHandler, onRequestHookHandler, onResponseAsyncHookHandler, onResponseHookHandler, onRouteHookHandler, onSendAsyncHookHandler, onSendHookHandler, onTimeoutAsyncHookHandler, onTimeoutHookHandler, preHandlerAsyncHookHandler, preHandlerHookHandler, preParsingAsyncHookHandler, preParsingHookHandler, preSerializationAsyncHookHandler, preSerializationHookHandler, preValidationAsyncHookHandler, preValidationHookHandler, onRequestAbortHookHandler, onRequestAbortAsyncHookHandler } from './types/hooks'
16
16
  import { FastifyListenOptions, FastifyInstance, PrintRoutesOptions } from './types/instance'
@@ -175,7 +175,7 @@ declare namespace fastify {
175
175
  FastifyPluginCallback, FastifyPluginAsync, FastifyPluginOptions, FastifyPlugin, // './types/plugin'
176
176
  FastifyListenOptions, FastifyInstance, PrintRoutesOptions, // './types/instance'
177
177
  FastifyLoggerOptions, FastifyBaseLogger, FastifyLoggerInstance, FastifyLogFn, LogLevel, // './types/logger'
178
- FastifyContext, FastifyContextConfig, // './types/context'
178
+ FastifyRequestContext, FastifyContextConfig, FastifyReplyContext, // './types/context'
179
179
  RouteHandler, RouteHandlerMethod, RouteOptions, RouteShorthandMethod, RouteShorthandOptions, RouteShorthandOptionsWithHandler, RouteGenericInterface, // './types/route'
180
180
  FastifyRegister, FastifyRegisterOptions, RegisterOptions, // './types/register'
181
181
  FastifyBodyParser, FastifyContentTypeParser, AddContentTypeParser, hasContentTypeParser, getDefaultJsonParser, ProtoAction, ConstructorAction, // './types/content-type-parser'
package/fastify.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const VERSION = '4.23.2'
3
+ const VERSION = '4.24.0'
4
4
 
5
5
  const Avvio = require('avvio')
6
6
  const http = require('node:http')
@@ -506,6 +506,13 @@ function fastify (options) {
506
506
  // versions of Node.js. In that event, we don't care, so ignore the error.
507
507
  }
508
508
 
509
+ // Older nodejs versions may not have asyncDispose
510
+ if ('asyncDispose' in Symbol) {
511
+ fastify[Symbol.asyncDispose] = function dispose () {
512
+ return fastify.close()
513
+ }
514
+ }
515
+
509
516
  return fastify
510
517
 
511
518
  function throwIfAlreadyStarted (msg) {
@@ -406,7 +406,7 @@ function ParserListItem (contentType) {
406
406
  // because it would become a match-all handler
407
407
  if (this.isEssence === false && parsed.type === '') {
408
408
  // handle semicolon or empty string
409
- const tmp = contentType.split(';')[0]
409
+ const tmp = contentType.split(';', 1)[0]
410
410
  this.type = tmp === '' ? contentType : tmp
411
411
  } else {
412
412
  this.type = parsed.type
package/lib/reply.js CHANGED
@@ -4,6 +4,7 @@ const eos = require('node:stream').finished
4
4
 
5
5
  const {
6
6
  kFourOhFourContext,
7
+ kPublicRouteContext,
7
8
  kReplyErrorHandlerCalled,
8
9
  kReplyHijacked,
9
10
  kReplyStartTime,
@@ -79,6 +80,7 @@ Object.defineProperties(Reply.prototype, {
79
80
  // Is temporary to avoid constant conflicts between `next` and `main`
80
81
  context: {
81
82
  get () {
83
+ warning.emit('FSTDEP019')
82
84
  return this.request[kRouteContext]
83
85
  }
84
86
  },
@@ -115,6 +117,11 @@ Object.defineProperties(Reply.prototype, {
115
117
  set (value) {
116
118
  this.code(value)
117
119
  }
120
+ },
121
+ [kPublicRouteContext]: {
122
+ get () {
123
+ return this.request[kPublicRouteContext]
124
+ }
118
125
  }
119
126
  })
120
127
 
@@ -244,7 +251,7 @@ Reply.prototype.header = function (key, value = '') {
244
251
  }
245
252
 
246
253
  if (Array.isArray(value)) {
247
- this[kReplyHeaders][key].push(...value)
254
+ Array.prototype.push.apply(this[kReplyHeaders][key], value)
248
255
  } else {
249
256
  this[kReplyHeaders][key].push(value)
250
257
  }
@@ -458,7 +465,6 @@ Reply.prototype.getResponseTime = function () {
458
465
  // Make reply a thenable, so it could be used with async/await.
459
466
  // See
460
467
  // - https://github.com/fastify/fastify/issues/1864 for the discussions
461
- // - https://promisesaplus.com/ for the definition of thenable
462
468
  // - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then for the signature
463
469
  Reply.prototype.then = function (fulfilled, rejected) {
464
470
  if (this.sent) {
@@ -591,6 +597,17 @@ function onSendEnd (reply, payload) {
591
597
  return
592
598
  }
593
599
 
600
+ if ((statusCode >= 100 && statusCode < 200) || statusCode === 204) {
601
+ // Responses without a content body must not send content-type
602
+ // or content-length headers.
603
+ // See https://www.rfc-editor.org/rfc/rfc9110.html#section-8.6.
604
+ reply.removeHeader('content-type')
605
+ reply.removeHeader('content-length')
606
+ safeWriteHead(reply, statusCode)
607
+ sendTrailer(undefined, res, reply)
608
+ return
609
+ }
610
+
594
611
  if (typeof payload.pipe === 'function') {
595
612
  sendStream(payload, res, reply)
596
613
  return
@@ -810,7 +827,7 @@ function onResponseCallback (err, request, reply) {
810
827
  }
811
828
 
812
829
  function buildReply (R) {
813
- const props = [...R.props]
830
+ const props = R.props.slice()
814
831
 
815
832
  function _Reply (res, request, log) {
816
833
  this.raw = res
@@ -12,6 +12,16 @@
12
12
  * @returns {GenerateRequestId}
13
13
  */
14
14
  function reqIdGenFactory (requestIdHeader, optGenReqId) {
15
+ const genReqId = optGenReqId || buildDefaultGenReqId()
16
+
17
+ if (requestIdHeader) {
18
+ return buildOptionalHeaderReqId(requestIdHeader, genReqId)
19
+ }
20
+
21
+ return genReqId
22
+ }
23
+
24
+ function buildDefaultGenReqId () {
15
25
  // 2,147,483,647 (2^31 − 1) stands for max SMI value (an internal optimization of V8).
16
26
  // With this upper bound, if you'll be generating 1k ids/sec, you're going to hit it in ~25 days.
17
27
  // This is very likely to happen in real-world applications, hence the limit is enforced.
@@ -20,20 +30,16 @@ function reqIdGenFactory (requestIdHeader, optGenReqId) {
20
30
  const maxInt = 2147483647
21
31
 
22
32
  let nextReqId = 0
23
- function defaultGenReqId (_req) {
33
+ return function defaultGenReqId () {
24
34
  nextReqId = (nextReqId + 1) & maxInt
25
35
  return `req-${nextReqId.toString(36)}`
26
36
  }
37
+ }
27
38
 
28
- const genReqId = optGenReqId || defaultGenReqId
29
-
30
- if (requestIdHeader) {
31
- return function (req) {
32
- return req.headers[requestIdHeader] || genReqId(req)
33
- }
39
+ function buildOptionalHeaderReqId (requestIdHeader, genReqId) {
40
+ return function (req) {
41
+ return req.headers[requestIdHeader] || genReqId(req)
34
42
  }
35
-
36
- return genReqId
37
43
  }
38
44
 
39
45
  module.exports = {
package/lib/request.js CHANGED
@@ -66,7 +66,7 @@ function buildRequest (R, trustProxy) {
66
66
  }
67
67
 
68
68
  function buildRegularRequest (R) {
69
- const props = [...R.props]
69
+ const props = R.props.slice()
70
70
  function _Request (id, params, req, query, log, context) {
71
71
  this.id = id
72
72
  this[kRouteContext] = context
package/lib/route.js CHANGED
@@ -28,7 +28,8 @@ const {
28
28
  FST_ERR_ROUTE_METHOD_NOT_SUPPORTED,
29
29
  FST_ERR_ROUTE_METHOD_INVALID,
30
30
  FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED,
31
- FST_ERR_ROUTE_BODY_LIMIT_OPTION_NOT_INT
31
+ FST_ERR_ROUTE_BODY_LIMIT_OPTION_NOT_INT,
32
+ FST_ERR_HOOK_INVALID_ASYNC_HANDLER
32
33
  } = require('./errors')
33
34
 
34
35
  const {
@@ -268,6 +269,20 @@ function buildRouting (options) {
268
269
  if (typeof func !== 'function') {
269
270
  throw new FST_ERR_HOOK_INVALID_HANDLER(hook, Object.prototype.toString.call(func))
270
271
  }
272
+
273
+ if (hook === 'onSend' || hook === 'preSerialization' || hook === 'onError' || hook === 'preParsing') {
274
+ if (func.constructor.name === 'AsyncFunction' && func.length === 4) {
275
+ throw new FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
276
+ }
277
+ } else if (hook === 'onRequestAbort') {
278
+ if (func.constructor.name === 'AsyncFunction' && func.length !== 1) {
279
+ throw new FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
280
+ }
281
+ } else {
282
+ if (func.constructor.name === 'AsyncFunction' && func.length === 3) {
283
+ throw new FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
284
+ }
285
+ }
271
286
  }
272
287
  } else if (opts[hook] !== undefined && typeof opts[hook] !== 'function') {
273
288
  throw new FST_ERR_HOOK_INVALID_HANDLER(hook, Object.prototype.toString.call(opts[hook]))
@@ -307,8 +322,8 @@ function buildRouting (options) {
307
322
  constraints.version = opts.version
308
323
  }
309
324
 
310
- const headHandler = router.find('HEAD', opts.url, constraints)
311
- const hasHEADHandler = headHandler != null
325
+ const headHandler = router.findRoute('HEAD', opts.url, constraints)
326
+ const hasHEADHandler = headHandler !== null
312
327
 
313
328
  // remove the head route created by fastify
314
329
  if (hasHEADHandler && !context[kRouteByFastify] && headHandler.store[kRouteByFastify]) {
package/lib/schemas.js CHANGED
@@ -158,7 +158,7 @@ function getSchemaSerializer (context, statusCode, contentType) {
158
158
  }
159
159
  if (responseSchemaDef[statusCode]) {
160
160
  if (responseSchemaDef[statusCode].constructor === Object && contentType) {
161
- const mediaName = contentType.split(';')[0]
161
+ const mediaName = contentType.split(';', 1)[0]
162
162
  if (responseSchemaDef[statusCode][mediaName]) {
163
163
  return responseSchemaDef[statusCode][mediaName]
164
164
  }
@@ -170,7 +170,7 @@ function getSchemaSerializer (context, statusCode, contentType) {
170
170
  const fallbackStatusCode = (statusCode + '')[0] + 'xx'
171
171
  if (responseSchemaDef[fallbackStatusCode]) {
172
172
  if (responseSchemaDef[fallbackStatusCode].constructor === Object && contentType) {
173
- const mediaName = contentType.split(';')[0]
173
+ const mediaName = contentType.split(';', 1)[0]
174
174
  if (responseSchemaDef[fallbackStatusCode][mediaName]) {
175
175
  return responseSchemaDef[fallbackStatusCode][mediaName]
176
176
  }
@@ -182,7 +182,7 @@ function getSchemaSerializer (context, statusCode, contentType) {
182
182
  }
183
183
  if (responseSchemaDef.default) {
184
184
  if (responseSchemaDef.default.constructor === Object && contentType) {
185
- const mediaName = contentType.split(';')[0]
185
+ const mediaName = contentType.split(';', 1)[0]
186
186
  if (responseSchemaDef.default[mediaName]) {
187
187
  return responseSchemaDef.default[mediaName]
188
188
  }
package/lib/warnings.js CHANGED
@@ -21,7 +21,7 @@ warning.create('FastifyDeprecation', 'FSTDEP010', 'Modifying the "reply.sent" pr
21
21
 
22
22
  warning.create('FastifyDeprecation', 'FSTDEP011', 'Variadic listen method is deprecated. Please use ".listen(optionsObject)" instead. The variadic signature will be removed in `fastify@5`.')
23
23
 
24
- warning.create('FastifyDeprecation', 'FSTDEP012', 'Request#context property access is deprecated. Please use "Request#routeConfig" or "Request#routeSchema" instead for accessing Route settings. The "Request#context" will be removed in `fastify@5`.')
24
+ warning.create('FastifyDeprecation', 'FSTDEP012', 'request.context property access is deprecated. Please use "request.routeOptions.config" or "request.routeOptions.schema" instead for accessing Route settings. The "request.context" will be removed in `fastify@5`.')
25
25
 
26
26
  warning.create('FastifyDeprecation', 'FSTDEP013', 'Direct return of "trailers" function is deprecated. Please use "callback" or "async-await" for return value. The support of direct return will removed in `fastify@5`.')
27
27
 
@@ -35,6 +35,8 @@ warning.create('FastifyDeprecation', 'FSTDEP017', 'You are accessing the depreca
35
35
 
36
36
  warning.create('FastifyDeprecation', 'FSTDEP018', 'You are accessing the deprecated "request.routerMethod" property. Use "request.routeOptions.method" instead. Property "req.routerMethod" will be removed in `fastify@5`.')
37
37
 
38
+ warning.create('FastifyDeprecation', 'FSTDEP019', 'reply.context property access is deprecated. Please use "reply.routeOptions.config" or "reply.routeOptions.schema" instead for accessing Route settings. The "reply.context" will be removed in `fastify@5`.')
39
+
38
40
  warning.create('FastifyWarning', 'FSTWRN001', 'The %s schema for %s: %s is missing. This may indicate the schema is not well specified.', { unlimited: true })
39
41
 
40
42
  module.exports = warning
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fastify",
3
- "version": "4.23.2",
3
+ "version": "4.24.0",
4
4
  "description": "Fast and low overhead web framework, for Node.js",
5
5
  "main": "fastify.js",
6
6
  "type": "commonjs",
@@ -195,7 +195,7 @@
195
195
  "avvio": "^8.2.1",
196
196
  "fast-content-type-parse": "^1.0.0",
197
197
  "fast-json-stringify": "^5.7.0",
198
- "find-my-way": "^7.6.0",
198
+ "find-my-way": "^7.7.0",
199
199
  "light-my-request": "^5.9.1",
200
200
  "pino": "^8.12.0",
201
201
  "process-warning": "^2.2.0",
@@ -0,0 +1,21 @@
1
+ 'use strict'
2
+
3
+ const t = require('tap')
4
+ const Fastify = require('../fastify')
5
+
6
+ // asyncDispose doesn't exist in node <= 16
7
+ t.test('async dispose should close fastify', { skip: !('asyncDispose' in Symbol) }, async t => {
8
+ t.plan(2)
9
+
10
+ const fastify = Fastify()
11
+
12
+ await fastify.listen({ port: 0 })
13
+
14
+ t.equal(fastify.server.listening, true)
15
+
16
+ // the same as syntax sugar for
17
+ // await using app = fastify()
18
+ await fastify[Symbol.asyncDispose]()
19
+
20
+ t.equal(fastify.server.listening, false)
21
+ })