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
@@ -1,53 +1,55 @@
1
1
  'use strict'
2
2
 
3
- const { test, skip } = require('tap')
3
+ const { describe, test } = require('node:test')
4
4
  const Fastify = require('..')
5
5
  const { connect } = require('node:net')
6
6
  const { once } = require('node:events')
7
7
  const dns = require('node:dns').promises
8
8
 
9
- async function setup () {
9
+ describe('upgrade to both servers', async t => {
10
10
  const localAddresses = await dns.lookup('localhost', { all: true })
11
11
  if (localAddresses.length === 1) {
12
- skip('requires both IPv4 and IPv6')
12
+ t.skip('requires both IPv4 and IPv6')
13
13
  return
14
14
  }
15
15
 
16
- test('upgrade to both servers', async t => {
16
+ await test('upgrade IPv4 and IPv6', async t => {
17
17
  t.plan(2)
18
- const app = Fastify()
19
- app.server.on('upgrade', (req, socket, head) => {
20
- t.pass(`upgrade event ${JSON.stringify(socket.address())}`)
18
+
19
+ const fastify = Fastify()
20
+ fastify.server.on('upgrade', (req, socket, head) => {
21
+ t.assert.ok(`upgrade event ${JSON.stringify(socket.address())}`)
21
22
  socket.end()
22
23
  })
23
- app.get('/', (req, res) => {
24
+
25
+ fastify.get('/', (req, res) => {
26
+ res.send()
24
27
  })
25
- await app.listen()
26
- t.teardown(app.close.bind(app))
28
+
29
+ await fastify.listen()
30
+ t.after(() => fastify.close())
27
31
 
28
32
  {
29
- const client = connect(app.server.address().port, '127.0.0.1')
30
- client.write('GET / HTTP/1.1\r\n')
31
- client.write('Upgrade: websocket\r\n')
32
- client.write('Connection: Upgrade\r\n')
33
- client.write('Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n')
34
- client.write('Sec-WebSocket-Protocol: com.xxx.service.v1\r\n')
35
- client.write('Sec-WebSocket-Version: 13\r\n\r\n')
36
- client.write('\r\n\r\n')
37
- await once(client, 'close')
33
+ const clientIPv4 = connect(fastify.server.address().port, '127.0.0.1')
34
+ clientIPv4.write('GET / HTTP/1.1\r\n')
35
+ clientIPv4.write('Upgrade: websocket\r\n')
36
+ clientIPv4.write('Connection: Upgrade\r\n')
37
+ clientIPv4.write('Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n')
38
+ clientIPv4.write('Sec-WebSocket-Protocol: com.xxx.service.v1\r\n')
39
+ clientIPv4.write('Sec-WebSocket-Version: 13\r\n\r\n')
40
+ clientIPv4.write('\r\n\r\n')
41
+ await once(clientIPv4, 'close')
38
42
  }
39
43
 
40
44
  {
41
- const client = connect(app.server.address().port, '::1')
42
- client.write('GET / HTTP/1.1\r\n')
43
- client.write('Upgrade: websocket\r\n')
44
- client.write('Connection: Upgrade\r\n')
45
- client.write('Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n')
46
- client.write('Sec-WebSocket-Protocol: com.xxx.service.v1\r\n')
47
- client.write('Sec-WebSocket-Version: 13\r\n\r\n')
48
- await once(client, 'close')
45
+ const clientIPv6 = connect(fastify.server.address().port, '::1')
46
+ clientIPv6.write('GET / HTTP/1.1\r\n')
47
+ clientIPv6.write('Upgrade: websocket\r\n')
48
+ clientIPv6.write('Connection: Upgrade\r\n')
49
+ clientIPv6.write('Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n')
50
+ clientIPv6.write('Sec-WebSocket-Protocol: com.xxx.service.v1\r\n')
51
+ clientIPv6.write('Sec-WebSocket-Version: 13\r\n\r\n')
52
+ await once(clientIPv6, 'close')
49
53
  }
50
54
  })
51
- }
52
-
53
- setup()
55
+ })
@@ -227,6 +227,50 @@ test('Error when Response.bodyUsed', async (t) => {
227
227
  t.assert.strictEqual(body.code, 'FST_ERR_REP_RESPONSE_BODY_CONSUMED')
228
228
  })
229
229
 
230
+ test('Error when Response.body.locked', async (t) => {
231
+ t.plan(3)
232
+
233
+ const fastify = Fastify()
234
+
235
+ fastify.get('/', async function (request, reply) {
236
+ const stream = Readable.toWeb(fs.createReadStream(__filename))
237
+ const response = new Response(stream, {
238
+ status: 200,
239
+ headers: {
240
+ hello: 'world'
241
+ }
242
+ })
243
+ stream.getReader()
244
+ t.assert.strictEqual(stream.locked, true)
245
+ return reply.send(response)
246
+ })
247
+
248
+ const response = await fastify.inject({ method: 'GET', path: '/' })
249
+
250
+ t.assert.strictEqual(response.statusCode, 500)
251
+ const body = response.json()
252
+ t.assert.strictEqual(body.code, 'FST_ERR_REP_READABLE_STREAM_LOCKED')
253
+ })
254
+
255
+ test('Error when ReadableStream.locked', async (t) => {
256
+ t.plan(3)
257
+
258
+ const fastify = Fastify()
259
+
260
+ fastify.get('/', async function (request, reply) {
261
+ const stream = Readable.toWeb(fs.createReadStream(__filename))
262
+ stream.getReader()
263
+ t.assert.strictEqual(stream.locked, true)
264
+ return reply.send(stream)
265
+ })
266
+
267
+ const response = await fastify.inject({ method: 'GET', path: '/' })
268
+
269
+ t.assert.strictEqual(response.statusCode, 500)
270
+ const body = response.json()
271
+ t.assert.strictEqual(body.code, 'FST_ERR_REP_READABLE_STREAM_LOCKED')
272
+ })
273
+
230
274
  test('allow to pipe with fetch', async (t) => {
231
275
  t.plan(2)
232
276
  const abortController = new AbortController()
@@ -548,6 +548,10 @@ export interface FastifyInstance<
548
548
  * Remove all content type parsers, including the default ones
549
549
  */
550
550
  removeAllContentTypeParsers: removeAllContentTypeParsers
551
+ /**
552
+ * Returns an array of strings containing the list of supported HTTP methods
553
+ */
554
+ supportedMethods: string[]
551
555
  /**
552
556
  * Add a non-standard HTTP method
553
557
  *
@@ -1,68 +0,0 @@
1
- function colorize (type, text) {
2
- if (type === 'pass') {
3
- const blackText = `\x1b[30m${text}`
4
- const boldblackText = `\x1b[1m${blackText}`
5
- // Green background with black text
6
- return `\x1b[42m${boldblackText}\x1b[0m`
7
- }
8
-
9
- if (type === 'fail') {
10
- const whiteText = `\x1b[37m${text}`
11
- const boldWhiteText = `\x1b[1m${whiteText}`
12
- // Red background with white text
13
- return `\x1b[41m${boldWhiteText}\x1b[0m`
14
- }
15
-
16
- return text
17
- }
18
-
19
- function formatDiagnosticStr (str) {
20
- return str.replace(/^(\w+)(\s*\d*)/i, (_, firstWord, rest) => {
21
- return firstWord.charAt(0).toUpperCase() + firstWord.slice(1).toLowerCase() + ':' + rest
22
- })
23
- }
24
-
25
- async function * reporter (source) {
26
- const failed = new Set()
27
- const diagnostics = new Set()
28
-
29
- for await (const event of source) {
30
- switch (event.type) {
31
- case 'test:pass': {
32
- yield `${colorize('pass', 'PASSED')}: ${event.data.file || event.data.name}\n`
33
- break
34
- }
35
-
36
- case 'test:fail': {
37
- failed.add(event.data.name || event.data.file)
38
- yield `${colorize('fail', 'FAILED')}: ${event.data.file || event.data.name}\n`
39
- break
40
- }
41
-
42
- case 'test:diagnostic': {
43
- diagnostics.add(`${formatDiagnosticStr(event.data.message)}\n`)
44
- break
45
- }
46
-
47
- default: {
48
- yield ''
49
- }
50
- }
51
- }
52
-
53
- if (failed.size > 0) {
54
- yield `\n\n${colorize('fail', 'Failed tests:')}\n`
55
- for (const file of failed) {
56
- yield `${file}\n`
57
- }
58
- }
59
-
60
- yield '\n'
61
-
62
- for (const diagnostic of diagnostics) {
63
- yield `${diagnostic}`
64
- }
65
- yield '\n'
66
- }
67
-
68
- export default reporter