fastify 5.4.0 → 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 (97) hide show
  1. package/.vscode/settings.json +22 -0
  2. package/LICENSE +1 -1
  3. package/SECURITY.md +158 -2
  4. package/build/build-validation.js +19 -1
  5. package/docs/Guides/Delay-Accepting-Requests.md +8 -5
  6. package/docs/Guides/Ecosystem.md +11 -0
  7. package/docs/Guides/Migration-Guide-V5.md +6 -10
  8. package/docs/Guides/Recommendations.md +1 -1
  9. package/docs/Reference/Errors.md +3 -1
  10. package/docs/Reference/Hooks.md +2 -6
  11. package/docs/Reference/Lifecycle.md +2 -2
  12. package/docs/Reference/Request.md +1 -1
  13. package/docs/Reference/Routes.md +4 -3
  14. package/docs/Reference/Server.md +306 -179
  15. package/docs/Reference/TypeScript.md +1 -3
  16. package/docs/Reference/Validation-and-Serialization.md +55 -3
  17. package/docs/Reference/Warnings.md +2 -1
  18. package/fastify.d.ts +2 -2
  19. package/fastify.js +34 -33
  20. package/lib/configValidator.js +196 -28
  21. package/lib/contentTypeParser.js +41 -48
  22. package/lib/error-handler.js +3 -3
  23. package/lib/errors.js +5 -0
  24. package/lib/handleRequest.js +13 -17
  25. package/lib/promise.js +23 -0
  26. package/lib/reply.js +17 -19
  27. package/lib/route.js +37 -3
  28. package/lib/server.js +36 -35
  29. package/lib/warnings.js +11 -1
  30. package/package.json +7 -7
  31. package/test/async-await.test.js +81 -134
  32. package/test/async_hooks.test.js +18 -37
  33. package/test/body-limit.test.js +51 -0
  34. package/test/buffer.test.js +22 -0
  35. package/test/case-insensitive.test.js +44 -65
  36. package/test/check.test.js +17 -21
  37. package/test/close-pipelining.test.js +24 -15
  38. package/test/constrained-routes.test.js +231 -0
  39. package/test/custom-http-server.test.js +7 -15
  40. package/test/custom-parser.0.test.js +267 -348
  41. package/test/custom-parser.1.test.js +141 -191
  42. package/test/custom-parser.2.test.js +34 -44
  43. package/test/custom-parser.3.test.js +56 -104
  44. package/test/custom-parser.4.test.js +106 -144
  45. package/test/custom-parser.5.test.js +56 -75
  46. package/test/custom-querystring-parser.test.js +51 -77
  47. package/test/decorator.test.js +76 -259
  48. package/test/delete.test.js +101 -110
  49. package/test/diagnostics-channel/404.test.js +7 -15
  50. package/test/diagnostics-channel/async-request.test.js +8 -16
  51. package/test/diagnostics-channel/error-request.test.js +7 -15
  52. package/test/diagnostics-channel/sync-request-reply.test.js +9 -16
  53. package/test/diagnostics-channel/sync-request.test.js +9 -16
  54. package/test/fastify-instance.test.js +1 -1
  55. package/test/header-overflow.test.js +18 -29
  56. package/test/helper.js +138 -134
  57. package/test/hooks-async.test.js +26 -32
  58. package/test/hooks.test.js +261 -447
  59. package/test/http-methods/copy.test.js +14 -19
  60. package/test/http-methods/get.test.js +131 -143
  61. package/test/http-methods/head.test.js +53 -84
  62. package/test/http-methods/mkcalendar.test.js +45 -72
  63. package/test/http-methods/move.test.js +6 -10
  64. package/test/http-methods/propfind.test.js +34 -44
  65. package/test/http-methods/unlock.test.js +5 -9
  66. package/test/http2/secure-with-fallback.test.js +3 -1
  67. package/test/https/custom-https-server.test.js +9 -13
  68. package/test/input-validation.js +139 -150
  69. package/test/internals/errors.test.js +50 -1
  70. package/test/internals/handle-request.test.js +29 -5
  71. package/test/internals/promise.test.js +63 -0
  72. package/test/internals/reply.test.js +277 -496
  73. package/test/plugin.1.test.js +40 -68
  74. package/test/plugin.2.test.js +40 -70
  75. package/test/plugin.3.test.js +25 -68
  76. package/test/promises.test.js +42 -63
  77. package/test/register.test.js +8 -18
  78. package/test/request-error.test.js +57 -100
  79. package/test/request-id.test.js +30 -49
  80. package/test/route-hooks.test.js +12 -16
  81. package/test/route-shorthand.test.js +9 -27
  82. package/test/route.1.test.js +74 -131
  83. package/test/route.8.test.js +9 -17
  84. package/test/router-options.test.js +450 -0
  85. package/test/schema-validation.test.js +30 -31
  86. package/test/server.test.js +143 -5
  87. package/test/stream.1.test.js +33 -50
  88. package/test/stream.4.test.js +18 -28
  89. package/test/stream.5.test.js +11 -19
  90. package/test/types/errors.test-d.ts +13 -1
  91. package/test/types/type-provider.test-d.ts +55 -0
  92. package/test/use-semicolon-delimiter.test.js +117 -59
  93. package/test/versioned-routes.test.js +39 -56
  94. package/types/errors.d.ts +11 -1
  95. package/types/hooks.d.ts +1 -1
  96. package/types/instance.d.ts +1 -1
  97. package/types/reply.d.ts +2 -2
@@ -3,7 +3,6 @@
3
3
  const { test, describe } = require('node:test')
4
4
  const Fastify = require('..')
5
5
  const fp = require('fastify-plugin')
6
- const sget = require('simple-get').concat
7
6
  const symbols = require('../lib/symbols.js')
8
7
 
9
8
  test('server methods should exist', t => {
@@ -118,8 +117,24 @@ test('should pass error for missing request decorator', (t, done) => {
118
117
  })
119
118
  })
120
119
 
121
- test('decorateReply inside register', (t, done) => {
122
- t.plan(11)
120
+ const runTests = async (t, fastifyServer) => {
121
+ const endpoints = [
122
+ { path: '/yes', expectedBody: { hello: 'world' } },
123
+ { path: '/no', expectedBody: { hello: 'world' } }
124
+ ]
125
+
126
+ for (const { path, expectedBody } of endpoints) {
127
+ const result = await fetch(`${fastifyServer}${path}`)
128
+ t.assert.ok(result.ok)
129
+ t.assert.strictEqual(result.status, 200)
130
+ const body = await result.text()
131
+ t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
132
+ t.assert.deepStrictEqual(JSON.parse(body), expectedBody)
133
+ }
134
+ }
135
+
136
+ test('decorateReply inside register', async (t) => {
137
+ t.plan(10)
123
138
  const fastify = Fastify()
124
139
 
125
140
  fastify.register((instance, opts, done) => {
@@ -138,44 +153,14 @@ test('decorateReply inside register', (t, done) => {
138
153
  reply.send({ hello: 'world' })
139
154
  })
140
155
 
141
- fastify.listen({ port: 0 }, err => {
142
- t.assert.ifError(err)
143
- t.after(() => fastify.close())
144
-
145
- let pending = 2
146
-
147
- function completed () {
148
- if (--pending === 0) {
149
- done()
150
- }
151
- }
152
-
153
- sget({
154
- method: 'GET',
155
- url: 'http://localhost:' + fastify.server.address().port + '/yes'
156
- }, (err, response, body) => {
157
- t.assert.ifError(err)
158
- t.assert.strictEqual(response.statusCode, 200)
159
- t.assert.strictEqual(response.headers['content-length'], '' + body.length)
160
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
161
- completed()
162
- })
156
+ const fastifyServer = await fastify.listen({ port: 0 })
157
+ t.after(() => fastify.close())
163
158
 
164
- sget({
165
- method: 'GET',
166
- url: 'http://localhost:' + fastify.server.address().port + '/no'
167
- }, (err, response, body) => {
168
- t.assert.ifError(err)
169
- t.assert.strictEqual(response.statusCode, 200)
170
- t.assert.strictEqual(response.headers['content-length'], '' + body.length)
171
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
172
- completed()
173
- })
174
- })
159
+ await runTests(t, fastifyServer)
175
160
  })
176
161
 
177
- test('decorateReply as plugin (inside .after)', (t, done) => {
178
- t.plan(11)
162
+ test('decorateReply as plugin (inside .after)', async t => {
163
+ t.plan(10)
179
164
  const fastify = Fastify()
180
165
 
181
166
  fastify.register((instance, opts, done) => {
@@ -196,44 +181,14 @@ test('decorateReply as plugin (inside .after)', (t, done) => {
196
181
  reply.send({ hello: 'world' })
197
182
  })
198
183
 
199
- fastify.listen({ port: 0 }, err => {
200
- t.assert.ifError(err)
201
- t.after(() => fastify.close())
202
-
203
- let pending = 2
204
-
205
- function completed () {
206
- if (--pending === 0) {
207
- done()
208
- }
209
- }
210
-
211
- sget({
212
- method: 'GET',
213
- url: 'http://localhost:' + fastify.server.address().port + '/yes'
214
- }, (err, response, body) => {
215
- t.assert.ifError(err)
216
- t.assert.strictEqual(response.statusCode, 200)
217
- t.assert.strictEqual(response.headers['content-length'], '' + body.length)
218
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
219
- completed()
220
- })
184
+ const fastifyServer = await fastify.listen({ port: 0 })
185
+ t.after(() => fastify.close())
221
186
 
222
- sget({
223
- method: 'GET',
224
- url: 'http://localhost:' + fastify.server.address().port + '/no'
225
- }, (err, response, body) => {
226
- t.assert.ifError(err)
227
- t.assert.strictEqual(response.statusCode, 200)
228
- t.assert.strictEqual(response.headers['content-length'], '' + body.length)
229
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
230
- completed()
231
- })
232
- })
187
+ await runTests(t, fastifyServer)
233
188
  })
234
189
 
235
- test('decorateReply as plugin (outside .after)', (t, done) => {
236
- t.plan(11)
190
+ test('decorateReply as plugin (outside .after)', async t => {
191
+ t.plan(10)
237
192
  const fastify = Fastify()
238
193
 
239
194
  fastify.register((instance, opts, done) => {
@@ -254,44 +209,14 @@ test('decorateReply as plugin (outside .after)', (t, done) => {
254
209
  reply.send({ hello: 'world' })
255
210
  })
256
211
 
257
- fastify.listen({ port: 0 }, err => {
258
- t.assert.ifError(err)
259
- t.after(() => fastify.close())
260
-
261
- let pending = 2
262
-
263
- function completed () {
264
- if (--pending === 0) {
265
- done()
266
- }
267
- }
268
-
269
- sget({
270
- method: 'GET',
271
- url: 'http://localhost:' + fastify.server.address().port + '/yes'
272
- }, (err, response, body) => {
273
- t.assert.ifError(err)
274
- t.assert.strictEqual(response.statusCode, 200)
275
- t.assert.strictEqual(response.headers['content-length'], '' + body.length)
276
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
277
- completed()
278
- })
212
+ const fastifyServer = await fastify.listen({ port: 0 })
213
+ t.after(() => fastify.close())
279
214
 
280
- sget({
281
- method: 'GET',
282
- url: 'http://localhost:' + fastify.server.address().port + '/no'
283
- }, (err, response, body) => {
284
- t.assert.ifError(err)
285
- t.assert.strictEqual(response.statusCode, 200)
286
- t.assert.strictEqual(response.headers['content-length'], '' + body.length)
287
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
288
- completed()
289
- })
290
- })
215
+ await runTests(t, fastifyServer)
291
216
  })
292
217
 
293
- test('decorateRequest inside register', (t, done) => {
294
- t.plan(11)
218
+ test('decorateRequest inside register', async t => {
219
+ t.plan(10)
295
220
  const fastify = Fastify()
296
221
 
297
222
  fastify.register((instance, opts, done) => {
@@ -310,44 +235,14 @@ test('decorateRequest inside register', (t, done) => {
310
235
  reply.send({ hello: 'world' })
311
236
  })
312
237
 
313
- fastify.listen({ port: 0 }, err => {
314
- t.assert.ifError(err)
315
- t.after(() => fastify.close())
316
-
317
- let pending = 2
318
-
319
- function completed () {
320
- if (--pending === 0) {
321
- done()
322
- }
323
- }
324
-
325
- sget({
326
- method: 'GET',
327
- url: 'http://localhost:' + fastify.server.address().port + '/yes'
328
- }, (err, response, body) => {
329
- t.assert.ifError(err)
330
- t.assert.strictEqual(response.statusCode, 200)
331
- t.assert.strictEqual(response.headers['content-length'], '' + body.length)
332
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
333
- completed()
334
- })
238
+ const fastifyServer = await fastify.listen({ port: 0 })
239
+ t.after(() => fastify.close())
335
240
 
336
- sget({
337
- method: 'GET',
338
- url: 'http://localhost:' + fastify.server.address().port + '/no'
339
- }, (err, response, body) => {
340
- t.assert.ifError(err)
341
- t.assert.strictEqual(response.statusCode, 200)
342
- t.assert.strictEqual(response.headers['content-length'], '' + body.length)
343
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
344
- completed()
345
- })
346
- })
241
+ await runTests(t, fastifyServer)
347
242
  })
348
243
 
349
- test('decorateRequest as plugin (inside .after)', (t, done) => {
350
- t.plan(11)
244
+ test('decorateRequest as plugin (inside .after)', async t => {
245
+ t.plan(10)
351
246
  const fastify = Fastify()
352
247
 
353
248
  fastify.register((instance, opts, done) => {
@@ -368,44 +263,14 @@ test('decorateRequest as plugin (inside .after)', (t, done) => {
368
263
  reply.send({ hello: 'world' })
369
264
  })
370
265
 
371
- fastify.listen({ port: 0 }, err => {
372
- t.assert.ifError(err)
373
- t.after(() => fastify.close())
374
-
375
- let pending = 2
376
-
377
- function completed () {
378
- if (--pending === 0) {
379
- done()
380
- }
381
- }
382
-
383
- sget({
384
- method: 'GET',
385
- url: 'http://localhost:' + fastify.server.address().port + '/yes'
386
- }, (err, response, body) => {
387
- t.assert.ifError(err)
388
- t.assert.strictEqual(response.statusCode, 200)
389
- t.assert.strictEqual(response.headers['content-length'], '' + body.length)
390
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
391
- completed()
392
- })
266
+ const fastifyServer = await fastify.listen({ port: 0 })
267
+ t.after(() => fastify.close())
393
268
 
394
- sget({
395
- method: 'GET',
396
- url: 'http://localhost:' + fastify.server.address().port + '/no'
397
- }, (err, response, body) => {
398
- t.assert.ifError(err)
399
- t.assert.strictEqual(response.statusCode, 200)
400
- t.assert.strictEqual(response.headers['content-length'], '' + body.length)
401
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
402
- completed()
403
- })
404
- })
269
+ await runTests(t, fastifyServer)
405
270
  })
406
271
 
407
- test('decorateRequest as plugin (outside .after)', (t, done) => {
408
- t.plan(11)
272
+ test('decorateRequest as plugin (outside .after)', async (t) => {
273
+ t.plan(10)
409
274
  const fastify = Fastify()
410
275
 
411
276
  fastify.register((instance, opts, done) => {
@@ -426,40 +291,10 @@ test('decorateRequest as plugin (outside .after)', (t, done) => {
426
291
  reply.send({ hello: 'world' })
427
292
  })
428
293
 
429
- fastify.listen({ port: 0 }, err => {
430
- t.assert.ifError(err)
431
- t.after(() => fastify.close())
432
-
433
- let pending = 2
434
-
435
- function completed () {
436
- if (--pending === 0) {
437
- done()
438
- }
439
- }
440
-
441
- sget({
442
- method: 'GET',
443
- url: 'http://localhost:' + fastify.server.address().port + '/yes'
444
- }, (err, response, body) => {
445
- t.assert.ifError(err)
446
- t.assert.strictEqual(response.statusCode, 200)
447
- t.assert.strictEqual(response.headers['content-length'], '' + body.length)
448
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
449
- completed()
450
- })
294
+ const fastifyServer = await fastify.listen({ port: 0 })
295
+ t.after(() => fastify.close())
451
296
 
452
- sget({
453
- method: 'GET',
454
- url: 'http://localhost:' + fastify.server.address().port + '/no'
455
- }, (err, response, body) => {
456
- t.assert.ifError(err)
457
- t.assert.strictEqual(response.statusCode, 200)
458
- t.assert.strictEqual(response.headers['content-length'], '' + body.length)
459
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
460
- completed()
461
- })
462
- })
297
+ await runTests(t, fastifyServer)
463
298
  })
464
299
 
465
300
  test('decorators should be instance separated', (t, done) => {
@@ -1064,8 +899,8 @@ test('plugin required decorators', async t => {
1064
899
  await app.ready()
1065
900
  })
1066
901
 
1067
- test('decorateRequest/decorateReply empty string', (t, done) => {
1068
- t.plan(7)
902
+ test('decorateRequest/decorateReply empty string', async t => {
903
+ t.plan(6)
1069
904
  const fastify = Fastify()
1070
905
 
1071
906
  fastify.decorateRequest('test', '')
@@ -1077,25 +912,19 @@ test('decorateRequest/decorateReply empty string', (t, done) => {
1077
912
  })
1078
913
  t.after(() => fastify.close())
1079
914
 
1080
- fastify.listen({ port: 0 }, err => {
1081
- t.assert.ifError(err)
1082
- t.after(() => fastify.close())
915
+ const fastifyServer = await fastify.listen({ port: 0 })
916
+ t.after(() => fastify.close())
1083
917
 
1084
- sget({
1085
- method: 'GET',
1086
- url: 'http://localhost:' + fastify.server.address().port + '/yes'
1087
- }, (err, response, body) => {
1088
- t.assert.ifError(err)
1089
- t.assert.strictEqual(response.statusCode, 200)
1090
- t.assert.strictEqual(response.headers['content-length'], '' + body.length)
1091
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
1092
- done()
1093
- })
1094
- })
918
+ const result = await fetch(`${fastifyServer}/yes`)
919
+ t.assert.ok(result.ok)
920
+ t.assert.strictEqual(result.status, 200)
921
+ const body = await result.text()
922
+ t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
923
+ t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
1095
924
  })
1096
925
 
1097
- test('decorateRequest/decorateReply is undefined', (t, done) => {
1098
- t.plan(7)
926
+ test('decorateRequest/decorateReply is undefined', async t => {
927
+ t.plan(6)
1099
928
  const fastify = Fastify()
1100
929
 
1101
930
  fastify.decorateRequest('test', undefined)
@@ -1107,25 +936,19 @@ test('decorateRequest/decorateReply is undefined', (t, done) => {
1107
936
  })
1108
937
  t.after(() => fastify.close())
1109
938
 
1110
- fastify.listen({ port: 0 }, err => {
1111
- t.assert.ifError(err)
1112
- t.after(() => fastify.close())
939
+ const fastifyServer = await fastify.listen({ port: 0 })
940
+ t.after(() => fastify.close())
1113
941
 
1114
- sget({
1115
- method: 'GET',
1116
- url: 'http://localhost:' + fastify.server.address().port + '/yes'
1117
- }, (err, response, body) => {
1118
- t.assert.ifError(err)
1119
- t.assert.strictEqual(response.statusCode, 200)
1120
- t.assert.strictEqual(response.headers['content-length'], '' + body.length)
1121
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
1122
- done()
1123
- })
1124
- })
942
+ const result = await fetch(`${fastifyServer}/yes`)
943
+ t.assert.ok(result.ok)
944
+ t.assert.strictEqual(result.status, 200)
945
+ const body = await result.text()
946
+ t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
947
+ t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
1125
948
  })
1126
949
 
1127
- test('decorateRequest/decorateReply is not set to a value', (t, done) => {
1128
- t.plan(7)
950
+ test('decorateRequest/decorateReply is not set to a value', async t => {
951
+ t.plan(6)
1129
952
  const fastify = Fastify()
1130
953
 
1131
954
  fastify.decorateRequest('test')
@@ -1137,21 +960,15 @@ test('decorateRequest/decorateReply is not set to a value', (t, done) => {
1137
960
  })
1138
961
  t.after(() => fastify.close())
1139
962
 
1140
- fastify.listen({ port: 0 }, err => {
1141
- t.assert.ifError(err)
1142
- t.after(() => fastify.close())
963
+ const fastifyServer = await fastify.listen({ port: 0 })
964
+ t.after(() => fastify.close())
1143
965
 
1144
- sget({
1145
- method: 'GET',
1146
- url: 'http://localhost:' + fastify.server.address().port + '/yes'
1147
- }, (err, response, body) => {
1148
- t.assert.ifError(err)
1149
- t.assert.strictEqual(response.statusCode, 200)
1150
- t.assert.strictEqual(response.headers['content-length'], '' + body.length)
1151
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
1152
- done()
1153
- })
1154
- })
966
+ const result = await fetch(`${fastifyServer}/yes`)
967
+ t.assert.ok(result.ok)
968
+ t.assert.strictEqual(result.status, 200)
969
+ const body = await result.text()
970
+ t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
971
+ t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
1155
972
  })
1156
973
 
1157
974
  test('decorateRequest with dependencies', (t, done) => {