fastify 5.11.2 → 6.0.0-alpha.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 +9 -9
- package/SPONSORS.md +1 -0
- package/build/build-validation.js +20 -34
- package/docs/Guides/Getting-Started.md +2 -2
- package/docs/Guides/Serverless.md +34 -2
- package/docs/Reference/Encapsulation.md +31 -2
- package/docs/Reference/Errors.md +81 -6
- package/docs/Reference/Hooks.md +0 -4
- package/docs/Reference/LTS.md +6 -5
- package/docs/Reference/Request.md +18 -6
- package/docs/Reference/Routes.md +13 -0
- package/docs/Reference/Server.md +8 -88
- package/docs/Reference/TypeScript.md +3 -49
- package/docs/Reference/Warnings.md +4 -11
- package/fastify.d.ts +10 -24
- package/fastify.js +17 -34
- package/lib/config-validator.js +368 -475
- package/lib/content-type-parser.js +4 -0
- package/lib/decorate.js +1 -1
- package/lib/errors.js +9 -3
- package/lib/log-controller.js +3 -5
- package/lib/logger-factory.js +1 -1
- package/lib/reply.js +7 -3
- package/lib/route.js +4 -9
- package/lib/server.js +2 -3
- package/lib/warnings.js +2 -46
- package/package.json +2 -3
- package/test/404s.test.js +0 -49
- package/test/allow-unsafe-regex.test.js +12 -4
- package/test/case-insensitive.test.js +12 -4
- package/test/content-parser.test.js +32 -0
- package/test/custom-querystring-parser.test.js +0 -16
- package/test/decorator-instance-properties.test.js +49 -0
- package/test/hooks.test.js +6 -1
- package/test/http-methods/custom-http-methods.test.js +12 -17
- package/test/http-methods/trace.test.js +1 -1
- package/test/https/custom-https-server.test.js +1 -1
- package/test/internals/errors.test.js +15 -5
- package/test/internals/initial-config.test.js +56 -38
- package/test/internals/logger.test.js +17 -4
- package/test/internals/reply.test.js +1 -1
- package/test/internals/request.test.js +8 -24
- package/test/logger/logging.test.js +2 -121
- package/test/logger/request.test.js +1 -36
- package/test/plugin.4.test.js +5 -4
- package/test/reply-trailers.test.js +92 -4
- package/test/route-prefix.test.js +46 -18
- package/test/route.1.test.js +2 -2
- package/test/router-options.test.js +25 -523
- package/test/set-error-handler.test.js +7 -9
- package/test/types/errors.tst.ts +2 -1
- package/test/types/fastify.tst.ts +44 -48
- package/test/types/instance.tst.ts +4 -5
- package/test/types/logger.tst.ts +3 -7
- package/test/use-semicolon-delimiter.test.js +0 -60
- package/types/context.d.ts +0 -20
- package/types/errors.d.ts +2 -1
- package/types/instance.d.ts +0 -4
- package/types/logger.d.ts +2 -17
- package/types/plugin.d.ts +0 -9
- package/types/request.d.ts +0 -4
- package/.pi/bash-confirm-whitelist.json +0 -180
- package/lib/promise.js +0 -23
- package/test/internals/promise.test.js +0 -63
|
@@ -145,6 +145,10 @@ ContentTypeParser.prototype.getParser = function (contentType) {
|
|
|
145
145
|
|
|
146
146
|
for (let j = 0; j !== this.parserRegExpList.length; ++j) {
|
|
147
147
|
const parserRegExp = this.parserRegExpList[j]
|
|
148
|
+
// A parser registered with a `g`/`y` flagged RegExp keeps a mutable
|
|
149
|
+
// lastIndex between calls, so a plain `.test()` would skip part of the
|
|
150
|
+
// next content type and match inconsistently. Reset it before testing.
|
|
151
|
+
parserRegExp.lastIndex = 0
|
|
148
152
|
if (parserRegExp.test(ct)) {
|
|
149
153
|
parser = this.customParsers.get(parserRegExp.toString())
|
|
150
154
|
this.cache.set(ct, parser)
|
package/lib/decorate.js
CHANGED
|
@@ -121,7 +121,7 @@ function checkDependencies (instance, name, deps) {
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
for (let i = 0; i !== deps.length; ++i) {
|
|
124
|
-
if (!checkExistence(instance, deps[i])) {
|
|
124
|
+
if (!checkExistence(instance, deps[i]) && !hasInstanceProperty(instance, deps[i])) {
|
|
125
125
|
throw new FST_ERR_DEC_MISSING_DEPENDENCY(deps[i])
|
|
126
126
|
}
|
|
127
127
|
}
|
package/lib/errors.js
CHANGED
|
@@ -416,6 +416,12 @@ const codes = {
|
|
|
416
416
|
500,
|
|
417
417
|
TypeError
|
|
418
418
|
),
|
|
419
|
+
FST_ERR_ROUTE_METHOD_ALREADY_SUPPORTED: createError(
|
|
420
|
+
'FST_ERR_ROUTE_METHOD_ALREADY_SUPPORTED',
|
|
421
|
+
'Method "%s" is already supported. Use `overrideExisting: true` to override it.',
|
|
422
|
+
500,
|
|
423
|
+
TypeError
|
|
424
|
+
),
|
|
419
425
|
FST_ERR_ROUTE_METHOD_NOT_SUPPORTED: createError(
|
|
420
426
|
'FST_ERR_ROUTE_METHOD_NOT_SUPPORTED',
|
|
421
427
|
'%s method is not supported.',
|
|
@@ -475,10 +481,10 @@ const codes = {
|
|
|
475
481
|
),
|
|
476
482
|
FST_ERR_REOPENED_SERVER: createError(
|
|
477
483
|
'FST_ERR_REOPENED_SERVER',
|
|
478
|
-
'Fastify is already
|
|
484
|
+
'Fastify is already started'
|
|
479
485
|
),
|
|
480
|
-
|
|
481
|
-
'
|
|
486
|
+
FST_ERR_INSTANCE_ALREADY_STARTED: createError(
|
|
487
|
+
'FST_ERR_INSTANCE_ALREADY_STARTED',
|
|
482
488
|
'Fastify instance is already listening. %s'
|
|
483
489
|
),
|
|
484
490
|
|
package/lib/log-controller.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { defaultInitOptions } = require('./initial-config-validation')
|
|
4
|
-
|
|
5
3
|
/**
|
|
6
4
|
* Internal logger layer that wraps Fastify's log calls with
|
|
7
5
|
* request-lifecycle awareness (disable-check, level selection).
|
|
@@ -20,9 +18,9 @@ class LogController {
|
|
|
20
18
|
*/
|
|
21
19
|
constructor (options) {
|
|
22
20
|
const opts = options || {}
|
|
23
|
-
this.disableRequestLogging = opts.disableRequestLogging ||
|
|
21
|
+
this.disableRequestLogging = opts.disableRequestLogging || false
|
|
24
22
|
this.isDisableRequestLoggingFunction = typeof this.disableRequestLogging === 'function'
|
|
25
|
-
this.requestIdLogLabel = opts.requestIdLogLabel ||
|
|
23
|
+
this.requestIdLogLabel = opts.requestIdLogLabel || 'reqId'
|
|
26
24
|
}
|
|
27
25
|
|
|
28
26
|
/**
|
|
@@ -54,7 +52,7 @@ class LogController {
|
|
|
54
52
|
* Logs the outcome of a completed request.
|
|
55
53
|
* Uses `error` level when an error is present, `info` otherwise.
|
|
56
54
|
*
|
|
57
|
-
* @param {Error | null} error Error that occurred during the response, if any.
|
|
55
|
+
* @param {Error | null | undefined} error Error that occurred during the response, if any.
|
|
58
56
|
* @param {object} request Fastify request object.
|
|
59
57
|
* @param {object} reply Fastify reply object.
|
|
60
58
|
* @param {object} [metadata] Extra contextual data (unused).
|
package/lib/logger-factory.js
CHANGED
|
@@ -132,7 +132,7 @@ function createLogger (options) {
|
|
|
132
132
|
function createLogController (options) {
|
|
133
133
|
const userController = options.logController
|
|
134
134
|
if (!userController) {
|
|
135
|
-
return new LogController(
|
|
135
|
+
return new LogController()
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
if (userController instanceof LogController) {
|
package/lib/reply.js
CHANGED
|
@@ -595,9 +595,13 @@ function onSendEnd (reply, payload) {
|
|
|
595
595
|
header += ' '
|
|
596
596
|
header += trailerName
|
|
597
597
|
}
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
598
|
+
if (header !== '') {
|
|
599
|
+
// it must be chunked for trailer to work
|
|
600
|
+
reply.header('Transfer-Encoding', 'chunked')
|
|
601
|
+
reply.header('Trailer', header.trim())
|
|
602
|
+
} else {
|
|
603
|
+
reply[kReplyTrailers] = null
|
|
604
|
+
}
|
|
601
605
|
}
|
|
602
606
|
|
|
603
607
|
// since Response contain status code, headers and body,
|
package/lib/route.js
CHANGED
|
@@ -6,6 +6,7 @@ const handleRequest = require('./handle-request.js')
|
|
|
6
6
|
const { onRequestAbortHookRunner, lifecycleHooks, preParsingHookRunner, onTimeoutHookRunner, onRequestHookRunner } = require('./hooks')
|
|
7
7
|
const { normalizeSchema } = require('./schemas')
|
|
8
8
|
const { parseHeadOnSendHandlers } = require('./head-route.js')
|
|
9
|
+
const { defaultInitOptions } = require('./initial-config-validation.js')
|
|
9
10
|
|
|
10
11
|
const {
|
|
11
12
|
compileSchemasForValidation,
|
|
@@ -57,8 +58,6 @@ const {
|
|
|
57
58
|
const { buildErrorHandler } = require('./error-handler')
|
|
58
59
|
const { createChildLogger, defaultChildLoggerFactory } = require('./logger-factory.js')
|
|
59
60
|
const { getGenReqId } = require('./req-id-gen-factory.js')
|
|
60
|
-
const { FSTDEP022 } = require('./warnings')
|
|
61
|
-
|
|
62
61
|
const routerKeys = [
|
|
63
62
|
'allowUnsafeRegex',
|
|
64
63
|
'buildPrettyMeta',
|
|
@@ -663,15 +662,11 @@ function buildRouterOptions (options, defaultOptions) {
|
|
|
663
662
|
? Object.create(null)
|
|
664
663
|
: Object.assign(Object.create(null), options.routerOptions)
|
|
665
664
|
|
|
666
|
-
const usedDeprecatedOptions = routerKeys.filter(key => Object.hasOwn(options, key))
|
|
667
|
-
|
|
668
|
-
if (usedDeprecatedOptions.length > 0) {
|
|
669
|
-
FSTDEP022(usedDeprecatedOptions.join(', '))
|
|
670
|
-
}
|
|
671
|
-
|
|
672
665
|
for (const key of routerKeys) {
|
|
673
666
|
if (!Object.hasOwn(routerOptions, key)) {
|
|
674
|
-
|
|
667
|
+
// defaultInitOptions are non-function default
|
|
668
|
+
// defaultOptions are function default
|
|
669
|
+
routerOptions[key] = options[key] ?? defaultInitOptions.routerOptions[key] ?? defaultOptions[key]
|
|
675
670
|
}
|
|
676
671
|
}
|
|
677
672
|
|
package/lib/server.js
CHANGED
|
@@ -16,7 +16,6 @@ const {
|
|
|
16
16
|
FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE
|
|
17
17
|
} = require('./errors')
|
|
18
18
|
const noopSet = require('./noop-set')
|
|
19
|
-
const PonyPromise = require('./promise')
|
|
20
19
|
|
|
21
20
|
module.exports.createServer = createServer
|
|
22
21
|
|
|
@@ -106,7 +105,7 @@ function createServer (options, httpHandler) {
|
|
|
106
105
|
if (cb === undefined) {
|
|
107
106
|
const listening = listenPromise.call(this, server, listenOptions)
|
|
108
107
|
return listening.then(address => {
|
|
109
|
-
const { promise, resolve } =
|
|
108
|
+
const { promise, resolve } = Promise.withResolvers()
|
|
110
109
|
if (host === 'localhost') {
|
|
111
110
|
multipleBindings.call(this, server, httpHandler, options, listenOptions, () => {
|
|
112
111
|
this[kState].listening = true
|
|
@@ -277,7 +276,7 @@ function listenPromise (server, listenOptions) {
|
|
|
277
276
|
// skip listen when aborted during ready
|
|
278
277
|
if (this[kState].aborted) return
|
|
279
278
|
|
|
280
|
-
const { promise, resolve, reject } =
|
|
279
|
+
const { promise, resolve, reject } = Promise.withResolvers()
|
|
281
280
|
|
|
282
281
|
const errEventHandler = (err) => {
|
|
283
282
|
cleanup()
|
package/lib/warnings.js
CHANGED
|
@@ -3,13 +3,9 @@
|
|
|
3
3
|
const { createWarning } = require('process-warning')
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* Warning codes:
|
|
7
7
|
* - FSTWRN001
|
|
8
8
|
* - FSTSEC001
|
|
9
|
-
* - FSTDEP022
|
|
10
|
-
* - FSTDEP023
|
|
11
|
-
* - FSTDEP024
|
|
12
|
-
* - FSTDEP025
|
|
13
9
|
*
|
|
14
10
|
* Deprecation Codes FSTDEP001 - FSTDEP021 were used by v4 and MUST NOT be reused.
|
|
15
11
|
* - FSTDEP022 - FSTDEP025 are used by v5 and MUST NOT be reused.
|
|
@@ -30,13 +26,6 @@ const FSTWRN003 = createWarning({
|
|
|
30
26
|
unlimited: true
|
|
31
27
|
})
|
|
32
28
|
|
|
33
|
-
const FSTWRN004 = createWarning({
|
|
34
|
-
name: 'FastifyWarning',
|
|
35
|
-
code: 'FSTWRN004',
|
|
36
|
-
message: 'It seems that you are overriding an errorHandler in the same scope, which can lead to subtle bugs. To disable this behavior, set \'allowErrorHandlerOverride\' to false. For more information, visit: https://fastify.dev/docs/latest/Reference/Server/#allowerrorhandleroverride',
|
|
37
|
-
unlimited: true
|
|
38
|
-
})
|
|
39
|
-
|
|
40
29
|
const FSTSEC001 = createWarning({
|
|
41
30
|
name: 'FastifySecurity',
|
|
42
31
|
code: 'FSTSEC001',
|
|
@@ -44,41 +33,8 @@ const FSTSEC001 = createWarning({
|
|
|
44
33
|
unlimited: true
|
|
45
34
|
})
|
|
46
35
|
|
|
47
|
-
const FSTDEP022 = createWarning({
|
|
48
|
-
name: 'FastifyWarning',
|
|
49
|
-
code: 'FSTDEP022',
|
|
50
|
-
message: 'The router options for %s property access is deprecated. Please use "options.routerOptions" instead for accessing router options. The router options will be removed in `fastify@6`.',
|
|
51
|
-
unlimited: true
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
const FSTDEP023 = createWarning({
|
|
55
|
-
name: 'FastifyDeprecation',
|
|
56
|
-
code: 'FSTDEP023',
|
|
57
|
-
message: 'disableRequestLogging option is deprecated. Use the logController option with disableRequestLogging or isLogDisabled override instead. The disableRequestLogging top-level option will be removed in `fastify@6`.',
|
|
58
|
-
unlimited: true
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
const FSTDEP024 = createWarning({
|
|
62
|
-
name: 'FastifyDeprecation',
|
|
63
|
-
code: 'FSTDEP024',
|
|
64
|
-
message: 'requestIdLogLabel option is deprecated. Use the logController option with requestIdLogLabel instead. The requestIdLogLabel top-level option will be removed in `fastify@6`.',
|
|
65
|
-
unlimited: true
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
const FSTDEP025 = createWarning({
|
|
69
|
-
name: 'FastifyDeprecation',
|
|
70
|
-
code: 'FSTDEP025',
|
|
71
|
-
message: 'Calling addHttpMethod for existing method "%s" without { overrideExisting: true } is deprecated. In Fastify v6, this will throw an error.',
|
|
72
|
-
unlimited: true
|
|
73
|
-
})
|
|
74
|
-
|
|
75
36
|
module.exports = {
|
|
76
37
|
FSTWRN001,
|
|
77
38
|
FSTWRN003,
|
|
78
|
-
|
|
79
|
-
FSTSEC001,
|
|
80
|
-
FSTDEP022,
|
|
81
|
-
FSTDEP023,
|
|
82
|
-
FSTDEP024,
|
|
83
|
-
FSTDEP025
|
|
39
|
+
FSTSEC001
|
|
84
40
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastify",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0-alpha.0",
|
|
4
4
|
"description": "Fast and low overhead web framework, for Node.js",
|
|
5
5
|
"main": "fastify.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -167,7 +167,6 @@
|
|
|
167
167
|
],
|
|
168
168
|
"devDependencies": {
|
|
169
169
|
"@jsumners/line-reporter": "^1.0.1",
|
|
170
|
-
"@sinonjs/fake-timers": "^11.2.2",
|
|
171
170
|
"@stylistic/eslint-plugin": "^5.1.0",
|
|
172
171
|
"@stylistic/eslint-plugin-js": "^4.1.0",
|
|
173
172
|
"@types/node": "^26.0.1",
|
|
@@ -199,7 +198,7 @@
|
|
|
199
198
|
"tstyche": "^7.0.0",
|
|
200
199
|
"typebox": "^1.0.81",
|
|
201
200
|
"typescript": "~6.0.2",
|
|
202
|
-
"undici": "^
|
|
201
|
+
"undici": "^8.0.0",
|
|
203
202
|
"vary": "^1.1.2",
|
|
204
203
|
"yup": "^1.4.0"
|
|
205
204
|
},
|
package/test/404s.test.js
CHANGED
|
@@ -1984,52 +1984,3 @@ test('hooks are applied to not found handlers /3', async t => {
|
|
|
1984
1984
|
const { statusCode } = await fastify.inject('/')
|
|
1985
1985
|
t.assert.strictEqual(statusCode, 401)
|
|
1986
1986
|
})
|
|
1987
|
-
|
|
1988
|
-
test('should honor disableRequestLogging function for 404', async t => {
|
|
1989
|
-
t.plan(3)
|
|
1990
|
-
|
|
1991
|
-
const Writable = require('node:stream').Writable
|
|
1992
|
-
|
|
1993
|
-
const logStream = new Writable()
|
|
1994
|
-
logStream.logs = []
|
|
1995
|
-
logStream._write = function (chunk, encoding, callback) {
|
|
1996
|
-
this.logs.push(JSON.parse(chunk.toString()))
|
|
1997
|
-
callback()
|
|
1998
|
-
}
|
|
1999
|
-
|
|
2000
|
-
const fastify = Fastify({
|
|
2001
|
-
logger: {
|
|
2002
|
-
level: 'info',
|
|
2003
|
-
stream: logStream
|
|
2004
|
-
},
|
|
2005
|
-
disableRequestLogging: (req) => {
|
|
2006
|
-
// Disable logging for URLs containing 'silent'
|
|
2007
|
-
return req.url.includes('silent')
|
|
2008
|
-
}
|
|
2009
|
-
})
|
|
2010
|
-
|
|
2011
|
-
fastify.get('/', function (req, reply) {
|
|
2012
|
-
reply.send({ hello: 'world' })
|
|
2013
|
-
})
|
|
2014
|
-
|
|
2015
|
-
t.after(() => { fastify.close() })
|
|
2016
|
-
|
|
2017
|
-
// First request to a non-existent route (no 'silent' in URL) - should log
|
|
2018
|
-
const response1 = await fastify.inject({
|
|
2019
|
-
method: 'GET',
|
|
2020
|
-
url: '/not-found'
|
|
2021
|
-
})
|
|
2022
|
-
t.assert.strictEqual(response1.statusCode, 404)
|
|
2023
|
-
|
|
2024
|
-
// Second request to a non-existent route with 'silent' in URL - should not log
|
|
2025
|
-
const response2 = await fastify.inject({
|
|
2026
|
-
method: 'GET',
|
|
2027
|
-
url: '/silent-route'
|
|
2028
|
-
})
|
|
2029
|
-
t.assert.strictEqual(response2.statusCode, 404)
|
|
2030
|
-
|
|
2031
|
-
// Check logs: first request should have logged, second should not
|
|
2032
|
-
// We expect: incoming request, Route not found info, request completed (for first request only)
|
|
2033
|
-
const infoLogs = logStream.logs.filter(log => log.msg && log.msg.includes('Route GET:/not-found not found'))
|
|
2034
|
-
t.assert.strictEqual(infoLogs.length, 1, 'Should log 404 info only for non-silent route')
|
|
2035
|
-
})
|
|
@@ -7,7 +7,9 @@ test('allow unsafe regex', async t => {
|
|
|
7
7
|
t.plan(2)
|
|
8
8
|
|
|
9
9
|
const fastify = Fastify({
|
|
10
|
-
|
|
10
|
+
routerOptions: {
|
|
11
|
+
allowUnsafeRegex: false
|
|
12
|
+
}
|
|
11
13
|
})
|
|
12
14
|
t.after(() => fastify.close())
|
|
13
15
|
|
|
@@ -26,7 +28,9 @@ test('allow unsafe regex not match', async t => {
|
|
|
26
28
|
t.plan(1)
|
|
27
29
|
|
|
28
30
|
const fastify = Fastify({
|
|
29
|
-
|
|
31
|
+
routerOptions: {
|
|
32
|
+
allowUnsafeRegex: false
|
|
33
|
+
}
|
|
30
34
|
})
|
|
31
35
|
t.after(() => fastify.close())
|
|
32
36
|
|
|
@@ -44,7 +48,9 @@ test('allow unsafe regex not safe', (t, done) => {
|
|
|
44
48
|
t.plan(1)
|
|
45
49
|
|
|
46
50
|
const fastify = Fastify({
|
|
47
|
-
|
|
51
|
+
routerOptions: {
|
|
52
|
+
allowUnsafeRegex: false
|
|
53
|
+
}
|
|
48
54
|
})
|
|
49
55
|
t.after(() => fastify.close())
|
|
50
56
|
|
|
@@ -74,7 +80,9 @@ test('allow unsafe regex allow unsafe', async t => {
|
|
|
74
80
|
t.plan(3)
|
|
75
81
|
|
|
76
82
|
const fastify = Fastify({
|
|
77
|
-
|
|
83
|
+
routerOptions: {
|
|
84
|
+
allowUnsafeRegex: true
|
|
85
|
+
}
|
|
78
86
|
})
|
|
79
87
|
t.after(() => fastify.close())
|
|
80
88
|
|
|
@@ -7,7 +7,9 @@ test('case insensitive', async (t) => {
|
|
|
7
7
|
t.plan(3)
|
|
8
8
|
|
|
9
9
|
const fastify = Fastify({
|
|
10
|
-
|
|
10
|
+
routerOptions: {
|
|
11
|
+
caseSensitive: false
|
|
12
|
+
}
|
|
11
13
|
})
|
|
12
14
|
t.after(() => fastify.close())
|
|
13
15
|
|
|
@@ -30,7 +32,9 @@ test('case insensitive inject', async t => {
|
|
|
30
32
|
t.plan(2)
|
|
31
33
|
|
|
32
34
|
const fastify = Fastify({
|
|
33
|
-
|
|
35
|
+
routerOptions: {
|
|
36
|
+
caseSensitive: false
|
|
37
|
+
}
|
|
34
38
|
})
|
|
35
39
|
t.after(() => fastify.close())
|
|
36
40
|
|
|
@@ -55,7 +59,9 @@ test('case insensitive (parametric)', async (t) => {
|
|
|
55
59
|
t.plan(4)
|
|
56
60
|
|
|
57
61
|
const fastify = Fastify({
|
|
58
|
-
|
|
62
|
+
routerOptions: {
|
|
63
|
+
caseSensitive: false
|
|
64
|
+
}
|
|
59
65
|
})
|
|
60
66
|
t.after(() => fastify.close())
|
|
61
67
|
|
|
@@ -81,7 +87,9 @@ test('case insensitive (wildcard)', async (t) => {
|
|
|
81
87
|
t.plan(4)
|
|
82
88
|
|
|
83
89
|
const fastify = Fastify({
|
|
84
|
-
|
|
90
|
+
routerOptions: {
|
|
91
|
+
caseSensitive: false
|
|
92
|
+
}
|
|
85
93
|
})
|
|
86
94
|
t.after(() => fastify.close())
|
|
87
95
|
|
|
@@ -578,6 +578,38 @@ test('content-type match parameters - regexp', async t => {
|
|
|
578
578
|
})
|
|
579
579
|
})
|
|
580
580
|
|
|
581
|
+
test('content-type match - RegExp with global flag', async t => {
|
|
582
|
+
t.plan(4)
|
|
583
|
+
|
|
584
|
+
const fastify = Fastify()
|
|
585
|
+
fastify.addContentTypeParser(/^application\/.+\+xml$/g, { parseAs: 'string' }, function (request, body, done) {
|
|
586
|
+
done(null, body)
|
|
587
|
+
})
|
|
588
|
+
|
|
589
|
+
fastify.post('/', async (request) => request.body)
|
|
590
|
+
|
|
591
|
+
// Two distinct content types that both match the parser. A RegExp with the
|
|
592
|
+
// `g` flag keeps a mutable lastIndex between test() calls, so the second
|
|
593
|
+
// content type must still match rather than fall through to 415.
|
|
594
|
+
const first = await fastify.inject({
|
|
595
|
+
method: 'POST',
|
|
596
|
+
path: '/',
|
|
597
|
+
headers: { 'content-type': 'application/vnd.a+xml' },
|
|
598
|
+
body: '<a/>'
|
|
599
|
+
})
|
|
600
|
+
const second = await fastify.inject({
|
|
601
|
+
method: 'POST',
|
|
602
|
+
path: '/',
|
|
603
|
+
headers: { 'content-type': 'application/vnd.b+xml' },
|
|
604
|
+
body: '<b/>'
|
|
605
|
+
})
|
|
606
|
+
|
|
607
|
+
t.assert.strictEqual(first.statusCode, 200)
|
|
608
|
+
t.assert.strictEqual(first.payload, '<a/>')
|
|
609
|
+
t.assert.strictEqual(second.statusCode, 200)
|
|
610
|
+
t.assert.strictEqual(second.payload, '<b/>')
|
|
611
|
+
})
|
|
612
|
+
|
|
581
613
|
test('content-type fail when parameters not match - string 1', async t => {
|
|
582
614
|
t.plan(1)
|
|
583
615
|
|
|
@@ -94,22 +94,6 @@ test('Querystring without value', async t => {
|
|
|
94
94
|
t.assert.strictEqual(injectResponse.statusCode, 200)
|
|
95
95
|
})
|
|
96
96
|
|
|
97
|
-
test('Custom querystring parser should be a function', t => {
|
|
98
|
-
t.plan(1)
|
|
99
|
-
|
|
100
|
-
try {
|
|
101
|
-
Fastify({
|
|
102
|
-
querystringParser: 10
|
|
103
|
-
})
|
|
104
|
-
t.assert.fail('Should throw')
|
|
105
|
-
} catch (err) {
|
|
106
|
-
t.assert.strictEqual(
|
|
107
|
-
err.message,
|
|
108
|
-
"querystringParser option should be a function, instead got 'number'"
|
|
109
|
-
)
|
|
110
|
-
}
|
|
111
|
-
})
|
|
112
|
-
|
|
113
97
|
test('Custom querystring parser should be a function', t => {
|
|
114
98
|
t.plan(1)
|
|
115
99
|
|
|
@@ -61,3 +61,52 @@ test('decorateRequest still works for non-built-in names', (t, done) => {
|
|
|
61
61
|
done()
|
|
62
62
|
})
|
|
63
63
|
})
|
|
64
|
+
|
|
65
|
+
test('decorateRequest accepts built-in request properties as dependencies', t => {
|
|
66
|
+
t.plan(6)
|
|
67
|
+
const fastify = Fastify()
|
|
68
|
+
for (const name of ['id', 'params', 'raw', 'query', 'log', 'body']) {
|
|
69
|
+
t.assert.doesNotThrow(() => fastify.decorateRequest(`uses_${name}`, null, [name]))
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
test('decorateReply accepts built-in reply properties as dependencies', t => {
|
|
74
|
+
t.plan(3)
|
|
75
|
+
const fastify = Fastify()
|
|
76
|
+
for (const name of ['raw', 'request', 'log']) {
|
|
77
|
+
t.assert.doesNotThrow(() => fastify.decorateReply(`uses_${name}`, null, [name]))
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
test('decorateRequest accepts built-in properties as dependencies inside a plugin', (t, done) => {
|
|
82
|
+
t.plan(2)
|
|
83
|
+
const fastify = Fastify()
|
|
84
|
+
fastify.register(async (instance) => {
|
|
85
|
+
instance.decorateRequest('scopedExtra', null, ['body'])
|
|
86
|
+
t.assert.equal(instance.hasRequestDecorator('scopedExtra'), true)
|
|
87
|
+
})
|
|
88
|
+
fastify.ready((err) => {
|
|
89
|
+
t.assert.ifError(err)
|
|
90
|
+
done()
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
test('decorateRequest accepts built-in properties mixed with user decorators as dependencies', t => {
|
|
95
|
+
t.plan(1)
|
|
96
|
+
const fastify = Fastify()
|
|
97
|
+
fastify.decorateRequest('userProp', null)
|
|
98
|
+
t.assert.doesNotThrow(() => fastify.decorateRequest('mixed', null, ['userProp', 'body']))
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
test('decorateRequest still throws FST_ERR_DEC_MISSING_DEPENDENCY for unknown dependencies', t => {
|
|
102
|
+
t.plan(2)
|
|
103
|
+
const fastify = Fastify()
|
|
104
|
+
t.assert.throws(
|
|
105
|
+
() => fastify.decorateRequest('withUnknown', null, ['nonExistent']),
|
|
106
|
+
(err) => err.code === 'FST_ERR_DEC_MISSING_DEPENDENCY'
|
|
107
|
+
)
|
|
108
|
+
t.assert.throws(
|
|
109
|
+
() => fastify.decorateReply('withUnknown', null, ['body']),
|
|
110
|
+
(err) => err.code === 'FST_ERR_DEC_MISSING_DEPENDENCY'
|
|
111
|
+
)
|
|
112
|
+
})
|
package/test/hooks.test.js
CHANGED
|
@@ -675,7 +675,12 @@ test('onRoute hook should be called once when prefixTrailingSlash', (t, testDone
|
|
|
675
675
|
let onRouteCalled = 0
|
|
676
676
|
let routePatched = 0
|
|
677
677
|
|
|
678
|
-
const fastify = Fastify({
|
|
678
|
+
const fastify = Fastify({
|
|
679
|
+
exposeHeadRoutes: false,
|
|
680
|
+
routerOptions: {
|
|
681
|
+
ignoreTrailingSlash: false
|
|
682
|
+
}
|
|
683
|
+
})
|
|
679
684
|
|
|
680
685
|
// a plugin that patches route options, similar to fastify-compress
|
|
681
686
|
fastify.register(fp(function myPlugin (instance, opts, next) {
|
|
@@ -113,36 +113,31 @@ test('addHttpMethod rejects fake http method', t => {
|
|
|
113
113
|
t.assert.throws(() => { fastify.addHttpMethod('FOOO') }, /Provided method is invalid!/)
|
|
114
114
|
})
|
|
115
115
|
|
|
116
|
-
test('addHttpMethod
|
|
116
|
+
test('addHttpMethod rejects an implicit override of an existing method', t => {
|
|
117
117
|
const fastify = Fastify()
|
|
118
|
-
const onWarning = warning => {
|
|
119
|
-
t.assert.strictEqual(warning.name, 'FastifyDeprecation')
|
|
120
|
-
t.assert.strictEqual(warning.code, 'FSTDEP025')
|
|
121
|
-
done()
|
|
122
|
-
}
|
|
123
|
-
process.once('warning', onWarning)
|
|
124
118
|
|
|
125
119
|
t.after(() => {
|
|
126
120
|
fastify.close()
|
|
127
|
-
process.removeListener('warning', onWarning)
|
|
128
121
|
})
|
|
129
122
|
|
|
130
|
-
|
|
123
|
+
t.assert.throws(
|
|
124
|
+
() => fastify.addHttpMethod('GET', { hasBody: true }),
|
|
125
|
+
{
|
|
126
|
+
code: 'FST_ERR_ROUTE_METHOD_ALREADY_SUPPORTED',
|
|
127
|
+
message: 'Method "GET" is already supported. Use `overrideExisting: true` to override it.'
|
|
128
|
+
}
|
|
129
|
+
)
|
|
131
130
|
})
|
|
132
131
|
|
|
133
|
-
test('addHttpMethod
|
|
134
|
-
const doNotWarn = () => {
|
|
135
|
-
t.assert.fail('should not warn')
|
|
136
|
-
}
|
|
137
|
-
process.on('warning', doNotWarn)
|
|
138
|
-
|
|
132
|
+
test('addHttpMethod allows an explicit override of an existing method', t => {
|
|
139
133
|
const fastify = Fastify()
|
|
140
134
|
t.after(() => {
|
|
141
135
|
fastify.close()
|
|
142
|
-
process.removeListener('warning', doNotWarn)
|
|
143
136
|
})
|
|
144
137
|
|
|
145
|
-
|
|
138
|
+
t.assert.doesNotThrow(() => {
|
|
139
|
+
fastify.addHttpMethod('POST', { overrideExisting: true })
|
|
140
|
+
})
|
|
146
141
|
})
|
|
147
142
|
|
|
148
143
|
test('addHttpMethod can change an existing method body behavior', async t => {
|
|
@@ -5,7 +5,7 @@ const Fastify = require('../..')
|
|
|
5
5
|
const https = require('node:https')
|
|
6
6
|
const dns = require('node:dns').promises
|
|
7
7
|
const { buildCertificate } = require('../build-certificate')
|
|
8
|
-
const { Agent } = require('undici')
|
|
8
|
+
const { Agent, fetch } = require('undici')
|
|
9
9
|
|
|
10
10
|
async function setup () {
|
|
11
11
|
await buildCertificate()
|
|
@@ -5,7 +5,7 @@ const errors = require('../../lib/errors')
|
|
|
5
5
|
const { readFileSync } = require('node:fs')
|
|
6
6
|
const { resolve } = require('node:path')
|
|
7
7
|
|
|
8
|
-
const expectedErrors =
|
|
8
|
+
const expectedErrors = 95
|
|
9
9
|
|
|
10
10
|
test(`should expose ${expectedErrors} errors`, t => {
|
|
11
11
|
t.plan(1)
|
|
@@ -710,6 +710,16 @@ test('FST_ERR_ROUTE_METHOD_INVALID', t => {
|
|
|
710
710
|
t.assert.ok(error instanceof TypeError)
|
|
711
711
|
})
|
|
712
712
|
|
|
713
|
+
test('FST_ERR_ROUTE_METHOD_ALREADY_SUPPORTED', t => {
|
|
714
|
+
t.plan(5)
|
|
715
|
+
const error = new errors.FST_ERR_ROUTE_METHOD_ALREADY_SUPPORTED('GET')
|
|
716
|
+
t.assert.strictEqual(error.name, 'FastifyError')
|
|
717
|
+
t.assert.strictEqual(error.code, 'FST_ERR_ROUTE_METHOD_ALREADY_SUPPORTED')
|
|
718
|
+
t.assert.strictEqual(error.message, 'Method "GET" is already supported. Use `overrideExisting: true` to override it.')
|
|
719
|
+
t.assert.strictEqual(error.statusCode, 500)
|
|
720
|
+
t.assert.ok(error instanceof TypeError)
|
|
721
|
+
})
|
|
722
|
+
|
|
713
723
|
test('FST_ERR_ROUTE_METHOD_NOT_SUPPORTED', t => {
|
|
714
724
|
t.plan(5)
|
|
715
725
|
const error = new errors.FST_ERR_ROUTE_METHOD_NOT_SUPPORTED()
|
|
@@ -805,16 +815,16 @@ test('FST_ERR_REOPENED_SERVER', t => {
|
|
|
805
815
|
const error = new errors.FST_ERR_REOPENED_SERVER()
|
|
806
816
|
t.assert.strictEqual(error.name, 'FastifyError')
|
|
807
817
|
t.assert.strictEqual(error.code, 'FST_ERR_REOPENED_SERVER')
|
|
808
|
-
t.assert.strictEqual(error.message, 'Fastify is already
|
|
818
|
+
t.assert.strictEqual(error.message, 'Fastify is already started')
|
|
809
819
|
t.assert.strictEqual(error.statusCode, 500)
|
|
810
820
|
t.assert.ok(error instanceof Error)
|
|
811
821
|
})
|
|
812
822
|
|
|
813
|
-
test('
|
|
823
|
+
test('FST_ERR_INSTANCE_ALREADY_STARTED', t => {
|
|
814
824
|
t.plan(5)
|
|
815
|
-
const error = new errors.
|
|
825
|
+
const error = new errors.FST_ERR_INSTANCE_ALREADY_STARTED()
|
|
816
826
|
t.assert.strictEqual(error.name, 'FastifyError')
|
|
817
|
-
t.assert.strictEqual(error.code, '
|
|
827
|
+
t.assert.strictEqual(error.code, 'FST_ERR_INSTANCE_ALREADY_STARTED')
|
|
818
828
|
t.assert.strictEqual(error.message, 'Fastify instance is already listening. %s')
|
|
819
829
|
t.assert.strictEqual(error.statusCode, 500)
|
|
820
830
|
t.assert.ok(error instanceof Error)
|