fastify 4.14.0 → 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/Hooks.md +1 -1
- package/docs/Reference/Server.md +85 -33
- package/fastify.js +1 -1
- package/lib/contentTypeParser.js +10 -2
- package/lib/context.js +4 -4
- package/lib/errors.js +5 -0
- package/lib/logger.js +51 -41
- package/lib/reply.js +9 -9
- package/lib/request.js +9 -9
- package/lib/symbols.js +2 -3
- package/lib/validation.js +8 -0
- package/package.json +12 -5
- package/test/content-parser.test.js +101 -0
- package/test/hooks.test.js +24 -2
- package/test/internals/context.test.js +33 -0
- package/test/internals/reply-serialize.test.js +7 -7
- package/test/internals/reply.test.js +3 -1
- package/test/internals/request-validate.test.js +7 -7
- package/test/internals/request.test.js +2 -0
- 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/Hooks.md
CHANGED
|
@@ -284,7 +284,7 @@ fastify.addHook('onRequestAbort', async (request, reply) => {
|
|
|
284
284
|
})
|
|
285
285
|
```
|
|
286
286
|
The `onRequestAbort` hook is executed when a client closes the connection before
|
|
287
|
-
the entire request has been
|
|
287
|
+
the entire request has been processed. Therefore, you will not be able to send
|
|
288
288
|
data to the client.
|
|
289
289
|
|
|
290
290
|
**Notice:** client abort detection is not completely reliable. See: [`Detecting-When-Clients-Abort.md`](../Guides/Detecting-When-Clients-Abort.md)
|
package/docs/Reference/Server.md
CHANGED
|
@@ -738,7 +738,8 @@ Fastify provides default error handlers for the most common use cases. It is
|
|
|
738
738
|
possible to override one or more of those handlers with custom code using this
|
|
739
739
|
option.
|
|
740
740
|
|
|
741
|
-
*Note: Only `FST_ERR_BAD_URL`
|
|
741
|
+
*Note: Only `FST_ERR_BAD_URL` and `FST_ERR_ASYNC_CONSTRAINT` are implemented at
|
|
742
|
+
the moment.*
|
|
742
743
|
|
|
743
744
|
```js
|
|
744
745
|
const fastify = require('fastify')({
|
|
@@ -1544,35 +1545,61 @@ a custom constraint strategy with the same name.
|
|
|
1544
1545
|
#### printRoutes
|
|
1545
1546
|
<a id="print-routes"></a>
|
|
1546
1547
|
|
|
1547
|
-
`fastify.printRoutes()`:
|
|
1548
|
-
|
|
1549
|
-
|
|
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.**
|
|
1550
1552
|
|
|
1551
1553
|
*Remember to call it inside or after a `ready` call.*
|
|
1552
1554
|
|
|
1553
1555
|
```js
|
|
1554
1556
|
fastify.get('/test', () => {})
|
|
1555
1557
|
fastify.get('/test/hello', () => {})
|
|
1556
|
-
fastify.get('/
|
|
1557
|
-
fastify.get('/
|
|
1558
|
+
fastify.get('/testing', () => {})
|
|
1559
|
+
fastify.get('/testing/:param', () => {})
|
|
1560
|
+
fastify.put('/update', () => {})
|
|
1558
1561
|
|
|
1559
1562
|
fastify.ready(() => {
|
|
1560
1563
|
console.log(fastify.printRoutes())
|
|
1561
1564
|
// └── /
|
|
1562
1565
|
// ├── test (GET)
|
|
1563
|
-
// │
|
|
1564
|
-
// └──
|
|
1565
|
-
//
|
|
1566
|
-
//
|
|
1566
|
+
// │ ├── /hello (GET)
|
|
1567
|
+
// │ └── ing (GET)
|
|
1568
|
+
// │ └── /
|
|
1569
|
+
// │ └── :param (GET)
|
|
1570
|
+
// └── update (PUT)
|
|
1571
|
+
})
|
|
1572
|
+
```
|
|
1567
1573
|
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
// │ └── /hello (GET)
|
|
1572
|
-
// ├── hello/world (GET)
|
|
1573
|
-
// └── 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.**
|
|
1574
1577
|
|
|
1575
|
-
|
|
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)
|
|
1576
1603
|
```
|
|
1577
1604
|
|
|
1578
1605
|
`fastify.printRoutes({ includeMeta: (true | []) })` will display properties from
|
|
@@ -1582,26 +1609,51 @@ A shorthand option, `fastify.printRoutes({ includeHooks: true })` will include
|
|
|
1582
1609
|
all [hooks](./Hooks.md).
|
|
1583
1610
|
|
|
1584
1611
|
```js
|
|
1585
|
-
|
|
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'] }))
|
|
1586
1621
|
// └── /
|
|
1587
|
-
//
|
|
1588
|
-
//
|
|
1589
|
-
//
|
|
1590
|
-
//
|
|
1591
|
-
//
|
|
1592
|
-
//
|
|
1593
|
-
//
|
|
1594
|
-
//
|
|
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()"
|
|
1595
1640
|
|
|
1596
1641
|
console.log(fastify.printRoutes({ includeHooks: true }))
|
|
1597
1642
|
// └── /
|
|
1598
|
-
//
|
|
1599
|
-
//
|
|
1600
|
-
//
|
|
1601
|
-
//
|
|
1602
|
-
//
|
|
1603
|
-
//
|
|
1604
|
-
//
|
|
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()"]
|
|
1605
1657
|
```
|
|
1606
1658
|
|
|
1607
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/context.js
CHANGED
|
@@ -11,8 +11,8 @@ const {
|
|
|
11
11
|
kLogLevel,
|
|
12
12
|
kContentTypeParser,
|
|
13
13
|
kRouteByFastify,
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
kRequestCacheValidateFns,
|
|
15
|
+
kReplyCacheSerializeFns,
|
|
16
16
|
kPublicRouteContext
|
|
17
17
|
} = require('./symbols.js')
|
|
18
18
|
|
|
@@ -68,8 +68,8 @@ function Context ({
|
|
|
68
68
|
defaultSchemaErrorFormatter
|
|
69
69
|
this[kRouteByFastify] = isFastify
|
|
70
70
|
|
|
71
|
-
this[
|
|
72
|
-
this[
|
|
71
|
+
this[kRequestCacheValidateFns] = null
|
|
72
|
+
this[kReplyCacheSerializeFns] = null
|
|
73
73
|
this.validatorCompiler = validatorCompiler || null
|
|
74
74
|
this.serializerCompiler = serializerCompiler || null
|
|
75
75
|
|
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/reply.js
CHANGED
|
@@ -18,7 +18,7 @@ const {
|
|
|
18
18
|
kReplyNextErrorHandler,
|
|
19
19
|
kDisableRequestLogging,
|
|
20
20
|
kSchemaResponse,
|
|
21
|
-
|
|
21
|
+
kReplyCacheSerializeFns,
|
|
22
22
|
kSchemaController,
|
|
23
23
|
kOptions,
|
|
24
24
|
kRouteContext
|
|
@@ -323,7 +323,7 @@ Reply.prototype.getSerializationFunction = function (schemaOrStatus, contentType
|
|
|
323
323
|
serialize = this[kRouteContext][kSchemaResponse]?.[schemaOrStatus]
|
|
324
324
|
}
|
|
325
325
|
} else if (typeof schemaOrStatus === 'object') {
|
|
326
|
-
serialize = this[kRouteContext][
|
|
326
|
+
serialize = this[kRouteContext][kReplyCacheSerializeFns]?.get(schemaOrStatus)
|
|
327
327
|
}
|
|
328
328
|
|
|
329
329
|
return serialize
|
|
@@ -334,8 +334,8 @@ Reply.prototype.compileSerializationSchema = function (schema, httpStatus = null
|
|
|
334
334
|
const { method, url } = request
|
|
335
335
|
|
|
336
336
|
// Check if serialize function already compiled
|
|
337
|
-
if (this[kRouteContext][
|
|
338
|
-
return this[kRouteContext][
|
|
337
|
+
if (this[kRouteContext][kReplyCacheSerializeFns]?.has(schema)) {
|
|
338
|
+
return this[kRouteContext][kReplyCacheSerializeFns].get(schema)
|
|
339
339
|
}
|
|
340
340
|
|
|
341
341
|
const serializerCompiler = this[kRouteContext].serializerCompiler ||
|
|
@@ -360,11 +360,11 @@ Reply.prototype.compileSerializationSchema = function (schema, httpStatus = null
|
|
|
360
360
|
// if it is not used
|
|
361
361
|
// TODO: Explore a central cache for all the schemas shared across
|
|
362
362
|
// encapsulated contexts
|
|
363
|
-
if (this[kRouteContext][
|
|
364
|
-
this[kRouteContext][
|
|
363
|
+
if (this[kRouteContext][kReplyCacheSerializeFns] == null) {
|
|
364
|
+
this[kRouteContext][kReplyCacheSerializeFns] = new WeakMap()
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
-
this[kRouteContext][
|
|
367
|
+
this[kRouteContext][kReplyCacheSerializeFns].set(schema, serializeFn)
|
|
368
368
|
|
|
369
369
|
return serializeFn
|
|
370
370
|
}
|
|
@@ -393,8 +393,8 @@ Reply.prototype.serializeInput = function (input, schema, httpStatus, contentTyp
|
|
|
393
393
|
}
|
|
394
394
|
} else {
|
|
395
395
|
// Check if serialize function already compiled
|
|
396
|
-
if (this[kRouteContext][
|
|
397
|
-
serialize = this[kRouteContext][
|
|
396
|
+
if (this[kRouteContext][kReplyCacheSerializeFns]?.has(schema)) {
|
|
397
|
+
serialize = this[kRouteContext][kReplyCacheSerializeFns].get(schema)
|
|
398
398
|
} else {
|
|
399
399
|
serialize = this.compileSerializationSchema(schema, httpStatus, contentType)
|
|
400
400
|
}
|
package/lib/request.js
CHANGED
|
@@ -11,7 +11,7 @@ const {
|
|
|
11
11
|
kSchemaQuerystring,
|
|
12
12
|
kSchemaController,
|
|
13
13
|
kOptions,
|
|
14
|
-
|
|
14
|
+
kRequestCacheValidateFns,
|
|
15
15
|
kRouteContext,
|
|
16
16
|
kPublicRouteContext
|
|
17
17
|
} = require('./symbols')
|
|
@@ -254,7 +254,7 @@ Object.defineProperties(Request.prototype, {
|
|
|
254
254
|
const symbol = HTTP_PART_SYMBOL_MAP[httpPartOrSchema]
|
|
255
255
|
return this[kRouteContext][symbol]
|
|
256
256
|
} else if (typeof httpPartOrSchema === 'object') {
|
|
257
|
-
return this[kRouteContext][
|
|
257
|
+
return this[kRouteContext][kRequestCacheValidateFns]?.get(httpPartOrSchema)
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
260
|
},
|
|
@@ -262,8 +262,8 @@ Object.defineProperties(Request.prototype, {
|
|
|
262
262
|
value: function (schema, httpPart = null) {
|
|
263
263
|
const { method, url } = this
|
|
264
264
|
|
|
265
|
-
if (this[kRouteContext][
|
|
266
|
-
return this[kRouteContext][
|
|
265
|
+
if (this[kRouteContext][kRequestCacheValidateFns]?.has(schema)) {
|
|
266
|
+
return this[kRouteContext][kRequestCacheValidateFns].get(schema)
|
|
267
267
|
}
|
|
268
268
|
|
|
269
269
|
const validatorCompiler = this[kRouteContext].validatorCompiler ||
|
|
@@ -287,11 +287,11 @@ Object.defineProperties(Request.prototype, {
|
|
|
287
287
|
// if it is not used
|
|
288
288
|
// TODO: Explore a central cache for all the schemas shared across
|
|
289
289
|
// encapsulated contexts
|
|
290
|
-
if (this[kRouteContext][
|
|
291
|
-
this[kRouteContext][
|
|
290
|
+
if (this[kRouteContext][kRequestCacheValidateFns] == null) {
|
|
291
|
+
this[kRouteContext][kRequestCacheValidateFns] = new WeakMap()
|
|
292
292
|
}
|
|
293
293
|
|
|
294
|
-
this[kRouteContext][
|
|
294
|
+
this[kRouteContext][kRequestCacheValidateFns].set(schema, validateFn)
|
|
295
295
|
|
|
296
296
|
return validateFn
|
|
297
297
|
}
|
|
@@ -317,8 +317,8 @@ Object.defineProperties(Request.prototype, {
|
|
|
317
317
|
}
|
|
318
318
|
|
|
319
319
|
if (validate == null) {
|
|
320
|
-
if (this[kRouteContext][
|
|
321
|
-
validate = this[kRouteContext][
|
|
320
|
+
if (this[kRouteContext][kRequestCacheValidateFns]?.has(schema)) {
|
|
321
|
+
validate = this[kRouteContext][kRequestCacheValidateFns].get(schema)
|
|
322
322
|
} else {
|
|
323
323
|
// We proceed to compile if there's no validate function yet
|
|
324
324
|
validate = this.compileValidationSchema(schema, httpPart)
|
package/lib/symbols.js
CHANGED
|
@@ -27,10 +27,9 @@ const keys = {
|
|
|
27
27
|
kSchemaVisited: Symbol('fastify.schemas.visited'),
|
|
28
28
|
// Request
|
|
29
29
|
kRequest: Symbol('fastify.Request'),
|
|
30
|
-
kRequestValidateFns: Symbol('fastify.request.cache.validateFns'),
|
|
31
30
|
kRequestPayloadStream: Symbol('fastify.RequestPayloadStream'),
|
|
32
31
|
kRequestAcceptVersion: Symbol('fastify.RequestAcceptVersion'),
|
|
33
|
-
|
|
32
|
+
kRequestCacheValidateFns: Symbol('fastify.request.cache.validateFns'),
|
|
34
33
|
// 404
|
|
35
34
|
kFourOhFour: Symbol('fastify.404'),
|
|
36
35
|
kCanSetNotFoundHandler: Symbol('fastify.canSetNotFoundHandler'),
|
|
@@ -51,7 +50,7 @@ const keys = {
|
|
|
51
50
|
kReplyErrorHandlerCalled: Symbol('fastify.reply.errorHandlerCalled'),
|
|
52
51
|
kReplyIsRunningOnErrorHook: Symbol('fastify.reply.isRunningOnErrorHook'),
|
|
53
52
|
kReplySerializerDefault: Symbol('fastify.replySerializerDefault'),
|
|
54
|
-
|
|
53
|
+
kReplyCacheSerializeFns: Symbol('fastify.reply.cache.serializeFns'),
|
|
55
54
|
// This symbol is only meant to be used for fastify tests and should not be used for any other purpose
|
|
56
55
|
kTestInternals: Symbol('fastify.testInternals'),
|
|
57
56
|
kErrorHandler: Symbol('fastify.errorHandler'),
|
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",
|