fastify 4.14.1 → 4.16.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 (51) hide show
  1. package/README.md +8 -4
  2. package/docs/Guides/Database.md +7 -8
  3. package/docs/Guides/Ecosystem.md +16 -7
  4. package/docs/Guides/Getting-Started.md +1 -1
  5. package/docs/Guides/Migration-Guide-V4.md +21 -0
  6. package/docs/Guides/Plugins-Guide.md +1 -1
  7. package/docs/Guides/Prototype-Poisoning.md +31 -39
  8. package/docs/Guides/Recommendations.md +1 -1
  9. package/docs/Guides/Write-Type-Provider.md +3 -3
  10. package/docs/Reference/Errors.md +6 -0
  11. package/docs/Reference/Hooks.md +42 -9
  12. package/docs/Reference/Reply.md +2 -2
  13. package/docs/Reference/Routes.md +13 -2
  14. package/docs/Reference/Server.md +102 -35
  15. package/docs/Reference/Type-Providers.md +1 -1
  16. package/docs/Reference/TypeScript.md +3 -3
  17. package/docs/index.md +2 -2
  18. package/examples/benchmark/parser.js +47 -0
  19. package/fastify.js +26 -23
  20. package/lib/contentTypeParser.js +10 -2
  21. package/lib/error-serializer.js +9 -162
  22. package/lib/errors.js +5 -0
  23. package/lib/hooks.js +3 -0
  24. package/lib/logger.js +51 -41
  25. package/lib/route.js +0 -1
  26. package/lib/server.js +9 -4
  27. package/lib/validation.js +10 -0
  28. package/lib/warnings.js +2 -0
  29. package/package.json +17 -9
  30. package/test/close.test.js +91 -0
  31. package/test/content-parser.test.js +101 -0
  32. package/test/hooks.test.js +2 -3
  33. package/test/pretty-print.test.js +194 -62
  34. package/test/route-hooks.test.js +29 -0
  35. package/test/route.test.js +1 -1
  36. package/test/schema-feature.test.js +178 -0
  37. package/test/serial/logger.0.test.js +861 -0
  38. package/test/serial/logger.1.test.js +862 -0
  39. package/test/serial/tap-parallel-not-ok +0 -0
  40. package/test/server.test.js +10 -0
  41. package/test/types/hooks.test-d.ts +66 -11
  42. package/test/types/import.js +1 -1
  43. package/test/types/instance.test-d.ts +4 -0
  44. package/test/types/route.test-d.ts +106 -5
  45. package/test/types/type-provider.test-d.ts +77 -10
  46. package/types/errors.d.ts +1 -0
  47. package/types/hooks.d.ts +28 -0
  48. package/types/instance.d.ts +22 -2
  49. package/types/logger.d.ts +1 -1
  50. package/types/route.d.ts +41 -11
  51. package/test/logger.test.js +0 -1691
@@ -15,7 +15,8 @@ test('pretty print - static routes', t => {
15
15
  fastify.ready(() => {
16
16
  const tree = fastify.printRoutes()
17
17
 
18
- const expected = `└── /
18
+ const expected = `\
19
+ └── /
19
20
  ├── test (GET)
20
21
  │ └── /hello (GET)
21
22
  └── hello/world (GET)
@@ -26,6 +27,41 @@ test('pretty print - static routes', t => {
26
27
  })
27
28
  })
28
29
 
30
+ test('pretty print - internal tree - static routes', t => {
31
+ t.plan(4)
32
+
33
+ const fastify = Fastify({ exposeHeadRoutes: false })
34
+ fastify.get('/test', () => {})
35
+ fastify.get('/test/hello', () => {})
36
+ fastify.get('/hello/world', () => {})
37
+
38
+ fastify.put('/test', () => {})
39
+ fastify.put('/test/foo', () => {})
40
+
41
+ fastify.ready(() => {
42
+ const getTree = fastify.printRoutes({ method: 'GET' })
43
+ const expectedGetTree = `\
44
+ └── /
45
+ ├── test (GET)
46
+ │ └── /hello (GET)
47
+ └── hello/world (GET)
48
+ `
49
+
50
+ t.equal(typeof getTree, 'string')
51
+ t.equal(getTree, expectedGetTree)
52
+
53
+ const putTree = fastify.printRoutes({ method: 'PUT' })
54
+ const expectedPutTree = `\
55
+ └── /
56
+ └── test (PUT)
57
+ └── /foo (PUT)
58
+ `
59
+
60
+ t.equal(typeof putTree, 'string')
61
+ t.equal(putTree, expectedPutTree)
62
+ })
63
+ })
64
+
29
65
  test('pretty print - parametric routes', t => {
30
66
  t.plan(2)
31
67
 
@@ -37,10 +73,13 @@ test('pretty print - parametric routes', t => {
37
73
  fastify.ready(() => {
38
74
  const tree = fastify.printRoutes()
39
75
 
40
- const expected = `└── /
76
+ const expected = `\
77
+ └── /
41
78
  ├── test (GET)
42
- │ └── /:hello (GET)
43
- └── hello/:world (GET)
79
+ │ └── /
80
+ └── :hello (GET)
81
+ └── hello/
82
+ └── :world (GET)
44
83
  `
45
84
 
46
85
  t.equal(typeof tree, 'string')
@@ -48,6 +87,44 @@ test('pretty print - parametric routes', t => {
48
87
  })
49
88
  })
50
89
 
90
+ test('pretty print - internal tree - parametric routes', t => {
91
+ t.plan(4)
92
+
93
+ const fastify = Fastify({ exposeHeadRoutes: false })
94
+ fastify.get('/test', () => {})
95
+ fastify.get('/test/:hello', () => {})
96
+ fastify.get('/hello/:world', () => {})
97
+
98
+ fastify.put('/test', () => {})
99
+ fastify.put('/test/:hello', () => {})
100
+
101
+ fastify.ready(() => {
102
+ const getTree = fastify.printRoutes({ method: 'GET' })
103
+ const expectedGetTree = `\
104
+ └── /
105
+ ├── test (GET)
106
+ │ └── /
107
+ │ └── :hello (GET)
108
+ └── hello/
109
+ └── :world (GET)
110
+ `
111
+
112
+ t.equal(typeof getTree, 'string')
113
+ t.equal(getTree, expectedGetTree)
114
+
115
+ const putTree = fastify.printRoutes({ method: 'PUT' })
116
+ const expectedPutTree = `\
117
+ └── /
118
+ └── test (PUT)
119
+ └── /
120
+ └── :hello (PUT)
121
+ `
122
+
123
+ t.equal(typeof putTree, 'string')
124
+ t.equal(putTree, expectedPutTree)
125
+ })
126
+ })
127
+
51
128
  test('pretty print - mixed parametric routes', t => {
52
129
  t.plan(2)
53
130
 
@@ -60,11 +137,12 @@ test('pretty print - mixed parametric routes', t => {
60
137
  fastify.ready(() => {
61
138
  const tree = fastify.printRoutes()
62
139
 
63
- const expected = `└── /test (GET)
64
- └── /
65
- └── :hello (GET)
66
- :hello (POST)
67
- └── /world (GET)
140
+ const expected = `\
141
+ └── /
142
+ └── test (GET)
143
+ └── /
144
+ └── :hello (GET, POST)
145
+ └── /world (GET)
68
146
  `
69
147
 
70
148
  t.equal(typeof tree, 'string')
@@ -83,10 +161,13 @@ test('pretty print - wildcard routes', t => {
83
161
  fastify.ready(() => {
84
162
  const tree = fastify.printRoutes()
85
163
 
86
- const expected = `└── /
164
+ const expected = `\
165
+ └── /
87
166
  ├── test (GET)
88
- │ └── /* (GET)
89
- └── hello/* (GET)
167
+ │ └── /
168
+ └── * (GET)
169
+ └── hello/
170
+ └── * (GET)
90
171
  `
91
172
 
92
173
  t.equal(typeof tree, 'string')
@@ -94,6 +175,44 @@ test('pretty print - wildcard routes', t => {
94
175
  })
95
176
  })
96
177
 
178
+ test('pretty print - internal tree - wildcard routes', t => {
179
+ t.plan(4)
180
+
181
+ const fastify = Fastify({ exposeHeadRoutes: false })
182
+ fastify.get('/test', () => {})
183
+ fastify.get('/test/*', () => {})
184
+ fastify.get('/hello/*', () => {})
185
+
186
+ fastify.put('/*', () => {})
187
+ fastify.put('/test/*', () => {})
188
+
189
+ fastify.ready(() => {
190
+ const getTree = fastify.printRoutes({ method: 'GET' })
191
+ const expectedGetTree = `\
192
+ └── /
193
+ ├── test (GET)
194
+ │ └── /
195
+ │ └── * (GET)
196
+ └── hello/
197
+ └── * (GET)
198
+ `
199
+
200
+ t.equal(typeof getTree, 'string')
201
+ t.equal(getTree, expectedGetTree)
202
+
203
+ const putTree = fastify.printRoutes({ method: 'PUT' })
204
+ const expectedPutTree = `\
205
+ └── /
206
+ ├── test/
207
+ │ └── * (PUT)
208
+ └── * (PUT)
209
+ `
210
+
211
+ t.equal(typeof putTree, 'string')
212
+ t.equal(putTree, expectedPutTree)
213
+ })
214
+ })
215
+
97
216
  test('pretty print - empty plugins', t => {
98
217
  t.plan(2)
99
218
 
@@ -134,17 +253,15 @@ test('pretty print - commonPrefix', t => {
134
253
  const radixTree = fastify.printRoutes()
135
254
  const flatTree = fastify.printRoutes({ commonPrefix: false })
136
255
 
137
- const radixExpected = `└── /
138
- ├── hel
139
- │ ├── lo (GET)
140
- │ │ lo (HEAD)
141
- └── icopter (GET)
142
- │ icopter (HEAD)
143
- └── hello (PUT)
256
+ const radixExpected = `\
257
+ └── /
258
+ └── hel
259
+ ├── lo (GET, HEAD, PUT)
260
+ └── icopter (GET, HEAD)
144
261
  `
145
- const flatExpected = `└── / (-)
146
- ├── helicopter (GET, HEAD)
147
- └── hello (GET, HEAD, PUT)
262
+ const flatExpected = `\
263
+ ├── /hello (GET, HEAD, PUT)
264
+ └── /helicopter (GET, HEAD)
148
265
  `
149
266
  t.equal(typeof radixTree, 'string')
150
267
  t.equal(typeof flatTree, 'string')
@@ -170,49 +287,64 @@ test('pretty print - includeMeta, includeHooks', t => {
170
287
  const flatTree = fastify.printRoutes({ commonPrefix: false, includeHooks: true, includeMeta: ['errorHandler'] })
171
288
  const hooksOnly = fastify.printRoutes({ commonPrefix: false, includeHooks: true })
172
289
 
173
- const radixExpected = `└── /
174
- ├── hel
175
- │ ├── lo (GET)
176
- │ │ • (onTimeout) ["onTimeout()"]
177
- • (onRequest) ["anonymous()"]
178
- • (errorHandler) "defaultErrorHandler()"
179
- │ lo (HEAD)
180
- │ • (onTimeout) ["onTimeout()"]
181
- • (onRequest) ["anonymous()"]
182
- • (onSend) ["headRouteOnSendHandler()"]
183
- • (errorHandler) "defaultErrorHandler()"
184
- └── icopter (GET)
185
- │ • (onTimeout) ["onTimeout()"]
186
- • (onRequest) ["anonymous()"]
187
- • (errorHandler) "defaultErrorHandler()"
188
- │ icopter (HEAD)
189
- │ • (onTimeout) ["onTimeout()"]
190
- • (onRequest) ["anonymous()"]
191
- • (onSend) ["headRouteOnSendHandler()"]
192
- • (errorHandler) "defaultErrorHandler()"
193
- └── hello (PUT)
194
- • (onTimeout) ["onTimeout()"]
195
- • (onRequest) ["anonymous()"]
196
- • (errorHandler) "defaultErrorHandler()"
290
+ const radixExpected = `\
291
+ └── /
292
+ └── hel
293
+ ├── lo (GET, PUT)
294
+ │ • (onTimeout) ["onTimeout()"]
295
+ │ • (onRequest) ["anonymous()"]
296
+ (errorHandler) "defaultErrorHandler()"
297
+ lo (HEAD)
298
+ │ • (onTimeout) ["onTimeout()"]
299
+ │ • (onRequest) ["anonymous()"]
300
+ │ • (onSend) ["headRouteOnSendHandler()"]
301
+ (errorHandler) "defaultErrorHandler()"
302
+ └── icopter (GET)
303
+ • (onTimeout) ["onTimeout()"]
304
+ • (onRequest) ["anonymous()"]
305
+ (errorHandler) "defaultErrorHandler()"
306
+ icopter (HEAD)
307
+ • (onTimeout) ["onTimeout()"]
308
+ • (onRequest) ["anonymous()"]
309
+ • (onSend) ["headRouteOnSendHandler()"]
310
+ (errorHandler) "defaultErrorHandler()"
197
311
  `
198
- const flatExpected = `└── / (-)
199
- ├── helicopter (GET, HEAD)
200
- │ • (onTimeout) ["onTimeout()"]
201
- │ • (onRequest) ["anonymous()"]
202
- │ • (errorHandler) "defaultErrorHandler()"
203
- └── hello (GET, HEAD, PUT)
204
- • (onTimeout) ["onTimeout()"]
205
- • (onRequest) ["anonymous()"]
206
- • (errorHandler) "defaultErrorHandler()"
312
+ const flatExpected = `\
313
+ ├── /hello (GET, PUT)
314
+ │ • (onTimeout) ["onTimeout()"]
315
+ │ • (onRequest) ["anonymous()"]
316
+ │ • (errorHandler) "defaultErrorHandler()"
317
+ │ /hello (HEAD)
318
+ • (onTimeout) ["onTimeout()"]
319
+ • (onRequest) ["anonymous()"]
320
+ • (onSend) ["headRouteOnSendHandler()"]
321
+ │ • (errorHandler) "defaultErrorHandler()"
322
+ └── /helicopter (GET)
323
+ • (onTimeout) ["onTimeout()"]
324
+ • (onRequest) ["anonymous()"]
325
+ • (errorHandler) "defaultErrorHandler()"
326
+ /helicopter (HEAD)
327
+ • (onTimeout) ["onTimeout()"]
328
+ • (onRequest) ["anonymous()"]
329
+ • (onSend) ["headRouteOnSendHandler()"]
330
+ • (errorHandler) "defaultErrorHandler()"
207
331
  `
208
332
 
209
- const hooksOnlyExpected = `└── / (-)
210
- ├── helicopter (GET, HEAD)
211
- │ • (onTimeout) ["onTimeout()"]
212
- │ • (onRequest) ["anonymous()"]
213
- └── hello (GET, HEAD, PUT)
214
- • (onTimeout) ["onTimeout()"]
215
- • (onRequest) ["anonymous()"]
333
+ const hooksOnlyExpected = `\
334
+ ├── /hello (GET, PUT)
335
+ │ • (onTimeout) ["onTimeout()"]
336
+ │ • (onRequest) ["anonymous()"]
337
+ │ /hello (HEAD)
338
+ • (onTimeout) ["onTimeout()"]
339
+ • (onRequest) ["anonymous()"]
340
+ │ • (onSend) ["headRouteOnSendHandler()"]
341
+ └── /helicopter (GET)
342
+ • (onTimeout) ["onTimeout()"]
343
+ • (onRequest) ["anonymous()"]
344
+ /helicopter (HEAD)
345
+ • (onTimeout) ["onTimeout()"]
346
+ • (onRequest) ["anonymous()"]
347
+ • (onSend) ["headRouteOnSendHandler()"]
216
348
  `
217
349
  t.equal(typeof radixTree, 'string')
218
350
  t.equal(typeof flatTree, 'string')
@@ -101,6 +101,35 @@ function testExecutionHook (hook) {
101
101
  })
102
102
  })
103
103
 
104
+ test(`${hook} option could accept an array of async functions`, t => {
105
+ t.plan(3)
106
+ const fastify = Fastify()
107
+ const checker = Object.defineProperty({ calledTimes: 0 }, 'check', {
108
+ get: function () { return ++this.calledTimes }
109
+ })
110
+
111
+ fastify.post('/', {
112
+ [hook]: [
113
+ async (req, reply) => {
114
+ t.equal(checker.check, 1)
115
+ },
116
+ async (req, reply) => {
117
+ t.equal(checker.check, 2)
118
+ }
119
+ ]
120
+ }, (req, reply) => {
121
+ reply.send({})
122
+ })
123
+
124
+ fastify.inject({
125
+ method: 'POST',
126
+ url: '/',
127
+ payload: { hello: 'world' }
128
+ }, (err, res) => {
129
+ t.error(err)
130
+ })
131
+ })
132
+
104
133
  test(`${hook} option does not interfere with ${hook} hook`, t => {
105
134
  t.plan(7)
106
135
  const fastify = Fastify()
@@ -1494,7 +1494,7 @@ test('exposeHeadRoute should not reuse the same route option', async t => {
1494
1494
  })
1495
1495
  })
1496
1496
 
1497
- test('using fastify.all when a catchall is defined does not degrade performance', { timeout: 5000 }, async t => {
1497
+ test('using fastify.all when a catchall is defined does not degrade performance', { timeout: 30000 }, async t => {
1498
1498
  t.plan(1)
1499
1499
 
1500
1500
  const fastify = Fastify()
@@ -6,6 +6,7 @@ const fp = require('fastify-plugin')
6
6
  const deepClone = require('rfdc')({ circles: true, proto: false })
7
7
  const Ajv = require('ajv')
8
8
  const { kSchemaController } = require('../lib/symbols.js')
9
+ const warning = require('../lib/warnings')
9
10
 
10
11
  const echoParams = (req, reply) => { reply.send(req.params) }
11
12
  const echoBody = (req, reply) => { reply.send(req.body) }
@@ -253,6 +254,183 @@ test('Should not change the input schemas', t => {
253
254
  })
254
255
  })
255
256
 
257
+ test('Should emit warning if the schema headers is undefined', t => {
258
+ t.plan(4)
259
+ const fastify = Fastify()
260
+
261
+ process.on('warning', onWarning)
262
+ function onWarning (warning) {
263
+ t.equal(warning.name, 'FastifyWarning')
264
+ t.equal(warning.code, 'FSTWRN001')
265
+ }
266
+
267
+ t.teardown(() => {
268
+ process.removeListener('warning', onWarning)
269
+ warning.emitted.set('FSTWRN001', false)
270
+ })
271
+
272
+ fastify.post('/:id', {
273
+ handler: echoParams,
274
+ schema: {
275
+ headers: undefined
276
+ }
277
+ })
278
+
279
+ fastify.inject({
280
+ method: 'POST',
281
+ url: '/123'
282
+ }, (error, res) => {
283
+ t.error(error)
284
+ t.equal(res.statusCode, 200)
285
+ })
286
+ })
287
+
288
+ test('Should emit warning if the schema body is undefined', t => {
289
+ t.plan(4)
290
+ const fastify = Fastify()
291
+
292
+ process.on('warning', onWarning)
293
+ function onWarning (warning) {
294
+ t.equal(warning.name, 'FastifyWarning')
295
+ t.equal(warning.code, 'FSTWRN001')
296
+ }
297
+
298
+ t.teardown(() => {
299
+ process.removeListener('warning', onWarning)
300
+ warning.emitted.set('FSTWRN001', false)
301
+ })
302
+
303
+ fastify.post('/:id', {
304
+ handler: echoParams,
305
+ schema: {
306
+ body: undefined
307
+ }
308
+ })
309
+
310
+ fastify.inject({
311
+ method: 'POST',
312
+ url: '/123'
313
+ }, (error, res) => {
314
+ t.error(error)
315
+ t.equal(res.statusCode, 200)
316
+ })
317
+ })
318
+
319
+ test('Should emit warning if the schema query is undefined', t => {
320
+ t.plan(4)
321
+ const fastify = Fastify()
322
+
323
+ process.on('warning', onWarning)
324
+ function onWarning (warning) {
325
+ t.equal(warning.name, 'FastifyWarning')
326
+ t.equal(warning.code, 'FSTWRN001')
327
+ }
328
+
329
+ t.teardown(() => {
330
+ process.removeListener('warning', onWarning)
331
+ warning.emitted.set('FSTWRN001', false)
332
+ })
333
+
334
+ fastify.post('/:id', {
335
+ handler: echoParams,
336
+ schema: {
337
+ querystring: undefined
338
+ }
339
+ })
340
+
341
+ fastify.inject({
342
+ method: 'POST',
343
+ url: '/123'
344
+ }, (error, res) => {
345
+ t.error(error)
346
+ t.equal(res.statusCode, 200)
347
+ })
348
+ })
349
+
350
+ test('Should emit warning if the schema params is undefined', t => {
351
+ t.plan(4)
352
+ const fastify = Fastify()
353
+
354
+ process.on('warning', onWarning)
355
+ function onWarning (warning) {
356
+ t.equal(warning.name, 'FastifyWarning')
357
+ t.equal(warning.code, 'FSTWRN001')
358
+ }
359
+
360
+ t.teardown(() => {
361
+ process.removeListener('warning', onWarning)
362
+ warning.emitted.set('FSTWRN001', false)
363
+ })
364
+
365
+ fastify.post('/:id', {
366
+ handler: echoParams,
367
+ schema: {
368
+ params: undefined
369
+ }
370
+ })
371
+
372
+ fastify.inject({
373
+ method: 'POST',
374
+ url: '/123'
375
+ }, (error, res) => {
376
+ t.error(error)
377
+ t.equal(res.statusCode, 200)
378
+ })
379
+ })
380
+
381
+ test('Should emit a warning for every route with undefined schema', t => {
382
+ t.plan(16)
383
+ const fastify = Fastify()
384
+
385
+ let runs = 0
386
+ const expectedWarningEmitted = [0, 1, 2, 3]
387
+ // It emits 4 warnings:
388
+ // - 2 - GET and HEAD for /undefinedParams/:id
389
+ // - 2 - GET and HEAD for /undefinedBody/:id
390
+ // => 3 x 4 assertions = 12 assertions
391
+ function onWarning (warning) {
392
+ t.equal(warning.name, 'FastifyWarning')
393
+ t.equal(warning.code, 'FSTWRN001')
394
+ t.equal(runs++, expectedWarningEmitted.shift())
395
+ }
396
+
397
+ process.on('warning', onWarning)
398
+ t.teardown(() => {
399
+ process.removeListener('warning', onWarning)
400
+ warning.emitted.set('FSTWRN001', false)
401
+ })
402
+
403
+ fastify.get('/undefinedParams/:id', {
404
+ handler: echoParams,
405
+ schema: {
406
+ params: undefined
407
+ }
408
+ })
409
+
410
+ fastify.get('/undefinedBody/:id', {
411
+ handler: echoParams,
412
+ schema: {
413
+ body: undefined
414
+ }
415
+ })
416
+
417
+ fastify.inject({
418
+ method: 'GET',
419
+ url: '/undefinedParams/123'
420
+ }, (error, res) => {
421
+ t.error(error)
422
+ t.equal(res.statusCode, 200)
423
+ })
424
+
425
+ fastify.inject({
426
+ method: 'GET',
427
+ url: '/undefinedBody/123'
428
+ }, (error, res) => {
429
+ t.error(error)
430
+ t.equal(res.statusCode, 200)
431
+ })
432
+ })
433
+
256
434
  test('First level $ref', t => {
257
435
  t.plan(2)
258
436
  const fastify = Fastify()