fastify 5.3.2 → 5.4.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 (103) hide show
  1. package/README.md +2 -0
  2. package/build/build-validation.js +2 -1
  3. package/docs/Guides/Delay-Accepting-Requests.md +3 -3
  4. package/docs/Guides/Ecosystem.md +16 -7
  5. package/docs/Guides/Serverless.md +28 -69
  6. package/docs/Reference/ContentTypeParser.md +1 -1
  7. package/docs/Reference/Errors.md +2 -4
  8. package/docs/Reference/Hooks.md +14 -14
  9. package/docs/Reference/Logging.md +3 -3
  10. package/docs/Reference/Middleware.md +1 -1
  11. package/docs/Reference/Reply.md +8 -8
  12. package/docs/Reference/Request.md +1 -1
  13. package/docs/Reference/Routes.md +3 -3
  14. package/docs/Reference/Server.md +40 -10
  15. package/docs/Reference/Validation-and-Serialization.md +1 -1
  16. package/eslint.config.js +17 -9
  17. package/fastify.d.ts +2 -1
  18. package/fastify.js +20 -4
  19. package/lib/configValidator.js +1 -1
  20. package/lib/decorate.js +2 -2
  21. package/lib/errors.js +6 -8
  22. package/lib/logger-factory.js +1 -1
  23. package/lib/logger-pino.js +2 -2
  24. package/lib/pluginOverride.js +3 -1
  25. package/lib/reply.js +9 -13
  26. package/lib/request.js +4 -11
  27. package/lib/server.js +30 -51
  28. package/lib/symbols.js +1 -0
  29. package/lib/warnings.js +8 -0
  30. package/package.json +11 -7
  31. package/test/404s.test.js +226 -325
  32. package/test/allow-unsafe-regex.test.js +19 -48
  33. package/test/als.test.js +28 -40
  34. package/test/async-await.test.js +11 -2
  35. package/test/body-limit.test.js +41 -65
  36. package/test/build-certificate.js +1 -1
  37. package/test/close-pipelining.test.js +5 -4
  38. package/test/custom-parser-async.test.js +17 -22
  39. package/test/decorator-namespace.test._js_ +3 -4
  40. package/test/decorator.test.js +422 -341
  41. package/test/diagnostics-channel/async-delay-request.test.js +7 -16
  42. package/test/diagnostics-channel/sync-delay-request.test.js +7 -16
  43. package/test/helper.js +108 -70
  44. package/test/hooks-async.test.js +248 -218
  45. package/test/hooks.on-listen.test.js +255 -239
  46. package/test/hooks.on-ready.test.js +110 -92
  47. package/test/hooks.test.js +910 -769
  48. package/test/http-methods/lock.test.js +31 -31
  49. package/test/http-methods/mkcol.test.js +5 -9
  50. package/test/http-methods/proppatch.test.js +23 -29
  51. package/test/http-methods/report.test.js +44 -69
  52. package/test/http-methods/search.test.js +67 -82
  53. package/test/http2/closing.test.js +38 -20
  54. package/test/http2/secure-with-fallback.test.js +28 -27
  55. package/test/https/https.test.js +56 -53
  56. package/test/inject.test.js +114 -97
  57. package/test/input-validation.js +63 -53
  58. package/test/internals/errors.test.js +0 -10
  59. package/test/internals/handle-request.test.js +49 -66
  60. package/test/internals/hooks.test.js +17 -0
  61. package/test/issue-4959.test.js +14 -5
  62. package/test/listen.4.test.js +31 -43
  63. package/test/logger/response.test.js +19 -20
  64. package/test/nullable-validation.test.js +33 -46
  65. package/test/options.error-handler.test.js +1 -1
  66. package/test/options.test.js +1 -1
  67. package/test/output-validation.test.js +49 -72
  68. package/test/patch.error-handler.test.js +1 -1
  69. package/test/patch.test.js +1 -1
  70. package/test/plugin.1.test.js +71 -60
  71. package/test/plugin.2.test.js +104 -86
  72. package/test/plugin.3.test.js +56 -35
  73. package/test/plugin.4.test.js +124 -119
  74. package/test/promises.test.js +36 -30
  75. package/test/proto-poisoning.test.js +78 -97
  76. package/test/put.error-handler.test.js +1 -1
  77. package/test/put.test.js +1 -1
  78. package/test/reply-error.test.js +169 -148
  79. package/test/reply-trailers.test.js +119 -108
  80. package/test/request-error.test.js +0 -46
  81. package/test/route-hooks.test.js +112 -92
  82. package/test/route-prefix.test.js +194 -133
  83. package/test/schema-feature.test.js +309 -238
  84. package/test/schema-serialization.test.js +177 -154
  85. package/test/schema-special-usage.test.js +165 -132
  86. package/test/schema-validation.test.js +278 -199
  87. package/test/set-error-handler.test.js +58 -1
  88. package/test/skip-reply-send.test.js +64 -69
  89. package/test/stream.1.test.js +30 -27
  90. package/test/stream.2.test.js +20 -10
  91. package/test/stream.3.test.js +37 -31
  92. package/test/trust-proxy.test.js +32 -58
  93. package/test/types/errors.test-d.ts +0 -1
  94. package/test/types/fastify.test-d.ts +3 -0
  95. package/test/types/plugin.test-d.ts +1 -1
  96. package/test/types/register.test-d.ts +1 -1
  97. package/test/types/request.test-d.ts +1 -0
  98. package/test/url-rewriting.test.js +45 -62
  99. package/test/use-semicolon-delimiter.test.js +1 -1
  100. package/types/errors.d.ts +0 -1
  101. package/types/request.d.ts +1 -0
  102. package/.taprc +0 -7
  103. package/test/http2/missing-http2-module.test.js +0 -17
@@ -2,19 +2,18 @@
2
2
 
3
3
  const stream = require('node:stream')
4
4
 
5
- const t = require('tap')
5
+ const t = require('node:test')
6
6
  const split = require('split2')
7
7
  const pino = require('pino')
8
8
 
9
9
  const Fastify = require('../../fastify')
10
+ const { partialDeepStrictEqual } = require('../toolkit')
10
11
  const { on } = stream
11
12
 
12
- t.test('response serialization', (t) => {
13
- t.setTimeout(60000)
14
-
13
+ t.test('response serialization', { timeout: 60000 }, async (t) => {
15
14
  t.plan(4)
16
15
 
17
- t.test('Should use serializers from plugin and route', async (t) => {
16
+ await t.test('Should use serializers from plugin and route', async (t) => {
18
17
  const lines = [
19
18
  { msg: 'incoming request' },
20
19
  { test: 'XHello', test2: 'ZHello' },
@@ -28,7 +27,7 @@ t.test('response serialization', (t) => {
28
27
  const fastify = Fastify({
29
28
  loggerInstance
30
29
  })
31
- t.teardown(fastify.close.bind(fastify))
30
+ t.after(() => fastify.close())
32
31
 
33
32
  fastify.register(context1, {
34
33
  logSerializers: { test: value => 'X' + value }
@@ -51,16 +50,16 @@ t.test('response serialization', (t) => {
51
50
  {
52
51
  const response = await fastify.inject({ method: 'GET', url: '/' })
53
52
  const body = await response.json()
54
- t.same(body, { hello: 'world' })
53
+ t.assert.deepStrictEqual(body, { hello: 'world' })
55
54
  }
56
55
 
57
56
  for await (const [line] of on(stream, 'data')) {
58
- t.match(line, lines.shift())
57
+ t.assert.ok(partialDeepStrictEqual(line, lines.shift()))
59
58
  if (lines.length === 0) break
60
59
  }
61
60
  })
62
61
 
63
- t.test('Should use serializers from instance fastify and route', async (t) => {
62
+ await t.test('Should use serializers from instance fastify and route', async (t) => {
64
63
  const lines = [
65
64
  { msg: 'incoming request' },
66
65
  { test: 'XHello', test2: 'ZHello' },
@@ -80,7 +79,7 @@ t.test('response serialization', (t) => {
80
79
  const fastify = Fastify({
81
80
  loggerInstance
82
81
  })
83
- t.teardown(fastify.close.bind(fastify))
82
+ t.after(() => fastify.close())
84
83
 
85
84
  fastify.get('/', {
86
85
  logSerializers: {
@@ -96,16 +95,16 @@ t.test('response serialization', (t) => {
96
95
  {
97
96
  const response = await fastify.inject({ method: 'GET', url: '/' })
98
97
  const body = await response.json()
99
- t.same(body, { hello: 'world' })
98
+ t.assert.deepStrictEqual(body, { hello: 'world' })
100
99
  }
101
100
 
102
101
  for await (const [line] of on(stream, 'data')) {
103
- t.match(line, lines.shift())
102
+ t.assert.ok(partialDeepStrictEqual(line, lines.shift()))
104
103
  if (lines.length === 0) break
105
104
  }
106
105
  })
107
106
 
108
- t.test('Should use serializers inherit from contexts', async (t) => {
107
+ await t.test('Should use serializers inherit from contexts', async (t) => {
109
108
  const lines = [
110
109
  { msg: 'incoming request' },
111
110
  { test: 'XHello', test2: 'YHello', test3: 'ZHello' },
@@ -123,7 +122,7 @@ t.test('response serialization', (t) => {
123
122
  }, stream)
124
123
 
125
124
  const fastify = Fastify({ loggerInstance })
126
- t.teardown(fastify.close.bind(fastify))
125
+ t.after(() => fastify.close())
127
126
 
128
127
  fastify.register(context1, { logSerializers: { test2: value => 'Y' + value } })
129
128
 
@@ -144,16 +143,16 @@ t.test('response serialization', (t) => {
144
143
  {
145
144
  const response = await fastify.inject({ method: 'GET', url: '/' })
146
145
  const body = await response.json()
147
- t.same(body, { hello: 'world' })
146
+ t.assert.deepStrictEqual(body, { hello: 'world' })
148
147
  }
149
148
 
150
149
  for await (const [line] of on(stream, 'data')) {
151
- t.match(line, lines.shift())
150
+ t.assert.ok(partialDeepStrictEqual(line, lines.shift()))
152
151
  if (lines.length === 0) break
153
152
  }
154
153
  })
155
154
 
156
- t.test('should serialize request and response', async (t) => {
155
+ await t.test('should serialize request and response', async (t) => {
157
156
  const lines = [
158
157
  { req: { method: 'GET', url: '/500' }, msg: 'incoming request' },
159
158
  { req: { method: 'GET', url: '/500' }, msg: '500 error' },
@@ -163,7 +162,7 @@ t.test('response serialization', (t) => {
163
162
 
164
163
  const stream = split(JSON.parse)
165
164
  const fastify = Fastify({ logger: { level: 'info', stream } })
166
- t.teardown(fastify.close.bind(fastify))
165
+ t.after(() => fastify.close())
167
166
 
168
167
  fastify.get('/500', (req, reply) => {
169
168
  reply.code(500).send(Error('500 error'))
@@ -173,11 +172,11 @@ t.test('response serialization', (t) => {
173
172
 
174
173
  {
175
174
  const response = await fastify.inject({ method: 'GET', url: '/500' })
176
- t.equal(response.statusCode, 500)
175
+ t.assert.strictEqual(response.statusCode, 500)
177
176
  }
178
177
 
179
178
  for await (const [line] of on(stream, 'data')) {
180
- t.match(line, lines.shift())
179
+ t.assert.ok(partialDeepStrictEqual(line, lines.shift()))
181
180
  if (lines.length === 0) break
182
181
  }
183
182
  })
@@ -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
 
7
6
  test('nullable string', (t, done) => {
@@ -52,8 +51,8 @@ test('nullable string', (t, done) => {
52
51
  })
53
52
  })
54
53
 
55
- test('object or null body', (t, done) => {
56
- t.plan(5)
54
+ test('object or null body', async (t) => {
55
+ t.plan(4)
57
56
 
58
57
  const fastify = Fastify()
59
58
 
@@ -88,24 +87,20 @@ test('object or null body', (t, done) => {
88
87
  }
89
88
  })
90
89
 
91
- fastify.listen({ port: 0 }, (err) => {
92
- t.assert.ifError(err)
93
- t.after(() => { fastify.close() })
94
-
95
- sget({
96
- method: 'POST',
97
- url: 'http://localhost:' + fastify.server.address().port
98
- }, (err, response, body) => {
99
- t.assert.ifError(err)
100
- t.assert.strictEqual(response.statusCode, 200)
101
- t.assert.deepStrictEqual(JSON.parse(body), { isUndefinedBody: true })
102
- done()
103
- })
90
+ const fastifyServer = await fastify.listen({ port: 0 })
91
+ t.after(() => { fastify.close() })
92
+
93
+ const result = await fetch(fastifyServer, {
94
+ method: 'POST'
104
95
  })
96
+
97
+ t.assert.ok(result.ok)
98
+ t.assert.strictEqual(result.status, 200)
99
+ t.assert.deepStrictEqual(await result.json(), { isUndefinedBody: true })
105
100
  })
106
101
 
107
- test('nullable body', (t, done) => {
108
- t.plan(5)
102
+ test('nullable body', async (t) => {
103
+ t.plan(4)
109
104
 
110
105
  const fastify = Fastify()
111
106
 
@@ -141,24 +136,20 @@ test('nullable body', (t, done) => {
141
136
  }
142
137
  })
143
138
 
144
- fastify.listen({ port: 0 }, (err) => {
145
- t.assert.ifError(err)
146
- t.after(() => fastify.close())
147
-
148
- sget({
149
- method: 'POST',
150
- url: 'http://localhost:' + fastify.server.address().port
151
- }, (err, response, body) => {
152
- t.assert.ifError(err)
153
- t.assert.strictEqual(response.statusCode, 200)
154
- t.assert.deepStrictEqual(JSON.parse(body), { isUndefinedBody: true })
155
- done()
156
- })
139
+ const fastifyServer = await fastify.listen({ port: 0 })
140
+ t.after(() => fastify.close())
141
+
142
+ const result = await fetch(fastifyServer, {
143
+ method: 'POST'
157
144
  })
145
+
146
+ t.assert.ok(result.ok)
147
+ t.assert.strictEqual(result.status, 200)
148
+ t.assert.deepStrictEqual(await result.json(), { isUndefinedBody: true })
158
149
  })
159
150
 
160
- test('Nullable body with 204', (t, done) => {
161
- t.plan(5)
151
+ test('Nullable body with 204', async (t) => {
152
+ t.plan(4)
162
153
 
163
154
  const fastify = Fastify()
164
155
 
@@ -183,18 +174,14 @@ test('Nullable body with 204', (t, done) => {
183
174
  }
184
175
  })
185
176
 
186
- fastify.listen({ port: 0 }, (err) => {
187
- t.assert.ifError(err)
188
- t.after(() => fastify.close())
189
-
190
- sget({
191
- method: 'POST',
192
- url: 'http://localhost:' + fastify.server.address().port
193
- }, (err, response, body) => {
194
- t.assert.ifError(err)
195
- t.assert.strictEqual(response.statusCode, 204)
196
- t.assert.strictEqual(body.length, 0)
197
- done()
198
- })
177
+ const fastifyServer = await fastify.listen({ port: 0 })
178
+ t.after(() => fastify.close())
179
+
180
+ const result = await fetch(fastifyServer, {
181
+ method: 'POST'
199
182
  })
183
+
184
+ t.assert.ok(result.ok)
185
+ t.assert.strictEqual(result.status, 204)
186
+ t.assert.strictEqual((await result.text()).length, 0)
200
187
  })
@@ -1,5 +1,5 @@
1
1
  'use strict'
2
2
 
3
- const t = require('tap')
3
+ const t = require('node:test')
4
4
  require('./helper').payloadMethod('options', t, true)
5
5
  require('./input-validation').payloadMethod('options', t)
@@ -1,5 +1,5 @@
1
1
  'use strict'
2
2
 
3
- const t = require('tap')
3
+ const t = require('node:test')
4
4
  require('./helper').payloadMethod('options', t)
5
5
  require('./input-validation').payloadMethod('options', t)
@@ -1,8 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const t = require('tap')
4
- const test = t.test
5
- const sget = require('simple-get').concat
3
+ const { test } = require('node:test')
6
4
  const fastify = require('..')()
7
5
 
8
6
  const opts = {
@@ -34,9 +32,9 @@ test('shorthand - output string', t => {
34
32
  fastify.get('/string', opts, function (req, reply) {
35
33
  reply.code(200).send({ hello: 'world' })
36
34
  })
37
- t.pass()
35
+ t.assert.ok(true)
38
36
  } catch (e) {
39
- t.fail()
37
+ t.assert.fail()
40
38
  }
41
39
  })
42
40
 
@@ -46,9 +44,9 @@ test('shorthand - output number', t => {
46
44
  fastify.get('/number', opts, function (req, reply) {
47
45
  reply.code(201).send({ hello: 55 })
48
46
  })
49
- t.pass()
47
+ t.assert.ok(true)
50
48
  } catch (e) {
51
- t.fail()
49
+ t.assert.fail()
52
50
  }
53
51
  })
54
52
 
@@ -59,9 +57,9 @@ test('wrong object for schema - output', t => {
59
57
  // will send { }
60
58
  reply.code(201).send({ hello: 'world' })
61
59
  })
62
- t.pass()
60
+ t.assert.ok(true)
63
61
  } catch (e) {
64
- t.fail()
62
+ t.assert.fail()
65
63
  }
66
64
  })
67
65
 
@@ -72,9 +70,9 @@ test('empty response', t => {
72
70
  fastify.get('/empty', opts, function (req, reply) {
73
71
  reply.code(204).send()
74
72
  })
75
- t.pass()
73
+ t.assert.ok(true)
76
74
  } catch (e) {
77
- t.fail()
75
+ t.assert.fail()
78
76
  }
79
77
  })
80
78
 
@@ -84,80 +82,59 @@ test('unlisted response code', t => {
84
82
  fastify.get('/400', opts, function (req, reply) {
85
83
  reply.code(400).send({ hello: 'DOOM' })
86
84
  })
87
- t.pass()
85
+ t.assert.ok(true)
88
86
  } catch (e) {
89
- t.fail()
87
+ t.assert.fail()
90
88
  }
91
89
  })
92
90
 
93
- fastify.listen({ port: 0 }, err => {
94
- t.error(err)
95
- t.teardown(() => { fastify.close() })
91
+ test('start server and run tests', async (t) => {
92
+ const fastifyServer = await fastify.listen({ port: 0 })
93
+ t.after(() => fastify.close())
96
94
 
97
- test('shorthand - string get ok', t => {
98
- t.plan(4)
99
- sget({
100
- method: 'GET',
101
- url: 'http://localhost:' + fastify.server.address().port + '/string'
102
- }, (err, response, body) => {
103
- t.error(err)
104
- t.equal(response.statusCode, 200)
105
- t.equal(response.headers['content-length'], '' + body.length)
106
- t.same(JSON.parse(body), { hello: 'world' })
107
- })
95
+ await test('shorthand - string get ok', async (t) => {
96
+ const result = await fetch(fastifyServer + '/string')
97
+ t.assert.ok(result.ok)
98
+ t.assert.strictEqual(result.status, 200)
99
+ const body = await result.text()
100
+ t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
101
+ t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
108
102
  })
109
103
 
110
- test('shorthand - number get ok', t => {
111
- t.plan(4)
112
- sget({
113
- method: 'GET',
114
- url: 'http://localhost:' + fastify.server.address().port + '/number'
115
- }, (err, response, body) => {
116
- t.error(err)
117
- t.equal(response.statusCode, 201)
118
- t.equal(response.headers['content-length'], '' + body.length)
119
- t.same(JSON.parse(body), { hello: 55 })
120
- })
104
+ await test('shorthand - number get ok', async (t) => {
105
+ const result = await fetch(fastifyServer + '/number')
106
+ t.assert.ok(result.ok)
107
+ t.assert.strictEqual(result.status, 201)
108
+ const body = await result.text()
109
+ t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
110
+ t.assert.deepStrictEqual(JSON.parse(body), { hello: 55 })
121
111
  })
122
112
 
123
- test('shorthand - wrong-object-for-schema', t => {
124
- t.plan(4)
125
- sget({
126
- method: 'GET',
127
- url: 'http://localhost:' + fastify.server.address().port + '/wrong-object-for-schema'
128
- }, (err, response, body) => {
129
- t.error(err)
130
- t.equal(response.statusCode, 500)
131
- t.equal(response.headers['content-length'], '' + body.length)
132
- t.same(JSON.parse(body), {
133
- statusCode: 500,
134
- error: 'Internal Server Error',
135
- message: 'The value "world" cannot be converted to a number.'
136
- })
113
+ await test('shorthand - wrong-object-for-schema', async (t) => {
114
+ const result = await fetch(fastifyServer + '/wrong-object-for-schema')
115
+ t.assert.ok(!result.ok)
116
+ t.assert.strictEqual(result.status, 500)
117
+ const body = await result.text()
118
+ t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
119
+ t.assert.deepStrictEqual(JSON.parse(body), {
120
+ statusCode: 500,
121
+ error: 'Internal Server Error',
122
+ message: 'The value "world" cannot be converted to a number.'
137
123
  })
138
124
  })
139
125
 
140
- test('shorthand - empty', t => {
141
- t.plan(2)
142
- sget({
143
- method: 'GET',
144
- url: 'http://localhost:' + fastify.server.address().port + '/empty'
145
- }, (err, response, body) => {
146
- t.error(err)
147
- t.equal(response.statusCode, 204)
148
- })
126
+ await test('shorthand - empty', async (t) => {
127
+ const result = await fetch(fastifyServer + '/empty')
128
+ t.assert.ok(result.ok)
129
+ t.assert.strictEqual(result.status, 204)
149
130
  })
150
131
 
151
- test('shorthand - 400', t => {
152
- t.plan(4)
153
- sget({
154
- method: 'GET',
155
- url: 'http://localhost:' + fastify.server.address().port + '/400'
156
- }, (err, response, body) => {
157
- t.error(err)
158
- t.equal(response.statusCode, 400)
159
- t.equal(response.headers['content-length'], '' + body.length)
160
- t.same(JSON.parse(body), { hello: 'DOOM' })
161
- })
132
+ await test('shorthand - 400', async (t) => {
133
+ const result = await fetch(fastifyServer + '/400')
134
+ t.assert.ok(!result.ok)
135
+ t.assert.strictEqual(result.status, 400)
136
+ const body = await result.text()
137
+ t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
138
+ t.assert.deepStrictEqual(JSON.parse(body), { hello: 'DOOM' })
162
139
  })
163
140
  })
@@ -1,5 +1,5 @@
1
1
  'use strict'
2
2
 
3
- const t = require('tap')
3
+ const t = require('node:test')
4
4
  require('./helper').payloadMethod('patch', t, true)
5
5
  require('./input-validation').payloadMethod('patch', t)
@@ -1,5 +1,5 @@
1
1
  'use strict'
2
2
 
3
- const t = require('tap')
3
+ const t = require('node:test')
4
4
  require('./helper').payloadMethod('patch', t)
5
5
  require('./input-validation').payloadMethod('patch', t)