fastify 5.2.0 → 5.2.2

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 (83) hide show
  1. package/LICENSE +1 -1
  2. package/PROJECT_CHARTER.md +7 -7
  3. package/README.md +65 -67
  4. package/SPONSORS.md +2 -0
  5. package/build/build-validation.js +1 -1
  6. package/docs/Guides/Benchmarking.md +4 -4
  7. package/docs/Guides/Database.md +1 -1
  8. package/docs/Guides/Delay-Accepting-Requests.md +10 -10
  9. package/docs/Guides/Ecosystem.md +5 -1
  10. package/docs/Guides/Fluent-Schema.md +1 -1
  11. package/docs/Guides/Getting-Started.md +9 -5
  12. package/docs/Guides/Index.md +1 -1
  13. package/docs/Guides/Migration-Guide-V4.md +1 -1
  14. package/docs/Guides/Migration-Guide-V5.md +12 -2
  15. package/docs/Guides/Plugins-Guide.md +6 -6
  16. package/docs/Guides/Serverless.md +14 -48
  17. package/docs/Guides/Style-Guide.md +2 -2
  18. package/docs/Guides/Testing.md +2 -2
  19. package/docs/Guides/Write-Plugin.md +2 -3
  20. package/docs/Reference/ContentTypeParser.md +58 -78
  21. package/docs/Reference/Decorators.md +50 -60
  22. package/docs/Reference/Encapsulation.md +28 -33
  23. package/docs/Reference/Errors.md +52 -53
  24. package/docs/Reference/HTTP2.md +7 -7
  25. package/docs/Reference/Hooks.md +31 -30
  26. package/docs/Reference/LTS.md +10 -15
  27. package/docs/Reference/Lifecycle.md +19 -24
  28. package/docs/Reference/Logging.md +59 -56
  29. package/docs/Reference/Middleware.md +19 -19
  30. package/docs/Reference/Plugins.md +55 -71
  31. package/docs/Reference/Principles.md +25 -30
  32. package/docs/Reference/Reply.md +11 -10
  33. package/docs/Reference/Request.md +89 -99
  34. package/docs/Reference/Routes.md +108 -128
  35. package/docs/Reference/Server.md +19 -17
  36. package/docs/Reference/Type-Providers.md +19 -21
  37. package/docs/Reference/TypeScript.md +1 -18
  38. package/docs/Reference/Validation-and-Serialization.md +134 -159
  39. package/docs/Reference/Warnings.md +22 -25
  40. package/fastify.js +1 -1
  41. package/lib/contentTypeParser.js +7 -8
  42. package/lib/error-handler.js +14 -12
  43. package/lib/errors.js +4 -0
  44. package/lib/headRoute.js +4 -2
  45. package/lib/pluginUtils.js +4 -2
  46. package/lib/reply.js +4 -0
  47. package/lib/request.js +13 -9
  48. package/lib/server.js +5 -0
  49. package/lib/validation.js +1 -1
  50. package/lib/warnings.js +9 -0
  51. package/lib/wrapThenable.js +8 -1
  52. package/package.json +28 -17
  53. package/test/build/error-serializer.test.js +2 -1
  54. package/test/bundler/esbuild/package.json +1 -1
  55. package/test/close.test.js +125 -108
  56. package/test/custom-parser-async.test.js +34 -36
  57. package/test/custom-parser.2.test.js +19 -20
  58. package/test/custom-parser.3.test.js +56 -45
  59. package/test/delete.test.js +79 -67
  60. package/test/genReqId.test.js +125 -174
  61. package/test/has-route.test.js +1 -3
  62. package/test/internals/content-type-parser.test.js +1 -1
  63. package/test/internals/errors.test.js +19 -7
  64. package/test/issue-4959.test.js +84 -0
  65. package/test/listen.1.test.js +37 -34
  66. package/test/listen.2.test.js +47 -40
  67. package/test/listen.3.test.js +28 -32
  68. package/test/listen.4.test.js +61 -45
  69. package/test/listen.5.test.js +23 -0
  70. package/test/nullable-validation.test.js +30 -27
  71. package/test/register.test.js +55 -50
  72. package/test/request-error.test.js +114 -94
  73. package/test/route-shorthand.test.js +36 -32
  74. package/test/server.test.js +0 -175
  75. package/test/stream.5.test.js +35 -33
  76. package/test/throw.test.js +87 -91
  77. package/test/toolkit.js +32 -0
  78. package/test/trust-proxy.test.js +23 -23
  79. package/test/types/instance.test-d.ts +1 -0
  80. package/test/upgrade.test.js +32 -30
  81. package/test/web-api.test.js +44 -0
  82. package/types/instance.d.ts +4 -0
  83. package/test/test-reporter.mjs +0 -68
@@ -2,8 +2,6 @@
2
2
 
3
3
  const { test } = require('node:test')
4
4
  const Fastify = require('..')
5
- const sget = require('simple-get').concat
6
- const undici = require('undici')
7
5
 
8
6
  test('listen should accept null port', async t => {
9
7
  const fastify = Fastify()
@@ -13,176 +11,3 @@ test('listen should accept null port', async t => {
13
11
  fastify.listen({ port: null })
14
12
  )
15
13
  })
16
-
17
- test('listen should accept undefined port', async t => {
18
- const fastify = Fastify()
19
- t.after(() => fastify.close())
20
-
21
- await t.assert.doesNotReject(
22
- fastify.listen({ port: undefined })
23
- )
24
- })
25
-
26
- test('listen should accept stringified number port', async t => {
27
- const fastify = Fastify()
28
- t.after(() => fastify.close())
29
-
30
- await t.assert.doesNotReject(
31
- fastify.listen({ port: '1234' })
32
- )
33
- })
34
-
35
- test('listen should accept log text resolution function', async t => {
36
- const fastify = Fastify()
37
- t.after(() => fastify.close())
38
-
39
- await t.assert.doesNotReject(
40
- fastify.listen({
41
- host: '127.0.0.1',
42
- port: '1234',
43
- listenTextResolver: (address) => {
44
- t.assert.strictEqual(address, 'http://127.0.0.1:1234')
45
- return 'hardcoded text'
46
- }
47
- })
48
- )
49
- })
50
-
51
- test('listen should reject string port', async (t) => {
52
- const fastify = Fastify()
53
- t.after(() => fastify.close())
54
-
55
- try {
56
- await fastify.listen({ port: 'hello-world' })
57
- } catch (error) {
58
- t.assert.strictEqual(error.code, 'ERR_SOCKET_BAD_PORT')
59
- }
60
-
61
- try {
62
- await fastify.listen({ port: '1234hello' })
63
- } catch (error) {
64
- t.assert.strictEqual(error.code, 'ERR_SOCKET_BAD_PORT')
65
- }
66
- })
67
-
68
- test('Test for hostname and port', (t, end) => {
69
- const app = Fastify()
70
- t.after(() => app.close())
71
- app.get('/host', (req, res) => {
72
- const host = 'localhost:8000'
73
- t.assert.strictEqual(req.host, host)
74
- t.assert.strictEqual(req.hostname, req.host.split(':')[0])
75
- t.assert.strictEqual(req.port, Number(req.host.split(':')[1]))
76
- res.send('ok')
77
- })
78
-
79
- app.listen({ port: 8000 }, () => {
80
- sget('http://localhost:8000/host', () => { end() })
81
- })
82
- })
83
-
84
- test('abort signal', async t => {
85
- await t.test('listen should not start server', (t, end) => {
86
- t.plan(2)
87
- function onClose (instance, done) {
88
- t.assert.strictEqual(instance, fastify)
89
- done()
90
- end()
91
- }
92
- const controller = new AbortController()
93
-
94
- const fastify = Fastify()
95
- fastify.addHook('onClose', onClose)
96
- fastify.listen({ port: 1234, signal: controller.signal }, (err) => {
97
- t.assert.ifError(err)
98
- })
99
- controller.abort()
100
- t.assert.strictEqual(fastify.server.listening, false)
101
- })
102
-
103
- await t.test('listen should not start server if already aborted', (t, end) => {
104
- t.plan(2)
105
- function onClose (instance, done) {
106
- t.assert.strictEqual(instance, fastify)
107
- done()
108
- end()
109
- }
110
-
111
- const controller = new AbortController()
112
- controller.abort()
113
- const fastify = Fastify()
114
- fastify.addHook('onClose', onClose)
115
- fastify.listen({ port: 1234, signal: controller.signal }, (err) => {
116
- t.assert.ifError(err)
117
- })
118
- t.assert.strictEqual(fastify.server.listening, false)
119
- })
120
-
121
- await t.test('listen should throw if received invalid signal', t => {
122
- t.plan(2)
123
- const fastify = Fastify()
124
-
125
- try {
126
- fastify.listen({ port: 1234, signal: {} }, (err) => {
127
- t.assert.ifError(err)
128
- })
129
- t.assert.fail('should throw')
130
- } catch (e) {
131
- t.assert.strictEqual(e.code, 'FST_ERR_LISTEN_OPTIONS_INVALID')
132
- t.assert.strictEqual(e.message, 'Invalid listen options: \'Invalid options.signal\'')
133
- }
134
- })
135
- })
136
-
137
- test('#5180 - preClose should be called before closing secondary server', async (t) => {
138
- t.plan(2)
139
- const fastify = Fastify({ forceCloseConnections: true })
140
- let flag = false
141
- t.after(() => fastify.close())
142
-
143
- fastify.addHook('preClose', () => {
144
- flag = true
145
- })
146
-
147
- fastify.get('/', async (req, reply) => {
148
- // request will be pending for 1 second to simulate a slow request
149
- await new Promise((resolve) => { setTimeout(resolve, 1000) })
150
- return { hello: 'world' }
151
- })
152
-
153
- fastify.listen({ port: 0 }, (err) => {
154
- t.assert.ifError(err)
155
- const addresses = fastify.addresses()
156
- const mainServerAddress = fastify.server.address()
157
- let secondaryAddress
158
- for (const addr of addresses) {
159
- if (addr.family !== mainServerAddress.family) {
160
- secondaryAddress = addr
161
- secondaryAddress.address = secondaryAddress.family === 'IPv6'
162
- ? `[${secondaryAddress.address}]`
163
- : secondaryAddress.address
164
- break
165
- }
166
- }
167
-
168
- if (!secondaryAddress) {
169
- t.assert.ok(true, 'Secondary address not found')
170
- return
171
- }
172
-
173
- undici.request(`http://${secondaryAddress.address}:${secondaryAddress.port}/`)
174
- .then(
175
- () => { t.assert.fail('Request should not succeed') },
176
- () => {
177
- t.assert.ok(flag)
178
- }
179
- )
180
-
181
- // Close the server while the slow request is pending
182
- setTimeout(fastify.close, 250)
183
- })
184
-
185
- // Wait 1000ms to ensure that the test is finished and async operations are
186
- // completed
187
- await new Promise((resolve) => { setTimeout(resolve, 1000) })
188
- })
@@ -1,14 +1,13 @@
1
1
  'use strict'
2
2
 
3
- const t = require('tap')
4
- const test = t.test
3
+ const { test } = require('node:test')
5
4
  const proxyquire = require('proxyquire')
6
5
  const fs = require('node:fs')
7
6
  const Readable = require('node:stream').Readable
8
7
  const sget = require('simple-get').concat
9
8
  const Fastify = require('..')
10
9
 
11
- test('should destroy stream when response is ended', t => {
10
+ test('should destroy stream when response is ended', (t, done) => {
12
11
  t.plan(4)
13
12
  const stream = require('node:stream')
14
13
  const fastify = Fastify()
@@ -17,7 +16,7 @@ test('should destroy stream when response is ended', t => {
17
16
  const reallyLongStream = new stream.Readable({
18
17
  read: function () { },
19
18
  destroy: function (err, callback) {
20
- t.ok('called')
19
+ t.assert.ok('called')
21
20
  callback(err)
22
21
  }
23
22
  })
@@ -26,23 +25,25 @@ test('should destroy stream when response is ended', t => {
26
25
  })
27
26
 
28
27
  fastify.listen({ port: 0 }, err => {
29
- t.error(err)
30
- t.teardown(() => { fastify.close() })
28
+ t.assert.ifError(err)
29
+ t.after(() => fastify.close())
31
30
 
32
31
  sget(`http://localhost:${fastify.server.address().port}/error`, function (err, response) {
33
- t.error(err)
34
- t.equal(response.statusCode, 200)
32
+ t.assert.ifError(err)
33
+ t.assert.strictEqual(response.statusCode, 200)
34
+ done()
35
35
  })
36
36
  })
37
37
  })
38
38
 
39
- test('should mark reply as sent before pumping the payload stream into response for async route handler', t => {
39
+ test('should mark reply as sent before pumping the payload stream into response for async route handler', (t, done) => {
40
40
  t.plan(3)
41
+ t.after(() => fastify.close())
41
42
 
42
43
  const handleRequest = proxyquire('../lib/handleRequest', {
43
44
  './wrapThenable': (thenable, reply) => {
44
45
  thenable.then(function (payload) {
45
- t.equal(reply.sent, true)
46
+ t.assert.strictEqual(reply.sent, true)
46
47
  })
47
48
  }
48
49
  })
@@ -66,20 +67,20 @@ test('should mark reply as sent before pumping the payload stream into response
66
67
  url: '/',
67
68
  method: 'GET'
68
69
  }, (err, res) => {
69
- t.error(err)
70
- t.equal(res.payload, fs.readFileSync(__filename, 'utf8'))
71
- fastify.close()
70
+ t.assert.ifError(err)
71
+ t.assert.strictEqual(res.payload, fs.readFileSync(__filename, 'utf8'))
72
+ done()
72
73
  })
73
74
  })
74
75
 
75
- test('reply.send handles aborted requests', t => {
76
+ test('reply.send handles aborted requests', (t, done) => {
76
77
  t.plan(2)
77
78
 
78
79
  const spyLogger = {
79
80
  level: 'error',
80
81
  fatal: () => { },
81
82
  error: () => {
82
- t.fail('should not log an error')
83
+ t.assert.fail('should not log an error')
83
84
  },
84
85
  warn: () => { },
85
86
  info: () => { },
@@ -103,31 +104,31 @@ test('reply.send handles aborted requests', t => {
103
104
  })
104
105
 
105
106
  fastify.listen({ port: 0 }, err => {
106
- t.error(err)
107
- t.teardown(() => { fastify.close() })
107
+ t.assert.ifError(err)
108
+ t.after(() => fastify.close())
108
109
 
109
110
  const port = fastify.server.address().port
110
111
  const http = require('node:http')
111
112
  const req = http.get(`http://localhost:${port}`)
112
113
  .on('error', (err) => {
113
- t.equal(err.code, 'ECONNRESET')
114
- fastify.close()
114
+ t.assert.strictEqual(err.code, 'ECONNRESET')
115
+ done()
115
116
  })
116
117
 
117
118
  setTimeout(() => {
118
- req.abort()
119
+ req.destroy()
119
120
  }, 1)
120
121
  })
121
122
  })
122
123
 
123
- test('request terminated should not crash fastify', t => {
124
+ test('request terminated should not crash fastify', (t, done) => {
124
125
  t.plan(10)
125
126
 
126
127
  const spyLogger = {
127
128
  level: 'error',
128
129
  fatal: () => { },
129
130
  error: () => {
130
- t.fail('should not log an error')
131
+ t.assert.fail('should not log an error')
131
132
  },
132
133
  warn: () => { },
133
134
  info: () => { },
@@ -156,18 +157,18 @@ test('request terminated should not crash fastify', t => {
156
157
  })
157
158
 
158
159
  fastify.listen({ port: 0 }, err => {
159
- t.error(err)
160
- t.teardown(() => { fastify.close() })
160
+ t.assert.ifError(err)
161
+ t.after(() => fastify.close())
161
162
 
162
163
  const port = fastify.server.address().port
163
164
  const http = require('node:http')
164
165
  const req = http.get(`http://localhost:${port}`, function (res) {
165
166
  const { statusCode, headers } = res
166
- t.equal(statusCode, 200)
167
- t.equal(headers['content-type'], 'text/html; charset=utf-8')
168
- t.equal(headers['transfer-encoding'], 'chunked')
167
+ t.assert.strictEqual(statusCode, 200)
168
+ t.assert.strictEqual(headers['content-type'], 'text/html; charset=utf-8')
169
+ t.assert.strictEqual(headers['transfer-encoding'], 'chunked')
169
170
  res.on('data', function (chunk) {
170
- t.equal(chunk.toString(), '<h1>HTML</h1>')
171
+ t.assert.strictEqual(chunk.toString(), '<h1>HTML</h1>')
171
172
  })
172
173
 
173
174
  setTimeout(() => {
@@ -176,16 +177,17 @@ test('request terminated should not crash fastify', t => {
176
177
  // the server is not crash, we can connect it
177
178
  http.get(`http://localhost:${port}`, function (res) {
178
179
  const { statusCode, headers } = res
179
- t.equal(statusCode, 200)
180
- t.equal(headers['content-type'], 'text/html; charset=utf-8')
181
- t.equal(headers['transfer-encoding'], 'chunked')
180
+ t.assert.strictEqual(statusCode, 200)
181
+ t.assert.strictEqual(headers['content-type'], 'text/html; charset=utf-8')
182
+ t.assert.strictEqual(headers['transfer-encoding'], 'chunked')
182
183
  let payload = ''
183
184
  res.on('data', function (chunk) {
184
185
  payload += chunk.toString()
185
186
  })
186
187
  res.on('end', function () {
187
- t.equal(payload, '<h1>HTML</h1><h1>should display on second stream</h1>')
188
- t.pass('should end properly')
188
+ t.assert.strictEqual(payload, '<h1>HTML</h1><h1>should display on second stream</h1>')
189
+ t.assert.ok('should end properly')
190
+ done()
189
191
  })
190
192
  })
191
193
  }, 1)