fastify 4.14.1 → 4.15.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.
- package/README.md +5 -1
- package/docs/Reference/Errors.md +6 -0
- package/docs/Reference/Server.md +83 -32
- package/fastify.js +1 -1
- package/lib/contentTypeParser.js +10 -2
- package/lib/errors.js +5 -0
- package/lib/logger.js +51 -41
- package/lib/route.js +0 -1
- package/lib/validation.js +8 -0
- package/package.json +12 -5
- package/test/content-parser.test.js +101 -0
- package/test/hooks.test.js +2 -3
- package/test/logger.test.js +31 -1
- package/test/pretty-print.test.js +194 -62
- package/test/schema-feature.test.js +85 -0
- package/test/types/instance.test-d.ts +2 -0
- package/types/errors.d.ts +1 -0
- package/types/instance.d.ts +2 -1
package/README.md
CHANGED
|
@@ -304,6 +304,8 @@ listed in alphabetical order.
|
|
|
304
304
|
<https://twitter.com/ayoubelkh>, <https://www.npmjs.com/~ayoubelk>
|
|
305
305
|
* [__Denis Fäcke__](https://github.com/SerayaEryn),
|
|
306
306
|
<https://twitter.com/serayaeryn>, <https://www.npmjs.com/~serayaeryn>
|
|
307
|
+
* [__Carlos Fuentes__](https://github.com/metcoder95),
|
|
308
|
+
<https://twitter.com/metcoder95>, <https://www.npmjs.com/~metcoder95>
|
|
307
309
|
* [__Rafael Gonzaga__](https://github.com/rafaelgss),
|
|
308
310
|
<https://twitter.com/_rafaelgss>, <https://www.npmjs.com/~rafaelgss>
|
|
309
311
|
* [__Vincent Le Goff__](https://github.com/zekth)
|
|
@@ -327,6 +329,8 @@ listed in alphabetical order.
|
|
|
327
329
|
<https://twitter.com/delvedor>, <https://www.npmjs.com/~delvedor>
|
|
328
330
|
* [__Ayoub El Khattabi__](https://github.com/AyoubElk),
|
|
329
331
|
<https://twitter.com/ayoubelkh>, <https://www.npmjs.com/~ayoubelk>
|
|
332
|
+
* [__Carlos Fuentes__](https://github.com/metcoder95),
|
|
333
|
+
<https://twitter.com/metcoder95>, <https://www.npmjs.com/~metcoder95>
|
|
330
334
|
* [__Vincent Le Goff__](https://github.com/zekth)
|
|
331
335
|
* [__Salman Mitha__](https://github.com/salmanm),
|
|
332
336
|
<https://www.npmjs.com/~salmanm>
|
|
@@ -338,7 +342,7 @@ listed in alphabetical order.
|
|
|
338
342
|
* [__Rafael Gonzaga__](https://github.com/rafaelgss),
|
|
339
343
|
<https://twitter.com/_rafaelgss>, <https://www.npmjs.com/~rafaelgss>
|
|
340
344
|
* [__Simone Busoli__](https://github.com/simoneb),
|
|
341
|
-
<https://twitter.com/simonebu>, <https://www.npmjs.com/~simoneb>
|
|
345
|
+
<https://twitter.com/simonebu>, <https://www.npmjs.com/~simoneb>
|
|
342
346
|
|
|
343
347
|
### Great Contributors
|
|
344
348
|
Great contributors on a specific area in the Fastify ecosystem will be invited
|
package/docs/Reference/Errors.md
CHANGED
|
@@ -281,6 +281,12 @@ A callback for a hook timed out
|
|
|
281
281
|
|
|
282
282
|
The logger accepts either a `'stream'` or a `'file'` as the destination.
|
|
283
283
|
|
|
284
|
+
#### FST_ERR_LOG_INVALID_LOGGER
|
|
285
|
+
<a id="FST_ERR_LOG_INVALID_LOGGER"></a>
|
|
286
|
+
|
|
287
|
+
The logger should have all these methods: `'info'`, `'error'`,
|
|
288
|
+
`'debug'`, `'fatal'`, `'warn'`, `'trace'`, `'child'`.
|
|
289
|
+
|
|
284
290
|
#### FST_ERR_REP_INVALID_PAYLOAD_TYPE
|
|
285
291
|
<a id="FST_ERR_REP_INVALID_PAYLOAD_TYPE"></a>
|
|
286
292
|
|
package/docs/Reference/Server.md
CHANGED
|
@@ -1545,35 +1545,61 @@ a custom constraint strategy with the same name.
|
|
|
1545
1545
|
#### printRoutes
|
|
1546
1546
|
<a id="print-routes"></a>
|
|
1547
1547
|
|
|
1548
|
-
`fastify.printRoutes()`:
|
|
1549
|
-
|
|
1550
|
-
|
|
1548
|
+
`fastify.printRoutes()`: Fastify router builds a tree of routes for each HTTP
|
|
1549
|
+
method. If you call the prettyPrint without specifying an HTTP method, it will
|
|
1550
|
+
merge all the trees into one and print it. The merged tree doesn't represent the
|
|
1551
|
+
internal router structure. **Don't use it for debugging.**
|
|
1551
1552
|
|
|
1552
1553
|
*Remember to call it inside or after a `ready` call.*
|
|
1553
1554
|
|
|
1554
1555
|
```js
|
|
1555
1556
|
fastify.get('/test', () => {})
|
|
1556
1557
|
fastify.get('/test/hello', () => {})
|
|
1557
|
-
fastify.get('/
|
|
1558
|
-
fastify.get('/
|
|
1558
|
+
fastify.get('/testing', () => {})
|
|
1559
|
+
fastify.get('/testing/:param', () => {})
|
|
1560
|
+
fastify.put('/update', () => {})
|
|
1559
1561
|
|
|
1560
1562
|
fastify.ready(() => {
|
|
1561
1563
|
console.log(fastify.printRoutes())
|
|
1562
1564
|
// └── /
|
|
1563
1565
|
// ├── test (GET)
|
|
1564
|
-
// │
|
|
1565
|
-
// └──
|
|
1566
|
-
//
|
|
1567
|
-
//
|
|
1566
|
+
// │ ├── /hello (GET)
|
|
1567
|
+
// │ └── ing (GET)
|
|
1568
|
+
// │ └── /
|
|
1569
|
+
// │ └── :param (GET)
|
|
1570
|
+
// └── update (PUT)
|
|
1571
|
+
})
|
|
1572
|
+
```
|
|
1568
1573
|
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
// │ └── /hello (GET)
|
|
1573
|
-
// ├── hello/world (GET)
|
|
1574
|
-
// └── helicopter (GET)
|
|
1574
|
+
If you want to print the internal router tree, you should specify the `method`
|
|
1575
|
+
param. Printed tree will represent the internal router structure.
|
|
1576
|
+
**You can use it for debugging.**
|
|
1575
1577
|
|
|
1576
|
-
|
|
1578
|
+
```js
|
|
1579
|
+
console.log(fastify.printRoutes({ method: 'GET' }))
|
|
1580
|
+
// └── /
|
|
1581
|
+
// └── test (GET)
|
|
1582
|
+
// ├── /hello (GET)
|
|
1583
|
+
// └── ing (GET)
|
|
1584
|
+
// └── /
|
|
1585
|
+
// └── :param (GET)
|
|
1586
|
+
|
|
1587
|
+
console.log(fastify.printRoutes({ method: 'PUT' }))
|
|
1588
|
+
// └── /
|
|
1589
|
+
// └── update (PUT)
|
|
1590
|
+
```
|
|
1591
|
+
|
|
1592
|
+
`fastify.printRoutes({ commonPrefix: false })` will print compressed trees. This
|
|
1593
|
+
might useful when you have a large number of routes with common prefixes.
|
|
1594
|
+
It doesn't represent the internal router structure. **Don't use it for debugging.**
|
|
1595
|
+
|
|
1596
|
+
```js
|
|
1597
|
+
console.log(fastify.printRoutes({ commonPrefix: false }))
|
|
1598
|
+
// ├── /test (GET)
|
|
1599
|
+
// │ ├── /hello (GET)
|
|
1600
|
+
// │ └── ing (GET)
|
|
1601
|
+
// │ └── /:param (GET)
|
|
1602
|
+
// └── /update (PUT)
|
|
1577
1603
|
```
|
|
1578
1604
|
|
|
1579
1605
|
`fastify.printRoutes({ includeMeta: (true | []) })` will display properties from
|
|
@@ -1583,26 +1609,51 @@ A shorthand option, `fastify.printRoutes({ includeHooks: true })` will include
|
|
|
1583
1609
|
all [hooks](./Hooks.md).
|
|
1584
1610
|
|
|
1585
1611
|
```js
|
|
1586
|
-
|
|
1612
|
+
fastify.get('/test', () => {})
|
|
1613
|
+
fastify.get('/test/hello', () => {})
|
|
1614
|
+
|
|
1615
|
+
const onTimeout = () => {}
|
|
1616
|
+
|
|
1617
|
+
fastify.addHook('onRequest', () => {})
|
|
1618
|
+
fastify.addHook('onTimeout', onTimeout)
|
|
1619
|
+
|
|
1620
|
+
console.log(fastify.printRoutes({ includeHooks: true, includeMeta: ['errorHandler'] }))
|
|
1587
1621
|
// └── /
|
|
1588
|
-
//
|
|
1589
|
-
//
|
|
1590
|
-
//
|
|
1591
|
-
//
|
|
1592
|
-
//
|
|
1593
|
-
//
|
|
1594
|
-
//
|
|
1595
|
-
//
|
|
1622
|
+
// └── test (GET)
|
|
1623
|
+
// • (onTimeout) ["onTimeout()"]
|
|
1624
|
+
// • (onRequest) ["anonymous()"]
|
|
1625
|
+
// • (errorHandler) "defaultErrorHandler()"
|
|
1626
|
+
// test (HEAD)
|
|
1627
|
+
// • (onTimeout) ["onTimeout()"]
|
|
1628
|
+
// • (onRequest) ["anonymous()"]
|
|
1629
|
+
// • (onSend) ["headRouteOnSendHandler()"]
|
|
1630
|
+
// • (errorHandler) "defaultErrorHandler()"
|
|
1631
|
+
// └── /hello (GET)
|
|
1632
|
+
// • (onTimeout) ["onTimeout()"]
|
|
1633
|
+
// • (onRequest) ["anonymous()"]
|
|
1634
|
+
// • (errorHandler) "defaultErrorHandler()"
|
|
1635
|
+
// /hello (HEAD)
|
|
1636
|
+
// • (onTimeout) ["onTimeout()"]
|
|
1637
|
+
// • (onRequest) ["anonymous()"]
|
|
1638
|
+
// • (onSend) ["headRouteOnSendHandler()"]
|
|
1639
|
+
// • (errorHandler) "defaultErrorHandler()"
|
|
1596
1640
|
|
|
1597
1641
|
console.log(fastify.printRoutes({ includeHooks: true }))
|
|
1598
1642
|
// └── /
|
|
1599
|
-
//
|
|
1600
|
-
//
|
|
1601
|
-
//
|
|
1602
|
-
//
|
|
1603
|
-
//
|
|
1604
|
-
//
|
|
1605
|
-
//
|
|
1643
|
+
// └── test (GET)
|
|
1644
|
+
// • (onTimeout) ["onTimeout()"]
|
|
1645
|
+
// • (onRequest) ["anonymous()"]
|
|
1646
|
+
// test (HEAD)
|
|
1647
|
+
// • (onTimeout) ["onTimeout()"]
|
|
1648
|
+
// • (onRequest) ["anonymous()"]
|
|
1649
|
+
// • (onSend) ["headRouteOnSendHandler()"]
|
|
1650
|
+
// └── /hello (GET)
|
|
1651
|
+
// • (onTimeout) ["onTimeout()"]
|
|
1652
|
+
// • (onRequest) ["anonymous()"]
|
|
1653
|
+
// /hello (HEAD)
|
|
1654
|
+
// • (onTimeout) ["onTimeout()"]
|
|
1655
|
+
// • (onRequest) ["anonymous()"]
|
|
1656
|
+
// • (onSend) ["headRouteOnSendHandler()"]
|
|
1606
1657
|
```
|
|
1607
1658
|
|
|
1608
1659
|
#### printPlugins
|
package/fastify.js
CHANGED
package/lib/contentTypeParser.js
CHANGED
|
@@ -396,10 +396,18 @@ function ParserListItem (contentType) {
|
|
|
396
396
|
// we pre-calculate all the needed information
|
|
397
397
|
// before content-type comparsion
|
|
398
398
|
const parsed = safeParseContentType(contentType)
|
|
399
|
-
this.
|
|
399
|
+
this.isEssence = contentType.indexOf(';') === -1
|
|
400
|
+
// we should not allow empty string for parser list item
|
|
401
|
+
// because it would become a match-all handler
|
|
402
|
+
if (this.isEssence === false && parsed.type === '') {
|
|
403
|
+
// handle semicolon or empty string
|
|
404
|
+
const tmp = contentType.split(';')[0]
|
|
405
|
+
this.type = tmp === '' ? contentType : tmp
|
|
406
|
+
} else {
|
|
407
|
+
this.type = parsed.type
|
|
408
|
+
}
|
|
400
409
|
this.parameters = parsed.parameters
|
|
401
410
|
this.parameterKeys = Object.keys(parsed.parameters)
|
|
402
|
-
this.isEssence = contentType.indexOf(';') === -1
|
|
403
411
|
}
|
|
404
412
|
|
|
405
413
|
// used in ContentTypeParser.remove
|
package/lib/errors.js
CHANGED
|
@@ -177,6 +177,11 @@ const codes = {
|
|
|
177
177
|
'Cannot specify both logger.stream and logger.file options'
|
|
178
178
|
),
|
|
179
179
|
|
|
180
|
+
FST_ERR_LOG_INVALID_LOGGER: createError(
|
|
181
|
+
'FST_ERR_LOG_INVALID_LOGGER',
|
|
182
|
+
"Invalid logger object provided. The logger instance should have these functions(s): '%s'."
|
|
183
|
+
),
|
|
184
|
+
|
|
180
185
|
/**
|
|
181
186
|
* reply
|
|
182
187
|
*/
|
package/lib/logger.js
CHANGED
|
@@ -9,17 +9,17 @@
|
|
|
9
9
|
const nullLogger = require('abstract-logging')
|
|
10
10
|
const pino = require('pino')
|
|
11
11
|
const { serializersSym } = pino.symbols
|
|
12
|
-
const {
|
|
12
|
+
const {
|
|
13
|
+
FST_ERR_LOG_INVALID_DESTINATION,
|
|
14
|
+
FST_ERR_LOG_INVALID_LOGGER
|
|
15
|
+
} = require('./errors')
|
|
13
16
|
|
|
14
|
-
function createPinoLogger (opts
|
|
15
|
-
|
|
16
|
-
delete opts.stream
|
|
17
|
-
|
|
18
|
-
if (stream && opts.file) {
|
|
17
|
+
function createPinoLogger (opts) {
|
|
18
|
+
if (opts.stream && opts.file) {
|
|
19
19
|
throw new FST_ERR_LOG_INVALID_DESTINATION()
|
|
20
20
|
} else if (opts.file) {
|
|
21
21
|
// we do not have stream
|
|
22
|
-
stream = pino.destination(opts.file)
|
|
22
|
+
opts.stream = pino.destination(opts.file)
|
|
23
23
|
delete opts.file
|
|
24
24
|
}
|
|
25
25
|
|
|
@@ -39,7 +39,7 @@ function createPinoLogger (opts, stream) {
|
|
|
39
39
|
opts.logger = prevLogger
|
|
40
40
|
opts.genReqId = prevGenReqId
|
|
41
41
|
} else {
|
|
42
|
-
logger = pino(opts, stream)
|
|
42
|
+
logger = pino(opts, opts.stream)
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
return logger
|
|
@@ -70,50 +70,60 @@ function now () {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
function createLogger (options) {
|
|
73
|
-
if (
|
|
73
|
+
if (!options.logger) {
|
|
74
|
+
const logger = nullLogger
|
|
75
|
+
logger.child = () => logger
|
|
76
|
+
return { logger, hasLogger: false }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (validateLogger(options.logger)) {
|
|
74
80
|
const logger = createPinoLogger({
|
|
75
81
|
logger: options.logger,
|
|
76
82
|
serializers: Object.assign({}, serializers, options.logger.serializers)
|
|
77
83
|
})
|
|
78
84
|
return { logger, hasLogger: true }
|
|
79
|
-
} else if (!options.logger) {
|
|
80
|
-
const logger = nullLogger
|
|
81
|
-
logger.child = () => logger
|
|
82
|
-
return { logger, hasLogger: false }
|
|
83
|
-
} else {
|
|
84
|
-
const localLoggerOptions = {}
|
|
85
|
-
if (Object.prototype.toString.call(options.logger) === '[object Object]') {
|
|
86
|
-
Reflect.ownKeys(options.logger).forEach(prop => {
|
|
87
|
-
Object.defineProperty(localLoggerOptions, prop, {
|
|
88
|
-
value: options.logger[prop],
|
|
89
|
-
writable: true,
|
|
90
|
-
enumerable: true,
|
|
91
|
-
configurable: true
|
|
92
|
-
})
|
|
93
|
-
})
|
|
94
|
-
}
|
|
95
|
-
localLoggerOptions.level = localLoggerOptions.level || 'info'
|
|
96
|
-
localLoggerOptions.serializers = Object.assign({}, serializers, localLoggerOptions.serializers)
|
|
97
|
-
options.logger = localLoggerOptions
|
|
98
|
-
const logger = createPinoLogger(options.logger)
|
|
99
|
-
return { logger, hasLogger: true }
|
|
100
85
|
}
|
|
101
|
-
}
|
|
102
86
|
|
|
103
|
-
|
|
104
|
-
if (
|
|
105
|
-
|
|
87
|
+
const localLoggerOptions = {}
|
|
88
|
+
if (Object.prototype.toString.call(options.logger) === '[object Object]') {
|
|
89
|
+
Reflect.ownKeys(options.logger).forEach(prop => {
|
|
90
|
+
Object.defineProperty(localLoggerOptions, prop, {
|
|
91
|
+
value: options.logger[prop],
|
|
92
|
+
writable: true,
|
|
93
|
+
enumerable: true,
|
|
94
|
+
configurable: true
|
|
95
|
+
})
|
|
96
|
+
})
|
|
106
97
|
}
|
|
98
|
+
localLoggerOptions.level = localLoggerOptions.level || 'info'
|
|
99
|
+
localLoggerOptions.serializers = Object.assign({}, serializers, localLoggerOptions.serializers)
|
|
100
|
+
options.logger = localLoggerOptions
|
|
101
|
+
const logger = createPinoLogger(options.logger)
|
|
102
|
+
return { logger, hasLogger: true }
|
|
103
|
+
}
|
|
107
104
|
|
|
108
|
-
|
|
105
|
+
/**
|
|
106
|
+
* Determines if a provided logger object meets the requirements
|
|
107
|
+
* of a Fastify compatible logger.
|
|
108
|
+
*
|
|
109
|
+
* @param {object} logger Object to validate.
|
|
110
|
+
*
|
|
111
|
+
* @returns {boolean} `true` when the logger meets the requirements.
|
|
112
|
+
*
|
|
113
|
+
* @throws {FST_ERR_LOG_INVALID_LOGGER} When the logger object is
|
|
114
|
+
* missing required methods.
|
|
115
|
+
*/
|
|
116
|
+
function validateLogger (logger) {
|
|
109
117
|
const methods = ['info', 'error', 'debug', 'fatal', 'warn', 'trace', 'child']
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
118
|
+
const missingMethods = methods.filter(method => !logger[method] || typeof logger[method] !== 'function')
|
|
119
|
+
|
|
120
|
+
if (!missingMethods.length) {
|
|
121
|
+
return true
|
|
122
|
+
} else if (missingMethods.length === methods.length) {
|
|
123
|
+
return false
|
|
124
|
+
} else {
|
|
125
|
+
throw FST_ERR_LOG_INVALID_LOGGER(missingMethods.join(','))
|
|
115
126
|
}
|
|
116
|
-
return result
|
|
117
127
|
}
|
|
118
128
|
|
|
119
129
|
module.exports = {
|
package/lib/route.js
CHANGED
package/lib/validation.js
CHANGED
|
@@ -80,18 +80,26 @@ function compileSchemasForValidation (context, compile, isCustom) {
|
|
|
80
80
|
})
|
|
81
81
|
}
|
|
82
82
|
context[headersSchema] = compile({ schema: headersSchemaLowerCase, method, url, httpPart: 'headers' })
|
|
83
|
+
} else if (Object.hasOwnProperty.call(schema, 'headers')) {
|
|
84
|
+
throw new Error('headers schema is undefined')
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
if (schema.body) {
|
|
86
88
|
context[bodySchema] = compile({ schema: schema.body, method, url, httpPart: 'body' })
|
|
89
|
+
} else if (Object.hasOwnProperty.call(schema, 'body')) {
|
|
90
|
+
throw new Error('body schema is undefined')
|
|
87
91
|
}
|
|
88
92
|
|
|
89
93
|
if (schema.querystring) {
|
|
90
94
|
context[querystringSchema] = compile({ schema: schema.querystring, method, url, httpPart: 'querystring' })
|
|
95
|
+
} else if (Object.hasOwnProperty.call(schema, 'querystring')) {
|
|
96
|
+
throw new Error('querystring schema is undefined')
|
|
91
97
|
}
|
|
92
98
|
|
|
93
99
|
if (schema.params) {
|
|
94
100
|
context[paramsSchema] = compile({ schema: schema.params, method, url, httpPart: 'params' })
|
|
101
|
+
} else if (Object.hasOwnProperty.call(schema, 'params')) {
|
|
102
|
+
throw new Error('params schema is undefined')
|
|
95
103
|
}
|
|
96
104
|
}
|
|
97
105
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastify",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.15.0",
|
|
4
4
|
"description": "Fast and low overhead web framework, for Node.js",
|
|
5
5
|
"main": "fastify.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"scripts": {
|
|
9
9
|
"bench": "branchcmp -r 2 -g -s \"npm run benchmark\"",
|
|
10
10
|
"benchmark": "npx concurrently -k -s first \"node ./examples/benchmark/simple.js\" \"npx autocannon -c 100 -d 30 -p 10 localhost:3000/\"",
|
|
11
|
+
"build:validation": "node build/build-error-serializer.js && node build/build-validation.js",
|
|
11
12
|
"coverage": "npm run unit -- --cov --coverage-report=html",
|
|
12
13
|
"coverage:ci": "npm run unit -- --cov --coverage-report=html --no-browser --no-check-coverage -R terse",
|
|
13
14
|
"coverage:ci-check-coverage": "nyc check-coverage --branches 100 --functions 100 --lines 100 --statements 100",
|
|
@@ -17,10 +18,11 @@
|
|
|
17
18
|
"lint:markdown": "markdownlint-cli2",
|
|
18
19
|
"lint:standard": "standard | snazzy",
|
|
19
20
|
"lint:typescript": "eslint -c types/.eslintrc.json types/**/*.d.ts test/types/**/*.test-d.ts",
|
|
20
|
-
"prepublishOnly": "PREPUBLISH=true tap --no-check-coverage test/build/**.test.js",
|
|
21
|
+
"prepublishOnly": "PREPUBLISH=true tap --no-check-coverage test/build/**.test.js && npm run test:validator:integrity",
|
|
21
22
|
"test": "npm run lint && npm run unit && npm run test:typescript",
|
|
22
23
|
"test:ci": "npm run unit -- -R terse --cov --coverage-report=lcovonly && npm run test:typescript",
|
|
23
24
|
"test:report": "npm run lint && npm run unit:report && npm run test:typescript",
|
|
25
|
+
"test:validator:integrity": "npm run build:validation && git diff --quiet --ignore-all-space --ignore-blank-lines --ignore-cr-at-eol lib/error-serializer.js && git diff --quiet --ignore-all-space --ignore-blank-lines --ignore-cr-at-eol lib/configValidator.js",
|
|
24
26
|
"test:typescript": "tsc test/types/import.ts && tsd",
|
|
25
27
|
"test:watch": "npm run unit -- -w --no-coverage-report -R terse",
|
|
26
28
|
"unit": "tap",
|
|
@@ -117,6 +119,11 @@
|
|
|
117
119
|
"name": "Luis Orbaiceta",
|
|
118
120
|
"email": "luisorbaiceta@gmail.com",
|
|
119
121
|
"url": "https://luisorbaiceta.com"
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"name": "Carlos Fuentes",
|
|
125
|
+
"email": "me@metcoder.dev",
|
|
126
|
+
"url": "https://metcoder.dev"
|
|
120
127
|
}
|
|
121
128
|
],
|
|
122
129
|
"license": "MIT",
|
|
@@ -164,7 +171,7 @@
|
|
|
164
171
|
"split2": "^4.1.0",
|
|
165
172
|
"standard": "^17.0.0",
|
|
166
173
|
"tap": "^16.3.0",
|
|
167
|
-
"tsd": "^0.
|
|
174
|
+
"tsd": "^0.26.0",
|
|
168
175
|
"typescript": "^4.8.3",
|
|
169
176
|
"undici": "^5.10.0",
|
|
170
177
|
"vary": "^1.1.2",
|
|
@@ -173,11 +180,11 @@
|
|
|
173
180
|
"dependencies": {
|
|
174
181
|
"@fastify/ajv-compiler": "^3.5.0",
|
|
175
182
|
"@fastify/error": "^3.0.0",
|
|
176
|
-
"@fastify/fast-json-stringify-compiler": "^4.
|
|
183
|
+
"@fastify/fast-json-stringify-compiler": "^4.2.0",
|
|
177
184
|
"abstract-logging": "^2.0.1",
|
|
178
185
|
"avvio": "^8.2.0",
|
|
179
186
|
"fast-content-type-parse": "^1.0.0",
|
|
180
|
-
"find-my-way": "^7.
|
|
187
|
+
"find-my-way": "^7.6.0",
|
|
181
188
|
"light-my-request": "^5.6.1",
|
|
182
189
|
"pino": "^8.5.0",
|
|
183
190
|
"process-warning": "^2.0.0",
|
|
@@ -649,3 +649,104 @@ test('content-type regexp list should be cloned when plugin override', async t =
|
|
|
649
649
|
t.same(payload, 'png')
|
|
650
650
|
}
|
|
651
651
|
})
|
|
652
|
+
|
|
653
|
+
test('allow partial content-type - essence check', async t => {
|
|
654
|
+
t.plan(1)
|
|
655
|
+
|
|
656
|
+
const fastify = Fastify()
|
|
657
|
+
fastify.removeAllContentTypeParsers()
|
|
658
|
+
fastify.addContentTypeParser('json', function (request, body, done) {
|
|
659
|
+
t.pass('should be called')
|
|
660
|
+
done(null, body)
|
|
661
|
+
})
|
|
662
|
+
|
|
663
|
+
fastify.post('/', async () => {
|
|
664
|
+
return 'ok'
|
|
665
|
+
})
|
|
666
|
+
|
|
667
|
+
await fastify.inject({
|
|
668
|
+
method: 'POST',
|
|
669
|
+
path: '/',
|
|
670
|
+
headers: {
|
|
671
|
+
'content-type': 'application/json; foo=bar; charset=utf8'
|
|
672
|
+
},
|
|
673
|
+
body: ''
|
|
674
|
+
})
|
|
675
|
+
|
|
676
|
+
await fastify.inject({
|
|
677
|
+
method: 'POST',
|
|
678
|
+
path: '/',
|
|
679
|
+
headers: {
|
|
680
|
+
'content-type': 'image/jpeg'
|
|
681
|
+
},
|
|
682
|
+
body: ''
|
|
683
|
+
})
|
|
684
|
+
})
|
|
685
|
+
|
|
686
|
+
test('allow partial content-type - not essence check', async t => {
|
|
687
|
+
t.plan(1)
|
|
688
|
+
|
|
689
|
+
const fastify = Fastify()
|
|
690
|
+
fastify.removeAllContentTypeParsers()
|
|
691
|
+
fastify.addContentTypeParser('json;', function (request, body, done) {
|
|
692
|
+
t.pass('should be called')
|
|
693
|
+
done(null, body)
|
|
694
|
+
})
|
|
695
|
+
|
|
696
|
+
fastify.post('/', async () => {
|
|
697
|
+
return 'ok'
|
|
698
|
+
})
|
|
699
|
+
|
|
700
|
+
await fastify.inject({
|
|
701
|
+
method: 'POST',
|
|
702
|
+
path: '/',
|
|
703
|
+
headers: {
|
|
704
|
+
'content-type': 'application/json; foo=bar; charset=utf8'
|
|
705
|
+
},
|
|
706
|
+
body: ''
|
|
707
|
+
})
|
|
708
|
+
|
|
709
|
+
await fastify.inject({
|
|
710
|
+
method: 'POST',
|
|
711
|
+
path: '/',
|
|
712
|
+
headers: {
|
|
713
|
+
'content-type': 'image/jpeg'
|
|
714
|
+
},
|
|
715
|
+
body: ''
|
|
716
|
+
})
|
|
717
|
+
})
|
|
718
|
+
|
|
719
|
+
test('edge case content-type - ;', async t => {
|
|
720
|
+
t.plan(1)
|
|
721
|
+
|
|
722
|
+
const fastify = Fastify()
|
|
723
|
+
fastify.removeAllContentTypeParsers()
|
|
724
|
+
fastify.addContentTypeParser(';', function (request, body, done) {
|
|
725
|
+
t.fail('should not be called')
|
|
726
|
+
done(null, body)
|
|
727
|
+
})
|
|
728
|
+
|
|
729
|
+
fastify.post('/', async () => {
|
|
730
|
+
return 'ok'
|
|
731
|
+
})
|
|
732
|
+
|
|
733
|
+
await fastify.inject({
|
|
734
|
+
method: 'POST',
|
|
735
|
+
path: '/',
|
|
736
|
+
headers: {
|
|
737
|
+
'content-type': 'application/json; foo=bar; charset=utf8'
|
|
738
|
+
},
|
|
739
|
+
body: ''
|
|
740
|
+
})
|
|
741
|
+
|
|
742
|
+
await fastify.inject({
|
|
743
|
+
method: 'POST',
|
|
744
|
+
path: '/',
|
|
745
|
+
headers: {
|
|
746
|
+
'content-type': 'image/jpeg'
|
|
747
|
+
},
|
|
748
|
+
body: ''
|
|
749
|
+
})
|
|
750
|
+
|
|
751
|
+
t.pass('end')
|
|
752
|
+
})
|
package/test/hooks.test.js
CHANGED
|
@@ -3413,7 +3413,7 @@ test('onRequestAbort should be triggered', t => {
|
|
|
3413
3413
|
const fastify = Fastify()
|
|
3414
3414
|
let order = 0
|
|
3415
3415
|
|
|
3416
|
-
t.plan(
|
|
3416
|
+
t.plan(7)
|
|
3417
3417
|
t.teardown(() => fastify.close())
|
|
3418
3418
|
|
|
3419
3419
|
fastify.addHook('onRequestAbort', function (req, done) {
|
|
@@ -3424,8 +3424,7 @@ test('onRequestAbort should be triggered', t => {
|
|
|
3424
3424
|
})
|
|
3425
3425
|
|
|
3426
3426
|
fastify.addHook('onError', function hook (request, reply, error, done) {
|
|
3427
|
-
t.
|
|
3428
|
-
t.ok(request.raw.aborted, 'request should be aborted')
|
|
3427
|
+
t.fail('onError should not be called')
|
|
3429
3428
|
done()
|
|
3430
3429
|
})
|
|
3431
3430
|
|
package/test/logger.test.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { test, teardown, before } = require('tap')
|
|
4
|
-
const helper = require('./helper')
|
|
5
4
|
const http = require('http')
|
|
6
5
|
const stream = require('stream')
|
|
7
6
|
const split = require('split2')
|
|
@@ -13,6 +12,9 @@ const fs = require('fs')
|
|
|
13
12
|
const sget = require('simple-get').concat
|
|
14
13
|
const dns = require('dns')
|
|
15
14
|
|
|
15
|
+
const helper = require('./helper')
|
|
16
|
+
const { FST_ERR_LOG_INVALID_LOGGER } = require('../lib/errors')
|
|
17
|
+
|
|
16
18
|
const files = []
|
|
17
19
|
let count = 0
|
|
18
20
|
let localhost
|
|
@@ -268,6 +270,34 @@ test('can use external logger instance with custom serializer', t => {
|
|
|
268
270
|
})
|
|
269
271
|
})
|
|
270
272
|
|
|
273
|
+
test('should throw in case the external logger provided does not have a child method', t => {
|
|
274
|
+
t.plan(1)
|
|
275
|
+
const loggerInstance = {
|
|
276
|
+
info: console.info,
|
|
277
|
+
error: console.error,
|
|
278
|
+
debug: console.debug,
|
|
279
|
+
fatal: console.error,
|
|
280
|
+
warn: console.warn,
|
|
281
|
+
trace: console.trace
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
t.throws(
|
|
285
|
+
() => Fastify({ logger: loggerInstance }),
|
|
286
|
+
FST_ERR_LOG_INVALID_LOGGER,
|
|
287
|
+
"Invalid logger object provided. The logger instance should have these functions(s): 'child'."
|
|
288
|
+
)
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
test('should throw in case a partially matching logger is provided', t => {
|
|
292
|
+
t.plan(1)
|
|
293
|
+
|
|
294
|
+
t.throws(
|
|
295
|
+
() => Fastify({ logger: console }),
|
|
296
|
+
FST_ERR_LOG_INVALID_LOGGER,
|
|
297
|
+
"Invalid logger object provided. The logger instance should have these functions(s): 'fatal,child'."
|
|
298
|
+
)
|
|
299
|
+
})
|
|
300
|
+
|
|
271
301
|
test('expose the logger', t => {
|
|
272
302
|
t.plan(2)
|
|
273
303
|
let fastify = null
|
|
@@ -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
|
-
│ └──
|
|
43
|
-
└── hello
|
|
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 =
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
└──
|
|
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
|
-
│ └──
|
|
89
|
-
└──
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
147
|
-
|
|
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
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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')
|
|
@@ -253,6 +253,91 @@ test('Should not change the input schemas', t => {
|
|
|
253
253
|
})
|
|
254
254
|
})
|
|
255
255
|
|
|
256
|
+
test('Should throw if the schema body is undefined', t => {
|
|
257
|
+
t.plan(2)
|
|
258
|
+
const fastify = Fastify()
|
|
259
|
+
|
|
260
|
+
fastify.get('/:id', {
|
|
261
|
+
handler: echoParams,
|
|
262
|
+
schema: {
|
|
263
|
+
body: undefined
|
|
264
|
+
}
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
fastify.ready(err => {
|
|
268
|
+
t.equal(err.code, 'FST_ERR_SCH_VALIDATION_BUILD')
|
|
269
|
+
t.equal(err.message, 'Failed building the validation schema for GET: /:id, due to error body schema is undefined')
|
|
270
|
+
})
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
test('Should throw if the schema headers is undefined', t => {
|
|
274
|
+
t.plan(2)
|
|
275
|
+
const fastify = Fastify()
|
|
276
|
+
|
|
277
|
+
fastify.get('/:id', {
|
|
278
|
+
handler: echoParams,
|
|
279
|
+
schema: {
|
|
280
|
+
headers: undefined
|
|
281
|
+
}
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
fastify.ready(err => {
|
|
285
|
+
t.equal(err.code, 'FST_ERR_SCH_VALIDATION_BUILD')
|
|
286
|
+
t.equal(err.message, 'Failed building the validation schema for GET: /:id, due to error headers schema is undefined')
|
|
287
|
+
})
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
test('Should throw if the schema params is undefined', t => {
|
|
291
|
+
t.plan(2)
|
|
292
|
+
const fastify = Fastify()
|
|
293
|
+
|
|
294
|
+
fastify.get('/:id', {
|
|
295
|
+
handler: echoParams,
|
|
296
|
+
schema: {
|
|
297
|
+
params: undefined
|
|
298
|
+
}
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
fastify.ready(err => {
|
|
302
|
+
t.equal(err.code, 'FST_ERR_SCH_VALIDATION_BUILD')
|
|
303
|
+
t.equal(err.message, 'Failed building the validation schema for GET: /:id, due to error params schema is undefined')
|
|
304
|
+
})
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
test('Should throw if the schema query is undefined', t => {
|
|
308
|
+
t.plan(2)
|
|
309
|
+
const fastify = Fastify()
|
|
310
|
+
|
|
311
|
+
fastify.get('/:id', {
|
|
312
|
+
handler: echoParams,
|
|
313
|
+
schema: {
|
|
314
|
+
querystring: undefined
|
|
315
|
+
}
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
fastify.ready(err => {
|
|
319
|
+
t.equal(err.code, 'FST_ERR_SCH_VALIDATION_BUILD')
|
|
320
|
+
t.equal(err.message, 'Failed building the validation schema for GET: /:id, due to error querystring schema is undefined')
|
|
321
|
+
})
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
test('Should throw if the schema query is undefined', t => {
|
|
325
|
+
t.plan(2)
|
|
326
|
+
const fastify = Fastify()
|
|
327
|
+
|
|
328
|
+
fastify.get('/:id', {
|
|
329
|
+
handler: echoParams,
|
|
330
|
+
schema: {
|
|
331
|
+
querystring: undefined
|
|
332
|
+
}
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
fastify.ready(err => {
|
|
336
|
+
t.equal(err.code, 'FST_ERR_SCH_VALIDATION_BUILD')
|
|
337
|
+
t.equal(err.message, 'Failed building the validation schema for GET: /:id, due to error querystring schema is undefined')
|
|
338
|
+
})
|
|
339
|
+
})
|
|
340
|
+
|
|
256
341
|
test('First level $ref', t => {
|
|
257
342
|
t.plan(2)
|
|
258
343
|
const fastify = Fastify()
|
|
@@ -284,6 +284,8 @@ expectType<string>(server.printRoutes({ includeHooks: true, commonPrefix: false,
|
|
|
284
284
|
|
|
285
285
|
expectType<string>(server.printRoutes({ includeMeta: ['key1', Symbol('key2')] }))
|
|
286
286
|
|
|
287
|
+
expectType<string>(server.printRoutes({ method: 'GET' }))
|
|
288
|
+
|
|
287
289
|
expectType<string>(server.printRoutes())
|
|
288
290
|
|
|
289
291
|
server.decorate<(x: string) => void>('test', function (x: string): void {
|
package/types/errors.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ export type FastifyErrorCodes = Record<
|
|
|
20
20
|
'FST_ERR_MISSING_MIDDLEWARE' |
|
|
21
21
|
'FST_ERR_HOOK_TIMEOUT' |
|
|
22
22
|
'FST_ERR_LOG_INVALID_DESTINATION' |
|
|
23
|
+
'FST_ERR_LOG_INVALID_LOGGER' |
|
|
23
24
|
'FST_ERR_REP_INVALID_PAYLOAD_TYPE' |
|
|
24
25
|
'FST_ERR_REP_ALREADY_SENT' |
|
|
25
26
|
'FST_ERR_REP_SENT_VALUE'|
|
package/types/instance.d.ts
CHANGED
|
@@ -20,10 +20,11 @@ import {
|
|
|
20
20
|
FastifyTypeProvider,
|
|
21
21
|
FastifyTypeProviderDefault
|
|
22
22
|
} from './type-provider'
|
|
23
|
-
import { ContextConfigDefault, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerBase, RawServerDefault } from './utils'
|
|
23
|
+
import { HTTPMethods, ContextConfigDefault, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerBase, RawServerDefault } from './utils'
|
|
24
24
|
import { AddressInfo } from 'net'
|
|
25
25
|
|
|
26
26
|
export interface PrintRoutesOptions {
|
|
27
|
+
method?: HTTPMethods;
|
|
27
28
|
includeMeta?: boolean | (string | symbol)[]
|
|
28
29
|
commonPrefix?: boolean
|
|
29
30
|
includeHooks?: boolean
|