fastify 5.8.4 → 5.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/SECURITY.md +1 -1
  2. package/SPONSORS.md +6 -4
  3. package/docs/Guides/Database.md +0 -28
  4. package/docs/Guides/Ecosystem.md +17 -2
  5. package/docs/Guides/Serverless.md +2 -2
  6. package/docs/Guides/Write-Plugin.md +1 -1
  7. package/docs/Reference/Encapsulation.md +27 -26
  8. package/docs/Reference/Errors.md +10 -4
  9. package/docs/Reference/HTTP2.md +10 -10
  10. package/docs/Reference/Hooks.md +4 -4
  11. package/docs/Reference/Index.md +14 -16
  12. package/docs/Reference/LTS.md +12 -13
  13. package/docs/Reference/Lifecycle.md +9 -8
  14. package/docs/Reference/Logging.md +44 -39
  15. package/docs/Reference/Middleware.md +21 -25
  16. package/docs/Reference/Principles.md +2 -2
  17. package/docs/Reference/Reply.md +6 -1
  18. package/docs/Reference/Request.md +27 -16
  19. package/docs/Reference/Routes.md +5 -2
  20. package/docs/Reference/Server.md +31 -3
  21. package/docs/Reference/Type-Providers.md +29 -5
  22. package/docs/Reference/Validation-and-Serialization.md +15 -2
  23. package/docs/Reference/Warnings.md +7 -6
  24. package/eslint.config.js +7 -2
  25. package/fastify.d.ts +8 -3
  26. package/fastify.js +43 -14
  27. package/lib/content-type-parser.js +13 -1
  28. package/lib/decorate.js +11 -3
  29. package/lib/error-handler.js +4 -3
  30. package/lib/error-serializer.js +59 -59
  31. package/lib/errors.js +16 -1
  32. package/lib/four-oh-four.js +14 -9
  33. package/lib/handle-request.js +11 -5
  34. package/lib/plugin-override.js +2 -1
  35. package/lib/plugin-utils.js +5 -5
  36. package/lib/reply.js +63 -8
  37. package/lib/request.js +23 -19
  38. package/lib/route.js +20 -6
  39. package/lib/schema-controller.js +1 -1
  40. package/lib/schemas.js +37 -30
  41. package/lib/symbols.js +3 -1
  42. package/lib/validation.js +1 -13
  43. package/lib/warnings.js +3 -3
  44. package/package.json +16 -18
  45. package/scripts/validate-ecosystem-links.js +1 -0
  46. package/test/bundler/esbuild/package.json +1 -1
  47. package/test/close-pipelining.test.js +1 -2
  48. package/test/custom-http-server.test.js +38 -0
  49. package/test/decorator-instance-properties.test.js +63 -0
  50. package/test/diagnostics-channel/async-error-handler.test.js +74 -0
  51. package/test/hooks.test.js +23 -0
  52. package/test/http-methods/get.test.js +1 -1
  53. package/test/http2/plain.test.js +135 -0
  54. package/test/http2/secure-with-fallback.test.js +1 -1
  55. package/test/https/https.test.js +1 -2
  56. package/test/internals/errors.test.js +31 -1
  57. package/test/internals/plugin.test.js +3 -1
  58. package/test/internals/request.test.js +27 -3
  59. package/test/internals/schema-controller-perf.test.js +33 -0
  60. package/test/logger/logging.test.js +18 -1
  61. package/test/logger/options.test.js +38 -1
  62. package/test/reply-error.test.js +1 -1
  63. package/test/reply-trailers.test.js +70 -0
  64. package/test/request-media-type.test.js +105 -0
  65. package/test/request-port.test.js +72 -0
  66. package/test/route-prefix.test.js +34 -0
  67. package/test/router-options.test.js +222 -11
  68. package/test/schema-serialization.test.js +108 -0
  69. package/test/schema-validation.test.js +45 -0
  70. package/test/scripts/validate-ecosystem-links.test.js +40 -57
  71. package/test/throw.test.js +14 -0
  72. package/test/trust-proxy.test.js +74 -0
  73. package/test/types/content-type-parser.tst.ts +70 -0
  74. package/test/types/{decorate-request-reply.test-d.ts → decorate-request-reply.tst.ts} +4 -4
  75. package/test/types/{dummy-plugin.ts → dummy-plugin.mts} +1 -1
  76. package/test/types/errors.tst.ts +91 -0
  77. package/test/types/fastify.tst.ts +351 -0
  78. package/test/types/hooks.tst.ts +578 -0
  79. package/test/types/instance.tst.ts +597 -0
  80. package/test/types/{logger.test-d.ts → logger.tst.ts} +61 -62
  81. package/test/types/plugin.tst.ts +96 -0
  82. package/test/types/register.tst.ts +245 -0
  83. package/test/types/reply.tst.ts +297 -0
  84. package/test/types/request.tst.ts +199 -0
  85. package/test/types/route.tst.ts +576 -0
  86. package/test/types/{schema.test-d.ts → schema.tst.ts} +22 -22
  87. package/test/types/{serverFactory.test-d.ts → serverFactory.tst.ts} +4 -4
  88. package/test/types/tsconfig.json +9 -0
  89. package/test/types/{type-provider.test-d.ts → type-provider.tst.ts} +256 -250
  90. package/test/types/using.tst.ts +14 -0
  91. package/types/errors.d.ts +3 -0
  92. package/types/request.d.ts +23 -2
  93. package/AGENTS.md +0 -290
  94. package/test/types/content-type-parser.test-d.ts +0 -72
  95. package/test/types/errors.test-d.ts +0 -90
  96. package/test/types/fastify.test-d.ts +0 -352
  97. package/test/types/hooks.test-d.ts +0 -550
  98. package/test/types/import.ts +0 -2
  99. package/test/types/instance.test-d.ts +0 -588
  100. package/test/types/plugin.test-d.ts +0 -97
  101. package/test/types/register.test-d.ts +0 -237
  102. package/test/types/reply.test-d.ts +0 -254
  103. package/test/types/request.test-d.ts +0 -188
  104. package/test/types/route.test-d.ts +0 -553
  105. package/test/types/using.test-d.ts +0 -17
package/fastify.d.ts CHANGED
@@ -6,7 +6,7 @@ import { Socket } from 'node:net'
6
6
  import { BuildCompilerFromPool, ValidatorFactory } from '@fastify/ajv-compiler'
7
7
  import { FastifyError } from '@fastify/error'
8
8
  import { Options as FJSOptions, SerializerFactory } from '@fastify/fast-json-stringify-compiler'
9
- import { Config as FindMyWayConfig, ConstraintStrategy, HTTPVersion } from 'find-my-way'
9
+ import { ConstraintStrategy, Config as FindMyWayConfig, HTTPVersion } from 'find-my-way'
10
10
  import { InjectOptions, CallbackFunc as LightMyRequestCallback, Chain as LightMyRequestChain, Response as LightMyRequestResponse } from 'light-my-request'
11
11
 
12
12
  import { AddContentTypeParser, ConstructorAction, FastifyBodyParser, FastifyContentTypeParser, getDefaultJsonParser, hasContentTypeParser, ProtoAction } from './types/content-type-parser'
@@ -28,7 +28,7 @@ import { FastifyRegister, FastifyRegisterOptions, RegisterOptions } from './type
28
28
  import { FastifyReply } from './types/reply'
29
29
  import { FastifyRequest, RequestGenericInterface } from './types/request'
30
30
  import { RouteGenericInterface, RouteHandler, RouteHandlerMethod, RouteOptions, RouteShorthandMethod, RouteShorthandOptions, RouteShorthandOptionsWithHandler } from './types/route'
31
- import { FastifySchema, FastifySchemaValidationError, FastifySchemaCompiler, FastifySerializerCompiler, SchemaErrorDataVar, SchemaErrorFormatter } from './types/schema'
31
+ import { FastifySchema, FastifySchemaCompiler, FastifySchemaValidationError, FastifySerializerCompiler, SchemaErrorDataVar, SchemaErrorFormatter } from './types/schema'
32
32
  import { FastifyServerFactory, FastifyServerFactoryHandler } from './types/server-factory'
33
33
  import { FastifyTypeProvider, FastifyTypeProviderDefault, SafePromiseLike } from './types/type-provider'
34
34
  import { ContextConfigDefault, HTTPMethods, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerBase, RawServerDefault, RequestBodyDefault, RequestHeadersDefault, RequestParamsDefault, RequestQuerystringDefault } from './types/utils'
@@ -90,7 +90,7 @@ declare namespace fastify {
90
90
 
91
91
  type TrustProxyFunction = (address: string, hop: number) => boolean
92
92
 
93
- export type FastifyRouterOptions<RawServer extends RawServerBase> = Omit<FindMyWayConfigForServer<RawServer>, 'defaultRoute' | 'onBadUrl' | 'querystringParser'> & {
93
+ export type FastifyRouterOptions<RawServer extends RawServerBase> = Omit<FindMyWayConfigForServer<RawServer>, 'defaultRoute' | 'onBadUrl' | 'onMaxParamLength' | 'querystringParser'> & {
94
94
  defaultRoute?: (
95
95
  req: RawRequestDefaultExpression<RawServer>,
96
96
  res: RawReplyDefaultExpression<RawServer>
@@ -100,6 +100,11 @@ declare namespace fastify {
100
100
  req: RawRequestDefaultExpression<RawServer>,
101
101
  res: RawReplyDefaultExpression<RawServer>
102
102
  ) => void,
103
+ onMaxParamLength?: (
104
+ path: string,
105
+ req: RawRequestDefaultExpression<RawServer>,
106
+ res: RawReplyDefaultExpression<RawServer>
107
+ ) => void,
103
108
  querystringParser?: (str: string) => { [key: string]: unknown }
104
109
  }
105
110
 
package/fastify.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const VERSION = '5.8.4'
3
+ const VERSION = '5.9.0'
4
4
 
5
5
  const Avvio = require('avvio')
6
6
  const http = require('node:http')
@@ -63,6 +63,7 @@ const { defaultInitOptions } = getSecuredInitialConfig
63
63
  const {
64
64
  FST_ERR_ASYNC_CONSTRAINT,
65
65
  FST_ERR_BAD_URL,
66
+ FST_ERR_MAX_PARAM_LENGTH,
66
67
  FST_ERR_OPTIONS_NOT_OBJ,
67
68
  FST_ERR_QSP_NOT_FN,
68
69
  FST_ERR_SCHEMA_CONTROLLER_BUCKET_OPT_NOT_FN,
@@ -92,7 +93,7 @@ function fastify (serverOptions) {
92
93
  disableRequestLogging,
93
94
  hasLogger,
94
95
  initialConfig
95
- } = processOptions(serverOptions, defaultRoute, onBadUrl)
96
+ } = processOptions(serverOptions, defaultRoute, onBadUrl, onMaxParamLength)
96
97
 
97
98
  // Default router
98
99
  const router = buildRouting(options.routerOptions)
@@ -381,8 +382,7 @@ function fastify (serverOptions) {
381
382
  hookRunnerApplication('preClose', fastify[kAvvioBoot], fastify, function () {
382
383
  if (fastify[kState].listening) {
383
384
  /* istanbul ignore next: Cannot test this without Node.js core support */
384
- if (forceCloseConnections === 'idle') {
385
- // Not needed in Node 19
385
+ if (forceCloseConnections === 'idle' && options.serverFactory) {
386
386
  instance.server.closeIdleConnections()
387
387
  /* istanbul ignore next: Cannot test this without Node.js core support */
388
388
  } else if (serverHasCloseAllConnections && forceCloseConnections) {
@@ -423,8 +423,8 @@ function fastify (serverOptions) {
423
423
  })
424
424
  })
425
425
 
426
- // Create bad URL context
427
- const onBadUrlContext = new Context({
426
+ // Create route event (bad URL, max param length, etc) context
427
+ const routeEventContext = new Context({
428
428
  server: fastify,
429
429
  config: {}
430
430
  })
@@ -637,10 +637,10 @@ function fastify (serverOptions) {
637
637
 
638
638
  function onBadUrl (path, req, res) {
639
639
  if (options.frameworkErrors) {
640
- const id = getGenReqId(onBadUrlContext.server, req)
641
- const childLogger = createChildLogger(onBadUrlContext, options.logger, req, id)
640
+ const id = getGenReqId(routeEventContext.server, req)
641
+ const childLogger = createChildLogger(routeEventContext, options.logger, req, id)
642
642
 
643
- const request = new Request(id, null, req, null, childLogger, onBadUrlContext)
643
+ const request = new Request(id, null, req, null, childLogger, routeEventContext)
644
644
  const reply = new Reply(res, request, childLogger)
645
645
 
646
646
  const resolvedDisableRequestLogging = typeof disableRequestLogging === 'function' ? disableRequestLogging(req) : disableRequestLogging
@@ -663,15 +663,43 @@ function fastify (serverOptions) {
663
663
  res.end(body)
664
664
  }
665
665
 
666
+ function onMaxParamLength (path, req, res) {
667
+ if (options.frameworkErrors) {
668
+ const id = getGenReqId(routeEventContext.server, req)
669
+ const childLogger = createChildLogger(routeEventContext, options.logger, req, id)
670
+
671
+ const request = new Request(id, null, req, null, childLogger, routeEventContext)
672
+ const reply = new Reply(res, request, childLogger)
673
+
674
+ const resolvedDisableRequestLogging = typeof disableRequestLogging === 'function' ? disableRequestLogging(req) : disableRequestLogging
675
+ if (resolvedDisableRequestLogging === false) {
676
+ childLogger.info({ req: request }, 'incoming request')
677
+ }
678
+
679
+ return options.frameworkErrors(new FST_ERR_MAX_PARAM_LENGTH(path), request, reply)
680
+ }
681
+ const body = JSON.stringify({
682
+ error: 'Bad Request',
683
+ code: 'FST_ERR_MAX_PARAM_LENGTH',
684
+ message: `'${path}' is exceeding the max param length`,
685
+ statusCode: 414
686
+ })
687
+ res.writeHead(414, {
688
+ 'Content-Type': 'application/json',
689
+ 'Content-Length': Buffer.byteLength(body)
690
+ })
691
+ res.end(body)
692
+ }
693
+
666
694
  function buildAsyncConstraintCallback (isAsync, req, res) {
667
695
  if (isAsync === false) return undefined
668
696
  return function onAsyncConstraintError (err) {
669
697
  if (err) {
670
698
  if (options.frameworkErrors) {
671
- const id = getGenReqId(onBadUrlContext.server, req)
672
- const childLogger = createChildLogger(onBadUrlContext, options.logger, req, id)
699
+ const id = getGenReqId(routeEventContext.server, req)
700
+ const childLogger = createChildLogger(routeEventContext, options.logger, req, id)
673
701
 
674
- const request = new Request(id, null, req, null, childLogger, onBadUrlContext)
702
+ const request = new Request(id, null, req, null, childLogger, routeEventContext)
675
703
  const reply = new Reply(res, request, childLogger)
676
704
 
677
705
  const resolvedDisableRequestLogging = typeof disableRequestLogging === 'function' ? disableRequestLogging(req) : disableRequestLogging
@@ -748,7 +776,7 @@ function fastify (serverOptions) {
748
776
  if (!options.allowErrorHandlerOverride && this[kErrorHandlerAlreadySet]) {
749
777
  throw new FST_ERR_ERROR_HANDLER_ALREADY_SET()
750
778
  } else if (this[kErrorHandlerAlreadySet]) {
751
- FSTWRN004("To disable this behavior, set 'allowErrorHandlerOverride' to false or ignore this message. For more information, visit: https://fastify.dev/docs/latest/Reference/Server/#allowerrorhandleroverride")
779
+ FSTWRN004()
752
780
  }
753
781
 
754
782
  this[kErrorHandlerAlreadySet] = true
@@ -821,7 +849,7 @@ function fastify (serverOptions) {
821
849
  }
822
850
  }
823
851
 
824
- function processOptions (options, defaultRoute, onBadUrl) {
852
+ function processOptions (options, defaultRoute, onBadUrl, onMaxParamLength) {
825
853
  // Options validations
826
854
  if (options && typeof options !== 'object') {
827
855
  throw new FST_ERR_OPTIONS_NOT_OBJ()
@@ -890,6 +918,7 @@ function processOptions (options, defaultRoute, onBadUrl) {
890
918
  options.routerOptions = buildRouterOptions(options, {
891
919
  defaultRoute,
892
920
  onBadUrl,
921
+ onMaxParamLength,
893
922
  ignoreTrailingSlash: defaultInitOptions.ignoreTrailingSlash,
894
923
  ignoreDuplicateSlashes: defaultInitOptions.ignoreDuplicateSlashes,
895
924
  maxParamLength: defaultInitOptions.maxParamLength,
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const { AsyncResource } = require('node:async_hooks')
4
- const { FifoMap: Fifo } = require('toad-cache')
4
+ const { FifoMap: Fifo, LruMap: Lru } = require('toad-cache')
5
5
  const { parse: secureJsonParse } = require('secure-json-parse')
6
6
  const ContentType = require('./content-type')
7
7
  const {
@@ -39,6 +39,15 @@ function ContentTypeParser (bodyLimit, onProtoPoisoning, onConstructorPoisoning)
39
39
  this.parserList = ['application/json', 'text/plain']
40
40
  this.parserRegExpList = []
41
41
  this.cache = new Fifo(100)
42
+ this.ctCache = new Lru(100)
43
+ }
44
+
45
+ ContentTypeParser.prototype.getContentType = function (raw) {
46
+ let ct = this.ctCache.get(raw)
47
+ if (ct !== undefined) return ct
48
+ ct = new ContentType(raw)
49
+ this.ctCache.set(raw, ct)
50
+ return ct
42
51
  }
43
52
 
44
53
  ContentTypeParser.prototype.add = function (contentType, opts, parserFn) {
@@ -118,6 +127,8 @@ ContentTypeParser.prototype.existingParser = function (contentType) {
118
127
 
119
128
  ContentTypeParser.prototype.getParser = function (contentType) {
120
129
  if (typeof contentType === 'string') {
130
+ const cached = this.cache.get(contentType)
131
+ if (cached !== undefined) return cached
121
132
  contentType = new ContentType(contentType)
122
133
  }
123
134
  const ct = contentType.toString()
@@ -158,6 +169,7 @@ ContentTypeParser.prototype.removeAll = function () {
158
169
  this.parserRegExpList = []
159
170
  this.parserList = []
160
171
  this.cache = new Fifo(100)
172
+ this.ctCache = new Lru(100)
161
173
  }
162
174
 
163
175
  ContentTypeParser.prototype.remove = function (contentType) {
package/lib/decorate.js CHANGED
@@ -47,7 +47,7 @@ function getInstanceDecorator (name) {
47
47
 
48
48
  function decorateConstructor (konstructor, name, fn, dependencies) {
49
49
  const instance = konstructor.prototype
50
- if (Object.hasOwn(instance, name) || hasKey(konstructor, name)) {
50
+ if (Object.hasOwn(instance, name) || hasKey(konstructor, name) || hasInstanceProperty(konstructor, name)) {
51
51
  throw new FST_ERR_DEC_ALREADY_PRESENT(name)
52
52
  }
53
53
 
@@ -87,19 +87,27 @@ function checkExistence (instance, name) {
87
87
  }
88
88
 
89
89
  function hasKey (fn, name) {
90
- if (fn.props) {
91
- return fn.props.find(({ key }) => key === name)
90
+ return fn.props ? fn.props.some(p => p.key === name) : false
91
+ }
92
+
93
+ function hasInstanceProperty (konstructor, name) {
94
+ let k = konstructor
95
+ while (k) {
96
+ if (k.instanceProperties && k.instanceProperties.has(name)) return true
97
+ k = k.parent
92
98
  }
93
99
  return false
94
100
  }
95
101
 
96
102
  function checkRequestExistence (name) {
97
103
  if (name && hasKey(this[kRequest], name)) return true
104
+ if (name && hasInstanceProperty(this[kRequest], name)) return true
98
105
  return checkExistence(this[kRequest].prototype, name)
99
106
  }
100
107
 
101
108
  function checkReplyExistence (name) {
102
109
  if (name && hasKey(this[kReply], name)) return true
110
+ if (name && hasInstanceProperty(this[kReply], name)) return true
103
111
  return checkExistence(this[kReply].prototype, name)
104
112
  }
105
113
 
@@ -8,7 +8,8 @@ const {
8
8
  kReplyNextErrorHandler,
9
9
  kReplyIsRunningOnErrorHook,
10
10
  kRouteContext,
11
- kDisableRequestLogging
11
+ kDisableRequestLogging,
12
+ kDiagnosticsStore
12
13
  } = require('./symbols.js')
13
14
 
14
15
  const {
@@ -19,7 +20,6 @@ const {
19
20
  const { getSchemaSerializer } = require('./schemas')
20
21
 
21
22
  const serializeError = require('./error-serializer')
22
-
23
23
  const rootErrorHandler = {
24
24
  func: defaultErrorHandler,
25
25
  toJSON () {
@@ -69,7 +69,8 @@ function handleError (reply, error, cb) {
69
69
  const result = func(error, reply.request, reply)
70
70
  if (result !== undefined) {
71
71
  if (result !== null && typeof result.then === 'function') {
72
- wrapThenable(result, reply)
72
+ const store = reply[kDiagnosticsStore] || null
73
+ wrapThenable(result, reply, store)
73
74
  } else {
74
75
  reply.send(result)
75
76
  }
@@ -40,93 +40,93 @@ const asInteger = serializer.asInteger.bind(serializer)
40
40
  const JSON_STR_NULL = 'null'
41
41
 
42
42
 
43
-
44
- // #
45
- function anonymous0 (input) {
46
- const obj = (input && typeof input.toJSON === 'function')
43
+ // #
44
+ function anonymous0 (input) {
45
+ const obj = (input && typeof input.toJSON === 'function')
47
46
  ? input.toJSON()
48
47
  : input
49
48
 
50
- if (obj === null) return JSON_STR_EMPTY_OBJECT
49
+ if (obj === null) return JSON_STR_EMPTY_OBJECT
50
+ let json = ''
51
51
 
52
- let value
53
- let json = JSON_STR_BEGIN_OBJECT
54
- let addComma = false
52
+ json += JSON_STR_BEGIN_OBJECT
53
+ let addComma_0 = false
55
54
 
56
- value = obj["statusCode"]
57
- if (value !== undefined) {
58
- !addComma && (addComma = true) || (json += JSON_STR_COMMA)
59
- json += "\"statusCode\":"
60
- json += asNumber(value)
61
- }
55
+ const value_statusCode_1 = obj["statusCode"]
56
+ if (value_statusCode_1 !== undefined) {
57
+ !addComma_0 && (addComma_0 = true) || (json += JSON_STR_COMMA)
58
+ json += "\"statusCode\":"
59
+ json += asNumber(value_statusCode_1)
60
+ }
62
61
 
63
- value = obj["code"]
64
- if (value !== undefined) {
65
- !addComma && (addComma = true) || (json += JSON_STR_COMMA)
66
- json += "\"code\":"
67
-
68
- if (typeof value !== 'string') {
69
- if (value === null) {
62
+ const value_code_2 = obj["code"]
63
+ if (value_code_2 !== undefined) {
64
+ !addComma_0 && (addComma_0 = true) || (json += JSON_STR_COMMA)
65
+ json += "\"code\":"
66
+
67
+ if (typeof value_code_2 !== 'string') {
68
+ if (value_code_2 === null) {
70
69
  json += JSON_STR_EMPTY_STRING
71
- } else if (value instanceof Date) {
72
- json += JSON_STR_QUOTE + value.toISOString() + JSON_STR_QUOTE
73
- } else if (value instanceof RegExp) {
74
- json += asString(value.source)
70
+ } else if (value_code_2 instanceof Date) {
71
+ json += JSON_STR_QUOTE + value_code_2.toISOString() + JSON_STR_QUOTE
72
+ } else if (value_code_2 instanceof RegExp) {
73
+ json += asString(value_code_2.source)
75
74
  } else {
76
- json += asString(value.toString())
75
+ json += asString(value_code_2.toString())
77
76
  }
78
77
  } else {
79
- json += asString(value)
78
+ json += asString(value_code_2)
80
79
  }
81
80
 
82
- }
81
+ }
83
82
 
84
- value = obj["error"]
85
- if (value !== undefined) {
86
- !addComma && (addComma = true) || (json += JSON_STR_COMMA)
87
- json += "\"error\":"
88
-
89
- if (typeof value !== 'string') {
90
- if (value === null) {
83
+ const value_error_3 = obj["error"]
84
+ if (value_error_3 !== undefined) {
85
+ !addComma_0 && (addComma_0 = true) || (json += JSON_STR_COMMA)
86
+ json += "\"error\":"
87
+
88
+ if (typeof value_error_3 !== 'string') {
89
+ if (value_error_3 === null) {
91
90
  json += JSON_STR_EMPTY_STRING
92
- } else if (value instanceof Date) {
93
- json += JSON_STR_QUOTE + value.toISOString() + JSON_STR_QUOTE
94
- } else if (value instanceof RegExp) {
95
- json += asString(value.source)
91
+ } else if (value_error_3 instanceof Date) {
92
+ json += JSON_STR_QUOTE + value_error_3.toISOString() + JSON_STR_QUOTE
93
+ } else if (value_error_3 instanceof RegExp) {
94
+ json += asString(value_error_3.source)
96
95
  } else {
97
- json += asString(value.toString())
96
+ json += asString(value_error_3.toString())
98
97
  }
99
98
  } else {
100
- json += asString(value)
99
+ json += asString(value_error_3)
101
100
  }
102
101
 
103
- }
102
+ }
104
103
 
105
- value = obj["message"]
106
- if (value !== undefined) {
107
- !addComma && (addComma = true) || (json += JSON_STR_COMMA)
108
- json += "\"message\":"
109
-
110
- if (typeof value !== 'string') {
111
- if (value === null) {
104
+ const value_message_4 = obj["message"]
105
+ if (value_message_4 !== undefined) {
106
+ !addComma_0 && (addComma_0 = true) || (json += JSON_STR_COMMA)
107
+ json += "\"message\":"
108
+
109
+ if (typeof value_message_4 !== 'string') {
110
+ if (value_message_4 === null) {
112
111
  json += JSON_STR_EMPTY_STRING
113
- } else if (value instanceof Date) {
114
- json += JSON_STR_QUOTE + value.toISOString() + JSON_STR_QUOTE
115
- } else if (value instanceof RegExp) {
116
- json += asString(value.source)
112
+ } else if (value_message_4 instanceof Date) {
113
+ json += JSON_STR_QUOTE + value_message_4.toISOString() + JSON_STR_QUOTE
114
+ } else if (value_message_4 instanceof RegExp) {
115
+ json += asString(value_message_4.source)
117
116
  } else {
118
- json += asString(value.toString())
117
+ json += asString(value_message_4.toString())
119
118
  }
120
119
  } else {
121
- json += asString(value)
120
+ json += asString(value_message_4)
122
121
  }
123
122
 
124
- }
123
+ }
125
124
 
126
- return json + JSON_STR_END_OBJECT
127
-
128
- }
125
+ json += JSON_STR_END_OBJECT
129
126
 
127
+ return json
128
+ }
129
+
130
130
  const main = anonymous0
131
131
  return main
132
132
 
package/lib/errors.js CHANGED
@@ -364,6 +364,12 @@ const codes = {
364
364
  400,
365
365
  URIError
366
366
  ),
367
+ FST_ERR_MAX_PARAM_LENGTH: createError(
368
+ 'FST_ERR_MAX_PARAM_LENGTH',
369
+ "'%s' is exceeding the max param length",
370
+ 414,
371
+ URIError
372
+ ),
367
373
  FST_ERR_ASYNC_CONSTRAINT: createError(
368
374
  'FST_ERR_ASYNC_CONSTRAINT',
369
375
  'Unexpected error from async constraint',
@@ -408,6 +414,12 @@ const codes = {
408
414
  '%s method is not supported.',
409
415
  500
410
416
  ),
417
+ FST_ERR_ROUTE_LOG_LEVEL_INVALID: createError(
418
+ 'FST_ERR_ROUTE_LOG_LEVEL_INVALID',
419
+ "Log level for '%s:%s' route must be a valid logger level. Received: '%s'",
420
+ 500,
421
+ TypeError
422
+ ),
411
423
  FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED: createError(
412
424
  'FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED',
413
425
  'Body validation schema for %s:%s route is not supported!',
@@ -470,7 +482,10 @@ const codes = {
470
482
  500,
471
483
  TypeError
472
484
  ),
473
-
485
+ FST_ERR_PLUGIN_DEPENDENCY_NOT_REGISTERED: createError(
486
+ 'FST_ERR_PLUGIN_DEPENDENCY_NOT_REGISTERED',
487
+ "The dependency '%s' of plugin '%s' is not registered"
488
+ ),
474
489
  /**
475
490
  * Avvio Errors
476
491
  */
@@ -32,8 +32,12 @@ function fourOhFour (options) {
32
32
  const { logger, disableRequestLogging } = options
33
33
 
34
34
  // 404 router, used for handling encapsulated 404 handlers
35
- const router = FindMyWay({ onBadUrl: createOnBadUrl(), defaultRoute: fourOhFourFallBack })
36
- let _onBadUrlHandler = null
35
+ const router = FindMyWay({
36
+ onBadUrl: createRouteEventHandler(),
37
+ onMaxParamLength: createRouteEventHandler(),
38
+ defaultRoute: fourOhFourFallBack
39
+ })
40
+ let _routeEventHandler = null
37
41
 
38
42
  return { router, setNotFoundHandler, setContext, arrange404 }
39
43
 
@@ -43,6 +47,7 @@ function fourOhFour (options) {
43
47
  instance[kCanSetNotFoundHandler] = true
44
48
  // we need to bind instance for the context
45
49
  router.onBadUrl = router.onBadUrl.bind(instance)
50
+ router.onMaxParamLength = router.onMaxParamLength.bind(instance)
46
51
  router.defaultRoute = router.defaultRoute.bind(instance)
47
52
  }
48
53
 
@@ -60,15 +65,15 @@ function fourOhFour (options) {
60
65
  })
61
66
  }
62
67
 
63
- function createOnBadUrl () {
64
- return function onBadUrl (path, req, res) {
68
+ function createRouteEventHandler () {
69
+ return function onRouteEvent (path, req, res) {
65
70
  const fourOhFourContext = this[kFourOhFourLevelInstance][kFourOhFourContext]
66
71
  const id = getGenReqId(fourOhFourContext.server, req)
67
72
  const childLogger = createChildLogger(fourOhFourContext, logger, req, id)
68
73
  const request = new Request(id, null, req, null, childLogger, fourOhFourContext)
69
74
  const reply = new Reply(res, request, childLogger)
70
75
 
71
- _onBadUrlHandler(request, reply)
76
+ _routeEventHandler(request, reply)
72
77
  }
73
78
  }
74
79
 
@@ -121,12 +126,12 @@ function fourOhFour (options) {
121
126
  if (handler) {
122
127
  this[kFourOhFourLevelInstance][kCanSetNotFoundHandler] = false
123
128
  handler = handler.bind(this)
124
- // update onBadUrl handler
125
- _onBadUrlHandler = handler
129
+ // update route event handler
130
+ _routeEventHandler = handler
126
131
  } else {
127
132
  handler = basic404
128
- // update onBadUrl handler
129
- _onBadUrlHandler = basic404
133
+ // update route event handler
134
+ _routeEventHandler = basic404
130
135
  }
131
136
 
132
137
  this.after((notHandledErr, done) => {
@@ -1,7 +1,6 @@
1
1
  'use strict'
2
2
 
3
3
  const diagnostics = require('node:diagnostics_channel')
4
- const ContentType = require('./content-type')
5
4
  const wrapThenable = require('./wrap-thenable')
6
5
  const { validate: validateSchema } = require('./validation')
7
6
  const { preValidationHookRunner, preHandlerHookRunner } = require('./hooks')
@@ -11,7 +10,9 @@ const {
11
10
  kReplyIsError,
12
11
  kRouteContext,
13
12
  kFourOhFourContext,
14
- kSupportedHTTPMethods
13
+ kSupportedHTTPMethods,
14
+ kRequestContentType,
15
+ kDiagnosticsStore
15
16
  } = require('./symbols')
16
17
 
17
18
  const channels = diagnostics.tracingChannel('fastify.request.handler')
@@ -51,13 +52,17 @@ function handleRequest (err, request, reply) {
51
52
  return
52
53
  }
53
54
 
54
- const contentType = new ContentType(ctHeader)
55
- if (contentType.isValid === false) {
55
+ // Conditional assignment to avoid creating a new ContentType instance
56
+ // It can be assigned when accessing the .mediaType in hooks
57
+ if (!request[kRequestContentType]) {
58
+ request[kRequestContentType] = request[kRouteContext].contentTypeParser.getContentType(ctHeader)
59
+ }
60
+ if (request[kRequestContentType].isValid === false) {
56
61
  reply[kReplyIsError] = true
57
62
  reply.status(415).send(new FST_ERR_CTP_INVALID_MEDIA_TYPE())
58
63
  return
59
64
  }
60
- request[kRouteContext].contentTypeParser.run(contentType.toString(), handler, request, reply)
65
+ request[kRouteContext].contentTypeParser.run(request[kRequestContentType].toString(), handler, request, reply)
61
66
  return
62
67
  }
63
68
 
@@ -142,6 +147,7 @@ function preHandlerCallback (err, request, reply) {
142
147
  method: context.config.method
143
148
  }
144
149
  }
150
+ reply[kDiagnosticsStore] = store
145
151
  channels.start.runStores(store, preHandlerCallbackInner, undefined, err, request, reply, store)
146
152
  }
147
153
  }
@@ -82,7 +82,8 @@ function buildRoutePrefix (instancePrefix, pluginPrefix) {
82
82
  if (instancePrefix.endsWith('/') && pluginPrefix[0] === '/') {
83
83
  // Remove the extra '/' to avoid: '/first//second'
84
84
  pluginPrefix = pluginPrefix.slice(1)
85
- } else if (pluginPrefix[0] !== '/') {
85
+ } else if (pluginPrefix[0] !== '/' && !instancePrefix.endsWith('/')) {
86
+ // Add the missing '/' to avoid: '/firstsecond'
86
87
  pluginPrefix = '/' + pluginPrefix
87
88
  }
88
89
 
@@ -10,7 +10,8 @@ const { exist, existReply, existRequest } = require('./decorate.js')
10
10
  const {
11
11
  FST_ERR_PLUGIN_VERSION_MISMATCH,
12
12
  FST_ERR_PLUGIN_NOT_PRESENT_IN_INSTANCE,
13
- FST_ERR_PLUGIN_INVALID_ASYNC_HANDLER
13
+ FST_ERR_PLUGIN_INVALID_ASYNC_HANDLER,
14
+ FST_ERR_PLUGIN_DEPENDENCY_NOT_REGISTERED
14
15
  } = require('./errors.js')
15
16
 
16
17
  const rcRegex = /-(?:rc|pre|alpha).+$/u
@@ -70,10 +71,9 @@ function checkDependencies (fn) {
70
71
  assert(Array.isArray(dependencies), 'The dependencies should be an array of strings')
71
72
 
72
73
  dependencies.forEach(dependency => {
73
- assert(
74
- this[kRegisteredPlugins].indexOf(dependency) > -1,
75
- `The dependency '${dependency}' of plugin '${meta.name}' is not registered`
76
- )
74
+ if (!this[kRegisteredPlugins].includes(dependency)) {
75
+ throw new FST_ERR_PLUGIN_DEPENDENCY_NOT_REGISTERED(dependency, meta.name)
76
+ }
77
77
  })
78
78
  }
79
79