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,42 +1,35 @@
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('../fastify')
6
5
  const jsonParser = require('fast-json-body')
7
- const { getServerUrl, plainTextParser } = require('./helper')
6
+ const { plainTextParser } = require('./helper')
8
7
 
9
8
  process.removeAllListeners('warning')
10
9
 
11
- test('cannot remove all content type parsers after binding', (t, done) => {
12
- t.plan(2)
10
+ test('cannot remove all content type parsers after binding', async (t) => {
11
+ t.plan(1)
13
12
 
14
13
  const fastify = Fastify()
15
14
 
16
- fastify.listen({ port: 0 }, function (err) {
17
- t.assert.ifError(err)
18
- t.assert.throws(() => fastify.removeAllContentTypeParsers())
19
- fastify.close()
20
- done()
21
- })
15
+ t.after(() => fastify.close())
16
+
17
+ await fastify.listen({ port: 0 })
18
+ t.assert.throws(() => fastify.removeAllContentTypeParsers())
22
19
  })
23
20
 
24
- test('cannot remove content type parsers after binding', (t, done) => {
25
- t.plan(2)
21
+ test('cannot remove content type parsers after binding', async (t) => {
22
+ t.plan(1)
26
23
 
27
24
  const fastify = Fastify()
28
-
29
25
  t.after(() => fastify.close())
30
26
 
31
- fastify.listen({ port: 0 }, function (err) {
32
- t.assert.ifError(err)
33
- t.assert.throws(() => fastify.removeContentTypeParser('application/json'))
34
- done()
35
- })
27
+ await fastify.listen({ port: 0 })
28
+ t.assert.throws(() => fastify.removeContentTypeParser('application/json'))
36
29
  })
37
30
 
38
- test('should be able to override the default json parser after removeAllContentTypeParsers', (t, done) => {
39
- t.plan(5)
31
+ test('should be able to override the default json parser after removeAllContentTypeParsers', async (t) => {
32
+ t.plan(4)
40
33
 
41
34
  const fastify = Fastify()
42
35
 
@@ -53,28 +46,24 @@ test('should be able to override the default json parser after removeAllContentT
53
46
  })
54
47
  })
55
48
 
56
- fastify.listen({ port: 0 }, err => {
57
- t.assert.ifError(err)
58
-
59
- sget({
60
- method: 'POST',
61
- url: getServerUrl(fastify),
62
- body: '{"hello":"world"}',
63
- headers: {
64
- 'Content-Type': 'application/json'
65
- }
66
- }, (err, response, body) => {
67
- t.assert.ifError(err)
68
- t.assert.strictEqual(response.statusCode, 200)
69
- t.assert.deepStrictEqual(body.toString(), JSON.stringify({ hello: 'world' }))
70
- fastify.close()
71
- done()
72
- })
49
+ const fastifyServer = await fastify.listen({ port: 0 })
50
+
51
+ const result = await fetch(fastifyServer, {
52
+ method: 'POST',
53
+ body: JSON.stringify({ hello: 'world' }),
54
+ headers: {
55
+ 'Content-Type': 'application/json'
56
+ }
73
57
  })
58
+
59
+ t.assert.ok(result.ok)
60
+ t.assert.strictEqual(result.status, 200)
61
+ t.assert.deepStrictEqual(await result.text(), JSON.stringify({ hello: 'world' }))
62
+ await fastify.close()
74
63
  })
75
64
 
76
- test('should be able to override the default plain text parser after removeAllContentTypeParsers', (t, done) => {
77
- t.plan(5)
65
+ test('should be able to override the default plain text parser after removeAllContentTypeParsers', async (t) => {
66
+ t.plan(4)
78
67
 
79
68
  const fastify = Fastify()
80
69
 
@@ -91,28 +80,24 @@ test('should be able to override the default plain text parser after removeAllCo
91
80
  })
92
81
  })
93
82
 
94
- fastify.listen({ port: 0 }, err => {
95
- t.assert.ifError(err)
96
-
97
- sget({
98
- method: 'POST',
99
- url: getServerUrl(fastify),
100
- body: 'hello world',
101
- headers: {
102
- 'Content-Type': 'text/plain'
103
- }
104
- }, (err, response, body) => {
105
- t.assert.ifError(err)
106
- t.assert.strictEqual(response.statusCode, 200)
107
- t.assert.strictEqual(body.toString(), 'hello world')
108
- fastify.close()
109
- done()
110
- })
83
+ const fastifyServer = await fastify.listen({ port: 0 })
84
+
85
+ const result = await fetch(fastifyServer, {
86
+ method: 'POST',
87
+ body: 'hello world',
88
+ headers: {
89
+ 'Content-Type': 'text/plain'
90
+ }
111
91
  })
92
+
93
+ t.assert.ok(result.ok)
94
+ t.assert.strictEqual(result.status, 200)
95
+ t.assert.strictEqual(await result.text(), 'hello world')
96
+ await fastify.close()
112
97
  })
113
98
 
114
- test('should be able to add a custom content type parser after removeAllContentTypeParsers', (t, done) => {
115
- t.plan(5)
99
+ test('should be able to add a custom content type parser after removeAllContentTypeParsers', async (t) => {
100
+ t.plan(4)
116
101
 
117
102
  const fastify = Fastify()
118
103
 
@@ -128,22 +113,18 @@ test('should be able to add a custom content type parser after removeAllContentT
128
113
  })
129
114
  })
130
115
 
131
- fastify.listen({ port: 0 }, err => {
132
- t.assert.ifError(err)
133
-
134
- sget({
135
- method: 'POST',
136
- url: getServerUrl(fastify),
137
- body: '{"hello":"world"}',
138
- headers: {
139
- 'Content-Type': 'application/jsoff'
140
- }
141
- }, (err, response, body) => {
142
- t.assert.ifError(err)
143
- t.assert.strictEqual(response.statusCode, 200)
144
- t.assert.deepStrictEqual(body.toString(), JSON.stringify({ hello: 'world' }))
145
- fastify.close()
146
- done()
147
- })
116
+ const fastifyServer = await fastify.listen({ port: 0 })
117
+
118
+ const result = await fetch(fastifyServer, {
119
+ method: 'POST',
120
+ body: JSON.stringify({ hello: 'world' }),
121
+ headers: {
122
+ 'Content-Type': 'application/jsoff'
123
+ }
148
124
  })
125
+
126
+ t.assert.ok(result.ok)
127
+ t.assert.strictEqual(result.status, 200)
128
+ t.assert.deepStrictEqual(await result.text(), JSON.stringify({ hello: 'world' }))
129
+ await fastify.close()
149
130
  })
@@ -2,12 +2,10 @@
2
2
 
3
3
  const { test } = require('node:test')
4
4
  const querystring = require('node:querystring')
5
- const sget = require('simple-get').concat
6
5
  const Fastify = require('..')
7
- const { waitForCb } = require('./toolkit')
8
6
 
9
- test('Custom querystring parser', t => {
10
- t.plan(9)
7
+ test('Custom querystring parser', async t => {
8
+ t.plan(7)
11
9
 
12
10
  const fastify = Fastify({
13
11
  querystringParser: function (str) {
@@ -24,36 +22,22 @@ test('Custom querystring parser', t => {
24
22
  reply.send({ hello: 'world' })
25
23
  })
26
24
 
27
- const completion = waitForCb({ steps: 2 })
25
+ const fastifyServer = await fastify.listen({ port: 0 })
26
+ t.after(() => fastify.close())
28
27
 
29
- fastify.listen({ port: 0 }, (err, address) => {
30
- t.assert.ifError(err)
31
- t.after(() => fastify.close())
28
+ const result = await fetch(`${fastifyServer}?foo=bar&baz=faz`)
29
+ t.assert.ok(result.ok)
30
+ t.assert.strictEqual(result.status, 200)
32
31
 
33
- sget({
34
- method: 'GET',
35
- url: `${address}?foo=bar&baz=faz`
36
- }, (err, response, body) => {
37
- t.assert.ifError(err)
38
- t.assert.strictEqual(response.statusCode, 200)
39
- completion.stepIn()
40
- })
41
-
42
- fastify.inject({
43
- method: 'GET',
44
- url: `${address}?foo=bar&baz=faz`
45
- }, (err, response, body) => {
46
- t.assert.ifError(err)
47
- t.assert.strictEqual(response.statusCode, 200)
48
- completion.stepIn()
49
- })
32
+ const injectResponse = await fastify.inject({
33
+ method: 'GET',
34
+ url: `${fastifyServer}?foo=bar&baz=faz`
50
35
  })
51
-
52
- return completion.patience
36
+ t.assert.strictEqual(injectResponse.statusCode, 200)
53
37
  })
54
38
 
55
- test('Custom querystring parser should be called also if there is nothing to parse', t => {
56
- t.plan(9)
39
+ test('Custom querystring parser should be called also if there is nothing to parse', async t => {
40
+ t.plan(7)
57
41
 
58
42
  const fastify = Fastify({
59
43
  querystringParser: function (str) {
@@ -67,36 +51,22 @@ test('Custom querystring parser should be called also if there is nothing to par
67
51
  reply.send({ hello: 'world' })
68
52
  })
69
53
 
70
- const completion = waitForCb({ steps: 2 })
54
+ const fastifyServer = await fastify.listen({ port: 0 })
55
+ t.after(() => fastify.close())
71
56
 
72
- fastify.listen({ port: 0 }, (err, address) => {
73
- t.assert.ifError(err)
74
- t.after(() => fastify.close())
57
+ const result = await fetch(fastifyServer)
58
+ t.assert.ok(result.ok)
59
+ t.assert.strictEqual(result.status, 200)
75
60
 
76
- sget({
77
- method: 'GET',
78
- url: address
79
- }, (err, response, body) => {
80
- t.assert.ifError(err)
81
- t.assert.strictEqual(response.statusCode, 200)
82
- completion.stepIn()
83
- })
84
-
85
- fastify.inject({
86
- method: 'GET',
87
- url: address
88
- }, (err, response, body) => {
89
- t.assert.ifError(err)
90
- t.assert.strictEqual(response.statusCode, 200)
91
- completion.stepIn()
92
- })
61
+ const injectResponse = await fastify.inject({
62
+ method: 'GET',
63
+ url: fastifyServer
93
64
  })
94
-
95
- return completion.patience
65
+ t.assert.strictEqual(injectResponse.statusCode, 200)
96
66
  })
97
67
 
98
- test('Querystring without value', t => {
99
- t.plan(9)
68
+ test('Querystring without value', async t => {
69
+ t.plan(7)
100
70
 
101
71
  const fastify = Fastify({
102
72
  querystringParser: function (str) {
@@ -110,32 +80,18 @@ test('Querystring without value', t => {
110
80
  reply.send({ hello: 'world' })
111
81
  })
112
82
 
113
- const completion = waitForCb({ steps: 2 })
114
-
115
- fastify.listen({ port: 0 }, (err, address) => {
116
- t.assert.ifError(err)
117
- t.after(() => fastify.close())
83
+ const fastifyServer = await fastify.listen({ port: 0 })
84
+ t.after(() => fastify.close())
118
85
 
119
- sget({
120
- method: 'GET',
121
- url: `${address}?foo`
122
- }, (err, response, body) => {
123
- t.assert.ifError(err)
124
- t.assert.strictEqual(response.statusCode, 200)
125
- completion.stepIn()
126
- })
86
+ const result = await fetch(`${fastifyServer}?foo`)
87
+ t.assert.ok(result.ok)
88
+ t.assert.strictEqual(result.status, 200)
127
89
 
128
- fastify.inject({
129
- method: 'GET',
130
- url: `${address}?foo`
131
- }, (err, response, body) => {
132
- t.assert.ifError(err)
133
- t.assert.strictEqual(response.statusCode, 200)
134
- completion.stepIn()
135
- })
90
+ const injectResponse = await fastify.inject({
91
+ method: 'GET',
92
+ url: `${fastifyServer}?foo`
136
93
  })
137
-
138
- return completion.patience
94
+ t.assert.strictEqual(injectResponse.statusCode, 200)
139
95
  })
140
96
 
141
97
  test('Custom querystring parser should be a function', t => {
@@ -153,3 +109,21 @@ test('Custom querystring parser should be a function', t => {
153
109
  )
154
110
  }
155
111
  })
112
+
113
+ test('Custom querystring parser should be a function', t => {
114
+ t.plan(1)
115
+
116
+ try {
117
+ Fastify({
118
+ routerOptions: {
119
+ querystringParser: 10
120
+ }
121
+ })
122
+ t.fail('Should throw')
123
+ } catch (err) {
124
+ t.assert.equal(
125
+ err.message,
126
+ "querystringParser option should be a function, instead got 'number'"
127
+ )
128
+ }
129
+ })
@@ -2,8 +2,7 @@
2
2
 
3
3
  /* eslint no-prototype-builtins: 0 */
4
4
 
5
- const t = require('tap')
6
- const test = t.test
5
+ const { test } = require('node:test')
7
6
  const Fastify = require('..')
8
7
  const fp = require('fastify-plugin')
9
8
 
@@ -24,8 +23,8 @@ test('plugin namespace', async t => {
24
23
  }, { namepace: 'foo' })
25
24
 
26
25
  // ! but outside the plugin the decorator would be app.foo.utility()
27
- t.ok(app.foo.utility)
26
+ t.assert.ok(app.foo.utility)
28
27
 
29
28
  const res = await app.inject('/')
30
- t.same(res.json(), { utility: 'utility' })
29
+ t.assert.deepStrictEqual(res.json(), { utility: 'utility' })
31
30
  })