fastify 4.28.1 → 4.29.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.
package/SPONSORS.md CHANGED
@@ -14,6 +14,8 @@ _Be the first!_
14
14
  ## Tier 3
15
15
 
16
16
  - [Mercedes-Benz Group](https://github.com/mercedes-benz)
17
+ - [Val Town, Inc.](https://opencollective.com/valtown)
18
+ - [Handsontable - JavaScript Data Grid](https://handsontable.com/docs/react-data-grid/?utm_source=Fastify_GH&utm_medium=sponsorship&utm_campaign=library_sponsorship_2024)
17
19
 
18
20
  ## Tier 2
19
21
 
@@ -6,6 +6,34 @@ Before migrating to v4, please ensure that you have fixed all deprecation
6
6
  warnings from v3. All v3 deprecations have been removed and they will no longer
7
7
  work after upgrading.
8
8
 
9
+ ## Codemods
10
+ ### Fastify v4 Codemods
11
+
12
+ To help with the upgrade, we’ve worked with the team at
13
+ [Codemod](https://github.com/codemod-com/codemod) to
14
+ publish codemods that will automatically update your code to many of
15
+ the new APIs and patterns in Fastify v4.
16
+
17
+ Run the following
18
+ [migration recipe](https://go.codemod.com/fastify-4-migration-recipe) to
19
+ automatically update your code to Fastify v4:
20
+
21
+ ```
22
+ npx codemod@latest fastify/4/migration-recipe
23
+ ```
24
+
25
+ This will run the following codemods:
26
+
27
+ - [`fastify/4/remove-app-use`](https://go.codemod.com/fastify-4-remove-app-use)
28
+ - [`fastify/4/reply-raw-access`](https://go.codemod.com/fastify-4-reply-raw-access)
29
+ - [`fastify/4/wrap-routes-plugin`](https://go.codemod.com/fastify-4-wrap-routes-plugin)
30
+ - [`fastify/4/await-register-calls`](https://go.codemod.com/fastify-4-await-register-calls)
31
+
32
+ Each of these codemods automates the changes listed in the v4 migration guide.
33
+ For a complete list of available Fastify codemods and further details,
34
+ see [Codemod Registry](https://go.codemod.com/fastify).
35
+
36
+
9
37
  ## Breaking Changes
10
38
 
11
39
  ### Error handling composition ([#3261](https://github.com/fastify/fastify/pull/3261))
@@ -55,11 +83,23 @@ If you need to use middleware, use
55
83
  continue to be maintained.
56
84
  However, it is strongly recommended that you migrate to Fastify's [hooks](../Reference/Hooks.md).
57
85
 
86
+ > **Note**: Codemod remove `app.use()` with:
87
+ >
88
+ > ```bash
89
+ > npx codemod@latest fastify/4/remove-app-use
90
+ > ```
91
+
58
92
  ### `reply.res` moved to `reply.raw`
59
93
 
60
94
  If you previously used the `reply.res` attribute to access the underlying Request
61
95
  object you will now need to use `reply.raw`.
62
96
 
97
+ > **Note**: Codemod `reply.res` to `reply.raw` with:
98
+ >
99
+ > ```bash
100
+ > npx codemod@latest fastify/4/reply-raw-access
101
+ > ```
102
+
63
103
  ### Need to `return reply` to signal a "fork" of the promise chain
64
104
 
65
105
  In some situations, like when a response is sent asynchronously or when you are
@@ -105,6 +145,11 @@ As a result, if you specify an `onRoute` hook in a plugin you should now either:
105
145
  done();
106
146
  });
107
147
  ```
148
+ > **Note**: Codemod synchronous route definitions with:
149
+ >
150
+ > ```bash
151
+ > npx codemod@latest fastify/4/wrap-routes-plugin
152
+ > ```
108
153
 
109
154
  * use `await register(...)`
110
155
 
@@ -130,6 +175,13 @@ As a result, if you specify an `onRoute` hook in a plugin you should now either:
130
175
  });
131
176
  ```
132
177
 
178
+ > **Note**: Codemod 'await register(...)' with:
179
+ >
180
+ > ```bash
181
+ > npx codemod@latest fastify/4/await-register-calls
182
+ > ```
183
+
184
+
133
185
  ### Optional URL parameters
134
186
 
135
187
  If you've already used any implicitly optional parameters, you'll get a 404
@@ -43,7 +43,8 @@ fastify.route(options)
43
43
  [here](./Validation-and-Serialization.md) for more info.
44
44
 
45
45
  * `body`: validates the body of the request if it is a POST, PUT, PATCH,
46
- TRACE, SEARCH, PROPFIND, PROPPATCH or LOCK method.
46
+ TRACE, SEARCH, PROPFIND, PROPPATCH, COPY, MOVE, MKCOL, REPORT, MKCALENDAR
47
+ or LOCK method.
47
48
  * `querystring` or `query`: validates the querystring. This can be a complete
48
49
  JSON Schema object, with the property `type` of `object` and `properties`
49
50
  object of parameters, or simply the values of what would be contained in the
@@ -272,6 +272,8 @@ fastify.get('///foo//bar//', function (req, reply) {
272
272
  ### `maxParamLength`
273
273
  <a id="factory-max-param-length"></a>
274
274
 
275
+ + Default: `100`
276
+
275
277
  You can set a custom length for parameters in parametric (standard, regex, and
276
278
  multi) routes by using `maxParamLength` option; the default value is 100
277
279
  characters. If the maximum length limit is reached, the not found route will
@@ -235,6 +235,28 @@ const schema = {
235
235
  fastify.post('/the/url', { schema }, handler)
236
236
  ```
237
237
 
238
+ For `body` schema, it is further possible to differentiate the schema per content
239
+ type by nesting the schemas inside `content` property. The schema validation
240
+ will be applied based on the `Content-Type` header in the request.
241
+
242
+ ```js
243
+ fastify.post('/the/url', {
244
+ schema: {
245
+ body: {
246
+ content: {
247
+ 'application/json': {
248
+ schema: { type: 'object' }
249
+ },
250
+ 'text/plain': {
251
+ schema: { type: 'string' }
252
+ }
253
+ // Other content types will not be validated
254
+ }
255
+ }
256
+ }
257
+ }, handler)
258
+ ```
259
+
238
260
  *Note that Ajv will try to [coerce](https://ajv.js.org/coercion.html) the values
239
261
  to the types specified in your schema `type` keywords, both to pass the
240
262
  validation and to use the correctly typed data afterwards.*
@@ -25,6 +25,7 @@
25
25
  - [FSTDEP019](#FSTDEP019)
26
26
  - [FSTDEP020](#FSTDEP020)
27
27
  - [FSTDEP021](#FSTDEP021)
28
+ - [FSTDEP022](#FSTDEP022)
28
29
 
29
30
 
30
31
  ## Warnings
@@ -92,3 +93,4 @@ Deprecation codes are further supported by the Node.js CLI options:
92
93
  | <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) |
93
94
  | <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) |
94
95
  | <a id="FSTDEP021">FSTDEP021</a> | The `reply.redirect()` method has a new signature: `reply.redirect(url: string, code?: number)`. It will be enforced in `fastify@v5`'. | [#5483](https://github.com/fastify/fastify/pull/5483) |
96
+ | <a id="FSTDEP022">FSTDEP022</a> | You are using the deprecated json shorthand schema on route %s. Specify full object schema instead. It will be removed in `fastify@v5` | [#5483](https://github.com/fastify/fastify/pull/0000) |
package/fastify.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const VERSION = '4.28.1'
3
+ const VERSION = '4.29.0'
4
4
 
5
5
  const Avvio = require('avvio')
6
6
  const http = require('node:http')
@@ -671,7 +671,7 @@ function fastify (options) {
671
671
  }
672
672
 
673
673
  if (name === 'onClose') {
674
- this.onClose(fn)
674
+ this.onClose(fn.bind(this))
675
675
  } else if (name === 'onReady' || name === 'onListen' || name === 'onRoute') {
676
676
  this[kHooks].add(name, fn)
677
677
  } else {
@@ -28,7 +28,8 @@ function handleRequest (err, request, reply) {
28
28
  const contentType = headers['content-type']
29
29
 
30
30
  if (method === 'POST' || method === 'PUT' || method === 'PATCH' || method === 'TRACE' || method === 'SEARCH' ||
31
- method === 'PROPFIND' || method === 'PROPPATCH' || method === 'LOCK' || method === 'REPORT' || method === 'MKCALENDAR') {
31
+ method === 'PROPFIND' || method === 'PROPPATCH' || method === 'LOCK' || method === 'COPY' || method === 'MOVE' ||
32
+ method === 'MKCOL' || method === 'REPORT' || method === 'MKCALENDAR') {
32
33
  if (contentType === undefined) {
33
34
  if (
34
35
  headers['transfer-encoding'] === undefined &&
package/lib/reply.js CHANGED
@@ -602,14 +602,30 @@ function onSendEnd (reply, payload) {
602
602
  reply.header('Trailer', header.trim())
603
603
  }
604
604
 
605
- // since Response contain status code, we need to update before
606
- // any action that used statusCode
607
- const isResponse = toString.call(payload) === '[object Response]'
608
- if (isResponse) {
605
+ // since Response contain status code, headers and body,
606
+ // we need to update the status, add the headers and use it's body as payload
607
+ // before continuing
608
+ if (toString.call(payload) === '[object Response]') {
609
609
  // https://developer.mozilla.org/en-US/docs/Web/API/Response/status
610
610
  if (typeof payload.status === 'number') {
611
611
  reply.code(payload.status)
612
612
  }
613
+
614
+ // https://developer.mozilla.org/en-US/docs/Web/API/Response/headers
615
+ if (typeof payload.headers === 'object' && typeof payload.headers.forEach === 'function') {
616
+ for (const [headerName, headerValue] of payload.headers) {
617
+ reply.header(headerName, headerValue)
618
+ }
619
+ }
620
+
621
+ // https://developer.mozilla.org/en-US/docs/Web/API/Response/body
622
+ if (payload.body !== null) {
623
+ if (payload.bodyUsed) {
624
+ throw new FST_ERR_REP_RESPONSE_BODY_CONSUMED()
625
+ }
626
+ }
627
+ // Keep going, body is either null or ReadableStream
628
+ payload = payload.body
613
629
  }
614
630
  const statusCode = res.statusCode
615
631
 
@@ -656,26 +672,6 @@ function onSendEnd (reply, payload) {
656
672
  return
657
673
  }
658
674
 
659
- // Response
660
- if (isResponse) {
661
- // https://developer.mozilla.org/en-US/docs/Web/API/Response/headers
662
- if (typeof payload.headers === 'object' && typeof payload.headers.forEach === 'function') {
663
- for (const [headerName, headerValue] of payload.headers) {
664
- reply.header(headerName, headerValue)
665
- }
666
- }
667
-
668
- // https://developer.mozilla.org/en-US/docs/Web/API/Response/body
669
- if (payload.body != null) {
670
- if (payload.bodyUsed) {
671
- throw new FST_ERR_REP_RESPONSE_BODY_CONSUMED()
672
- }
673
- // Response.body always a ReadableStream
674
- sendWebStream(payload.body, res, reply)
675
- }
676
- return
677
- }
678
-
679
675
  if (typeof payload !== 'string' && !Buffer.isBuffer(payload)) {
680
676
  throw new FST_ERR_REP_INVALID_PAYLOAD_TYPE(typeof payload)
681
677
  }
@@ -726,7 +722,7 @@ function sendStream (payload, res, reply) {
726
722
  if (res.headersSent || reply.request.raw.aborted === true) {
727
723
  if (!errorLogged) {
728
724
  errorLogged = true
729
- logStreamError(reply.log, err, res)
725
+ logStreamError(reply.log, err, reply)
730
726
  }
731
727
  res.destroy()
732
728
  } else {
package/lib/route.js CHANGED
@@ -366,7 +366,7 @@ function buildRouting (options) {
366
366
  // any route insertion error created by fastify can be safely ignore
367
367
  // because it only duplicate route for head
368
368
  if (!context[kRouteByFastify]) {
369
- const isDuplicatedRoute = error.message.includes(`Method '${opts.method}' already declared for route '${opts.url}'`)
369
+ const isDuplicatedRoute = error.message.includes(`Method '${opts.method}' already declared for route`)
370
370
  if (isDuplicatedRoute) {
371
371
  throw new FST_ERR_DUPLICATED_ROUTE(opts.method, opts.url)
372
372
  }
@@ -407,7 +407,7 @@ function buildRouting (options) {
407
407
  fourOhFour.setContext(this, context)
408
408
 
409
409
  if (opts.schema) {
410
- context.schema = normalizeSchema(context.schema, this.initialConfig)
410
+ context.schema = normalizeSchema(opts, context.schema, this.initialConfig)
411
411
 
412
412
  const schemaController = this[kSchemaController]
413
413
  if (!opts.validatorCompiler && (opts.schema.body || opts.schema.headers || opts.schema.querystring || opts.schema.params)) {
package/lib/schemas.js CHANGED
@@ -4,6 +4,9 @@ const fastClone = require('rfdc')({ circles: false, proto: true })
4
4
  const { kSchemaVisited, kSchemaResponse } = require('./symbols')
5
5
  const kFluentSchema = Symbol.for('fluent-schema-object')
6
6
 
7
+ const {
8
+ FSTDEP022
9
+ } = require('./warnings')
7
10
  const {
8
11
  FST_ERR_SCH_MISSING_ID,
9
12
  FST_ERR_SCH_ALREADY_PRESENT,
@@ -54,7 +57,7 @@ function isCustomSchemaPrototype (schema) {
54
57
  return typeof schema === 'object' && Object.getPrototypeOf(schema) !== Object.prototype
55
58
  }
56
59
 
57
- function normalizeSchema (routeSchemas, serverOptions) {
60
+ function normalizeSchema (opts, routeSchemas, serverOptions) {
58
61
  if (routeSchemas[kSchemaVisited]) {
59
62
  return routeSchemas
60
63
  }
@@ -73,7 +76,20 @@ function normalizeSchema (routeSchemas, serverOptions) {
73
76
  for (const key of SCHEMAS_SOURCE) {
74
77
  const schema = routeSchemas[key]
75
78
  if (schema && !isCustomSchemaPrototype(schema)) {
76
- routeSchemas[key] = getSchemaAnyway(schema, serverOptions.jsonShorthand)
79
+ if (key === 'body' && schema.content) {
80
+ const contentProperty = schema.content
81
+ const keys = Object.keys(contentProperty)
82
+ for (let i = 0; i < keys.length; i++) {
83
+ const contentType = keys[i]
84
+ const contentSchema = contentProperty[contentType].schema
85
+ if (!contentSchema) {
86
+ throw new FST_ERR_SCH_CONTENT_MISSING_SCHEMA(contentType)
87
+ }
88
+ routeSchemas.body.content[contentType].schema = getSchemaAnyway(opts.url, contentSchema, serverOptions.jsonShorthand)
89
+ }
90
+ continue
91
+ }
92
+ routeSchemas[key] = getSchemaAnyway(opts.url, schema, serverOptions.jsonShorthand)
77
93
  }
78
94
  }
79
95
 
@@ -95,7 +111,7 @@ function normalizeSchema (routeSchemas, serverOptions) {
95
111
  if (keys.length === 1) { break }
96
112
  throw new FST_ERR_SCH_CONTENT_MISSING_SCHEMA(mediaName)
97
113
  }
98
- routeSchemas.response[code].content[mediaName].schema = getSchemaAnyway(contentProperty[mediaName].schema, serverOptions.jsonShorthand)
114
+ routeSchemas.response[code].content[mediaName].schema = getSchemaAnyway(opts.url, contentProperty[mediaName].schema, serverOptions.jsonShorthand)
99
115
  if (i === keys.length - 1) {
100
116
  hasContentMultipleContentTypes = true
101
117
  }
@@ -103,7 +119,7 @@ function normalizeSchema (routeSchemas, serverOptions) {
103
119
  }
104
120
 
105
121
  if (!hasContentMultipleContentTypes) {
106
- routeSchemas.response[code] = getSchemaAnyway(routeSchemas.response[code], serverOptions.jsonShorthand)
122
+ routeSchemas.response[code] = getSchemaAnyway(opts.url, routeSchemas.response[code], serverOptions.jsonShorthand)
107
123
  }
108
124
  }
109
125
  }
@@ -129,9 +145,10 @@ function generateFluentSchema (schema) {
129
145
  }
130
146
  }
131
147
 
132
- function getSchemaAnyway (schema, jsonShorthand) {
148
+ function getSchemaAnyway (url, schema, jsonShorthand) {
133
149
  if (!jsonShorthand || schema.$ref || schema.oneOf || schema.allOf || schema.anyOf || schema.$merge || schema.$patch) return schema
134
150
  if (!schema.type && !schema.properties) {
151
+ FSTDEP022(url)
135
152
  return {
136
153
  type: 'object',
137
154
  properties: schema
package/lib/server.js CHANGED
@@ -72,7 +72,8 @@ function createServer (options, httpHandler) {
72
72
  } else {
73
73
  host = listenOptions.host
74
74
  }
75
- if (Object.prototype.hasOwnProperty.call(listenOptions, 'host') === false) {
75
+ if (Object.prototype.hasOwnProperty.call(listenOptions, 'host') === false ||
76
+ listenOptions.host == null) {
76
77
  listenOptions.host = host
77
78
  }
78
79
  if (host === 'localhost') {
package/lib/validation.js CHANGED
@@ -87,7 +87,17 @@ function compileSchemasForValidation (context, compile, isCustom) {
87
87
  }
88
88
 
89
89
  if (schema.body) {
90
- context[bodySchema] = compile({ schema: schema.body, method, url, httpPart: 'body' })
90
+ const contentProperty = schema.body.content
91
+ if (contentProperty) {
92
+ const contentTypeSchemas = {}
93
+ for (const contentType of Object.keys(contentProperty)) {
94
+ const contentSchema = contentProperty[contentType].schema
95
+ contentTypeSchemas[contentType] = compile({ schema: contentSchema, method, url, httpPart: 'body', contentType })
96
+ }
97
+ context[bodySchema] = contentTypeSchemas
98
+ } else {
99
+ context[bodySchema] = compile({ schema: schema.body, method, url, httpPart: 'body' })
100
+ }
91
101
  } else if (Object.prototype.hasOwnProperty.call(schema, 'body')) {
92
102
  FSTWRN001('body', method, url)
93
103
  }
@@ -140,7 +150,18 @@ function validate (context, request, execution) {
140
150
  }
141
151
 
142
152
  if (runExecution || !execution.skipBody) {
143
- const body = validateParam(context[bodySchema], request, 'body')
153
+ let validatorFunction = null
154
+ if (typeof context[bodySchema] === 'function') {
155
+ validatorFunction = context[bodySchema]
156
+ } else if (context[bodySchema]) {
157
+ // TODO: add request.contentType and reuse it here
158
+ const contentType = request.headers['content-type']?.split(';', 1)[0]
159
+ const contentSchema = context[bodySchema][contentType]
160
+ if (contentSchema) {
161
+ validatorFunction = contentSchema
162
+ }
163
+ }
164
+ const body = validateParam(validatorFunction, request, 'body')
144
165
  if (body) {
145
166
  if (typeof body.then !== 'function') {
146
167
  return wrapValidationError(body, 'body', context.schemaErrorFormatter)
package/lib/warnings.js CHANGED
@@ -87,6 +87,11 @@ const FSTDEP021 = createDeprecation({
87
87
  message: 'The `reply.redirect()` method has a new signature: `reply.redirect(url: string, code?: number)`. It will be enforced in `fastify@v5`'
88
88
  })
89
89
 
90
+ const FSTDEP022 = createDeprecation({
91
+ code: 'FSTDEP021',
92
+ message: 'You are using the deprecated json shorthand schema on route %s. Specify full object schema instead. It will be removed in `fastify@v5`'
93
+ })
94
+
90
95
  const FSTWRN001 = createWarning({
91
96
  name: 'FastifyWarning',
92
97
  code: 'FSTWRN001',
@@ -119,6 +124,7 @@ module.exports = {
119
124
  FSTDEP019,
120
125
  FSTDEP020,
121
126
  FSTDEP021,
127
+ FSTDEP022,
122
128
  FSTWRN001,
123
129
  FSTWRN002
124
130
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fastify",
3
- "version": "4.28.1",
3
+ "version": "4.29.0",
4
4
  "description": "Fast and low overhead web framework, for Node.js",
5
5
  "main": "fastify.js",
6
6
  "type": "commonjs",
@@ -10,11 +10,14 @@ const split = require('split2')
10
10
  const { sleep } = require('./helper')
11
11
 
12
12
  test('close callback', t => {
13
- t.plan(4)
13
+ t.plan(7)
14
14
  const fastify = Fastify()
15
15
  fastify.addHook('onClose', onClose)
16
16
  function onClose (instance, done) {
17
+ t.type(fastify, this)
17
18
  t.type(fastify, instance)
19
+ t.equal(fastify, this)
20
+ t.equal(fastify, instance)
18
21
  done()
19
22
  }
20
23
 
package/test/copy.test.js CHANGED
@@ -12,7 +12,10 @@ test('can be created - copy', t => {
12
12
  method: 'COPY',
13
13
  url: '*',
14
14
  handler: function (req, reply) {
15
- reply.code(204).send()
15
+ reply.code(204)
16
+ .header('location', req.headers.destination)
17
+ .header('body', req.body.toString())
18
+ .send()
16
19
  }
17
20
  })
18
21
  t.pass()
@@ -26,15 +29,19 @@ fastify.listen({ port: 0 }, err => {
26
29
  t.teardown(() => { fastify.close() })
27
30
 
28
31
  test('request - copy', t => {
29
- t.plan(2)
32
+ t.plan(4)
30
33
  sget({
31
34
  url: `http://localhost:${fastify.server.address().port}/test.txt`,
32
35
  method: 'COPY',
33
36
  headers: {
34
- Destination: '/test2.txt'
35
- }
36
- }, (err, response, body) => {
37
+ destination: '/test2.txt',
38
+ 'Content-Type': 'text/plain'
39
+ },
40
+ body: '/test3.txt'
41
+ }, (err, response) => {
37
42
  t.error(err)
43
+ t.equal(response.headers.location, '/test2.txt')
44
+ t.equal(response.headers.body, '/test3.txt')
38
45
  t.equal(response.statusCode, 204)
39
46
  })
40
47
  })
@@ -63,7 +63,7 @@ test('build schema - output schema', t => {
63
63
  t.equal(typeof opts[symbols.responseSchema]['201'], 'function')
64
64
  })
65
65
 
66
- test('build schema - payload schema', t => {
66
+ test('build schema - body schema', t => {
67
67
  t.plan(1)
68
68
  const opts = {
69
69
  schema: {
@@ -79,6 +79,32 @@ test('build schema - payload schema', t => {
79
79
  t.equal(typeof opts[symbols.bodySchema], 'function')
80
80
  })
81
81
 
82
+ test('build schema - body with multiple content type schemas', t => {
83
+ t.plan(2)
84
+ const opts = {
85
+ schema: {
86
+ body: {
87
+ content: {
88
+ 'application/json': {
89
+ schema: {
90
+ type: 'object',
91
+ properties: {
92
+ hello: { type: 'string' }
93
+ }
94
+ }
95
+ },
96
+ 'text/plain': {
97
+ schema: { type: 'string' }
98
+ }
99
+ }
100
+ }
101
+ }
102
+ }
103
+ validation.compileSchemasForValidation(opts, ({ schema, method, url, httpPart }) => ajv.compile(schema))
104
+ t.type(opts[symbols.bodySchema]['application/json'], 'function')
105
+ t.type(opts[symbols.bodySchema]['text/plain'], 'function')
106
+ })
107
+
82
108
  test('build schema - avoid repeated normalize schema', t => {
83
109
  t.plan(3)
84
110
  const serverConfig = {
@@ -94,10 +120,10 @@ test('build schema - avoid repeated normalize schema', t => {
94
120
  }
95
121
  }
96
122
  }
97
- opts.schema = normalizeSchema(opts.schema, serverConfig)
123
+ opts.schema = normalizeSchema({}, opts.schema, serverConfig)
98
124
  t.not(kSchemaVisited, undefined)
99
125
  t.equal(opts.schema[kSchemaVisited], true)
100
- t.equal(opts.schema, normalizeSchema(opts.schema, serverConfig))
126
+ t.equal(opts.schema, normalizeSchema({}, opts.schema, serverConfig))
101
127
  })
102
128
 
103
129
  test('build schema - query schema', t => {
@@ -115,7 +141,7 @@ test('build schema - query schema', t => {
115
141
  }
116
142
  }
117
143
  }
118
- opts.schema = normalizeSchema(opts.schema, serverConfig)
144
+ opts.schema = normalizeSchema({}, opts.schema, serverConfig)
119
145
  validation.compileSchemasForValidation(opts, ({ schema, method, url, httpPart }) => ajv.compile(schema))
120
146
  t.type(opts[symbols.querystringSchema].schema.type, 'string')
121
147
  t.equal(typeof opts[symbols.querystringSchema], 'function')
@@ -133,7 +159,7 @@ test('build schema - query schema abbreviated', t => {
133
159
  }
134
160
  }
135
161
  }
136
- opts.schema = normalizeSchema(opts.schema, serverConfig)
162
+ opts.schema = normalizeSchema({}, opts.schema, serverConfig)
137
163
  validation.compileSchemasForValidation(opts, ({ schema, method, url, httpPart }) => ajv.compile(schema))
138
164
  t.type(opts[symbols.querystringSchema].schema.type, 'string')
139
165
  t.equal(typeof opts[symbols.querystringSchema], 'function')
@@ -168,7 +194,7 @@ test('build schema - querystring schema abbreviated', t => {
168
194
  }
169
195
  }
170
196
  }
171
- opts.schema = normalizeSchema(opts.schema, serverConfig)
197
+ opts.schema = normalizeSchema({}, opts.schema, serverConfig)
172
198
  validation.compileSchemasForValidation(opts, ({ schema, method, url, httpPart }) => ajv.compile(schema))
173
199
  t.type(opts[symbols.querystringSchema].schema.type, 'string')
174
200
  t.equal(typeof opts[symbols.querystringSchema], 'function')
@@ -196,7 +222,7 @@ test('build schema - must throw if querystring and query schema exist', t => {
196
222
  }
197
223
  }
198
224
  }
199
- opts.schema = normalizeSchema(opts.schema, serverConfig)
225
+ opts.schema = normalizeSchema({}, opts.schema, serverConfig)
200
226
  } catch (err) {
201
227
  t.equal(err.code, 'FST_ERR_SCH_DUPLICATE')
202
228
  t.equal(err.message, 'Schema with \'querystring\' already present!')
@@ -354,7 +380,7 @@ test('build schema - mixed schema types are individually skipped or normalized',
354
380
  }]
355
381
 
356
382
  testCases.forEach((testCase) => {
357
- const result = normalizeSchema(testCase.schema, { jsonShorthand: true })
383
+ const result = normalizeSchema({}, testCase.schema, { jsonShorthand: true })
358
384
  testCase.assertions(result)
359
385
  })
360
386
  })
@@ -0,0 +1,131 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('tap')
4
+ const Fastify = require('..')
5
+ const {
6
+ FSTDEP022
7
+ } = require('../lib/warnings')
8
+
9
+ test('use of deprecated json shorthand in params should warn', t => {
10
+ t.plan(3)
11
+
12
+ process.removeAllListeners('warning')
13
+ process.on('warning', onWarning)
14
+ function onWarning (warning) {
15
+ t.equal(warning.name, 'DeprecationWarning')
16
+ t.equal(warning.code, FSTDEP022.code)
17
+ }
18
+
19
+ const fastify = Fastify()
20
+
21
+ fastify.get('/:id', {
22
+ schema: {
23
+ params: {
24
+ id: { type: 'string' }
25
+ }
26
+ }
27
+ }, (_, reply) => {
28
+ return reply.code(200).send('')
29
+ })
30
+
31
+ fastify.inject({ method: 'GET', url: '/1' }, (err, res) => {
32
+ t.error(err)
33
+
34
+ process.removeListener('warning', onWarning)
35
+ })
36
+
37
+ FSTDEP022.emitted = false
38
+ })
39
+
40
+ test('use of deprecated json shorthand in headers should warn', t => {
41
+ t.plan(3)
42
+
43
+ process.removeAllListeners('warning')
44
+ process.on('warning', onWarning)
45
+ function onWarning (warning) {
46
+ t.equal(warning.name, 'DeprecationWarning')
47
+ t.equal(warning.code, FSTDEP022.code)
48
+ }
49
+
50
+ const fastify = Fastify()
51
+
52
+ fastify.get('/', {
53
+ schema: {
54
+ headers: {
55
+ hello: { type: 'string' }
56
+ }
57
+ }
58
+ }, (_, reply) => {
59
+ return reply.code(200).send('')
60
+ })
61
+
62
+ fastify.inject({ method: 'GET', url: '/' }, (err, res) => {
63
+ t.error(err)
64
+
65
+ process.removeListener('warning', onWarning)
66
+ })
67
+
68
+ FSTDEP022.emitted = false
69
+ })
70
+
71
+ test('use of deprecated json shorthand in querystring should warn', t => {
72
+ t.plan(3)
73
+
74
+ process.removeAllListeners('warning')
75
+ process.on('warning', onWarning)
76
+ function onWarning (warning) {
77
+ t.equal(warning.name, 'DeprecationWarning')
78
+ t.equal(warning.code, FSTDEP022.code)
79
+ }
80
+
81
+ const fastify = Fastify()
82
+
83
+ fastify.get('/', {
84
+ schema: {
85
+ querystring: {
86
+ hello: { type: 'string' }
87
+ }
88
+ }
89
+ }, (_, reply) => {
90
+ return reply.code(200).send('')
91
+ })
92
+
93
+ fastify.inject({ method: 'GET', url: '/', query: { hello: 'world' } }, (err, res) => {
94
+ t.error(err)
95
+
96
+ process.removeListener('warning', onWarning)
97
+ })
98
+
99
+ FSTDEP022.emitted = false
100
+ })
101
+
102
+ test('use of deprecated json shorthand in body should warn', t => {
103
+ t.plan(3)
104
+
105
+ process.removeAllListeners('warning')
106
+ process.on('warning', onWarning)
107
+ function onWarning (warning) {
108
+ t.equal(warning.name, 'DeprecationWarning')
109
+ t.equal(warning.code, FSTDEP022.code)
110
+ }
111
+
112
+ const fastify = Fastify()
113
+
114
+ fastify.post('/', {
115
+ schema: {
116
+ body: {
117
+ hello: { type: 'string' }
118
+ }
119
+ }
120
+ }, (_, reply) => {
121
+ return reply.code(200).send('')
122
+ })
123
+
124
+ fastify.inject({ method: 'POST', url: '/', body: { hello: 'world' } }, (err, res) => {
125
+ t.error(err)
126
+
127
+ process.removeListener('warning', onWarning)
128
+ })
129
+
130
+ FSTDEP022.emitted = false
131
+ })
@@ -99,3 +99,39 @@ test('listen after Promise.resolve()', t => {
99
99
  })
100
100
  })
101
101
  })
102
+
103
+ test('listen works with undefined host', async t => {
104
+ const doNotWarn = () => {
105
+ t.fail('should not be deprecated')
106
+ }
107
+ process.on('warning', doNotWarn)
108
+
109
+ const fastify = Fastify()
110
+ t.teardown(fastify.close.bind(fastify))
111
+ t.teardown(() => {
112
+ fastify.close()
113
+ process.removeListener('warning', doNotWarn)
114
+ })
115
+ await fastify.listen({ host: undefined, port: 0 })
116
+ const address = fastify.server.address()
117
+ t.equal(address.address, localhost)
118
+ t.ok(address.port > 0)
119
+ })
120
+
121
+ test('listen works with null host', async t => {
122
+ const doNotWarn = () => {
123
+ t.fail('should not be deprecated')
124
+ }
125
+ process.on('warning', doNotWarn)
126
+
127
+ const fastify = Fastify()
128
+ t.teardown(fastify.close.bind(fastify))
129
+ t.teardown(() => {
130
+ fastify.close()
131
+ process.removeListener('warning', doNotWarn)
132
+ })
133
+ await fastify.listen({ host: null, port: 0 })
134
+ const address = fastify.server.address()
135
+ t.equal(address.address, localhost)
136
+ t.ok(address.port > 0)
137
+ })
@@ -12,7 +12,7 @@ test('can be created - mkcol', t => {
12
12
  method: 'MKCOL',
13
13
  url: '*',
14
14
  handler: function (req, reply) {
15
- reply.code(201).send()
15
+ reply.code(201).header('body', req.body.toString()).send()
16
16
  }
17
17
  })
18
18
  t.pass()
@@ -26,12 +26,15 @@ fastify.listen({ port: 0 }, err => {
26
26
  t.teardown(() => { fastify.close() })
27
27
 
28
28
  test('request - mkcol', t => {
29
- t.plan(2)
29
+ t.plan(3)
30
30
  sget({
31
31
  url: `http://localhost:${fastify.server.address().port}/test/`,
32
- method: 'MKCOL'
33
- }, (err, response, body) => {
32
+ method: 'MKCOL',
33
+ headers: { 'Content-Type': 'text/plain' },
34
+ body: '/test.txt'
35
+ }, (err, response) => {
34
36
  t.error(err)
37
+ t.equal(response.headers.body, '/test.txt')
35
38
  t.equal(response.statusCode, 201)
36
39
  })
37
40
  })
package/test/move.test.js CHANGED
@@ -12,9 +12,9 @@ test('shorthand - move', t => {
12
12
  method: 'MOVE',
13
13
  url: '*',
14
14
  handler: function (req, reply) {
15
- const destination = req.headers.destination
16
15
  reply.code(201)
17
- .header('location', destination)
16
+ .header('location', req.headers.destination)
17
+ .header('body', req.body.toString())
18
18
  .send()
19
19
  }
20
20
  })
@@ -29,17 +29,20 @@ fastify.listen({ port: 0 }, err => {
29
29
  t.teardown(() => { fastify.close() })
30
30
 
31
31
  test('request - move', t => {
32
- t.plan(3)
32
+ t.plan(4)
33
33
  sget({
34
34
  url: `http://localhost:${fastify.server.address().port}/test.txt`,
35
35
  method: 'MOVE',
36
36
  headers: {
37
- Destination: '/test2.txt'
38
- }
39
- }, (err, response, body) => {
37
+ destination: '/test2.txt',
38
+ 'Content-Type': 'text/plain'
39
+ },
40
+ body: '/test3.txt'
41
+ }, (err, response) => {
40
42
  t.error(err)
41
43
  t.equal(response.statusCode, 201)
42
44
  t.equal(response.headers.location, '/test2.txt')
45
+ t.equal(response.headers.body, '/test3.txt')
43
46
  })
44
47
  })
45
48
  })
@@ -140,3 +140,30 @@ test('using fastify.all when a catchall is defined does not degrade performance'
140
140
 
141
141
  t.pass()
142
142
  })
143
+
144
+ test('Adding manually HEAD route after GET with the same path throws Fastify duplicated route instance error', t => {
145
+ t.plan(1)
146
+
147
+ const fastify = Fastify()
148
+
149
+ fastify.route({
150
+ method: 'GET',
151
+ path: '/:param1',
152
+ handler: (req, reply) => {
153
+ reply.send({ hello: 'world' })
154
+ }
155
+ })
156
+
157
+ try {
158
+ fastify.route({
159
+ method: 'HEAD',
160
+ path: '/:param2',
161
+ handler: (req, reply) => {
162
+ reply.send({ hello: 'world' })
163
+ }
164
+ })
165
+ t.fail('Should throw fastify duplicated route declaration')
166
+ } catch (error) {
167
+ t.equal(error.code, 'FST_ERR_DUPLICATED_ROUTE')
168
+ }
169
+ })
@@ -190,6 +190,27 @@ test('Should throw of the schema does not exists in input', t => {
190
190
  })
191
191
  })
192
192
 
193
+ test('Should throw if schema is missing for content type', t => {
194
+ t.plan(2)
195
+
196
+ const fastify = Fastify()
197
+ fastify.post('/', {
198
+ handler: echoBody,
199
+ schema: {
200
+ body: {
201
+ content: {
202
+ 'application/json': {}
203
+ }
204
+ }
205
+ }
206
+ })
207
+
208
+ fastify.ready(err => {
209
+ t.equal(err.code, 'FST_ERR_SCH_CONTENT_MISSING_SCHEMA')
210
+ t.equal(err.message, "Schema is missing for the content type 'application/json'")
211
+ })
212
+ })
213
+
193
214
  test('Should throw of the schema does not exists in output', t => {
194
215
  t.plan(2)
195
216
  const fastify = Fastify()
@@ -105,6 +105,142 @@ test('Basic validation test', t => {
105
105
  })
106
106
  })
107
107
 
108
+ test('Different schema per content type', t => {
109
+ t.plan(12)
110
+
111
+ const fastify = Fastify()
112
+ fastify.addContentTypeParser('application/octet-stream', {
113
+ parseAs: 'buffer'
114
+ }, async function (_, payload) {
115
+ return payload
116
+ })
117
+ fastify.post('/', {
118
+ schema: {
119
+ body: {
120
+ content: {
121
+ 'application/json': {
122
+ schema: schemaArtist
123
+ },
124
+ 'application/octet-stream': {
125
+ schema: {} // Skip validation
126
+ },
127
+ 'text/plain': {
128
+ schema: { type: 'string' }
129
+ }
130
+ }
131
+ }
132
+ }
133
+ }, async function (req, reply) {
134
+ return reply.send(req.body)
135
+ })
136
+
137
+ fastify.inject({
138
+ url: '/',
139
+ method: 'POST',
140
+ headers: { 'Content-Type': 'application/json' },
141
+ body: {
142
+ name: 'michelangelo',
143
+ work: 'sculptor, painter, architect and poet'
144
+ }
145
+ }, (err, res) => {
146
+ t.error(err)
147
+ t.same(JSON.parse(res.payload).name, 'michelangelo')
148
+ t.equal(res.statusCode, 200)
149
+ })
150
+
151
+ fastify.inject({
152
+ url: '/',
153
+ method: 'POST',
154
+ headers: { 'Content-Type': 'application/json' },
155
+ body: { name: 'michelangelo' }
156
+ }, (err, res) => {
157
+ t.error(err)
158
+ t.same(res.json(), { statusCode: 400, code: 'FST_ERR_VALIDATION', error: 'Bad Request', message: "body must have required property 'work'" })
159
+ t.equal(res.statusCode, 400)
160
+ })
161
+
162
+ fastify.inject({
163
+ url: '/',
164
+ method: 'POST',
165
+ headers: { 'Content-Type': 'application/octet-stream' },
166
+ body: Buffer.from('AAAAAAAA')
167
+ }, (err, res) => {
168
+ t.error(err)
169
+ t.same(res.payload, 'AAAAAAAA')
170
+ t.equal(res.statusCode, 200)
171
+ })
172
+
173
+ fastify.inject({
174
+ url: '/',
175
+ method: 'POST',
176
+ headers: { 'Content-Type': 'text/plain' },
177
+ body: 'AAAAAAAA'
178
+ }, (err, res) => {
179
+ t.error(err)
180
+ t.same(res.payload, 'AAAAAAAA')
181
+ t.equal(res.statusCode, 200)
182
+ })
183
+ })
184
+
185
+ test('Skip validation if no schema for content type', t => {
186
+ t.plan(3)
187
+
188
+ const fastify = Fastify()
189
+ fastify.post('/', {
190
+ schema: {
191
+ body: {
192
+ content: {
193
+ 'application/json': {
194
+ schema: schemaArtist
195
+ }
196
+ // No schema for 'text/plain'
197
+ }
198
+ }
199
+ }
200
+ }, async function (req, reply) {
201
+ return reply.send(req.body)
202
+ })
203
+
204
+ fastify.inject({
205
+ url: '/',
206
+ method: 'POST',
207
+ headers: { 'Content-Type': 'text/plain' },
208
+ body: 'AAAAAAAA'
209
+ }, (err, res) => {
210
+ t.error(err)
211
+ t.same(res.payload, 'AAAAAAAA')
212
+ t.equal(res.statusCode, 200)
213
+ })
214
+ })
215
+
216
+ test('Skip validation if no content type schemas', t => {
217
+ t.plan(3)
218
+
219
+ const fastify = Fastify()
220
+ fastify.post('/', {
221
+ schema: {
222
+ body: {
223
+ content: {
224
+ // No schemas
225
+ }
226
+ }
227
+ }
228
+ }, async function (req, reply) {
229
+ return reply.send(req.body)
230
+ })
231
+
232
+ fastify.inject({
233
+ url: '/',
234
+ method: 'POST',
235
+ headers: { 'Content-Type': 'text/plain' },
236
+ body: 'AAAAAAAA'
237
+ }, (err, res) => {
238
+ t.error(err)
239
+ t.same(res.payload, 'AAAAAAAA')
240
+ t.equal(res.statusCode, 200)
241
+ })
242
+ })
243
+
108
244
  test('External AJV instance', t => {
109
245
  t.plan(5)
110
246
 
@@ -0,0 +1,37 @@
1
+ 'use strict'
2
+
3
+ const t = require('tap')
4
+ const test = t.test
5
+ const Fastify = require('..')
6
+ const Reply = require('../lib/reply')
7
+
8
+ test('should serialize reply when response stream is ended', t => {
9
+ t.plan(3)
10
+ const stream = require('node:stream')
11
+ const fastify = Fastify({
12
+ logger: {
13
+ serializers: {
14
+ res (reply) {
15
+ t.type(reply, Reply)
16
+ return reply
17
+ }
18
+ }
19
+ }
20
+ })
21
+
22
+ fastify.get('/error', function (req, reply) {
23
+ const reallyLongStream = new stream.Readable({
24
+ read: () => { }
25
+ })
26
+ reply.code(200).send(reallyLongStream)
27
+ reply.raw.end(Buffer.from('hello\n'))
28
+ })
29
+
30
+ fastify.inject({
31
+ url: '/error',
32
+ method: 'GET'
33
+ }, (err) => {
34
+ t.error(err)
35
+ fastify.close()
36
+ })
37
+ })
@@ -22,9 +22,9 @@ test('Fastify should throw on multiple assignment to the same route', t => {
22
22
 
23
23
  try {
24
24
  fastify.get('/', () => {})
25
- t.fail('Should throw on duplicated route declaration')
25
+ t.fail('Should throw fastify duplicated route declaration')
26
26
  } catch (error) {
27
- t.equal(error.message, "Method 'GET' already declared for route '/'")
27
+ t.equal(error.code, 'FST_ERR_DUPLICATED_ROUTE')
28
28
  }
29
29
  })
30
30
 
@@ -151,7 +151,8 @@ server.addHook('onListen', function (done) {
151
151
  expectAssignable<(err?: NodeJS.ErrnoException) => void>(done)
152
152
  })
153
153
 
154
- server.addHook('onClose', (instance, done) => {
154
+ server.addHook('onClose', function (instance, done) {
155
+ expectType<FastifyInstance>(this)
155
156
  expectType<FastifyInstance>(instance)
156
157
  expectAssignable<(err?: FastifyError) => void>(done)
157
158
  expectAssignable<(err?: NodeJS.ErrnoException) => void>(done)
@@ -236,7 +237,8 @@ server.addHook('onListen', async function () {
236
237
  expectType<FastifyInstance>(this)
237
238
  })
238
239
 
239
- server.addHook('onClose', async (instance) => {
240
+ server.addHook('onClose', async function (instance) {
241
+ expectType<FastifyInstance>(this)
240
242
  expectType<FastifyInstance>(instance)
241
243
  })
242
244
 
@@ -19,6 +19,13 @@ declare module '../../fastify' {
19
19
  interface FastifyContextConfig {
20
20
  foo: string;
21
21
  bar: number;
22
+ includeMessage?: boolean;
23
+ }
24
+
25
+ interface FastifyRequest<RouteGeneric, RawServer, RawRequest, SchemaCompiler, TypeProvider, ContextConfig, Logger, RequestType> {
26
+ message: ContextConfig extends { includeMessage: true }
27
+ ? string
28
+ : null;
22
29
  }
23
30
  }
24
31
 
@@ -36,6 +43,22 @@ const routeHandlerWithReturnValue: RouteHandlerMethod = function (request, reply
36
43
  return reply.send()
37
44
  }
38
45
 
46
+ fastify().get(
47
+ '/',
48
+ { config: { foo: 'bar', bar: 100, includeMessage: true } },
49
+ (req) => {
50
+ expectType<string>(req.message)
51
+ }
52
+ )
53
+
54
+ fastify().get(
55
+ '/',
56
+ { config: { foo: 'bar', bar: 100, includeMessage: false } },
57
+ (req) => {
58
+ expectType<null>(req.message)
59
+ }
60
+ )
61
+
39
62
  type LowerCaseHTTPMethods = 'get' | 'post' | 'put' | 'patch' | 'head' | 'delete' | 'options';
40
63
 
41
64
  ['GET', 'POST', 'PUT', 'PATCH', 'HEAD', 'DELETE', 'OPTIONS'].forEach(method => {
@@ -20,6 +20,25 @@ expectAssignable<FastifyInstance>(server.get(
20
20
  () => { }
21
21
  ))
22
22
 
23
+ expectAssignable<FastifyInstance>(server.post(
24
+ '/multiple-content-schema',
25
+ {
26
+ schema: {
27
+ body: {
28
+ content: {
29
+ 'application/json': {
30
+ schema: { type: 'object' }
31
+ },
32
+ 'text/plain': {
33
+ schema: { type: 'string' }
34
+ }
35
+ }
36
+ }
37
+ }
38
+ },
39
+ () => { }
40
+ ))
41
+
23
42
  expectAssignable<FastifyInstance>(server.get(
24
43
  '/empty-schema',
25
44
  {
@@ -62,6 +62,81 @@ test('should response with a Response', async (t) => {
62
62
  t.equal(headers.hello, 'world')
63
63
  })
64
64
 
65
+ test('should response with a Response 204', async (t) => {
66
+ t.plan(3)
67
+
68
+ const fastify = Fastify()
69
+
70
+ fastify.get('/', function (request, reply) {
71
+ reply.send(new Response(null, {
72
+ status: 204,
73
+ headers: {
74
+ hello: 'world'
75
+ }
76
+ }))
77
+ })
78
+
79
+ const {
80
+ statusCode,
81
+ headers,
82
+ body
83
+ } = await fastify.inject({ method: 'GET', path: '/' })
84
+
85
+ t.equal(statusCode, 204)
86
+ t.equal(body, '')
87
+ t.equal(headers.hello, 'world')
88
+ })
89
+
90
+ test('should response with a Response 304', async (t) => {
91
+ t.plan(3)
92
+
93
+ const fastify = Fastify()
94
+
95
+ fastify.get('/', function (request, reply) {
96
+ reply.send(new Response(null, {
97
+ status: 304,
98
+ headers: {
99
+ hello: 'world'
100
+ }
101
+ }))
102
+ })
103
+
104
+ const {
105
+ statusCode,
106
+ headers,
107
+ body
108
+ } = await fastify.inject({ method: 'GET', path: '/' })
109
+
110
+ t.equal(statusCode, 304)
111
+ t.equal(body, '')
112
+ t.equal(headers.hello, 'world')
113
+ })
114
+
115
+ test('should response with a Response without body', async (t) => {
116
+ t.plan(3)
117
+
118
+ const fastify = Fastify()
119
+
120
+ fastify.get('/', function (request, reply) {
121
+ reply.send(new Response(null, {
122
+ status: 200,
123
+ headers: {
124
+ hello: 'world'
125
+ }
126
+ }))
127
+ })
128
+
129
+ const {
130
+ statusCode,
131
+ headers,
132
+ body
133
+ } = await fastify.inject({ method: 'GET', path: '/' })
134
+
135
+ t.equal(statusCode, 200)
136
+ t.equal(body, '')
137
+ t.equal(headers.hello, 'world')
138
+ })
139
+
65
140
  test('able to use in onSend hook - ReadableStream', async (t) => {
66
141
  t.plan(4)
67
142
 
package/types/hooks.d.ts CHANGED
@@ -782,6 +782,7 @@ export interface onCloseHookHandler<
782
782
  TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault,
783
783
  > {
784
784
  (
785
+ this: FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>,
785
786
  instance: FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>,
786
787
  done: HookHandlerDoneFunction
787
788
  ): void;
@@ -795,6 +796,7 @@ export interface onCloseAsyncHookHandler<
795
796
  TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault
796
797
  > {
797
798
  (
799
+ this: FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>,
798
800
  instance: FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>
799
801
  ): Promise<unknown>;
800
802
  }
package/types/route.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { FastifyError } from '@fastify/error'
2
2
  import { ConstraintStrategy } from 'find-my-way'
3
- import { FastifyRequestContext } from './context'
3
+ import { FastifyContextConfig } from './context'
4
4
  import { onErrorMetaHookHandler, onRequestAbortMetaHookHandler, onRequestMetaHookHandler, onResponseMetaHookHandler, onSendMetaHookHandler, onTimeoutMetaHookHandler, preHandlerMetaHookHandler, preParsingMetaHookHandler, preSerializationMetaHookHandler, preValidationMetaHookHandler } from './hooks'
5
5
  import { FastifyInstance } from './instance'
6
6
  import { FastifyBaseLogger, FastifyChildLoggerFactory, LogLevel } from './logger'
@@ -52,7 +52,7 @@ export interface RouteShorthandOptions<
52
52
  serializerCompiler?: FastifySerializerCompiler<NoInferCompat<SchemaCompiler>>;
53
53
  bodyLimit?: number;
54
54
  logLevel?: LogLevel;
55
- config?: Omit<FastifyRequestContext<ContextConfig>['config'], 'url' | 'method'>;
55
+ config?: FastifyContextConfig & ContextConfig;
56
56
  version?: string;
57
57
  constraints?: RouteConstraint,
58
58
  prefixTrailingSlash?: 'slash'|'no-slash'|'both';
@@ -1,2 +0,0 @@
1
- 'use strict'
2
- Object.defineProperty(exports, '__esModule', { value: true })