fastify 5.0.0 → 5.2.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/.borp.yaml +3 -0
- package/.vscode/settings.json +22 -0
- package/README.md +12 -7
- package/docs/Guides/Database.md +15 -15
- package/docs/Guides/Detecting-When-Clients-Abort.md +28 -28
- package/docs/Guides/Ecosystem.md +14 -15
- package/docs/Guides/Index.md +1 -1
- package/docs/Guides/Migration-Guide-V4.md +11 -11
- package/docs/Guides/Migration-Guide-V5.md +133 -9
- package/docs/Guides/Plugins-Guide.md +1 -1
- package/docs/Guides/Prototype-Poisoning.md +3 -3
- package/docs/Guides/Recommendations.md +9 -9
- package/docs/Guides/Serverless.md +5 -5
- package/docs/Guides/Testing.md +58 -57
- package/docs/Guides/Write-Plugin.md +2 -2
- package/docs/Guides/Write-Type-Provider.md +3 -3
- package/docs/Reference/ContentTypeParser.md +4 -4
- package/docs/Reference/Decorators.md +2 -2
- package/docs/Reference/Errors.md +3 -3
- package/docs/Reference/Hooks.md +7 -7
- package/docs/Reference/LTS.md +8 -0
- package/docs/Reference/Logging.md +5 -4
- package/docs/Reference/Reply.md +55 -58
- package/docs/Reference/Request.md +49 -42
- package/docs/Reference/Routes.md +16 -13
- package/docs/Reference/Server.md +32 -28
- package/docs/Reference/TypeScript.md +9 -9
- package/docs/Reference/Validation-and-Serialization.md +5 -5
- package/examples/typescript-server.ts +1 -1
- package/fastify.d.ts +14 -5
- package/fastify.js +8 -6
- package/lib/contentTypeParser.js +9 -7
- package/lib/context.js +1 -2
- package/lib/error-handler.js +9 -9
- package/lib/errors.js +1 -1
- package/lib/fourOhFour.js +1 -1
- package/lib/hooks.js +4 -1
- package/lib/{logger.js → logger-factory.js} +70 -122
- package/lib/logger-pino.js +68 -0
- package/lib/pluginOverride.js +1 -1
- package/lib/pluginUtils.js +2 -2
- package/lib/reply.js +4 -5
- package/lib/request.js +16 -9
- package/lib/route.js +23 -22
- package/lib/validation.js +2 -2
- package/package.json +13 -15
- package/test/404s.test.js +675 -629
- package/test/500s.test.js +72 -63
- package/test/{allowUnsafeRegex.test.js → allow-unsafe-regex.test.js} +30 -26
- package/test/als.test.js +48 -45
- package/test/async-await.test.js +148 -134
- package/test/async-dispose.test.js +4 -5
- package/test/async_hooks.test.js +30 -28
- package/test/{bodyLimit.test.js → body-limit.test.js} +61 -58
- package/test/buffer.test.js +9 -10
- package/test/build/error-serializer.test.js +3 -4
- package/test/build/version.test.js +2 -3
- package/test/build-certificate.js +1 -1
- package/test/bundler/README.md +5 -5
- package/test/bundler/esbuild/bundler-test.js +10 -9
- package/test/bundler/webpack/bundler-test.js +10 -9
- package/test/case-insensitive.test.js +31 -28
- package/test/chainable.test.js +4 -5
- package/test/check.test.js +8 -10
- package/test/{childLoggerFactory.test.js → child-logger-factory.test.js} +56 -19
- package/test/client-timeout.test.js +5 -5
- package/test/close-pipelining.test.js +6 -8
- package/test/conditional-pino.test.js +47 -0
- package/test/{connectionTimeout.test.js → connection-timeout.test.js} +10 -11
- package/test/constrained-routes.test.js +243 -236
- package/test/content-length.test.js +53 -68
- package/test/content-parser.test.js +186 -158
- package/test/content-type.test.js +8 -9
- package/test/context-config.test.js +44 -54
- package/test/custom-http-server.test.js +16 -20
- package/test/custom-parser.5.test.js +32 -32
- package/test/diagnostics-channel/404.test.js +15 -15
- package/test/diagnostics-channel/async-delay-request.test.js +25 -25
- package/test/diagnostics-channel/async-request.test.js +24 -24
- package/test/diagnostics-channel/error-before-handler.test.js +4 -5
- package/test/diagnostics-channel/error-request.test.js +19 -19
- package/test/diagnostics-channel/error-status.test.js +8 -8
- package/test/diagnostics-channel/init.test.js +6 -7
- package/test/diagnostics-channel/sync-delay-request.test.js +16 -16
- package/test/diagnostics-channel/sync-request-reply.test.js +16 -16
- package/test/diagnostics-channel/sync-request.test.js +19 -19
- package/test/encapsulated-child-logger-factory.test.js +8 -8
- package/test/encapsulated-error-handler.test.js +20 -20
- package/test/esm/errorCodes.test.mjs +5 -5
- package/test/esm/esm.test.mjs +3 -3
- package/test/esm/named-exports.mjs +3 -3
- package/test/esm/other.mjs +2 -2
- package/test/fastify-instance.test.js +33 -34
- package/test/{findRoute.test.js → find-route.test.js} +11 -10
- package/test/fluent-schema.test.js +33 -36
- package/test/handler-context.test.js +11 -11
- package/test/has-route.test.js +12 -15
- package/test/header-overflow.test.js +13 -12
- package/test/hooks.on-ready.test.js +2 -2
- package/test/hooks.test.js +25 -25
- package/test/http-methods/copy.test.js +22 -24
- package/test/http-methods/custom-http-methods.test.js +24 -21
- package/test/http-methods/get.test.js +97 -84
- package/test/http-methods/head.test.js +63 -57
- package/test/http-methods/lock.test.js +21 -20
- package/test/http-methods/mkcalendar.test.js +31 -27
- package/test/http-methods/mkcol.test.js +10 -10
- package/test/http-methods/move.test.js +11 -11
- package/test/http-methods/propfind.test.js +32 -27
- package/test/http-methods/proppatch.test.js +21 -19
- package/test/http-methods/report.test.js +32 -27
- package/test/http-methods/search.test.js +52 -47
- package/test/http-methods/trace.test.js +3 -4
- package/test/http-methods/unlock.test.js +10 -10
- package/test/http2/closing.test.js +50 -58
- package/test/http2/constraint.test.js +47 -50
- package/test/http2/head.test.js +18 -19
- package/test/http2/missing-http2-module.test.js +4 -5
- package/test/http2/plain.test.js +31 -31
- package/test/http2/secure-with-fallback.test.js +61 -61
- package/test/http2/secure.test.js +28 -31
- package/test/http2/unknown-http-method.test.js +13 -14
- package/test/https/custom-https-server.test.js +6 -7
- package/test/https/https.test.js +78 -78
- package/test/imports.test.js +5 -6
- package/test/internals/all.test.js +8 -11
- package/test/internals/{contentTypeParser.test.js → content-type-parser.test.js} +5 -6
- package/test/internals/context.test.js +9 -11
- package/test/internals/decorator.test.js +20 -21
- package/test/internals/errors.test.js +427 -427
- package/test/internals/{handleRequest.test.js → handle-request.test.js} +53 -42
- package/test/internals/{hookRunner.test.js → hook-runner.test.js} +99 -100
- package/test/internals/hooks.test.js +31 -35
- package/test/internals/{initialConfig.test.js → initial-config.test.js} +92 -80
- package/test/internals/logger.test.js +28 -28
- package/test/internals/plugin.test.js +17 -18
- package/test/internals/reply-serialize.test.js +106 -106
- package/test/internals/reply.test.js +620 -585
- package/test/internals/{reqIdGenFactory.test.js → req-id-gen-factory.test.js} +31 -31
- package/test/internals/request-validate.test.js +218 -221
- package/test/internals/request.test.js +225 -107
- package/test/internals/server.test.js +15 -12
- package/test/internals/validation.test.js +35 -36
- package/test/{keepAliveTimeout.test.js → keep-alive-timeout.test.js} +9 -10
- package/test/listen.5.test.js +9 -9
- package/test/{maxRequestsPerSocket.test.js → max-requests-per-socket.test.js} +30 -30
- package/test/middleware.test.js +4 -5
- package/test/noop-set.test.js +5 -5
- package/test/post-empty-body.test.js +18 -11
- package/test/pretty-print.test.js +59 -49
- package/test/proto-poisoning.test.js +42 -37
- package/test/reply-code.test.js +34 -32
- package/test/{reply-earlyHints.test.js → reply-early-hints.test.js} +21 -19
- package/test/request-error.test.js +122 -0
- package/test/request-header-host.test.js +339 -0
- package/test/request-id.test.js +31 -25
- package/test/{requestTimeout.test.js → request-timeout.test.js} +11 -11
- package/test/route.1.test.js +79 -72
- package/test/route.2.test.js +17 -16
- package/test/route.3.test.js +32 -27
- package/test/route.4.test.js +21 -25
- package/test/route.5.test.js +45 -64
- package/test/route.6.test.js +70 -89
- package/test/route.7.test.js +61 -65
- package/test/route.8.test.js +80 -18
- package/test/router-options.test.js +80 -77
- package/test/same-shape.test.js +5 -5
- package/test/schema-examples.test.js +72 -38
- package/test/serialize-response.test.js +9 -10
- package/test/server.test.js +75 -78
- package/test/set-error-handler.test.js +2 -3
- package/test/stream-serializers.test.js +10 -7
- package/test/sync-routes.test.js +18 -18
- package/test/test-reporter.mjs +68 -0
- package/test/trust-proxy.test.js +51 -45
- package/test/type-provider.test.js +8 -6
- package/test/types/content-type-parser.test-d.ts +1 -1
- package/test/types/fastify.test-d.ts +16 -4
- package/test/types/hooks.test-d.ts +2 -1
- package/test/types/instance.test-d.ts +13 -13
- package/test/types/logger.test-d.ts +2 -2
- package/test/types/plugin.test-d.ts +17 -9
- package/test/types/register.test-d.ts +22 -6
- package/test/types/reply.test-d.ts +1 -1
- package/test/types/route.test-d.ts +34 -4
- package/test/types/serverFactory.test-d.ts +1 -1
- package/test/types/type-provider.test-d.ts +1 -1
- package/test/url-rewriting.test.js +35 -38
- package/test/{useSemicolonDelimiter.test.js → use-semicolon-delimiter.test.js} +30 -30
- package/test/validation-error-handling.test.js +259 -285
- package/test/versioned-routes.test.js +126 -113
- package/test/web-api.test.js +48 -37
- package/test/{wrapThenable.test.js → wrap-thenable.test.js} +10 -9
- package/types/hooks.d.ts +2 -1
- package/types/instance.d.ts +9 -2
- package/types/register.d.ts +12 -3
- package/types/reply.d.ts +1 -1
- package/types/request.d.ts +2 -6
- package/types/serverFactory.d.ts +3 -3
- package/types/utils.d.ts +13 -5
- package/test/types/import.js +0 -2
|
@@ -1,88 +1,90 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* Code imported from `pino-http`
|
|
5
|
-
* Repo: https://github.com/pinojs/pino-http
|
|
6
|
-
* License: MIT (https://raw.githubusercontent.com/pinojs/pino-http/master/LICENSE)
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const nullLogger = require('abstract-logging')
|
|
10
|
-
const pino = require('pino')
|
|
11
|
-
const { serializersSym } = pino.symbols
|
|
12
3
|
const {
|
|
13
|
-
|
|
14
|
-
FST_ERR_LOG_INVALID_LOGGER,
|
|
15
|
-
FST_ERR_LOG_INVALID_LOGGER_INSTANCE,
|
|
4
|
+
FST_ERR_LOG_LOGGER_AND_LOGGER_INSTANCE_PROVIDED,
|
|
16
5
|
FST_ERR_LOG_INVALID_LOGGER_CONFIG,
|
|
17
|
-
|
|
6
|
+
FST_ERR_LOG_INVALID_LOGGER_INSTANCE,
|
|
7
|
+
FST_ERR_LOG_INVALID_LOGGER
|
|
18
8
|
} = require('./errors')
|
|
19
9
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Utility for creating a child logger with the appropriate bindings, logger factory
|
|
12
|
+
* and validation.
|
|
13
|
+
* @param {object} context
|
|
14
|
+
* @param {import('../fastify').FastifyBaseLogger} logger
|
|
15
|
+
* @param {import('../fastify').RawRequestDefaultExpression<any>} req
|
|
16
|
+
* @param {string} reqId
|
|
17
|
+
* @param {import('../types/logger.js').ChildLoggerOptions?} loggerOpts
|
|
18
|
+
*
|
|
19
|
+
* @returns {object} New logger instance, inheriting all parent bindings,
|
|
20
|
+
* with child bindings added.
|
|
21
|
+
*/
|
|
22
|
+
function createChildLogger (context, logger, req, reqId, loggerOpts) {
|
|
23
|
+
const loggerBindings = {
|
|
24
|
+
[context.requestIdLogLabel]: reqId
|
|
27
25
|
}
|
|
26
|
+
const child = context.childLoggerFactory.call(context.server, logger, loggerBindings, loggerOpts || {}, req)
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (prevLogger) {
|
|
34
|
-
opts.logger = undefined
|
|
35
|
-
opts.genReqId = undefined
|
|
36
|
-
// we need to tap into pino internals because in v5 it supports
|
|
37
|
-
// adding serializers in child loggers
|
|
38
|
-
if (prevLogger[serializersSym]) {
|
|
39
|
-
opts.serializers = Object.assign({}, opts.serializers, prevLogger[serializersSym])
|
|
40
|
-
}
|
|
41
|
-
logger = prevLogger.child({}, opts)
|
|
42
|
-
opts.logger = prevLogger
|
|
43
|
-
opts.genReqId = prevGenReqId
|
|
44
|
-
} else {
|
|
45
|
-
logger = pino(opts, opts.stream)
|
|
28
|
+
// Optimization: bypass validation if the factory is our own default factory
|
|
29
|
+
if (context.childLoggerFactory !== defaultChildLoggerFactory) {
|
|
30
|
+
validateLogger(child, true) // throw if the child is not a valid logger
|
|
46
31
|
}
|
|
47
32
|
|
|
48
|
-
return
|
|
33
|
+
return child
|
|
49
34
|
}
|
|
50
35
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
},
|
|
62
|
-
err: pino.stdSerializers.err,
|
|
63
|
-
res: function asResValue (reply) {
|
|
64
|
-
return {
|
|
65
|
-
statusCode: reply.statusCode
|
|
66
|
-
}
|
|
67
|
-
}
|
|
36
|
+
/** Default factory to create child logger instance
|
|
37
|
+
*
|
|
38
|
+
* @param {import('../fastify.js').FastifyBaseLogger} logger
|
|
39
|
+
* @param {import('../types/logger.js').Bindings} bindings
|
|
40
|
+
* @param {import('../types/logger.js').ChildLoggerOptions} opts
|
|
41
|
+
*
|
|
42
|
+
* @returns {import('../types/logger.js').FastifyBaseLogger}
|
|
43
|
+
*/
|
|
44
|
+
function defaultChildLoggerFactory (logger, bindings, opts) {
|
|
45
|
+
return logger.child(bindings, opts)
|
|
68
46
|
}
|
|
69
47
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Determines if a provided logger object meets the requirements
|
|
50
|
+
* of a Fastify compatible logger.
|
|
51
|
+
*
|
|
52
|
+
* @param {object} logger Object to validate.
|
|
53
|
+
* @param {boolean?} strict `true` if the object must be a logger (always throw if any methods missing)
|
|
54
|
+
*
|
|
55
|
+
* @returns {boolean} `true` when the logger meets the requirements.
|
|
56
|
+
*
|
|
57
|
+
* @throws {FST_ERR_LOG_INVALID_LOGGER} When the logger object is
|
|
58
|
+
* missing required methods.
|
|
59
|
+
*/
|
|
60
|
+
function validateLogger (logger, strict) {
|
|
61
|
+
const methods = ['info', 'error', 'debug', 'fatal', 'warn', 'trace', 'child']
|
|
62
|
+
const missingMethods = logger
|
|
63
|
+
? methods.filter(method => !logger[method] || typeof logger[method] !== 'function')
|
|
64
|
+
: methods
|
|
65
|
+
|
|
66
|
+
if (!missingMethods.length) {
|
|
67
|
+
return true
|
|
68
|
+
} else if ((missingMethods.length === methods.length) && !strict) {
|
|
69
|
+
return false
|
|
70
|
+
} else {
|
|
71
|
+
throw FST_ERR_LOG_INVALID_LOGGER(missingMethods.join(','))
|
|
72
|
+
}
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
function createLogger (options) {
|
|
76
|
-
|
|
76
|
+
if (options.logger && options.loggerInstance) {
|
|
77
|
+
throw new FST_ERR_LOG_LOGGER_AND_LOGGER_INSTANCE_PROVIDED()
|
|
78
|
+
}
|
|
79
|
+
|
|
77
80
|
if (!options.loggerInstance && !options.logger) {
|
|
81
|
+
const nullLogger = require('abstract-logging')
|
|
78
82
|
const logger = nullLogger
|
|
79
83
|
logger.child = () => logger
|
|
80
84
|
return { logger, hasLogger: false }
|
|
81
85
|
}
|
|
82
86
|
|
|
83
|
-
|
|
84
|
-
throw new FST_ERR_LOG_LOGGER_AND_LOGGER_INSTANCE_PROVIDED()
|
|
85
|
-
}
|
|
87
|
+
const { createPinoLogger, serializers } = require('./logger-pino.js')
|
|
86
88
|
|
|
87
89
|
// check if the logger instance has all required properties
|
|
88
90
|
if (validateLogger(options.loggerInstance)) {
|
|
@@ -120,69 +122,15 @@ function createLogger (options) {
|
|
|
120
122
|
return { logger, hasLogger: true }
|
|
121
123
|
}
|
|
122
124
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
* @param {object} logger Object to validate.
|
|
128
|
-
* @param {boolean?} strict `true` if the object must be a logger (always throw if any methods missing)
|
|
129
|
-
*
|
|
130
|
-
* @returns {boolean} `true` when the logger meets the requirements.
|
|
131
|
-
*
|
|
132
|
-
* @throws {FST_ERR_LOG_INVALID_LOGGER} When the logger object is
|
|
133
|
-
* missing required methods.
|
|
134
|
-
*/
|
|
135
|
-
function validateLogger (logger, strict) {
|
|
136
|
-
const methods = ['info', 'error', 'debug', 'fatal', 'warn', 'trace', 'child']
|
|
137
|
-
const missingMethods = logger
|
|
138
|
-
? methods.filter(method => !logger[method] || typeof logger[method] !== 'function')
|
|
139
|
-
: methods
|
|
140
|
-
|
|
141
|
-
if (!missingMethods.length) {
|
|
142
|
-
return true
|
|
143
|
-
} else if ((missingMethods.length === methods.length) && !strict) {
|
|
144
|
-
return false
|
|
145
|
-
} else {
|
|
146
|
-
throw FST_ERR_LOG_INVALID_LOGGER(missingMethods.join(','))
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Utility for creating a child logger with the appropriate bindings, logger factory
|
|
152
|
-
* and validation.
|
|
153
|
-
* @param {object} context
|
|
154
|
-
* @param {import('../fastify').FastifyBaseLogger} logger
|
|
155
|
-
* @param {import('../fastify').RawRequestDefaultExpression<any>} req
|
|
156
|
-
* @param {string} reqId
|
|
157
|
-
* @param {import('../types/logger.js').ChildLoggerOptions?} loggerOpts
|
|
158
|
-
*/
|
|
159
|
-
function createChildLogger (context, logger, req, reqId, loggerOpts) {
|
|
160
|
-
const loggerBindings = {
|
|
161
|
-
[context.requestIdLogLabel]: reqId
|
|
162
|
-
}
|
|
163
|
-
const child = context.childLoggerFactory.call(context.server, logger, loggerBindings, loggerOpts || {}, req)
|
|
164
|
-
|
|
165
|
-
// Optimization: bypass validation if the factory is our own default factory
|
|
166
|
-
if (context.childLoggerFactory !== defaultChildLoggerFactory) {
|
|
167
|
-
validateLogger(child, true) // throw if the child is not a valid logger
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return child
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* @param {import('../fastify.js').FastifyBaseLogger} logger
|
|
175
|
-
* @param {import('../types/logger.js').Bindings} bindings
|
|
176
|
-
* @param {import('../types/logger.js').ChildLoggerOptions} opts
|
|
177
|
-
*/
|
|
178
|
-
function defaultChildLoggerFactory (logger, bindings, opts) {
|
|
179
|
-
return logger.child(bindings, opts)
|
|
125
|
+
function now () {
|
|
126
|
+
const ts = process.hrtime()
|
|
127
|
+
return (ts[0] * 1e3) + (ts[1] / 1e6)
|
|
180
128
|
}
|
|
181
129
|
|
|
182
130
|
module.exports = {
|
|
183
|
-
createLogger,
|
|
184
131
|
createChildLogger,
|
|
185
132
|
defaultChildLoggerFactory,
|
|
186
|
-
|
|
187
|
-
|
|
133
|
+
createLogger,
|
|
134
|
+
validateLogger,
|
|
135
|
+
now,
|
|
188
136
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Code imported from `pino-http`
|
|
5
|
+
* Repo: https://github.com/pinojs/pino-http
|
|
6
|
+
* License: MIT (https://raw.githubusercontent.com/pinojs/pino-http/master/LICENSE)
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const pino = require('pino')
|
|
10
|
+
const { serializersSym } = pino.symbols
|
|
11
|
+
const {
|
|
12
|
+
FST_ERR_LOG_INVALID_DESTINATION,
|
|
13
|
+
} = require('./errors')
|
|
14
|
+
|
|
15
|
+
function createPinoLogger (opts) {
|
|
16
|
+
if (opts.stream && opts.file) {
|
|
17
|
+
throw new FST_ERR_LOG_INVALID_DESTINATION()
|
|
18
|
+
} else if (opts.file) {
|
|
19
|
+
// we do not have stream
|
|
20
|
+
opts.stream = pino.destination(opts.file)
|
|
21
|
+
delete opts.file
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const prevLogger = opts.logger
|
|
25
|
+
const prevGenReqId = opts.genReqId
|
|
26
|
+
let logger = null
|
|
27
|
+
|
|
28
|
+
if (prevLogger) {
|
|
29
|
+
opts.logger = undefined
|
|
30
|
+
opts.genReqId = undefined
|
|
31
|
+
// we need to tap into pino internals because in v5 it supports
|
|
32
|
+
// adding serializers in child loggers
|
|
33
|
+
if (prevLogger[serializersSym]) {
|
|
34
|
+
opts.serializers = Object.assign({}, opts.serializers, prevLogger[serializersSym])
|
|
35
|
+
}
|
|
36
|
+
logger = prevLogger.child({}, opts)
|
|
37
|
+
opts.logger = prevLogger
|
|
38
|
+
opts.genReqId = prevGenReqId
|
|
39
|
+
} else {
|
|
40
|
+
logger = pino(opts, opts.stream)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return logger
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const serializers = {
|
|
47
|
+
req: function asReqValue (req) {
|
|
48
|
+
return {
|
|
49
|
+
method: req.method,
|
|
50
|
+
url: req.url,
|
|
51
|
+
version: req.headers && req.headers['accept-version'],
|
|
52
|
+
host: req.host,
|
|
53
|
+
remoteAddress: req.ip,
|
|
54
|
+
remotePort: req.socket ? req.socket.remotePort : undefined
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
err: pino.stdSerializers.err,
|
|
58
|
+
res: function asResValue (reply) {
|
|
59
|
+
return {
|
|
60
|
+
statusCode: reply.statusCode
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
module.exports = {
|
|
66
|
+
serializers,
|
|
67
|
+
createPinoLogger,
|
|
68
|
+
}
|
package/lib/pluginOverride.js
CHANGED
|
@@ -66,7 +66,7 @@ module.exports = function override (old, fn, opts) {
|
|
|
66
66
|
instance[kFourOhFour].arrange404(instance)
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
for (const hook of instance[kHooks].onRegister) hook.call(
|
|
69
|
+
for (const hook of instance[kHooks].onRegister) hook.call(old, instance, opts)
|
|
70
70
|
|
|
71
71
|
return instance
|
|
72
72
|
}
|
package/lib/pluginUtils.js
CHANGED
|
@@ -48,7 +48,7 @@ function getPluginName (func) {
|
|
|
48
48
|
|
|
49
49
|
function getFuncPreview (func) {
|
|
50
50
|
// takes the first two lines of the function if nothing else works
|
|
51
|
-
return func.toString().split('\n'
|
|
51
|
+
return func.toString().split('\n', 2).map(s => s.trim()).join(' -- ')
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
function getDisplayName (fn) {
|
|
@@ -110,7 +110,7 @@ function checkVersion (fn) {
|
|
|
110
110
|
|
|
111
111
|
const requiredVersion = meta.fastify
|
|
112
112
|
|
|
113
|
-
const fastifyRc = /-(rc|pre|alpha).+$/.test(this.version)
|
|
113
|
+
const fastifyRc = /-(?:rc|pre|alpha).+$/.test(this.version)
|
|
114
114
|
if (fastifyRc === true && semver.gt(this.version, semver.coerce(requiredVersion)) === true) {
|
|
115
115
|
// A Fastify release candidate phase is taking place. In order to reduce
|
|
116
116
|
// the effort needed to test plugins with the RC, we allow plugins targeting
|
package/lib/reply.js
CHANGED
|
@@ -32,7 +32,7 @@ const {
|
|
|
32
32
|
} = require('./hooks')
|
|
33
33
|
|
|
34
34
|
const internals = require('./handleRequest')[Symbol.for('internals')]
|
|
35
|
-
const loggerUtils = require('./logger')
|
|
35
|
+
const loggerUtils = require('./logger-factory')
|
|
36
36
|
const now = loggerUtils.now
|
|
37
37
|
const { handleError } = require('./error-handler')
|
|
38
38
|
const { getSchemaSerializer } = require('./schemas')
|
|
@@ -263,8 +263,7 @@ Reply.prototype.header = function (key, value = '') {
|
|
|
263
263
|
|
|
264
264
|
Reply.prototype.headers = function (headers) {
|
|
265
265
|
const keys = Object.keys(headers)
|
|
266
|
-
|
|
267
|
-
for (var i = 0; i !== keys.length; ++i) {
|
|
266
|
+
for (let i = 0; i !== keys.length; ++i) {
|
|
268
267
|
const key = keys[i]
|
|
269
268
|
this.header(key, headers[key])
|
|
270
269
|
}
|
|
@@ -868,9 +867,9 @@ function buildReply (R) {
|
|
|
868
867
|
this[kReplyEndTime] = undefined
|
|
869
868
|
this.log = log
|
|
870
869
|
|
|
871
|
-
|
|
870
|
+
let prop
|
|
872
871
|
|
|
873
|
-
for (
|
|
872
|
+
for (let i = 0; i < props.length; i++) {
|
|
874
873
|
prop = props[i]
|
|
875
874
|
this[prop.key] = prop.value
|
|
876
875
|
}
|
package/lib/request.js
CHANGED
|
@@ -73,10 +73,8 @@ function buildRegularRequest (R) {
|
|
|
73
73
|
this.log = log
|
|
74
74
|
this.body = undefined
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
// eslint-disable-next-line no-var
|
|
79
|
-
for (var i = 0; i < props.length; i++) {
|
|
76
|
+
let prop
|
|
77
|
+
for (let i = 0; i < props.length; i++) {
|
|
80
78
|
prop = props[i]
|
|
81
79
|
this[prop.key] = prop.value
|
|
82
80
|
}
|
|
@@ -119,7 +117,11 @@ function buildRequestWithTrustProxy (R, trustProxy) {
|
|
|
119
117
|
if (this.ip !== undefined && this.headers['x-forwarded-host']) {
|
|
120
118
|
return getLastEntryInMultiHeaderValue(this.headers['x-forwarded-host'])
|
|
121
119
|
}
|
|
122
|
-
|
|
120
|
+
// the last fallback is used to support the following cases:
|
|
121
|
+
// 1. support http.requireHostHeader === false
|
|
122
|
+
// 2. support HTTP/1.0 without Host Header
|
|
123
|
+
// 3. support headers schema which may remove the Host Header
|
|
124
|
+
return this.headers.host ?? this.headers[':authority'] ?? ''
|
|
123
125
|
}
|
|
124
126
|
},
|
|
125
127
|
protocol: {
|
|
@@ -211,23 +213,28 @@ Object.defineProperties(Request.prototype, {
|
|
|
211
213
|
},
|
|
212
214
|
host: {
|
|
213
215
|
get () {
|
|
214
|
-
|
|
216
|
+
// the last fallback is used to support the following cases:
|
|
217
|
+
// 1. support http.requireHostHeader === false
|
|
218
|
+
// 2. support HTTP/1.0 without Host Header
|
|
219
|
+
// 3. support headers schema which may remove the Host Header
|
|
220
|
+
return this.raw.headers.host ?? this.raw.headers[':authority'] ?? ''
|
|
215
221
|
}
|
|
216
222
|
},
|
|
217
223
|
hostname: {
|
|
218
224
|
get () {
|
|
219
|
-
return
|
|
225
|
+
return this.host.split(':', 1)[0]
|
|
220
226
|
}
|
|
221
227
|
},
|
|
222
228
|
port: {
|
|
223
229
|
get () {
|
|
224
230
|
// first try taking port from host
|
|
225
|
-
const portFromHost = parseInt(
|
|
231
|
+
const portFromHost = parseInt(this.host.split(':').slice(-1)[0])
|
|
226
232
|
if (!isNaN(portFromHost)) {
|
|
227
233
|
return portFromHost
|
|
228
234
|
}
|
|
229
235
|
// now fall back to port from host/:authority header
|
|
230
|
-
const
|
|
236
|
+
const host = (this.headers.host ?? this.headers[':authority'] ?? '')
|
|
237
|
+
const portFromHeader = parseInt(host.split(':').slice(-1)[0])
|
|
231
238
|
if (!isNaN(portFromHeader)) {
|
|
232
239
|
return portFromHeader
|
|
233
240
|
}
|
package/lib/route.js
CHANGED
|
@@ -49,7 +49,7 @@ const {
|
|
|
49
49
|
kRouteContext
|
|
50
50
|
} = require('./symbols.js')
|
|
51
51
|
const { buildErrorHandler } = require('./error-handler')
|
|
52
|
-
const { createChildLogger } = require('./logger')
|
|
52
|
+
const { createChildLogger } = require('./logger-factory.js')
|
|
53
53
|
const { getGenReqId } = require('./reqIdGenFactory.js')
|
|
54
54
|
|
|
55
55
|
function buildRouting (options) {
|
|
@@ -181,45 +181,46 @@ function buildRouting (options) {
|
|
|
181
181
|
* @param {{ options: import('../fastify').RouteOptions, isFastify: boolean }}
|
|
182
182
|
*/
|
|
183
183
|
function route ({ options, isFastify }) {
|
|
184
|
+
throwIfAlreadyStarted('Cannot add route!')
|
|
185
|
+
|
|
184
186
|
// Since we are mutating/assigning only top level props, it is fine to have a shallow copy using the spread operator
|
|
185
187
|
const opts = { ...options }
|
|
186
188
|
|
|
187
|
-
const
|
|
188
|
-
const hasRouteExposeHeadRouteFlag = exposeHeadRoute != null
|
|
189
|
-
const shouldExposeHead = hasRouteExposeHeadRouteFlag ? exposeHeadRoute : globalExposeHeadRoutes
|
|
189
|
+
const path = opts.url || opts.path || ''
|
|
190
190
|
|
|
191
|
-
|
|
192
|
-
(
|
|
193
|
-
|
|
194
|
-
(Array.isArray(opts.method) && opts.method.includes('HEAD'))
|
|
191
|
+
if (!opts.handler) {
|
|
192
|
+
throw new FST_ERR_ROUTE_MISSING_HANDLER(opts.method, path)
|
|
193
|
+
}
|
|
195
194
|
|
|
196
|
-
|
|
197
|
-
|
|
195
|
+
if (opts.errorHandler !== undefined && typeof opts.errorHandler !== 'function') {
|
|
196
|
+
throw new FST_ERR_ROUTE_HANDLER_NOT_FN(opts.method, path)
|
|
197
|
+
}
|
|
198
198
|
|
|
199
|
-
|
|
199
|
+
validateBodyLimitOption(opts.bodyLimit)
|
|
200
200
|
|
|
201
|
-
const
|
|
201
|
+
const shouldExposeHead = opts.exposeHeadRoute ?? globalExposeHeadRoutes
|
|
202
|
+
|
|
203
|
+
let isGetRoute = false
|
|
204
|
+
let isHeadRoute = false
|
|
202
205
|
|
|
203
206
|
if (Array.isArray(opts.method)) {
|
|
204
|
-
|
|
205
|
-
for (var i = 0; i < opts.method.length; ++i) {
|
|
207
|
+
for (let i = 0; i < opts.method.length; ++i) {
|
|
206
208
|
opts.method[i] = normalizeAndValidateMethod.call(this, opts.method[i])
|
|
207
209
|
validateSchemaBodyOption.call(this, opts.method[i], path, opts.schema)
|
|
210
|
+
|
|
211
|
+
isGetRoute = opts.method.includes('GET')
|
|
212
|
+
isHeadRoute = opts.method.includes('HEAD')
|
|
208
213
|
}
|
|
209
214
|
} else {
|
|
210
215
|
opts.method = normalizeAndValidateMethod.call(this, opts.method)
|
|
211
216
|
validateSchemaBodyOption.call(this, opts.method, path, opts.schema)
|
|
212
|
-
}
|
|
213
217
|
|
|
214
|
-
|
|
215
|
-
|
|
218
|
+
isGetRoute = opts.method === 'GET'
|
|
219
|
+
isHeadRoute = opts.method === 'HEAD'
|
|
216
220
|
}
|
|
217
221
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
validateBodyLimitOption(opts.bodyLimit)
|
|
222
|
+
// we need to clone a set of initial options for HEAD route
|
|
223
|
+
const headOpts = shouldExposeHead && isGetRoute ? { ...options } : null
|
|
223
224
|
|
|
224
225
|
const prefix = this[kRoutePrefix]
|
|
225
226
|
|
package/lib/validation.js
CHANGED
|
@@ -7,7 +7,7 @@ const {
|
|
|
7
7
|
kSchemaBody: bodySchema,
|
|
8
8
|
kSchemaResponse: responseSchema
|
|
9
9
|
} = require('./symbols')
|
|
10
|
-
const scChecker = /^[1-5]{
|
|
10
|
+
const scChecker = /^[1-5](?:\d{2}|xx)$|^default$/
|
|
11
11
|
|
|
12
12
|
const {
|
|
13
13
|
FST_ERR_SCH_RESPONSE_SCHEMA_NOT_NESTED_2XX
|
|
@@ -24,7 +24,7 @@ function compileSchemasForSerialization (context, compile) {
|
|
|
24
24
|
.reduce(function (acc, statusCode) {
|
|
25
25
|
const schema = context.schema.response[statusCode]
|
|
26
26
|
statusCode = statusCode.toLowerCase()
|
|
27
|
-
if (!scChecker.
|
|
27
|
+
if (!scChecker.test(statusCode)) {
|
|
28
28
|
throw new FST_ERR_SCH_RESPONSE_SCHEMA_NOT_NESTED_2XX()
|
|
29
29
|
}
|
|
30
30
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastify",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.2.0",
|
|
4
4
|
"description": "Fast and low overhead web framework, for Node.js",
|
|
5
5
|
"main": "fastify.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -10,24 +10,22 @@
|
|
|
10
10
|
"benchmark": "concurrently -k -s first \"node ./examples/benchmark/simple.js\" \"autocannon -c 100 -d 30 -p 10 localhost:3000/\"",
|
|
11
11
|
"benchmark:parser": "concurrently -k -s first \"node ./examples/benchmark/parser.js\" \"autocannon -c 100 -d 30 -p 10 -i ./examples/benchmark/body.json -H \"content-type:application/jsoff\" -m POST localhost:3000/\"",
|
|
12
12
|
"build:validation": "node build/build-error-serializer.js && node build/build-validation.js",
|
|
13
|
-
"coverage": "
|
|
14
|
-
"coverage:ci": "
|
|
15
|
-
"coverage:ci-check-coverage": "tap replay",
|
|
13
|
+
"coverage": "c8 --reporter html borp --reporter=./test/test-reporter.mjs --coverage --check-coverage --lines 100 ",
|
|
14
|
+
"coverage:ci-check-coverage": "borp --reporter=./test/test-reporter.mjs --coverage --check-coverage --lines 100",
|
|
16
15
|
"lint": "npm run lint:eslint",
|
|
17
16
|
"lint:fix": "eslint --fix",
|
|
18
17
|
"lint:markdown": "markdownlint-cli2",
|
|
19
18
|
"lint:eslint": "eslint",
|
|
20
|
-
"prepublishOnly": "cross-env PREPUBLISH=true
|
|
19
|
+
"prepublishOnly": "cross-env PREPUBLISH=true borp --reporter=./test/test-reporter.mjs && npm run test:validator:integrity",
|
|
21
20
|
"test": "npm run lint && npm run unit && npm run test:typescript",
|
|
22
|
-
"test:ci": "npm run unit
|
|
21
|
+
"test:ci": "npm run unit && npm run test:typescript",
|
|
23
22
|
"test:report": "npm run lint && npm run unit:report && npm run test:typescript",
|
|
24
23
|
"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",
|
|
25
|
-
"test:typescript": "tsc test/types/import.ts --noEmit && tsd",
|
|
24
|
+
"test:typescript": "tsc test/types/import.ts --target es2022 --moduleResolution node16 --module node16 --noEmit && tsd",
|
|
26
25
|
"test:watch": "npm run unit -- --watch --coverage-report=none --reporter=terse",
|
|
27
|
-
"unit": "
|
|
28
|
-
"unit:
|
|
29
|
-
"
|
|
30
|
-
"citgm": "tap --jobs=1 --timeout=120"
|
|
26
|
+
"unit": "borp --reporter=./test/test-reporter.mjs --coverage --check-coverage",
|
|
27
|
+
"unit:report": "c8 --reporter html borp --reporter=./test/test-reporter.mjs",
|
|
28
|
+
"citgm": "borp --reporter=./test/test-reporter.mjs --coverage --check-coverage --concurrency=1"
|
|
31
29
|
},
|
|
32
30
|
"repository": {
|
|
33
31
|
"type": "git",
|
|
@@ -163,12 +161,13 @@
|
|
|
163
161
|
"ajv-i18n": "^4.2.0",
|
|
164
162
|
"ajv-merge-patch": "^5.0.1",
|
|
165
163
|
"autocannon": "^7.15.0",
|
|
164
|
+
"borp": "^0.18.0",
|
|
166
165
|
"branch-comparer": "^1.1.0",
|
|
167
166
|
"concurrently": "^8.2.2",
|
|
168
167
|
"cross-env": "^7.0.3",
|
|
169
168
|
"eslint": "^9.0.0",
|
|
170
169
|
"fast-json-body": "^1.1.0",
|
|
171
|
-
"fastify-plugin": "^
|
|
170
|
+
"fastify-plugin": "^5.0.0",
|
|
172
171
|
"fluent-json-schema": "^5.0.0",
|
|
173
172
|
"h2url": "^0.2.0",
|
|
174
173
|
"http-errors": "^2.0.0",
|
|
@@ -179,12 +178,11 @@
|
|
|
179
178
|
"neostandard": "^0.11.3",
|
|
180
179
|
"node-forge": "^1.3.1",
|
|
181
180
|
"proxyquire": "^2.1.3",
|
|
182
|
-
"send": "^0.18.0",
|
|
183
181
|
"simple-get": "^4.0.1",
|
|
184
182
|
"split2": "^4.2.0",
|
|
185
183
|
"tap": "^21.0.0",
|
|
186
184
|
"tsd": "^0.31.0",
|
|
187
|
-
"typescript": "
|
|
185
|
+
"typescript": "~5.4.5",
|
|
188
186
|
"undici": "^6.13.0",
|
|
189
187
|
"vary": "^1.1.2",
|
|
190
188
|
"yup": "^1.4.0"
|
|
@@ -202,7 +200,7 @@
|
|
|
202
200
|
"process-warning": "^4.0.0",
|
|
203
201
|
"proxy-addr": "^2.0.7",
|
|
204
202
|
"rfdc": "^1.3.1",
|
|
205
|
-
"secure-json-parse": "^
|
|
203
|
+
"secure-json-parse": "^3.0.1",
|
|
206
204
|
"semver": "^7.6.0",
|
|
207
205
|
"toad-cache": "^3.7.0"
|
|
208
206
|
},
|