fastify 5.3.3 → 5.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/.vscode/settings.json +15 -15
  2. package/LICENSE +1 -1
  3. package/README.md +2 -0
  4. package/SECURITY.md +158 -2
  5. package/build/build-validation.js +20 -1
  6. package/docs/Guides/Delay-Accepting-Requests.md +8 -5
  7. package/docs/Guides/Ecosystem.md +20 -5
  8. package/docs/Guides/Migration-Guide-V5.md +6 -10
  9. package/docs/Guides/Recommendations.md +1 -1
  10. package/docs/Reference/ContentTypeParser.md +1 -1
  11. package/docs/Reference/Errors.md +5 -3
  12. package/docs/Reference/Hooks.md +16 -20
  13. package/docs/Reference/Lifecycle.md +2 -2
  14. package/docs/Reference/Logging.md +3 -3
  15. package/docs/Reference/Middleware.md +1 -1
  16. package/docs/Reference/Reply.md +8 -8
  17. package/docs/Reference/Request.md +2 -2
  18. package/docs/Reference/Routes.md +7 -6
  19. package/docs/Reference/Server.md +341 -200
  20. package/docs/Reference/TypeScript.md +1 -3
  21. package/docs/Reference/Validation-and-Serialization.md +56 -4
  22. package/docs/Reference/Warnings.md +2 -1
  23. package/fastify.d.ts +4 -3
  24. package/fastify.js +47 -34
  25. package/lib/configValidator.js +196 -28
  26. package/lib/contentTypeParser.js +41 -48
  27. package/lib/error-handler.js +3 -3
  28. package/lib/errors.js +11 -0
  29. package/lib/handleRequest.js +13 -17
  30. package/lib/pluginOverride.js +3 -1
  31. package/lib/promise.js +23 -0
  32. package/lib/reply.js +24 -30
  33. package/lib/request.js +3 -10
  34. package/lib/route.js +37 -3
  35. package/lib/server.js +36 -35
  36. package/lib/symbols.js +1 -0
  37. package/lib/warnings.js +19 -1
  38. package/package.json +14 -10
  39. package/test/404s.test.js +226 -325
  40. package/test/allow-unsafe-regex.test.js +19 -48
  41. package/test/als.test.js +28 -40
  42. package/test/async-await.test.js +84 -128
  43. package/test/async_hooks.test.js +18 -37
  44. package/test/body-limit.test.js +90 -63
  45. package/test/buffer.test.js +22 -0
  46. package/test/build-certificate.js +1 -1
  47. package/test/case-insensitive.test.js +44 -65
  48. package/test/check.test.js +17 -21
  49. package/test/close-pipelining.test.js +24 -15
  50. package/test/constrained-routes.test.js +231 -0
  51. package/test/custom-http-server.test.js +7 -15
  52. package/test/custom-parser-async.test.js +17 -22
  53. package/test/custom-parser.0.test.js +267 -348
  54. package/test/custom-parser.1.test.js +141 -191
  55. package/test/custom-parser.2.test.js +34 -44
  56. package/test/custom-parser.3.test.js +56 -104
  57. package/test/custom-parser.4.test.js +106 -144
  58. package/test/custom-parser.5.test.js +56 -75
  59. package/test/custom-querystring-parser.test.js +51 -77
  60. package/test/decorator-namespace.test._js_ +3 -4
  61. package/test/decorator.test.js +76 -259
  62. package/test/delete.test.js +101 -110
  63. package/test/diagnostics-channel/404.test.js +7 -15
  64. package/test/diagnostics-channel/async-delay-request.test.js +7 -16
  65. package/test/diagnostics-channel/async-request.test.js +8 -16
  66. package/test/diagnostics-channel/error-request.test.js +7 -15
  67. package/test/diagnostics-channel/sync-delay-request.test.js +7 -16
  68. package/test/diagnostics-channel/sync-request-reply.test.js +9 -16
  69. package/test/diagnostics-channel/sync-request.test.js +9 -16
  70. package/test/fastify-instance.test.js +1 -1
  71. package/test/header-overflow.test.js +18 -29
  72. package/test/helper.js +139 -135
  73. package/test/hooks-async.test.js +259 -235
  74. package/test/hooks.test.js +951 -996
  75. package/test/http-methods/copy.test.js +14 -19
  76. package/test/http-methods/get.test.js +131 -143
  77. package/test/http-methods/head.test.js +53 -84
  78. package/test/http-methods/lock.test.js +31 -31
  79. package/test/http-methods/mkcalendar.test.js +45 -72
  80. package/test/http-methods/mkcol.test.js +5 -9
  81. package/test/http-methods/move.test.js +6 -10
  82. package/test/http-methods/propfind.test.js +34 -44
  83. package/test/http-methods/proppatch.test.js +23 -29
  84. package/test/http-methods/report.test.js +44 -69
  85. package/test/http-methods/search.test.js +67 -82
  86. package/test/http-methods/unlock.test.js +5 -9
  87. package/test/http2/closing.test.js +38 -20
  88. package/test/http2/secure-with-fallback.test.js +31 -28
  89. package/test/https/custom-https-server.test.js +9 -13
  90. package/test/https/https.test.js +56 -53
  91. package/test/input-validation.js +139 -150
  92. package/test/internals/errors.test.js +50 -1
  93. package/test/internals/handle-request.test.js +72 -65
  94. package/test/internals/promise.test.js +63 -0
  95. package/test/internals/reply.test.js +277 -496
  96. package/test/issue-4959.test.js +12 -3
  97. package/test/listen.4.test.js +31 -43
  98. package/test/nullable-validation.test.js +33 -46
  99. package/test/output-validation.test.js +24 -26
  100. package/test/plugin.1.test.js +40 -68
  101. package/test/plugin.2.test.js +108 -120
  102. package/test/plugin.3.test.js +50 -72
  103. package/test/plugin.4.test.js +124 -119
  104. package/test/promises.test.js +42 -63
  105. package/test/proto-poisoning.test.js +78 -97
  106. package/test/register.test.js +8 -18
  107. package/test/request-error.test.js +57 -146
  108. package/test/request-id.test.js +30 -49
  109. package/test/route-hooks.test.js +117 -101
  110. package/test/route-prefix.test.js +194 -133
  111. package/test/route-shorthand.test.js +9 -27
  112. package/test/route.1.test.js +74 -131
  113. package/test/route.8.test.js +9 -17
  114. package/test/router-options.test.js +450 -0
  115. package/test/schema-serialization.test.js +177 -154
  116. package/test/schema-special-usage.test.js +165 -132
  117. package/test/schema-validation.test.js +254 -218
  118. package/test/server.test.js +143 -5
  119. package/test/set-error-handler.test.js +58 -1
  120. package/test/skip-reply-send.test.js +64 -69
  121. package/test/stream.1.test.js +33 -50
  122. package/test/stream.4.test.js +18 -28
  123. package/test/stream.5.test.js +11 -19
  124. package/test/trust-proxy.test.js +32 -58
  125. package/test/types/errors.test-d.ts +13 -1
  126. package/test/types/fastify.test-d.ts +3 -0
  127. package/test/types/request.test-d.ts +1 -0
  128. package/test/types/type-provider.test-d.ts +55 -0
  129. package/test/url-rewriting.test.js +45 -62
  130. package/test/use-semicolon-delimiter.test.js +117 -59
  131. package/test/versioned-routes.test.js +39 -56
  132. package/types/errors.d.ts +11 -1
  133. package/types/hooks.d.ts +1 -1
  134. package/types/instance.d.ts +1 -1
  135. package/types/reply.d.ts +2 -2
  136. package/types/request.d.ts +1 -0
  137. package/.taprc +0 -7
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const { describe, test } = require('node:test')
4
- const sget = require('simple-get').concat
4
+ const { Client } = require('undici')
5
5
  const Fastify = require('..')
6
6
 
7
7
  describe('route-shorthand', () => {
@@ -19,19 +19,10 @@ describe('route-shorthand', () => {
19
19
  await fastify.listen({ port: 0 })
20
20
  t.after(() => fastify.close())
21
21
 
22
- await new Promise((resolve, reject) => {
23
- sget({
24
- method,
25
- url: `http://localhost:${fastify.server.address().port}`
26
- }, (err, response, body) => {
27
- if (err) {
28
- t.assert.ifError(err)
29
- return reject(err)
30
- }
31
- t.assert.strictEqual(response.statusCode, 200)
32
- resolve()
33
- })
34
- })
22
+ const instance = new Client(`http://localhost:${fastify.server.address().port}`)
23
+
24
+ const response = await instance.request({ path: '/', method })
25
+ t.assert.strictEqual(response.statusCode, 200)
35
26
  })
36
27
  }
37
28
 
@@ -48,19 +39,10 @@ describe('route-shorthand', () => {
48
39
 
49
40
  for (const method of supportedMethods) {
50
41
  currentMethod = method
51
- await new Promise((resolve, reject) => {
52
- sget({
53
- method,
54
- url: `http://localhost:${fastify.server.address().port}`
55
- }, (err, response, body) => {
56
- if (err) {
57
- t.assert.ifError(err)
58
- return reject(err)
59
- }
60
- t.assert.strictEqual(response.statusCode, 200)
61
- resolve()
62
- })
63
- })
42
+ const instance = new Client(`http://localhost:${fastify.server.address().port}`)
43
+
44
+ const response = await instance.request({ path: '/', method })
45
+ t.assert.strictEqual(response.statusCode, 200)
64
46
  }
65
47
  })
66
48
  })
@@ -1,7 +1,6 @@
1
1
  'use strict'
2
2
 
3
3
  const { test } = require('node:test')
4
- const sget = require('simple-get').concat
5
4
  const Fastify = require('..')
6
5
  const {
7
6
  FST_ERR_INSTANCE_ALREADY_LISTENING,
@@ -12,7 +11,7 @@ const { getServerUrl } = require('./helper')
12
11
  test('route', async t => {
13
12
  t.plan(10)
14
13
 
15
- await t.test('route - get', (t, done) => {
14
+ await t.test('route - get', async (t) => {
16
15
  t.plan(4)
17
16
 
18
17
  const fastify = Fastify()
@@ -38,22 +37,16 @@ test('route', async t => {
38
37
  })
39
38
  )
40
39
 
41
- fastify.listen({ port: 0 }, function (err) {
42
- if (err) t.assert.ifError(err)
43
- t.after(() => { fastify.close() })
44
- sget({
45
- method: 'GET',
46
- url: getServerUrl(fastify) + '/'
47
- }, (err, response, body) => {
48
- t.assert.ifError(err)
49
- t.assert.strictEqual(response.statusCode, 200)
50
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
51
- done()
52
- })
53
- })
40
+ await fastify.listen({ port: 0 })
41
+ t.after(() => { fastify.close() })
42
+
43
+ const response = await fetch(getServerUrl(fastify) + '/')
44
+ t.assert.ok(response.ok)
45
+ t.assert.strictEqual(response.status, 200)
46
+ t.assert.deepStrictEqual(await response.json(), { hello: 'world' })
54
47
  })
55
48
 
56
- await t.test('missing schema - route', (t, done) => {
49
+ await t.test('missing schema - route', async (t) => {
57
50
  t.plan(4)
58
51
 
59
52
  const fastify = Fastify()
@@ -67,19 +60,13 @@ test('route', async t => {
67
60
  })
68
61
  )
69
62
 
70
- fastify.listen({ port: 0 }, function (err) {
71
- if (err) t.assert.ifError(err)
72
- t.after(() => { fastify.close() })
73
- sget({
74
- method: 'GET',
75
- url: getServerUrl(fastify) + '/missing'
76
- }, (err, response, body) => {
77
- t.assert.ifError(err)
78
- t.assert.strictEqual(response.statusCode, 200)
79
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
80
- done()
81
- })
82
- })
63
+ await fastify.listen({ port: 0 })
64
+ t.after(() => { fastify.close() })
65
+
66
+ const response = await fetch(getServerUrl(fastify) + '/missing')
67
+ t.assert.ok(response.ok)
68
+ t.assert.strictEqual(response.status, 200)
69
+ t.assert.deepStrictEqual(await response.json(), { hello: 'world' })
83
70
  })
84
71
 
85
72
  await t.test('invalid handler attribute - route', t => {
@@ -89,7 +76,7 @@ test('route', async t => {
89
76
  t.assert.throws(() => fastify.get('/', { handler: 'not a function' }, () => { }))
90
77
  })
91
78
 
92
- await t.test('Add Multiple methods per route all uppercase', (t, done) => {
79
+ await t.test('Add Multiple methods per route all uppercase', async (t) => {
93
80
  t.plan(7)
94
81
 
95
82
  const fastify = Fastify()
@@ -102,31 +89,21 @@ test('route', async t => {
102
89
  }
103
90
  }))
104
91
 
105
- fastify.listen({ port: 0 }, function (err) {
106
- if (err) t.assert.ifError(err)
107
- t.after(() => { fastify.close() })
108
- sget({
109
- method: 'GET',
110
- url: getServerUrl(fastify) + '/multiple'
111
- }, (err, response, body) => {
112
- t.assert.ifError(err)
113
- t.assert.strictEqual(response.statusCode, 200)
114
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
115
- })
92
+ await fastify.listen({ port: 0 })
93
+ t.after(() => { fastify.close() })
116
94
 
117
- sget({
118
- method: 'DELETE',
119
- url: getServerUrl(fastify) + '/multiple'
120
- }, (err, response, body) => {
121
- t.assert.ifError(err)
122
- t.assert.strictEqual(response.statusCode, 200)
123
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
124
- done()
125
- })
126
- })
95
+ const getResponse = await fetch(getServerUrl(fastify) + '/multiple')
96
+ t.assert.ok(getResponse.ok)
97
+ t.assert.strictEqual(getResponse.status, 200)
98
+ t.assert.deepStrictEqual(await getResponse.json(), { hello: 'world' })
99
+
100
+ const deleteResponse = await fetch(getServerUrl(fastify) + '/multiple', { method: 'DELETE' })
101
+ t.assert.ok(deleteResponse.ok)
102
+ t.assert.strictEqual(deleteResponse.status, 200)
103
+ t.assert.deepStrictEqual(await deleteResponse.json(), { hello: 'world' })
127
104
  })
128
105
 
129
- await t.test('Add Multiple methods per route all lowercase', (t, done) => {
106
+ await t.test('Add Multiple methods per route all lowercase', async (t) => {
130
107
  t.plan(7)
131
108
 
132
109
  const fastify = Fastify()
@@ -139,31 +116,21 @@ test('route', async t => {
139
116
  }
140
117
  }))
141
118
 
142
- fastify.listen({ port: 0 }, function (err) {
143
- if (err) t.assert.ifError(err)
144
- t.after(() => { fastify.close() })
145
- sget({
146
- method: 'GET',
147
- url: getServerUrl(fastify) + '/multiple'
148
- }, (err, response, body) => {
149
- t.assert.ifError(err)
150
- t.assert.strictEqual(response.statusCode, 200)
151
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
152
- })
119
+ await fastify.listen({ port: 0 })
120
+ t.after(() => { fastify.close() })
153
121
 
154
- sget({
155
- method: 'DELETE',
156
- url: getServerUrl(fastify) + '/multiple'
157
- }, (err, response, body) => {
158
- t.assert.ifError(err)
159
- t.assert.strictEqual(response.statusCode, 200)
160
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
161
- done()
162
- })
163
- })
122
+ const getResponse = await fetch(getServerUrl(fastify) + '/multiple')
123
+ t.assert.ok(getResponse.ok)
124
+ t.assert.strictEqual(getResponse.status, 200)
125
+ t.assert.deepStrictEqual(await getResponse.json(), { hello: 'world' })
126
+
127
+ const deleteResponse = await fetch(getServerUrl(fastify) + '/multiple', { method: 'DELETE' })
128
+ t.assert.ok(deleteResponse.ok)
129
+ t.assert.strictEqual(deleteResponse.status, 200)
130
+ t.assert.deepStrictEqual(await deleteResponse.json(), { hello: 'world' })
164
131
  })
165
132
 
166
- await t.test('Add Multiple methods per route mixed uppercase and lowercase', (t, done) => {
133
+ await t.test('Add Multiple methods per route mixed uppercase and lowercase', async (t) => {
167
134
  t.plan(7)
168
135
 
169
136
  const fastify = Fastify()
@@ -176,28 +143,18 @@ test('route', async t => {
176
143
  }
177
144
  }))
178
145
 
179
- fastify.listen({ port: 0 }, function (err) {
180
- if (err) t.assert.ifError(err)
181
- t.after(() => { fastify.close() })
182
- sget({
183
- method: 'GET',
184
- url: getServerUrl(fastify) + '/multiple'
185
- }, (err, response, body) => {
186
- t.assert.ifError(err)
187
- t.assert.strictEqual(response.statusCode, 200)
188
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
189
- })
146
+ await fastify.listen({ port: 0 })
147
+ t.after(() => { fastify.close() })
190
148
 
191
- sget({
192
- method: 'DELETE',
193
- url: getServerUrl(fastify) + '/multiple'
194
- }, (err, response, body) => {
195
- t.assert.ifError(err)
196
- t.assert.strictEqual(response.statusCode, 200)
197
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
198
- done()
199
- })
200
- })
149
+ const getResponse = await fetch(getServerUrl(fastify) + '/multiple')
150
+ t.assert.ok(getResponse.ok)
151
+ t.assert.strictEqual(getResponse.status, 200)
152
+ t.assert.deepStrictEqual(await getResponse.json(), { hello: 'world' })
153
+
154
+ const deleteResponse = await fetch(getServerUrl(fastify) + '/multiple', { method: 'DELETE' })
155
+ t.assert.ok(deleteResponse.ok)
156
+ t.assert.strictEqual(deleteResponse.status, 200)
157
+ t.assert.deepStrictEqual(await deleteResponse.json(), { hello: 'world' })
201
158
  })
202
159
 
203
160
  t.test('Add invalid Multiple methods per route', t => {
@@ -228,7 +185,7 @@ test('route', async t => {
228
185
  }), new FST_ERR_ROUTE_METHOD_INVALID())
229
186
  })
230
187
 
231
- await t.test('Add additional multiple methods to existing route', (t, done) => {
188
+ await t.test('Add additional multiple methods to existing route', async (t) => {
232
189
  t.plan(7)
233
190
 
234
191
  const fastify = Fastify()
@@ -245,49 +202,35 @@ test('route', async t => {
245
202
  })
246
203
  })
247
204
 
248
- fastify.listen({ port: 0 }, function (err) {
249
- if (err) t.assert.ifError(err)
250
- t.after(() => { fastify.close() })
251
- sget({
252
- method: 'PUT',
253
- url: getServerUrl(fastify) + '/add-multiple'
254
- }, (err, response, body) => {
255
- t.assert.ifError(err)
256
- t.assert.strictEqual(response.statusCode, 200)
257
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
258
- })
205
+ await fastify.listen({ port: 0 })
206
+ t.after(() => { fastify.close() })
259
207
 
260
- sget({
261
- method: 'DELETE',
262
- url: getServerUrl(fastify) + '/add-multiple'
263
- }, (err, response, body) => {
264
- t.assert.ifError(err)
265
- t.assert.strictEqual(response.statusCode, 200)
266
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
267
- done()
268
- })
269
- })
208
+ const putResponse = await fetch(getServerUrl(fastify) + '/add-multiple', { method: 'PUT' })
209
+ t.assert.ok(putResponse.ok)
210
+ t.assert.strictEqual(putResponse.status, 200)
211
+ t.assert.deepStrictEqual(await putResponse.json(), { hello: 'world' })
212
+
213
+ const deleteResponse = await fetch(getServerUrl(fastify) + '/add-multiple', { method: 'DELETE' })
214
+ t.assert.ok(deleteResponse.ok)
215
+ t.assert.strictEqual(deleteResponse.status, 200)
216
+ t.assert.deepStrictEqual(await deleteResponse.json(), { hello: 'world' })
270
217
  })
271
218
 
272
- await t.test('cannot add another route after binding', (t, done) => {
219
+ await t.test('cannot add another route after binding', async (t) => {
273
220
  t.plan(1)
274
221
 
275
222
  const fastify = Fastify()
276
223
 
277
- fastify.listen({ port: 0 }, function (err) {
278
- if (err) t.assert.ifError(err)
279
- t.after(() => { fastify.close() })
280
-
281
- t.assert.throws(() => fastify.route({
282
- method: 'GET',
283
- url: '/another-get-route',
284
- handler: function (req, reply) {
285
- reply.send({ hello: 'world' })
286
- }
287
- }), new FST_ERR_INSTANCE_ALREADY_LISTENING('Cannot add route!'))
224
+ await fastify.listen({ port: 0 })
225
+ t.after(() => { fastify.close() })
288
226
 
289
- done()
290
- })
227
+ t.assert.throws(() => fastify.route({
228
+ method: 'GET',
229
+ url: '/another-get-route',
230
+ handler: function (req, reply) {
231
+ reply.send({ hello: 'world' })
232
+ }
233
+ }), new FST_ERR_INSTANCE_ALREADY_LISTENING('Cannot add route!'))
291
234
  })
292
235
  })
293
236
 
@@ -1,12 +1,10 @@
1
1
  'use strict'
2
2
 
3
3
  const { test } = require('node:test')
4
- const sget = require('simple-get').concat
5
4
  const Fastify = require('..')
6
5
  const {
7
6
  FST_ERR_INVALID_URL
8
7
  } = require('../lib/errors')
9
- const { getServerUrl } = require('./helper')
10
8
 
11
9
  test('Request and Reply share the route options', async t => {
12
10
  t.plan(3)
@@ -64,8 +62,8 @@ test('Will not try to re-createprefixed HEAD route if it already exists and expo
64
62
  t.assert.ok(true)
65
63
  })
66
64
 
67
- test('route with non-english characters', (t, done) => {
68
- t.plan(4)
65
+ test('route with non-english characters', async (t) => {
66
+ t.plan(3)
69
67
 
70
68
  const fastify = Fastify()
71
69
 
@@ -73,20 +71,14 @@ test('route with non-english characters', (t, done) => {
73
71
  reply.send('here /föö')
74
72
  })
75
73
 
76
- fastify.listen({ port: 0 }, err => {
77
- t.assert.ifError(err)
78
- t.after(() => fastify.close())
74
+ const fastifyServer = await fastify.listen({ port: 0 })
75
+ t.after(() => fastify.close())
79
76
 
80
- sget({
81
- method: 'GET',
82
- url: getServerUrl(fastify) + encodeURI('/föö')
83
- }, (err, response, body) => {
84
- t.assert.ifError(err)
85
- t.assert.strictEqual(response.statusCode, 200)
86
- t.assert.strictEqual(body.toString(), 'here /föö')
87
- done()
88
- })
89
- })
77
+ const response = await fetch(fastifyServer + encodeURI('/föö'))
78
+ t.assert.ok(response.ok)
79
+ t.assert.strictEqual(response.status, 200)
80
+ const body = await response.text()
81
+ t.assert.strictEqual(body, 'here /föö')
90
82
  })
91
83
 
92
84
  test('invalid url attribute - non string URL', t => {