fastify 3.27.2 → 4.0.0-alpha.1

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 (116) hide show
  1. package/README.md +5 -4
  2. package/build/build-error-serializer.js +27 -0
  3. package/build/build-validation.js +47 -35
  4. package/docs/Migration-Guide-V4.md +12 -0
  5. package/docs/Reference/ContentTypeParser.md +4 -0
  6. package/docs/Reference/Errors.md +51 -6
  7. package/docs/Reference/Hooks.md +4 -7
  8. package/docs/Reference/LTS.md +5 -4
  9. package/docs/Reference/Reply.md +23 -22
  10. package/docs/Reference/Request.md +1 -3
  11. package/docs/Reference/Routes.md +17 -10
  12. package/docs/Reference/Server.md +48 -63
  13. package/docs/Reference/TypeScript.md +11 -13
  14. package/docs/Reference/Validation-and-Serialization.md +28 -53
  15. package/docs/Type-Providers.md +257 -0
  16. package/examples/hooks.js +1 -1
  17. package/examples/simple-stream.js +18 -0
  18. package/fastify.d.ts +34 -22
  19. package/fastify.js +37 -35
  20. package/lib/configValidator.js +902 -1023
  21. package/lib/contentTypeParser.js +6 -16
  22. package/lib/context.js +36 -10
  23. package/lib/decorate.js +3 -1
  24. package/lib/error-handler.js +158 -0
  25. package/lib/error-serializer.js +257 -0
  26. package/lib/errors.js +43 -9
  27. package/lib/fourOhFour.js +31 -20
  28. package/lib/handleRequest.js +10 -13
  29. package/lib/hooks.js +14 -9
  30. package/lib/pluginOverride.js +0 -3
  31. package/lib/pluginUtils.js +3 -2
  32. package/lib/reply.js +28 -157
  33. package/lib/request.js +13 -10
  34. package/lib/route.js +131 -138
  35. package/lib/schema-controller.js +2 -2
  36. package/lib/schemas.js +27 -1
  37. package/lib/server.js +219 -116
  38. package/lib/symbols.js +4 -3
  39. package/lib/validation.js +2 -1
  40. package/lib/warnings.js +2 -12
  41. package/lib/wrapThenable.js +4 -11
  42. package/package.json +31 -35
  43. package/test/404s.test.js +243 -110
  44. package/test/500s.test.js +2 -2
  45. package/test/async-await.test.js +13 -69
  46. package/test/content-parser.test.js +32 -0
  47. package/test/context-config.test.js +52 -0
  48. package/test/custom-http-server.test.js +14 -7
  49. package/test/custom-parser-async.test.js +0 -65
  50. package/test/custom-parser.test.js +54 -121
  51. package/test/decorator.test.js +1 -3
  52. package/test/delete.test.js +5 -5
  53. package/test/encapsulated-error-handler.test.js +50 -0
  54. package/test/esm/index.test.js +0 -14
  55. package/test/fastify-instance.test.js +4 -4
  56. package/test/fluent-schema.test.js +4 -4
  57. package/test/get.test.js +3 -3
  58. package/test/helper.js +18 -3
  59. package/test/hooks-async.test.js +14 -47
  60. package/test/hooks.on-ready.test.js +9 -4
  61. package/test/hooks.test.js +58 -99
  62. package/test/http2/closing.test.js +5 -11
  63. package/test/http2/unknown-http-method.test.js +3 -9
  64. package/test/https/custom-https-server.test.js +12 -6
  65. package/test/input-validation.js +2 -2
  66. package/test/internals/handleRequest.test.js +3 -40
  67. package/test/internals/initialConfig.test.js +33 -12
  68. package/test/internals/reply.test.js +245 -3
  69. package/test/internals/request.test.js +13 -7
  70. package/test/internals/server.test.js +88 -0
  71. package/test/listen.test.js +84 -1
  72. package/test/logger.test.js +80 -40
  73. package/test/maxRequestsPerSocket.test.js +6 -4
  74. package/test/middleware.test.js +2 -25
  75. package/test/nullable-validation.test.js +51 -14
  76. package/test/plugin.test.js +31 -5
  77. package/test/pretty-print.test.js +22 -10
  78. package/test/reply-error.test.js +123 -12
  79. package/test/request-error.test.js +2 -5
  80. package/test/route-hooks.test.js +17 -17
  81. package/test/route-prefix.test.js +2 -1
  82. package/test/route.test.js +204 -20
  83. package/test/router-options.test.js +1 -1
  84. package/test/schema-examples.test.js +11 -5
  85. package/test/schema-feature.test.js +24 -19
  86. package/test/schema-serialization.test.js +9 -9
  87. package/test/schema-special-usage.test.js +14 -81
  88. package/test/schema-validation.test.js +9 -9
  89. package/test/skip-reply-send.test.js +1 -1
  90. package/test/stream.test.js +23 -12
  91. package/test/throw.test.js +8 -5
  92. package/test/type-provider.test.js +20 -0
  93. package/test/types/fastify.test-d.ts +10 -18
  94. package/test/types/import.js +2 -0
  95. package/test/types/import.ts +1 -0
  96. package/test/types/instance.test-d.ts +35 -14
  97. package/test/types/logger.test-d.ts +44 -15
  98. package/test/types/route.test-d.ts +8 -2
  99. package/test/types/schema.test-d.ts +2 -39
  100. package/test/types/type-provider.test-d.ts +417 -0
  101. package/test/validation-error-handling.test.js +8 -8
  102. package/test/versioned-routes.test.js +28 -16
  103. package/test/wrapThenable.test.js +7 -6
  104. package/types/content-type-parser.d.ts +17 -8
  105. package/types/hooks.d.ts +102 -59
  106. package/types/instance.d.ts +124 -104
  107. package/types/logger.d.ts +18 -104
  108. package/types/plugin.d.ts +10 -4
  109. package/types/reply.d.ts +16 -11
  110. package/types/request.d.ts +10 -5
  111. package/types/route.d.ts +42 -31
  112. package/types/schema.d.ts +1 -1
  113. package/types/type-provider.d.ts +99 -0
  114. package/types/utils.d.ts +1 -1
  115. package/lib/schema-compilers.js +0 -12
  116. package/test/emit-warning.test.js +0 -166
@@ -79,7 +79,7 @@ test('ignore the result of the promise if reply.send is called beforehand (undef
79
79
  const payload = { hello: 'world' }
80
80
 
81
81
  server.get('/', async function awaitMyFunc (req, reply) {
82
- reply.send(payload)
82
+ await reply.send(payload)
83
83
  })
84
84
 
85
85
  t.teardown(server.close.bind(server))
@@ -104,7 +104,7 @@ test('ignore the result of the promise if reply.send is called beforehand (objec
104
104
  const payload = { hello: 'world2' }
105
105
 
106
106
  server.get('/', async function awaitMyFunc (req, reply) {
107
- reply.send(payload)
107
+ await reply.send(payload)
108
108
  return { hello: 'world' }
109
109
  })
110
110
 
@@ -139,7 +139,7 @@ test('server logs an error if reply.send is called and a value is returned via a
139
139
  })
140
140
 
141
141
  fastify.get('/', async (req, reply) => {
142
- reply.send({ hello: 'world' })
142
+ await reply.send({ hello: 'world' })
143
143
  return { hello: 'world2' }
144
144
  })
145
145
 
@@ -160,7 +160,7 @@ test('ignore the result of the promise if reply.send is called beforehand (undef
160
160
  const payload = { hello: 'world' }
161
161
 
162
162
  server.get('/', async function awaitMyFunc (req, reply) {
163
- reply.send(payload)
163
+ await reply.send(payload)
164
164
  })
165
165
 
166
166
  t.teardown(server.close.bind(server))
@@ -185,7 +185,7 @@ test('ignore the result of the promise if reply.send is called beforehand (objec
185
185
  const payload = { hello: 'world2' }
186
186
 
187
187
  server.get('/', async function awaitMyFunc (req, reply) {
188
- reply.send(payload)
188
+ await reply.send(payload)
189
189
  return { hello: 'world' }
190
190
  })
191
191
 
@@ -279,11 +279,13 @@ test('support reply decorators with await', t => {
279
279
  setImmediate(() => {
280
280
  this.send({ hello: 'world' })
281
281
  })
282
+
283
+ return this
282
284
  })
283
285
 
284
286
  fastify.get('/', async (req, reply) => {
285
287
  await sleep(1)
286
- reply.wow()
288
+ await reply.wow()
287
289
  })
288
290
 
289
291
  fastify.inject({
@@ -296,24 +298,6 @@ test('support reply decorators with await', t => {
296
298
  })
297
299
  })
298
300
 
299
- test('support 204', t => {
300
- t.plan(2)
301
-
302
- const fastify = Fastify()
303
-
304
- fastify.get('/', async (req, reply) => {
305
- reply.code(204)
306
- })
307
-
308
- fastify.inject({
309
- method: 'GET',
310
- url: '/'
311
- }, (err, res) => {
312
- t.error(err)
313
- t.equal(res.statusCode, 204)
314
- })
315
- })
316
-
317
301
  test('inject async await', async t => {
318
302
  t.plan(1)
319
303
 
@@ -399,48 +383,8 @@ test('does not call reply.send() twice if 204 response equal already sent', t =>
399
383
  })
400
384
  })
401
385
 
402
- test('error is logged because promise was fulfilled with undefined', t => {
403
- t.plan(3)
404
-
405
- let fastify = null
406
- const stream = split(JSON.parse)
407
- try {
408
- fastify = Fastify({
409
- logger: {
410
- stream,
411
- level: 'error'
412
- }
413
- })
414
- } catch (e) {
415
- t.fail()
416
- }
417
-
418
- t.teardown(fastify.close.bind(fastify))
419
-
420
- fastify.get('/', async (req, reply) => {
421
- reply.code(200)
422
- })
423
-
424
- stream.once('data', line => {
425
- t.equal(line.msg, 'Promise may not be fulfilled with \'undefined\' when statusCode is not 204')
426
- })
427
-
428
- fastify.listen(0, (err) => {
429
- t.error(err)
430
- fastify.server.unref()
431
-
432
- sget({
433
- method: 'GET',
434
- url: 'http://localhost:' + fastify.server.address().port + '/',
435
- timeout: 500
436
- }, (err, res, body) => {
437
- t.equal(err.message, 'Request timed out')
438
- })
439
- })
440
- })
441
-
442
- test('error is not logged because promise was fulfilled with undefined but statusCode 204 was set', t => {
443
- t.plan(3)
386
+ test('promise was fulfilled with undefined', t => {
387
+ t.plan(4)
444
388
 
445
389
  let fastify = null
446
390
  const stream = split(JSON.parse)
@@ -458,7 +402,6 @@ test('error is not logged because promise was fulfilled with undefined but statu
458
402
  t.teardown(fastify.close.bind(fastify))
459
403
 
460
404
  fastify.get('/', async (req, reply) => {
461
- reply.code(204)
462
405
  })
463
406
 
464
407
  stream.once('data', line => {
@@ -474,7 +417,8 @@ test('error is not logged because promise was fulfilled with undefined but statu
474
417
  url: 'http://localhost:' + fastify.server.address().port + '/'
475
418
  }, (err, res, body) => {
476
419
  t.error(err)
477
- t.equal(res.statusCode, 204)
420
+ t.equal(res.body, undefined)
421
+ t.equal(res.statusCode, 200)
478
422
  })
479
423
  })
480
424
  })
@@ -601,7 +545,7 @@ test('customErrorHandler support without throwing', t => {
601
545
 
602
546
  fastify.setErrorHandler(async (err, req, reply) => {
603
547
  t.equal(err.message, 'ouch')
604
- reply.code(401).send('kaboom')
548
+ await reply.code(401).send('kaboom')
605
549
  reply.send = t.fail.bind(t, 'should not be called')
606
550
  })
607
551
 
@@ -187,6 +187,38 @@ test('add', t => {
187
187
  t.end()
188
188
  })
189
189
 
190
+ test('non-Error thrown from content parser is properly handled', t => {
191
+ t.plan(3)
192
+
193
+ const fastify = Fastify()
194
+
195
+ const throwable = 'test'
196
+ const payload = 'error'
197
+
198
+ fastify.addContentTypeParser('text/test', (request, payload, done) => {
199
+ done(throwable)
200
+ })
201
+
202
+ fastify.post('/', (req, reply) => {
203
+ })
204
+
205
+ fastify.setErrorHandler((err, req, res) => {
206
+ t.equal(err, throwable)
207
+
208
+ res.send(payload)
209
+ })
210
+
211
+ fastify.inject({
212
+ method: 'POST',
213
+ url: '/',
214
+ headers: { 'Content-Type': 'text/test' },
215
+ body: 'some text'
216
+ }, (err, res) => {
217
+ t.error(err)
218
+ t.equal(res.payload, payload)
219
+ })
220
+ })
221
+
190
222
  test('remove', t => {
191
223
  test('should remove default parser', t => {
192
224
  t.plan(2)
@@ -119,3 +119,55 @@ test('config with exposeHeadRoutes', t => {
119
119
  t.same(JSON.parse(response.payload), { url: '/no-config', method: 'GET' })
120
120
  })
121
121
  })
122
+
123
+ test('config without exposeHeadRoutes', t => {
124
+ t.plan(9)
125
+ const fastify = Fastify({ exposeHeadRoutes: false })
126
+
127
+ fastify.get('/get', {
128
+ schema: schema.schema,
129
+ config: Object.assign({}, schema.config)
130
+ }, handler)
131
+
132
+ fastify.route({
133
+ method: 'GET',
134
+ url: '/route',
135
+ schema: schema.schema,
136
+ handler,
137
+ config: Object.assign({}, schema.config)
138
+ })
139
+
140
+ fastify.route({
141
+ method: 'GET',
142
+ url: '/no-config',
143
+ schema: schema.schema,
144
+ handler
145
+ })
146
+
147
+ fastify.inject({
148
+ method: 'GET',
149
+ url: '/get'
150
+ }, (err, response) => {
151
+ t.error(err)
152
+ t.equal(response.statusCode, 200)
153
+ t.same(JSON.parse(response.payload), Object.assign({ url: '/get', method: 'GET' }, schema.config))
154
+ })
155
+
156
+ fastify.inject({
157
+ method: 'GET',
158
+ url: '/route'
159
+ }, (err, response) => {
160
+ t.error(err)
161
+ t.equal(response.statusCode, 200)
162
+ t.same(JSON.parse(response.payload), Object.assign({ url: '/route', method: 'GET' }, schema.config))
163
+ })
164
+
165
+ fastify.inject({
166
+ method: 'GET',
167
+ url: '/no-config'
168
+ }, (err, response) => {
169
+ t.error(err)
170
+ t.equal(response.statusCode, 200)
171
+ t.same(JSON.parse(response.payload), { url: '/no-config', method: 'GET' })
172
+ })
173
+ })
@@ -5,12 +5,15 @@ const test = t.test
5
5
  const Fastify = require('..')
6
6
  const http = require('http')
7
7
  const sget = require('simple-get').concat
8
+ const dns = require('dns').promises
8
9
 
9
- test('Should support a custom http server', t => {
10
- t.plan(6)
10
+ test('Should support a custom http server', async t => {
11
+ const localAddresses = await dns.lookup('localhost', { all: true })
12
+
13
+ t.plan(localAddresses.length + 3)
11
14
 
12
15
  const serverFactory = (handler, opts) => {
13
- t.ok(opts.serverFactory)
16
+ t.ok(opts.serverFactory, 'it is called twice for every HOST interface')
14
17
 
15
18
  const server = http.createServer((req, res) => {
16
19
  req.custom = true
@@ -29,16 +32,20 @@ test('Should support a custom http server', t => {
29
32
  reply.send({ hello: 'world' })
30
33
  })
31
34
 
32
- fastify.listen(0, err => {
33
- t.error(err)
35
+ await fastify.listen(0)
34
36
 
37
+ await new Promise((resolve, reject) => {
35
38
  sget({
36
39
  method: 'GET',
37
- url: 'http://localhost:' + fastify.server.address().port
40
+ url: 'http://localhost:' + fastify.server.address().port,
41
+ rejectUnauthorized: false
38
42
  }, (err, response, body) => {
39
- t.error(err)
43
+ if (err) {
44
+ return reject(err)
45
+ }
40
46
  t.equal(response.statusCode, 200)
41
47
  t.same(JSON.parse(body), { hello: 'world' })
48
+ resolve()
42
49
  })
43
50
  })
44
51
  })
@@ -64,68 +64,3 @@ test('contentTypeParser should add a custom async parser', t => {
64
64
  })
65
65
  })
66
66
  })
67
-
68
- test('contentTypeParser should add a custom async parser - deprecated syntax', t => {
69
- t.plan(5)
70
- const fastify = Fastify()
71
-
72
- process.on('warning', onWarning)
73
- function onWarning (warning) {
74
- t.equal(warning.name, 'FastifyDeprecation')
75
- t.equal(warning.code, 'FSTDEP003')
76
- }
77
-
78
- fastify.post('/', (req, reply) => {
79
- reply.send(req.body)
80
- })
81
-
82
- fastify.options('/', (req, reply) => {
83
- reply.send(req.body)
84
- })
85
-
86
- fastify.addContentTypeParser('application/jsoff', async function (req) {
87
- const res = await new Promise((resolve, reject) => resolve(req))
88
- return res
89
- })
90
-
91
- fastify.listen(0, err => {
92
- t.error(err)
93
-
94
- t.teardown(() => fastify.close())
95
-
96
- t.test('in POST', t => {
97
- t.plan(3)
98
-
99
- sget({
100
- method: 'POST',
101
- url: 'http://localhost:' + fastify.server.address().port,
102
- body: '{"hello":"world"}',
103
- headers: {
104
- 'Content-Type': 'application/jsoff'
105
- }
106
- }, (err, response, body) => {
107
- t.error(err)
108
- t.equal(response.statusCode, 200)
109
- t.same(body.toString(), JSON.stringify({ hello: 'world' }))
110
- })
111
- })
112
-
113
- t.test('in OPTIONS', t => {
114
- t.plan(3)
115
-
116
- sget({
117
- method: 'OPTIONS',
118
- url: 'http://localhost:' + fastify.server.address().port,
119
- body: '{"hello":"world"}',
120
- headers: {
121
- 'Content-Type': 'application/jsoff'
122
- }
123
- }, (err, response, body) => {
124
- t.error(err)
125
- t.equal(response.statusCode, 200)
126
- t.same(body.toString(), JSON.stringify({ hello: 'world' }))
127
- process.removeListener('warning', onWarning)
128
- })
129
- })
130
- })
131
- })