fastify 3.27.4 → 4.0.0-alpha.3

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 (168) hide show
  1. package/.taprc +3 -0
  2. package/README.md +7 -7
  3. package/build/build-error-serializer.js +27 -0
  4. package/build/build-validation.js +47 -35
  5. package/docs/Guides/Database.md +320 -0
  6. package/docs/Guides/Ecosystem.md +9 -0
  7. package/docs/Guides/Getting-Started.md +7 -7
  8. package/docs/Guides/Plugins-Guide.md +1 -1
  9. package/docs/Guides/Serverless.md +3 -3
  10. package/docs/Guides/Testing.md +2 -2
  11. package/docs/Migration-Guide-V4.md +12 -0
  12. package/docs/Reference/ContentTypeParser.md +4 -0
  13. package/docs/Reference/Decorators.md +2 -2
  14. package/docs/Reference/Encapsulation.md +2 -2
  15. package/docs/Reference/Errors.md +51 -6
  16. package/docs/Reference/HTTP2.md +3 -3
  17. package/docs/Reference/Hooks.md +4 -7
  18. package/docs/Reference/LTS.md +5 -4
  19. package/docs/Reference/Plugins.md +3 -3
  20. package/docs/Reference/Reply.md +73 -22
  21. package/docs/Reference/Request.md +1 -3
  22. package/docs/Reference/Routes.md +22 -15
  23. package/docs/Reference/Server.md +69 -119
  24. package/docs/Reference/TypeScript.md +20 -22
  25. package/docs/Reference/Validation-and-Serialization.md +30 -55
  26. package/docs/Type-Providers.md +257 -0
  27. package/examples/asyncawait.js +1 -1
  28. package/examples/benchmark/hooks-benchmark-async-await.js +1 -1
  29. package/examples/benchmark/hooks-benchmark.js +1 -1
  30. package/examples/benchmark/simple.js +1 -1
  31. package/examples/hooks.js +2 -2
  32. package/examples/http2.js +1 -1
  33. package/examples/https.js +1 -1
  34. package/examples/parser.js +13 -3
  35. package/examples/route-prefix.js +1 -1
  36. package/examples/shared-schema.js +1 -1
  37. package/examples/simple-stream.js +18 -0
  38. package/examples/simple.js +1 -1
  39. package/examples/simple.mjs +1 -1
  40. package/examples/typescript-server.ts +1 -1
  41. package/examples/use-plugin.js +1 -1
  42. package/fastify.d.ts +34 -22
  43. package/fastify.js +40 -36
  44. package/lib/configValidator.js +902 -1023
  45. package/lib/contentTypeParser.js +6 -16
  46. package/lib/context.js +36 -10
  47. package/lib/decorate.js +3 -1
  48. package/lib/error-handler.js +158 -0
  49. package/lib/error-serializer.js +257 -0
  50. package/lib/errors.js +51 -9
  51. package/lib/fourOhFour.js +31 -20
  52. package/lib/handleRequest.js +10 -13
  53. package/lib/hooks.js +14 -9
  54. package/lib/pluginOverride.js +0 -3
  55. package/lib/pluginUtils.js +3 -2
  56. package/lib/reply.js +121 -175
  57. package/lib/request.js +13 -10
  58. package/lib/route.js +131 -138
  59. package/lib/schema-controller.js +2 -2
  60. package/lib/schemas.js +27 -1
  61. package/lib/server.js +242 -116
  62. package/lib/symbols.js +5 -3
  63. package/lib/validation.js +11 -9
  64. package/lib/warnings.js +4 -12
  65. package/lib/wrapThenable.js +4 -11
  66. package/package.json +37 -39
  67. package/test/404s.test.js +258 -125
  68. package/test/500s.test.js +3 -3
  69. package/test/als.test.js +1 -1
  70. package/test/async-await.test.js +20 -76
  71. package/test/bodyLimit.test.js +1 -1
  72. package/test/build-certificate.js +6 -7
  73. package/test/case-insensitive.test.js +4 -4
  74. package/test/close-pipelining.test.js +2 -2
  75. package/test/close.test.js +11 -11
  76. package/test/content-parser.test.js +32 -0
  77. package/test/context-config.test.js +52 -0
  78. package/test/custom-http-server.test.js +14 -7
  79. package/test/custom-parser-async.test.js +1 -66
  80. package/test/custom-parser.test.js +92 -159
  81. package/test/custom-querystring-parser.test.js +3 -3
  82. package/test/decorator.test.js +11 -13
  83. package/test/delete.test.js +6 -6
  84. package/test/encapsulated-error-handler.test.js +50 -0
  85. package/test/esm/index.test.js +0 -14
  86. package/test/fastify-instance.test.js +4 -4
  87. package/test/fluent-schema.test.js +4 -4
  88. package/test/genReqId.test.js +1 -1
  89. package/test/get.test.js +4 -4
  90. package/test/handler-context.test.js +2 -2
  91. package/test/head.test.js +1 -1
  92. package/test/helper.js +19 -4
  93. package/test/hooks-async.test.js +15 -48
  94. package/test/hooks.on-ready.test.js +10 -5
  95. package/test/hooks.test.js +78 -119
  96. package/test/http2/closing.test.js +10 -16
  97. package/test/http2/constraint.test.js +1 -1
  98. package/test/http2/head.test.js +1 -1
  99. package/test/http2/plain.test.js +1 -1
  100. package/test/http2/secure-with-fallback.test.js +1 -1
  101. package/test/http2/secure.test.js +1 -1
  102. package/test/http2/unknown-http-method.test.js +4 -10
  103. package/test/https/custom-https-server.test.js +12 -6
  104. package/test/https/https.test.js +1 -1
  105. package/test/input-validation.js +3 -3
  106. package/test/internals/handleRequest.test.js +6 -43
  107. package/test/internals/initialConfig.test.js +41 -12
  108. package/test/internals/logger.test.js +2 -2
  109. package/test/internals/reply.test.js +317 -48
  110. package/test/internals/request.test.js +13 -7
  111. package/test/internals/server.test.js +88 -0
  112. package/test/listen.deprecated.test.js +202 -0
  113. package/test/listen.test.js +140 -145
  114. package/test/logger.test.js +82 -42
  115. package/test/maxRequestsPerSocket.test.js +8 -6
  116. package/test/middleware.test.js +2 -25
  117. package/test/nullable-validation.test.js +53 -16
  118. package/test/output-validation.test.js +1 -1
  119. package/test/plugin.test.js +47 -21
  120. package/test/pretty-print.test.js +22 -10
  121. package/test/promises.test.js +1 -1
  122. package/test/proto-poisoning.test.js +6 -6
  123. package/test/register.test.js +3 -3
  124. package/test/reply-error.test.js +126 -15
  125. package/test/reply-trailers.test.js +270 -0
  126. package/test/request-error.test.js +3 -6
  127. package/test/route-hooks.test.js +18 -18
  128. package/test/route-prefix.test.js +2 -1
  129. package/test/route.test.js +206 -22
  130. package/test/router-options.test.js +2 -2
  131. package/test/schema-examples.test.js +11 -5
  132. package/test/schema-feature.test.js +25 -20
  133. package/test/schema-serialization.test.js +9 -9
  134. package/test/schema-special-usage.test.js +5 -153
  135. package/test/schema-validation.test.js +9 -9
  136. package/test/skip-reply-send.test.js +2 -2
  137. package/test/stream.test.js +82 -23
  138. package/test/throw.test.js +8 -5
  139. package/test/trust-proxy.test.js +6 -6
  140. package/test/type-provider.test.js +20 -0
  141. package/test/types/fastify.test-d.ts +10 -18
  142. package/test/types/hooks.test-d.ts +61 -5
  143. package/test/types/import.js +2 -0
  144. package/test/types/import.ts +1 -0
  145. package/test/types/instance.test-d.ts +68 -17
  146. package/test/types/logger.test-d.ts +44 -15
  147. package/test/types/reply.test-d.ts +2 -1
  148. package/test/types/request.test-d.ts +71 -1
  149. package/test/types/route.test-d.ts +8 -2
  150. package/test/types/schema.test-d.ts +2 -39
  151. package/test/types/type-provider.test-d.ts +424 -0
  152. package/test/url-rewriting.test.js +3 -3
  153. package/test/validation-error-handling.test.js +8 -8
  154. package/test/versioned-routes.test.js +30 -18
  155. package/test/wrapThenable.test.js +7 -6
  156. package/types/content-type-parser.d.ts +17 -8
  157. package/types/hooks.d.ts +182 -85
  158. package/types/instance.d.ts +286 -118
  159. package/types/logger.d.ts +18 -104
  160. package/types/plugin.d.ts +10 -4
  161. package/types/reply.d.ts +18 -12
  162. package/types/request.d.ts +13 -8
  163. package/types/route.d.ts +62 -34
  164. package/types/schema.d.ts +1 -1
  165. package/types/type-provider.d.ts +99 -0
  166. package/types/utils.d.ts +1 -1
  167. package/lib/schema-compilers.js +0 -12
  168. package/test/emit-warning.test.js +0 -166
@@ -6,21 +6,15 @@ const Fastify = require('../..')
6
6
  const h2url = require('h2url')
7
7
  const msg = { hello: 'world' }
8
8
 
9
- let fastify
10
- try {
11
- fastify = Fastify({
12
- http2: true
13
- })
14
- t.pass('http2 successfully loaded')
15
- } catch (e) {
16
- t.fail('http2 loading failed', e)
17
- }
9
+ const fastify = Fastify({
10
+ http2: true
11
+ })
18
12
 
19
13
  fastify.get('/', function (req, reply) {
20
14
  reply.code(200).send(msg)
21
15
  })
22
16
 
23
- fastify.listen(0, err => {
17
+ fastify.listen({ port: 0 }, err => {
24
18
  t.error(err)
25
19
  fastify.server.unref()
26
20
 
@@ -5,15 +5,18 @@ const test = t.test
5
5
  const Fastify = require('../..')
6
6
  const https = require('https')
7
7
  const sget = require('simple-get').concat
8
+ const dns = require('dns').promises
8
9
 
9
10
  const { buildCertificate } = require('../build-certificate')
10
11
  t.before(buildCertificate)
11
12
 
12
- test('Should support a custom https server', t => {
13
- t.plan(6)
13
+ test('Should support a custom https server', async t => {
14
+ const localAddresses = await dns.lookup('localhost', { all: true })
15
+
16
+ t.plan(localAddresses.length + 3)
14
17
 
15
18
  const serverFactory = (handler, opts) => {
16
- t.ok(opts.serverFactory)
19
+ t.ok(opts.serverFactory, 'it is called twice for every HOST interface')
17
20
 
18
21
  const options = {
19
22
  key: global.context.key,
@@ -37,17 +40,20 @@ test('Should support a custom https server', t => {
37
40
  reply.send({ hello: 'world' })
38
41
  })
39
42
 
40
- fastify.listen(0, err => {
41
- t.error(err)
43
+ await fastify.listen({ port: 0 })
42
44
 
45
+ await new Promise((resolve, reject) => {
43
46
  sget({
44
47
  method: 'GET',
45
48
  url: 'https://localhost:' + fastify.server.address().port,
46
49
  rejectUnauthorized: false
47
50
  }, (err, response, body) => {
48
- t.error(err)
51
+ if (err) {
52
+ return reject(err)
53
+ }
49
54
  t.equal(response.statusCode, 200)
50
55
  t.same(JSON.parse(body), { hello: 'world' })
56
+ resolve()
51
57
  })
52
58
  })
53
59
  })
@@ -32,7 +32,7 @@ test('https', (t) => {
32
32
  reply.code(200).send({ proto: req.protocol })
33
33
  })
34
34
 
35
- fastify.listen(0, err => {
35
+ fastify.listen({ port: 0 }, err => {
36
36
  t.error(err)
37
37
  fastify.server.unref()
38
38
 
@@ -3,7 +3,7 @@
3
3
 
4
4
  const sget = require('simple-get').concat
5
5
  const Ajv = require('ajv')
6
- const Joi = require('@hapi/joi')
6
+ const Joi = require('joi')
7
7
  const yup = require('yup')
8
8
 
9
9
  module.exports.payloadMethod = function (method, t) {
@@ -134,7 +134,7 @@ module.exports.payloadMethod = function (method, t) {
134
134
  }
135
135
  })
136
136
 
137
- fastify.listen(0, function (err) {
137
+ fastify.listen({ port: 0 }, function (err) {
138
138
  if (err) {
139
139
  t.error(err)
140
140
  }
@@ -182,7 +182,7 @@ module.exports.payloadMethod = function (method, t) {
182
182
  t.equal(response.statusCode, 400)
183
183
  t.same(body, {
184
184
  error: 'Bad Request',
185
- message: 'body.hello should be integer',
185
+ message: 'body/hello must be integer',
186
186
  statusCode: 400
187
187
  })
188
188
  })
@@ -1,7 +1,6 @@
1
1
  'use strict'
2
2
 
3
3
  const { test } = require('tap')
4
- const semver = require('semver')
5
4
  const handleRequest = require('../../lib/handleRequest')
6
5
  const internals = require('../../lib/handleRequest')[Symbol.for('internals')]
7
6
  const Request = require('../../lib/request')
@@ -40,13 +39,8 @@ test('handleRequest function - invoke with error', t => {
40
39
  })
41
40
 
42
41
  test('handler function - invalid schema', t => {
43
- t.plan(2)
42
+ t.plan(1)
44
43
  const res = {}
45
- res.end = () => {
46
- t.equal(res.statusCode, 400)
47
- t.pass()
48
- }
49
- res.writeHead = () => {}
50
44
  res.log = { error: () => {}, info: () => {} }
51
45
  const context = {
52
46
  config: {
@@ -61,6 +55,7 @@ test('handler function - invalid schema', t => {
61
55
  }
62
56
  }
63
57
  },
58
+ errorHandler: { func: () => { t.pass('errorHandler called') } },
64
59
  handler: () => {},
65
60
  Reply,
66
61
  Request,
@@ -108,39 +103,7 @@ test('handler function - preValidationCallback with finished response', t => {
108
103
  t.plan(0)
109
104
  const res = {}
110
105
  // Be sure to check only `writableEnded` where is available
111
- if (semver.gte(process.versions.node, '12.9.0')) {
112
- res.writableEnded = true
113
- } else {
114
- res.writable = false
115
- res.finished = true
116
- }
117
- res.end = () => {
118
- t.fail()
119
- }
120
- res.writeHead = () => {}
121
- const context = {
122
- handler: (req, reply) => {
123
- t.fail()
124
- reply.send(undefined)
125
- },
126
- Reply,
127
- Request,
128
- preValidation: null,
129
- preHandler: [],
130
- onSend: [],
131
- onError: []
132
- }
133
- buildSchema(context, schemaValidator)
134
- internals.handler({}, new Reply(res, { context }))
135
- })
136
-
137
- test('handler function - preValidationCallback with finished response (< v12.9.0)', t => {
138
- t.plan(0)
139
- const res = {}
140
- // Be sure to check only `writableEnded` where is available
141
- res.writable = false
142
- res.finished = true
143
-
106
+ res.writableEnded = true
144
107
  res.end = () => {
145
108
  t.fail()
146
109
  }
@@ -176,7 +139,7 @@ test('request should be defined in onSend Hook on post request with content type
176
139
  fastify.post('/', (request, reply) => {
177
140
  reply.send(200)
178
141
  })
179
- fastify.listen(0, err => {
142
+ fastify.listen({ port: 0 }, err => {
180
143
  fastify.server.unref()
181
144
  t.error(err)
182
145
  sget({
@@ -207,7 +170,7 @@ test('request should be defined in onSend Hook on post request with content type
207
170
  fastify.post('/', (request, reply) => {
208
171
  reply.send(200)
209
172
  })
210
- fastify.listen(0, err => {
173
+ fastify.listen({ port: 0 }, err => {
211
174
  fastify.server.unref()
212
175
  t.error(err)
213
176
  sget({
@@ -238,7 +201,7 @@ test('request should be defined in onSend Hook on options request with content t
238
201
  fastify.options('/', (request, reply) => {
239
202
  reply.send(200)
240
203
  })
241
- fastify.listen(0, err => {
204
+ fastify.listen({ port: 0 }, err => {
242
205
  fastify.server.unref()
243
206
  t.error(err)
244
207
  sget({
@@ -2,6 +2,7 @@
2
2
 
3
3
  const { test, before } = require('tap')
4
4
  const Fastify = require('../..')
5
+ const helper = require('../helper')
5
6
  const http = require('http')
6
7
  const pino = require('pino')
7
8
  const split = require('split2')
@@ -9,10 +10,17 @@ const deepClone = require('rfdc')({ circles: true, proto: false })
9
10
  const { deepFreezeObject } = require('../../lib/initialConfigValidation').utils
10
11
 
11
12
  const { buildCertificate } = require('../build-certificate')
12
- before(buildCertificate)
13
13
 
14
14
  process.removeAllListeners('warning')
15
15
 
16
+ let localhost
17
+ let localhostForURL
18
+
19
+ before(async function () {
20
+ await buildCertificate();
21
+ [localhost, localhostForURL] = await helper.getLoopbackHost()
22
+ })
23
+
16
24
  test('Fastify.initialConfig is an object', t => {
17
25
  t.plan(1)
18
26
  t.type(Fastify().initialConfig, 'object')
@@ -23,7 +31,8 @@ test('without options passed to Fastify, initialConfig should expose default val
23
31
 
24
32
  const fastifyDefaultOptions = {
25
33
  connectionTimeout: 0,
26
- keepAliveTimeout: 5000,
34
+ keepAliveTimeout: 72000,
35
+ forceCloseConnections: false,
27
36
  maxRequestsPerSocket: 0,
28
37
  requestTimeout: 0,
29
38
  bodyLimit: 1024 * 1024,
@@ -37,7 +46,8 @@ test('without options passed to Fastify, initialConfig should expose default val
37
46
  pluginTimeout: 10000,
38
47
  requestIdHeader: 'request-id',
39
48
  requestIdLogLabel: 'reqId',
40
- http2SessionTimeout: 5000
49
+ http2SessionTimeout: 72000,
50
+ exposeHeadRoutes: true
41
51
  }
42
52
 
43
53
  t.same(Fastify().initialConfig, fastifyDefaultOptions)
@@ -81,7 +91,7 @@ test('Fastify.initialConfig should expose all options', t => {
81
91
  ignoreTrailingSlash: true,
82
92
  maxParamLength: 200,
83
93
  connectionTimeout: 0,
84
- keepAliveTimeout: 5000,
94
+ keepAliveTimeout: 72000,
85
95
  bodyLimit: 1049600,
86
96
  onProtoPoisoning: 'remove',
87
97
  serverFactory,
@@ -103,11 +113,11 @@ test('Fastify.initialConfig should expose all options', t => {
103
113
 
104
114
  const fastify = Fastify(options)
105
115
  t.equal(fastify.initialConfig.http2, true)
106
- t.equal(fastify.initialConfig.https, true)
116
+ t.equal(fastify.initialConfig.https, true, 'for security reason the key cert is hidden')
107
117
  t.equal(fastify.initialConfig.ignoreTrailingSlash, true)
108
118
  t.equal(fastify.initialConfig.maxParamLength, 200)
109
119
  t.equal(fastify.initialConfig.connectionTimeout, 0)
110
- t.equal(fastify.initialConfig.keepAliveTimeout, 5000)
120
+ t.equal(fastify.initialConfig.keepAliveTimeout, 72000)
111
121
  t.equal(fastify.initialConfig.bodyLimit, 1049600)
112
122
  t.equal(fastify.initialConfig.onProtoPoisoning, 'remove')
113
123
  t.equal(fastify.initialConfig.caseSensitive, true)
@@ -157,10 +167,19 @@ test('We must avoid shallow freezing and ensure that the whole object is freezed
157
167
  t.type(error, TypeError)
158
168
  t.equal(error.message, "Cannot assign to read only property 'allowHTTP1' of object '#<Object>'")
159
169
  t.ok(error.stack)
160
- t.pass()
170
+ t.same(fastify.initialConfig.https, {
171
+ allowHTTP1: true
172
+ }, 'key cert removed')
161
173
  }
162
174
  })
163
175
 
176
+ test('https value check', t => {
177
+ t.plan(1)
178
+
179
+ const fastify = Fastify({})
180
+ t.notOk(fastify.initialConfig.https)
181
+ })
182
+
164
183
  test('Return an error if options do not match the validation schema', t => {
165
184
  t.plan(6)
166
185
 
@@ -171,7 +190,7 @@ test('Return an error if options do not match the validation schema', t => {
171
190
  } catch (error) {
172
191
  t.type(error, Error)
173
192
  t.equal(error.name, 'FastifyError')
174
- t.equal(error.message, 'Invalid initialization options: \'["should be boolean"]\'')
193
+ t.equal(error.message, 'Invalid initialization options: \'["must be boolean"]\'')
175
194
  t.equal(error.code, 'FST_ERR_INIT_OPTS_INVALID')
176
195
  t.ok(error.stack)
177
196
  t.pass()
@@ -241,7 +260,8 @@ test('Should not have issues when passing stream options to Pino.js', t => {
241
260
  t.type(fastify, 'object')
242
261
  t.same(fastify.initialConfig, {
243
262
  connectionTimeout: 0,
244
- keepAliveTimeout: 5000,
263
+ keepAliveTimeout: 72000,
264
+ forceCloseConnections: false,
245
265
  maxRequestsPerSocket: 0,
246
266
  requestTimeout: 0,
247
267
  bodyLimit: 1024 * 1024,
@@ -255,7 +275,8 @@ test('Should not have issues when passing stream options to Pino.js', t => {
255
275
  pluginTimeout: 10000,
256
276
  requestIdHeader: 'request-id',
257
277
  requestIdLogLabel: 'reqId',
258
- http2SessionTimeout: 5000
278
+ http2SessionTimeout: 72000,
279
+ exposeHeadRoutes: true
259
280
  })
260
281
  } catch (error) {
261
282
  t.fail()
@@ -287,11 +308,11 @@ test('Should not have issues when passing stream options to Pino.js', t => {
287
308
  })
288
309
  })
289
310
 
290
- fastify.listen(0, err => {
311
+ fastify.listen({ port: 0, host: localhost }, err => {
291
312
  t.error(err)
292
313
  fastify.server.unref()
293
314
 
294
- http.get('http://localhost:' + fastify.server.address().port)
315
+ http.get(`http://${localhostForURL}:${fastify.server.address().port}`)
295
316
  })
296
317
  })
297
318
 
@@ -356,3 +377,11 @@ test('Fastify.initialConfig should accept the deprecated versioning option', t =
356
377
  t.end()
357
378
  })
358
379
  })
380
+
381
+ test('pluginTimeout should be parsed correctly', t => {
382
+ const withDisabledTimeout = Fastify({ pluginTimeout: '0' })
383
+ t.equal(withDisabledTimeout.initialConfig.pluginTimeout, 0)
384
+ const withInvalidTimeout = Fastify({ pluginTimeout: undefined })
385
+ t.equal(withInvalidTimeout.initialConfig.pluginTimeout, 10000)
386
+ t.end()
387
+ })
@@ -20,7 +20,7 @@ test('The logger should add a unique id for every request', t => {
20
20
  reply.send({ id: req.id })
21
21
  })
22
22
 
23
- fastify.listen(0, err => {
23
+ fastify.listen({ port: 0 }, err => {
24
24
  t.error(err)
25
25
  const queue = new Queue()
26
26
  for (let i = 0; i < 10; i++) {
@@ -53,7 +53,7 @@ test('The logger should reuse request id header for req.id', t => {
53
53
  reply.send({ id: req.id })
54
54
  })
55
55
 
56
- fastify.listen(0, err => {
56
+ fastify.listen({ port: 0 }, err => {
57
57
  t.error(err)
58
58
 
59
59
  fastify.inject({