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
@@ -0,0 +1,84 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('node:test')
4
+ const http = require('node:http')
5
+ const Fastify = require('../fastify')
6
+
7
+ function runBadClientCall (reqOptions, payload) {
8
+ let innerResolve, innerReject
9
+ const promise = new Promise((resolve, reject) => {
10
+ innerResolve = resolve
11
+ innerReject = reject
12
+ })
13
+
14
+ const postData = JSON.stringify(payload)
15
+
16
+ const req = http.request({
17
+ ...reqOptions,
18
+ headers: {
19
+ 'Content-Type': 'application/json',
20
+ 'Content-Length': Buffer.byteLength(postData),
21
+ }
22
+ }, () => {
23
+ innerReject(new Error('Request should have failed'))
24
+ })
25
+
26
+ // Kill the socket immediately (before sending data)
27
+ req.on('socket', (socket) => {
28
+ setTimeout(() => { socket.destroy() }, 5)
29
+ })
30
+ req.on('error', innerResolve)
31
+ req.write(postData)
32
+ req.end()
33
+
34
+ return promise
35
+ }
36
+
37
+ test('should handle a soket error', async (t) => {
38
+ t.plan(4)
39
+ const fastify = Fastify()
40
+
41
+ function shouldNotHappen () {
42
+ t.assert.fail('This should not happen')
43
+ }
44
+ process.on('unhandledRejection', shouldNotHappen)
45
+
46
+ t.after(() => {
47
+ fastify.close()
48
+ process.removeListener('unhandledRejection', shouldNotHappen)
49
+ })
50
+
51
+ fastify.addHook('onRequest', async (request, reply) => {
52
+ t.assert.ok('onRequest hook called')
53
+ })
54
+
55
+ fastify.addHook('onSend', async (request, reply, payload) => {
56
+ if (request.onSendCalled) {
57
+ t.assert.fail('onSend hook called more than once')
58
+ return
59
+ }
60
+
61
+ t.assert.ok('onSend hook called')
62
+ request.onSendCalled = true
63
+
64
+ // Introduce a delay
65
+ await new Promise(resolve => setTimeout(resolve, 5))
66
+ return payload
67
+ })
68
+
69
+ // The handler must be async to trigger the error
70
+ fastify.put('/', async (request, reply) => {
71
+ t.assert.ok('PUT handler called')
72
+ return reply.send({ hello: 'world' })
73
+ })
74
+
75
+ await fastify.listen({ port: 0 })
76
+
77
+ const err = await runBadClientCall({
78
+ hostname: 'localhost',
79
+ port: fastify.server.address().port,
80
+ path: '/',
81
+ method: 'PUT',
82
+ }, { test: 'me' })
83
+ t.assert.equal(err.code, 'ECONNRESET')
84
+ })
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { test, before } = require('tap')
3
+ const { test, before } = require('node:test')
4
4
  const Fastify = require('..')
5
5
  const helper = require('./helper')
6
6
 
@@ -13,69 +13,70 @@ before(async function () {
13
13
 
14
14
  test('listen works without arguments', async t => {
15
15
  const doNotWarn = () => {
16
- t.fail('should not be deprecated')
16
+ t.assert.fail('should not be deprecated')
17
17
  }
18
18
  process.on('warning', doNotWarn)
19
19
 
20
20
  const fastify = Fastify()
21
- t.teardown(() => {
21
+ t.after(() => {
22
22
  fastify.close()
23
23
  process.removeListener('warning', doNotWarn)
24
24
  })
25
25
  await fastify.listen()
26
26
  const address = fastify.server.address()
27
- t.equal(address.address, localhost)
28
- t.ok(address.port > 0)
27
+ t.assert.strictEqual(address.address, localhost)
28
+ t.assert.ok(address.port > 0)
29
29
  })
30
30
 
31
31
  test('Async/await listen with arguments', async t => {
32
32
  const doNotWarn = () => {
33
- t.fail('should not be deprecated')
33
+ t.assert.fail('should not be deprecated')
34
34
  }
35
35
  process.on('warning', doNotWarn)
36
36
 
37
37
  const fastify = Fastify()
38
- t.teardown(() => {
38
+ t.after(() => {
39
39
  fastify.close()
40
40
  process.removeListener('warning', doNotWarn)
41
41
  })
42
42
  const addr = await fastify.listen({ port: 0, host: '0.0.0.0' })
43
43
  const address = fastify.server.address()
44
- t.equal(addr, `http://127.0.0.1:${address.port}`)
45
- t.same(address, {
44
+ t.assert.strictEqual(addr, `http://127.0.0.1:${address.port}`)
45
+ t.assert.deepEqual(address, {
46
46
  address: '0.0.0.0',
47
47
  family: 'IPv4',
48
48
  port: address.port
49
49
  })
50
50
  })
51
51
 
52
- test('listen accepts a callback', t => {
52
+ test('listen accepts a callback', (t, done) => {
53
53
  t.plan(2)
54
54
  const doNotWarn = () => {
55
- t.fail('should not be deprecated')
55
+ t.assert.fail('should not be deprecated')
56
56
  }
57
57
  process.on('warning', doNotWarn)
58
58
 
59
59
  const fastify = Fastify()
60
- t.teardown(() => {
60
+ t.after(() => {
61
61
  fastify.close()
62
62
  process.removeListener('warning', doNotWarn)
63
63
  })
64
64
  fastify.listen({ port: 0 }, (err) => {
65
- t.equal(fastify.server.address().address, localhost)
66
- t.error(err)
65
+ t.assert.ifError(err)
66
+ t.assert.strictEqual(fastify.server.address().address, localhost)
67
+ done()
67
68
  })
68
69
  })
69
70
 
70
- test('listen accepts options and a callback', t => {
71
+ test('listen accepts options and a callback', (t, done) => {
71
72
  t.plan(1)
72
73
  const doNotWarn = () => {
73
- t.fail('should not be deprecated')
74
+ t.assert.fail('should not be deprecated')
74
75
  }
75
76
  process.on('warning', doNotWarn)
76
77
 
77
78
  const fastify = Fastify()
78
- t.teardown(() => {
79
+ t.after(() => {
79
80
  fastify.close()
80
81
  process.removeListener('warning', doNotWarn)
81
82
  })
@@ -88,40 +89,42 @@ test('listen accepts options and a callback', t => {
88
89
  writableAll: false,
89
90
  ipv6Only: false
90
91
  }, (err) => {
91
- t.error(err)
92
+ t.assert.ifError(err)
93
+ done()
92
94
  })
93
95
  })
94
96
 
95
- test('listen after Promise.resolve()', t => {
97
+ test('listen after Promise.resolve()', (t, done) => {
96
98
  t.plan(2)
97
- const f = Fastify()
98
- t.teardown(f.close.bind(f))
99
+ const fastify = Fastify()
100
+ t.after(() => fastify.close())
99
101
  Promise.resolve()
100
102
  .then(() => {
101
- f.listen({ port: 0 }, (err, address) => {
102
- f.server.unref()
103
- t.equal(address, `http://${localhostForURL}:${f.server.address().port}`)
104
- t.error(err)
103
+ fastify.listen({ port: 0 }, (err, address) => {
104
+ fastify.server.unref()
105
+ t.assert.strictEqual(address, `http://${localhostForURL}:${fastify.server.address().port}`)
106
+ t.assert.ifError(err)
107
+ done()
105
108
  })
106
109
  })
107
110
  })
108
111
 
109
112
  test('listen works with undefined host', async t => {
110
113
  const doNotWarn = () => {
111
- t.fail('should not be deprecated')
114
+ t.assert.fail('should not be deprecated')
112
115
  }
113
116
  process.on('warning', doNotWarn)
114
117
 
115
118
  const fastify = Fastify()
116
- t.teardown(fastify.close.bind(fastify))
117
- t.teardown(() => {
119
+ t.after(() => fastify.close())
120
+ t.after(() => {
118
121
  fastify.close()
119
122
  process.removeListener('warning', doNotWarn)
120
123
  })
121
124
  await fastify.listen({ host: undefined, port: 0 })
122
125
  const address = fastify.server.address()
123
- t.equal(address.address, localhost)
124
- t.ok(address.port > 0)
126
+ t.assert.strictEqual(address.address, localhost)
127
+ t.assert.ok(address.port > 0)
125
128
  })
126
129
 
127
130
  test('listen works with null host', async t => {
@@ -131,13 +134,13 @@ test('listen works with null host', async t => {
131
134
  process.on('warning', doNotWarn)
132
135
 
133
136
  const fastify = Fastify()
134
- t.teardown(fastify.close.bind(fastify))
135
- t.teardown(() => {
137
+ t.after(() => fastify.close())
138
+ t.after(() => {
136
139
  fastify.close()
137
140
  process.removeListener('warning', doNotWarn)
138
141
  })
139
142
  await fastify.listen({ host: null, port: 0 })
140
143
  const address = fastify.server.address()
141
- t.equal(address.address, localhost)
142
- t.ok(address.port > 0)
144
+ t.assert.strictEqual(address.address, localhost)
145
+ t.assert.ok(address.port > 0)
143
146
  })
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { test, before } = require('tap')
3
+ const { test, before } = require('node:test')
4
4
  const Fastify = require('..')
5
5
  const helper = require('./helper')
6
6
 
@@ -10,94 +10,101 @@ before(async function () {
10
10
  [, localhostForURL] = await helper.getLoopbackHost()
11
11
  })
12
12
 
13
- test('register after listen using Promise.resolve()', t => {
13
+ test('register after listen using Promise.resolve()', async t => {
14
14
  t.plan(1)
15
- const f = Fastify()
15
+ const fastify = Fastify()
16
16
 
17
17
  const handler = (req, res) => res.send({})
18
- Promise.resolve()
18
+ await Promise.resolve()
19
19
  .then(() => {
20
- f.get('/', handler)
21
- f.register((f2, options, done) => {
20
+ fastify.get('/', handler)
21
+ fastify.register((f2, options, done) => {
22
22
  f2.get('/plugin', handler)
23
23
  done()
24
24
  })
25
- return f.ready()
25
+ return fastify.ready()
26
+ })
27
+ .catch((err) => {
28
+ t.assert.fail(err.message)
26
29
  })
27
- .catch(t.error)
28
- .then(() => t.pass('resolved'))
30
+ .then(() => t.assert.ok('resolved'))
29
31
  })
30
32
 
31
- test('double listen errors', t => {
33
+ test('double listen errors', (t, done) => {
32
34
  t.plan(3)
33
35
  const fastify = Fastify()
34
- t.teardown(fastify.close.bind(fastify))
36
+ t.after(() => fastify.close())
35
37
  fastify.listen({ port: 0 }, (err) => {
36
- t.error(err)
38
+ t.assert.ifError(err)
37
39
  fastify.listen({ port: fastify.server.address().port }, (err, address) => {
38
- t.equal(address, null)
39
- t.ok(err)
40
+ t.assert.strictEqual(address, null)
41
+ t.assert.ok(err)
42
+ done()
40
43
  })
41
44
  })
42
45
  })
43
46
 
44
- test('double listen errors callback with (err, address)', t => {
47
+ test('double listen errors callback with (err, address)', (t, done) => {
45
48
  t.plan(4)
46
49
  const fastify = Fastify()
47
- t.teardown(fastify.close.bind(fastify))
50
+ t.after(() => fastify.close())
48
51
  fastify.listen({ port: 0 }, (err1, address1) => {
49
- t.equal(address1, `http://${localhostForURL}:${fastify.server.address().port}`)
50
- t.error(err1)
52
+ t.assert.strictEqual(address1, `http://${localhostForURL}:${fastify.server.address().port}`)
53
+ t.assert.ifError(err1)
51
54
  fastify.listen({ port: fastify.server.address().port }, (err2, address2) => {
52
- t.equal(address2, null)
53
- t.ok(err2)
55
+ t.assert.strictEqual(address2, null)
56
+ t.assert.ok(err2)
57
+ done()
54
58
  })
55
59
  })
56
60
  })
57
61
 
58
- test('nonlocalhost double listen errors callback with (err, address)', t => {
62
+ test('nonlocalhost double listen errors callback with (err, address)', (t, done) => {
59
63
  t.plan(4)
60
64
  const fastify = Fastify()
61
- t.teardown(fastify.close.bind(fastify))
65
+ t.after(() => fastify.close())
62
66
  fastify.listen({ host: '::1', port: 0 }, (err, address) => {
63
- t.equal(address, `http://${'[::1]'}:${fastify.server.address().port}`)
64
- t.error(err)
67
+ t.assert.strictEqual(address, `http://${'[::1]'}:${fastify.server.address().port}`)
68
+ t.assert.ifError(err)
65
69
  fastify.listen({ host: '::1', port: fastify.server.address().port }, (err2, address2) => {
66
- t.equal(address2, null)
67
- t.ok(err2)
70
+ t.assert.strictEqual(address2, null)
71
+ t.assert.ok(err2)
72
+ done()
68
73
  })
69
74
  })
70
75
  })
71
76
 
72
- test('listen twice on the same port', t => {
77
+ test('listen twice on the same port', (t, done) => {
73
78
  t.plan(4)
74
79
  const fastify = Fastify()
75
- t.teardown(fastify.close.bind(fastify))
80
+ t.after(() => fastify.close())
76
81
  fastify.listen({ port: 0 }, (err1, address1) => {
77
- t.equal(address1, `http://${localhostForURL}:${fastify.server.address().port}`)
78
- t.error(err1)
82
+ t.assert.strictEqual(address1, `http://${localhostForURL}:${fastify.server.address().port}`)
83
+ t.assert.ifError(err1)
79
84
  const s2 = Fastify()
80
- t.teardown(s2.close.bind(s2))
85
+ t.after(() => fastify.close())
81
86
  s2.listen({ port: fastify.server.address().port }, (err2, address2) => {
82
- t.equal(address2, null)
83
- t.ok(err2)
87
+ t.assert.strictEqual(address2, null)
88
+ t.assert.ok(err2)
89
+ done()
84
90
  })
85
91
  })
86
92
  })
87
93
 
88
- test('listen twice on the same port callback with (err, address)', t => {
94
+ test('listen twice on the same port callback with (err, address)', (t, done) => {
89
95
  t.plan(4)
90
96
  const fastify = Fastify()
91
- t.teardown(fastify.close.bind(fastify))
97
+ t.after(() => fastify.close())
92
98
  fastify.listen({ port: 0 }, (err1, address1) => {
93
99
  const _port = fastify.server.address().port
94
- t.equal(address1, `http://${localhostForURL}:${_port}`)
95
- t.error(err1)
100
+ t.assert.strictEqual(address1, `http://${localhostForURL}:${_port}`)
101
+ t.assert.ifError(err1)
96
102
  const s2 = Fastify()
97
- t.teardown(s2.close.bind(s2))
103
+ t.after(() => fastify.close())
98
104
  s2.listen({ port: _port }, (err2, address2) => {
99
- t.equal(address2, null)
100
- t.ok(err2)
105
+ t.assert.strictEqual(address2, null)
106
+ t.assert.ok(err2)
107
+ done()
101
108
  })
102
109
  })
103
110
  })
@@ -3,7 +3,7 @@
3
3
  const os = require('node:os')
4
4
  const path = require('node:path')
5
5
  const fs = require('node:fs')
6
- const { test, before } = require('tap')
6
+ const { test, before } = require('node:test')
7
7
  const Fastify = require('..')
8
8
  const helper = require('./helper')
9
9
 
@@ -15,73 +15,69 @@ before(async function () {
15
15
 
16
16
  // https://nodejs.org/api/net.html#net_ipc_support
17
17
  if (os.platform() !== 'win32') {
18
- test('listen on socket', t => {
19
- t.plan(3)
18
+ test('listen on socket', async t => {
19
+ t.plan(2)
20
20
  const fastify = Fastify()
21
- t.teardown(fastify.close.bind(fastify))
21
+ t.after(() => fastify.close())
22
22
 
23
23
  const sockFile = path.join(os.tmpdir(), `${(Math.random().toString(16) + '0000000').slice(2, 10)}-server.sock`)
24
24
  try {
25
25
  fs.unlinkSync(sockFile)
26
26
  } catch (e) { }
27
27
 
28
- fastify.listen({ path: sockFile }, (err, address) => {
29
- t.error(err)
30
- t.strictSame(fastify.addresses(), [sockFile])
31
- t.equal(address, sockFile)
32
- })
28
+ await fastify.listen({ path: sockFile })
29
+ t.assert.deepStrictEqual(fastify.addresses(), [sockFile])
30
+ t.assert.strictEqual(fastify.server.address(), sockFile)
33
31
  })
34
32
  } else {
35
- test('listen on socket', t => {
36
- t.plan(3)
33
+ test('listen on socket', async t => {
34
+ t.plan(2)
37
35
  const fastify = Fastify()
38
- t.teardown(fastify.close.bind(fastify))
36
+ t.after(() => fastify.close())
39
37
 
40
38
  const sockFile = `\\\\.\\pipe\\${(Math.random().toString(16) + '0000000').slice(2, 10)}-server-sock`
41
39
 
42
- fastify.listen({ path: sockFile }, (err, address) => {
43
- t.error(err)
44
- t.strictSame(fastify.addresses(), [sockFile])
45
- t.equal(address, sockFile)
46
- })
40
+ await fastify.listen({ path: sockFile })
41
+ t.assert.deepStrictEqual(fastify.addresses(), [sockFile])
42
+ t.assert.strictEqual(fastify.server.address(), sockFile)
47
43
  })
48
44
  }
49
45
 
50
- test('listen without callback with (address)', t => {
46
+ test('listen without callback with (address)', async t => {
51
47
  t.plan(1)
52
48
  const fastify = Fastify()
53
- t.teardown(fastify.close.bind(fastify))
54
- fastify.listen({ port: 0 })
55
- .then(address => {
56
- t.equal(address, `http://${localhostForURL}:${fastify.server.address().port}`)
57
- })
49
+ t.after(() => fastify.close())
50
+ const address = await fastify.listen({ port: 0 })
51
+ t.assert.strictEqual(address, `http://${localhostForURL}:${fastify.server.address().port}`)
58
52
  })
59
53
 
60
- test('double listen without callback rejects', t => {
54
+ test('double listen without callback rejects', (t, done) => {
61
55
  t.plan(1)
62
56
  const fastify = Fastify()
63
- t.teardown(fastify.close.bind(fastify))
57
+ t.after(() => fastify.close())
64
58
  fastify.listen({ port: 0 })
65
59
  .then(() => {
66
60
  fastify.listen({ port: 0 })
67
61
  .catch(err => {
68
- t.ok(err)
62
+ t.assert.ok(err)
63
+ done()
69
64
  })
70
65
  })
71
- .catch(err => t.error(err))
66
+ .catch(err => t.assert.ifError(err))
72
67
  })
73
68
 
74
- test('double listen without callback with (address)', t => {
69
+ test('double listen without callback with (address)', (t, done) => {
75
70
  t.plan(2)
76
71
  const fastify = Fastify()
77
- t.teardown(fastify.close.bind(fastify))
72
+ t.after(() => fastify.close())
78
73
  fastify.listen({ port: 0 })
79
74
  .then(address => {
80
- t.equal(address, `http://${localhostForURL}:${fastify.server.address().port}`)
75
+ t.assert.strictEqual(address, `http://${localhostForURL}:${fastify.server.address().port}`)
81
76
  fastify.listen({ port: 0 })
82
77
  .catch(err => {
83
- t.ok(err)
78
+ t.assert.ok(err)
79
+ done()
84
80
  })
85
81
  })
86
- .catch(err => t.error(err))
82
+ .catch(err => t.assert.ifError(err))
87
83
  })