fastify 5.0.0-alpha.2 → 5.0.0-alpha.4

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 (290) hide show
  1. package/README.md +2 -2
  2. package/SPONSORS.md +2 -0
  3. package/build/build-validation.js +3 -15
  4. package/docs/Guides/Ecosystem.md +4 -0
  5. package/docs/Guides/Getting-Started.md +0 -2
  6. package/docs/Guides/Migration-Guide-V4.md +48 -0
  7. package/docs/Guides/Recommendations.md +8 -6
  8. package/docs/Reference/Errors.md +0 -2
  9. package/docs/Reference/Hooks.md +5 -9
  10. package/docs/Reference/Logging.md +1 -1
  11. package/docs/Reference/Reply.md +9 -11
  12. package/docs/Reference/Request.md +0 -11
  13. package/docs/Reference/Routes.md +4 -23
  14. package/docs/Reference/Server.md +30 -40
  15. package/docs/Reference/Type-Providers.md +2 -2
  16. package/docs/Reference/TypeScript.md +16 -18
  17. package/docs/Reference/Validation-and-Serialization.md +62 -27
  18. package/docs/Reference/Warnings.md +0 -26
  19. package/eslint.config.js +9 -25
  20. package/fastify.d.ts +10 -23
  21. package/fastify.js +60 -61
  22. package/lib/configValidator.js +130 -182
  23. package/lib/context.js +1 -22
  24. package/lib/decorate.js +2 -2
  25. package/lib/errors.js +0 -6
  26. package/lib/handleRequest.js +5 -5
  27. package/lib/reply.js +34 -74
  28. package/lib/request.js +0 -45
  29. package/lib/route.js +12 -27
  30. package/lib/schemas.js +27 -22
  31. package/lib/server.js +6 -11
  32. package/lib/symbols.js +1 -1
  33. package/lib/validation.js +27 -6
  34. package/lib/warnings.js +1 -92
  35. package/lib/wrapThenable.js +1 -1
  36. package/package.json +14 -15
  37. package/test/decorator.test.js +1 -1
  38. package/test/diagnostics-channel/404.test.js +1 -1
  39. package/test/diagnostics-channel/async-delay-request.test.js +1 -1
  40. package/test/diagnostics-channel/async-request.test.js +1 -1
  41. package/test/diagnostics-channel/error-before-handler.test.js +1 -1
  42. package/test/diagnostics-channel/error-request.test.js +1 -1
  43. package/test/diagnostics-channel/error-status.test.js +1 -1
  44. package/test/diagnostics-channel/init.test.js +2 -2
  45. package/test/diagnostics-channel/sync-delay-request.test.js +1 -1
  46. package/test/diagnostics-channel/sync-request-reply.test.js +1 -1
  47. package/test/diagnostics-channel/sync-request.test.js +1 -1
  48. package/test/{copy.test.js → http-methods/copy.test.js} +2 -1
  49. package/test/http-methods/custom-http-methods.test.js +111 -0
  50. package/test/{get.test.js → http-methods/get.test.js} +1 -1
  51. package/test/{head.test.js → http-methods/head.test.js} +7 -87
  52. package/test/{lock.test.js → http-methods/lock.test.js} +2 -1
  53. package/test/{mkcalendar.test.js → http-methods/mkcalendar.test.js} +2 -1
  54. package/test/{mkcol.test.js → http-methods/mkcol.test.js} +2 -1
  55. package/test/{move.test.js → http-methods/move.test.js} +2 -1
  56. package/test/{propfind.test.js → http-methods/propfind.test.js} +2 -1
  57. package/test/{proppatch.test.js → http-methods/proppatch.test.js} +2 -1
  58. package/test/{report.test.js → http-methods/report.test.js} +2 -1
  59. package/test/{search.test.js → http-methods/search.test.js} +2 -1
  60. package/test/{trace.test.js → http-methods/trace.test.js} +2 -1
  61. package/test/{unlock.test.js → http-methods/unlock.test.js} +2 -1
  62. package/test/internals/all.test.js +3 -3
  63. package/test/internals/decorator.test.js +2 -2
  64. package/test/internals/errors.test.js +7 -17
  65. package/test/internals/initialConfig.test.js +0 -31
  66. package/test/internals/reply-serialize.test.js +25 -10
  67. package/test/internals/reply.test.js +31 -153
  68. package/test/internals/request-validate.test.js +21 -12
  69. package/test/internals/request.test.js +1 -18
  70. package/test/internals/validation.test.js +49 -56
  71. package/test/listen.1.test.js +66 -14
  72. package/test/listen.5.test.js +11 -0
  73. package/test/reply-trailers.test.js +1 -32
  74. package/test/route-shorthand.test.js +3 -1
  75. package/test/route.3.test.js +4 -1
  76. package/test/route.7.test.js +2 -12
  77. package/test/route.8.test.js +34 -5
  78. package/test/router-options.test.js +6 -3
  79. package/test/schema-examples.test.js +15 -6
  80. package/test/schema-feature.test.js +178 -35
  81. package/test/schema-serialization.test.js +125 -21
  82. package/test/schema-validation.test.js +154 -3
  83. package/test/skip-reply-send.test.js +6 -6
  84. package/test/stream-serializers.test.js +37 -0
  85. package/test/throw.test.js +2 -14
  86. package/test/types/errors.test-d.ts +1 -2
  87. package/test/types/fastify.test-d.ts +23 -34
  88. package/test/types/hooks.test-d.ts +56 -56
  89. package/test/types/instance.test-d.ts +3 -3
  90. package/test/types/reply.test-d.ts +7 -8
  91. package/test/types/request.test-d.ts +2 -12
  92. package/test/types/route.test-d.ts +158 -158
  93. package/test/types/schema.test-d.ts +22 -5
  94. package/test/versioned-routes.test.js +0 -90
  95. package/test/web-api.test.js +75 -0
  96. package/types/errors.d.ts +78 -79
  97. package/types/hooks.d.ts +18 -18
  98. package/types/instance.d.ts +1 -1
  99. package/types/logger.d.ts +7 -7
  100. package/types/reply.d.ts +18 -22
  101. package/types/request.d.ts +8 -14
  102. package/types/route.d.ts +5 -6
  103. package/types/type-provider.d.ts +1 -1
  104. package/.tap/processinfo/09002e93-10ad-430c-bc86-c0576928b0ed.json +0 -241
  105. package/.tap/processinfo/ee66c5ab-635d-48b5-8be6-3dc3ceea5bfc.json +0 -268
  106. package/.tap/test-results/test/404s.test.js.tap +0 -623
  107. package/.tap/test-results/test/500s.test.js.tap +0 -64
  108. package/.tap/test-results/test/allowUnsafeRegex.test.js.tap +0 -36
  109. package/.tap/test-results/test/als.test.js.tap +0 -15
  110. package/.tap/test-results/test/async-await.test.js.tap +0 -184
  111. package/.tap/test-results/test/async-dispose.test.js.tap +0 -8
  112. package/.tap/test-results/test/async_hooks.test.js.tap +0 -10
  113. package/.tap/test-results/test/bodyLimit.test.js.tap +0 -48
  114. package/.tap/test-results/test/buffer.test.js.tap +0 -20
  115. package/.tap/test-results/test/build/error-serializer.test.js.tap +0 -12
  116. package/.tap/test-results/test/build/version.test.js.tap +0 -7
  117. package/.tap/test-results/test/case-insensitive.test.js.tap +0 -36
  118. package/.tap/test-results/test/chainable.test.js.tap +0 -17
  119. package/.tap/test-results/test/check.test.js.tap +0 -10
  120. package/.tap/test-results/test/childLoggerFactory.test.js.tap +0 -23
  121. package/.tap/test-results/test/client-timeout.test.js.tap +0 -7
  122. package/.tap/test-results/test/close-pipelining.test.js.tap +0 -15
  123. package/.tap/test-results/test/close.test.js.tap +0 -172
  124. package/.tap/test-results/test/connectionTimeout.test.js.tap +0 -12
  125. package/.tap/test-results/test/constrained-routes.test.js.tap +0 -173
  126. package/.tap/test-results/test/content-length.test.js.tap +0 -46
  127. package/.tap/test-results/test/content-parser.test.js.tap +0 -266
  128. package/.tap/test-results/test/content-type.test.js.tap +0 -14
  129. package/.tap/test-results/test/context-config.test.js.tap +0 -41
  130. package/.tap/test-results/test/copy.test.js.tap +0 -14
  131. package/.tap/test-results/test/custom-http-server.test.js.tap +0 -30
  132. package/.tap/test-results/test/custom-parser-async.test.js.tap +0 -21
  133. package/.tap/test-results/test/custom-parser.0.test.js.tap +0 -199
  134. package/.tap/test-results/test/custom-parser.1.test.js.tap +0 -90
  135. package/.tap/test-results/test/custom-parser.2.test.js.tap +0 -22
  136. package/.tap/test-results/test/custom-parser.3.test.js.tap +0 -53
  137. package/.tap/test-results/test/custom-parser.4.test.js.tap +0 -45
  138. package/.tap/test-results/test/custom-parser.5.test.js.tap +0 -41
  139. package/.tap/test-results/test/custom-querystring-parser.test.js.tap +0 -46
  140. package/.tap/test-results/test/decorator.test.js.tap +0 -465
  141. package/.tap/test-results/test/delete.test.js.tap +0 -110
  142. package/.tap/test-results/test/diagnostics-channel/404.test.js.tap +0 -15
  143. package/.tap/test-results/test/diagnostics-channel/async-delay-request.test.js.tap +0 -25
  144. package/.tap/test-results/test/diagnostics-channel/async-request.test.js.tap +0 -24
  145. package/.tap/test-results/test/diagnostics-channel/error-before-handler.test.js.tap +0 -9
  146. package/.tap/test-results/test/diagnostics-channel/error-request.test.js.tap +0 -20
  147. package/.tap/test-results/test/diagnostics-channel/error-status.test.js.tap +0 -10
  148. package/.tap/test-results/test/diagnostics-channel/init.test.js.tap +0 -14
  149. package/.tap/test-results/test/diagnostics-channel/sync-delay-request.test.js.tap +0 -16
  150. package/.tap/test-results/test/diagnostics-channel/sync-request-reply.test.js.tap +0 -16
  151. package/.tap/test-results/test/diagnostics-channel/sync-request.test.js.tap +0 -19
  152. package/.tap/test-results/test/encapsulated-child-logger-factory.test.js.tap +0 -18
  153. package/.tap/test-results/test/encapsulated-error-handler.test.js.tap +0 -243
  154. package/.tap/test-results/test/esm/errorCodes.test.mjs.tap +0 -9
  155. package/.tap/test-results/test/esm/esm.test.mjs.tap +0 -8
  156. package/.tap/test-results/test/esm/index.test.js.tap +0 -8
  157. package/.tap/test-results/test/fastify-instance.test.js.tap +0 -114
  158. package/.tap/test-results/test/findRoute.test.js.tap +0 -37
  159. package/.tap/test-results/test/fluent-schema.test.js.tap +0 -36
  160. package/.tap/test-results/test/genReqId.test.js.tap +0 -106
  161. package/.tap/test-results/test/get.test.js.tap +0 -151
  162. package/.tap/test-results/test/handler-context.test.js.tap +0 -19
  163. package/.tap/test-results/test/has-route.test.js.tap +0 -30
  164. package/.tap/test-results/test/head.test.js.tap +0 -130
  165. package/.tap/test-results/test/header-overflow.test.js.tap +0 -16
  166. package/.tap/test-results/test/hooks-async.test.js.tap +0 -286
  167. package/.tap/test-results/test/hooks.on-listen.test.js.tap +0 -311
  168. package/.tap/test-results/test/hooks.on-ready.test.js.tap +0 -151
  169. package/.tap/test-results/test/hooks.test.js.tap +0 -966
  170. package/.tap/test-results/test/http2/closing.test.js.tap +0 -35
  171. package/.tap/test-results/test/http2/constraint.test.js.tap +0 -32
  172. package/.tap/test-results/test/http2/head.test.js.tap +0 -9
  173. package/.tap/test-results/test/http2/missing-http2-module.test.js.tap +0 -8
  174. package/.tap/test-results/test/http2/plain.test.js.tap +0 -22
  175. package/.tap/test-results/test/http2/secure-with-fallback.test.js.tap +0 -40
  176. package/.tap/test-results/test/http2/secure.test.js.tap +0 -27
  177. package/.tap/test-results/test/http2/unknown-http-method.test.js.tap +0 -9
  178. package/.tap/test-results/test/https/custom-https-server.test.js.tap +0 -10
  179. package/.tap/test-results/test/https/https.test.js.tap +0 -45
  180. package/.tap/test-results/test/imports.test.js.tap +0 -14
  181. package/.tap/test-results/test/inject.test.js.tap +0 -165
  182. package/.tap/test-results/test/internals/all.test.js.tap +0 -42
  183. package/.tap/test-results/test/internals/contentTypeParser.test.js.tap +0 -14
  184. package/.tap/test-results/test/internals/context.test.js.tap +0 -14
  185. package/.tap/test-results/test/internals/decorator.test.js.tap +0 -51
  186. package/.tap/test-results/test/internals/errors.test.js.tap +0 -1212
  187. package/.tap/test-results/test/internals/handleRequest.test.js.tap +0 -69
  188. package/.tap/test-results/test/internals/hookRunner.test.js.tap +0 -143
  189. package/.tap/test-results/test/internals/hooks.test.js.tap +0 -45
  190. package/.tap/test-results/test/internals/initialConfig.test.js.tap +0 -125
  191. package/.tap/test-results/test/internals/logger.test.js.tap +0 -71
  192. package/.tap/test-results/test/internals/plugin.test.js.tap +0 -48
  193. package/.tap/test-results/test/internals/reply-serialize.test.js.tap +0 -166
  194. package/.tap/test-results/test/internals/reply.test.js.tap +0 -688
  195. package/.tap/test-results/test/internals/reqIdGenFactory.test.js.tap +0 -74
  196. package/.tap/test-results/test/internals/request-validate.test.js.tap +0 -384
  197. package/.tap/test-results/test/internals/request.test.js.tap +0 -163
  198. package/.tap/test-results/test/internals/server.test.js.tap +0 -30
  199. package/.tap/test-results/test/internals/validation.test.js.tap +0 -121
  200. package/.tap/test-results/test/keepAliveTimeout.test.js.tap +0 -12
  201. package/.tap/test-results/test/listen.1.test.js.tap +0 -31
  202. package/.tap/test-results/test/listen.2.test.js.tap +0 -46
  203. package/.tap/test-results/test/listen.3.test.js.tap +0 -25
  204. package/.tap/test-results/test/listen.4.test.js.tap +0 -51
  205. package/.tap/test-results/test/lock.test.js.tap +0 -29
  206. package/.tap/test-results/test/logger/instantiation.test.js.tap +0 -92
  207. package/.tap/test-results/test/logger/logging.test.js.tap +0 -117
  208. package/.tap/test-results/test/logger/options.test.js.tap +0 -165
  209. package/.tap/test-results/test/logger/request.test.js.tap +0 -82
  210. package/.tap/test-results/test/logger/response.test.js.tap +0 -38
  211. package/.tap/test-results/test/maxRequestsPerSocket.test.js.tap +0 -44
  212. package/.tap/test-results/test/method-missing.test.js.tap +0 -8
  213. package/.tap/test-results/test/middleware.test.js.tap +0 -17
  214. package/.tap/test-results/test/mkcalendar.test.js.tap +0 -43
  215. package/.tap/test-results/test/mkcol.test.js.tap +0 -14
  216. package/.tap/test-results/test/move.test.js.tap +0 -15
  217. package/.tap/test-results/test/noop-set.test.js.tap +0 -8
  218. package/.tap/test-results/test/nullable-validation.test.js.tap +0 -36
  219. package/.tap/test-results/test/options.error-handler.test.js.tap +0 -186
  220. package/.tap/test-results/test/options.test.js.tap +0 -174
  221. package/.tap/test-results/test/output-validation.test.js.tap +0 -66
  222. package/.tap/test-results/test/patch.error-handler.test.js.tap +0 -206
  223. package/.tap/test-results/test/patch.test.js.tap +0 -182
  224. package/.tap/test-results/test/plugin.1.test.js.tap +0 -78
  225. package/.tap/test-results/test/plugin.2.test.js.tap +0 -102
  226. package/.tap/test-results/test/plugin.3.test.js.tap +0 -58
  227. package/.tap/test-results/test/plugin.4.test.js.tap +0 -164
  228. package/.tap/test-results/test/post-empty-body.test.js.tap +0 -8
  229. package/.tap/test-results/test/pretty-print.test.js.tap +0 -82
  230. package/.tap/test-results/test/promises.test.js.tap +0 -46
  231. package/.tap/test-results/test/propfind.test.js.tap +0 -43
  232. package/.tap/test-results/test/proppatch.test.js.tap +0 -29
  233. package/.tap/test-results/test/proto-poisoning.test.js.tap +0 -47
  234. package/.tap/test-results/test/put.error-handler.test.js.tap +0 -206
  235. package/.tap/test-results/test/put.test.js.tap +0 -182
  236. package/.tap/test-results/test/register.test.js.tap +0 -61
  237. package/.tap/test-results/test/reply-code.test.js.tap +0 -40
  238. package/.tap/test-results/test/reply-earlyHints.test.js.tap +0 -22
  239. package/.tap/test-results/test/reply-error.test.js.tap +0 -643
  240. package/.tap/test-results/test/reply-trailers.test.js.tap +0 -176
  241. package/.tap/test-results/test/report.test.js.tap +0 -43
  242. package/.tap/test-results/test/request-error.test.js.tap +0 -98
  243. package/.tap/test-results/test/request-id.test.js.tap +0 -38
  244. package/.tap/test-results/test/request.deprecated.test.js.tap +0 -13
  245. package/.tap/test-results/test/requestTimeout.test.js.tap +0 -21
  246. package/.tap/test-results/test/route-hooks.test.js.tap +0 -498
  247. package/.tap/test-results/test/route-prefix.test.js.tap +0 -195
  248. package/.tap/test-results/test/route-shorthand.test.js.tap +0 -190
  249. package/.tap/test-results/test/route.1.test.js.tap +0 -93
  250. package/.tap/test-results/test/route.2.test.js.tap +0 -28
  251. package/.tap/test-results/test/route.3.test.js.tap +0 -39
  252. package/.tap/test-results/test/route.4.test.js.tap +0 -32
  253. package/.tap/test-results/test/route.5.test.js.tap +0 -54
  254. package/.tap/test-results/test/route.6.test.js.tap +0 -81
  255. package/.tap/test-results/test/route.7.test.js.tap +0 -93
  256. package/.tap/test-results/test/route.8.test.js.tap +0 -38
  257. package/.tap/test-results/test/router-options.test.js.tap +0 -104
  258. package/.tap/test-results/test/same-shape.test.js.tap +0 -22
  259. package/.tap/test-results/test/schema-examples.test.js.tap +0 -85
  260. package/.tap/test-results/test/schema-feature.test.js.tap +0 -445
  261. package/.tap/test-results/test/schema-serialization.test.js.tap +0 -194
  262. package/.tap/test-results/test/schema-special-usage.test.js.tap +0 -186
  263. package/.tap/test-results/test/schema-validation.test.js.tap +0 -199
  264. package/.tap/test-results/test/search.test.js.tap +0 -77
  265. package/.tap/test-results/test/serialize-response.test.js.tap +0 -26
  266. package/.tap/test-results/test/server.test.js.tap +0 -65
  267. package/.tap/test-results/test/set-error-handler.test.js.tap +0 -7
  268. package/.tap/test-results/test/skip-reply-send.test.js.tap +0 -272
  269. package/.tap/test-results/test/stream.1.test.js.tap +0 -36
  270. package/.tap/test-results/test/stream.2.test.js.tap +0 -20
  271. package/.tap/test-results/test/stream.3.test.js.tap +0 -34
  272. package/.tap/test-results/test/stream.4.test.js.tap +0 -40
  273. package/.tap/test-results/test/stream.5.test.js.tap +0 -37
  274. package/.tap/test-results/test/sync-routes.test.js.tap +0 -19
  275. package/.tap/test-results/test/throw.test.js.tap +0 -116
  276. package/.tap/test-results/test/trace.test.js.tap +0 -7
  277. package/.tap/test-results/test/trust-proxy.test.js.tap +0 -109
  278. package/.tap/test-results/test/type-provider.test.js.tap +0 -12
  279. package/.tap/test-results/test/unlock.test.js.tap +0 -14
  280. package/.tap/test-results/test/upgrade.test.js.tap +0 -8
  281. package/.tap/test-results/test/url-rewriting.test.js.tap +0 -39
  282. package/.tap/test-results/test/useSemicolonDelimiter.test.js.tap +0 -33
  283. package/.tap/test-results/test/validation-error-handling.test.js.tap +0 -180
  284. package/.tap/test-results/test/versioned-routes.test.js.tap +0 -151
  285. package/.tap/test-results/test/web-api.test.js.tap +0 -51
  286. package/.tap/test-results/test/wrapThenable.test.js.tap +0 -11
  287. package/EXPENSE_POLICY.md +0 -105
  288. package/lib/httpMethods.js +0 -40
  289. package/test/method-missing.test.js +0 -24
  290. package/test/request.deprecated.test.js +0 -38
package/lib/reply.js CHANGED
@@ -5,7 +5,6 @@ const Readable = require('node:stream').Readable
5
5
 
6
6
  const {
7
7
  kFourOhFourContext,
8
- kPublicRouteContext,
9
8
  kReplyErrorHandlerCalled,
10
9
  kReplyHijacked,
11
10
  kReplyStartTime,
@@ -47,7 +46,6 @@ const {
47
46
  FST_ERR_REP_INVALID_PAYLOAD_TYPE,
48
47
  FST_ERR_REP_RESPONSE_BODY_CONSUMED,
49
48
  FST_ERR_REP_ALREADY_SENT,
50
- FST_ERR_REP_SENT_VALUE,
51
49
  FST_ERR_SEND_INSIDE_ONERR,
52
50
  FST_ERR_BAD_STATUS_CODE,
53
51
  FST_ERR_BAD_TRAILER_NAME,
@@ -55,7 +53,6 @@ const {
55
53
  FST_ERR_MISSING_SERIALIZATION_FN,
56
54
  FST_ERR_MISSING_CONTENTTYPE_SERIALIZATION_FN
57
55
  } = require('./errors')
58
- const { FSTDEP010, FSTDEP013, FSTDEP019, FSTDEP021 } = require('./warnings')
59
56
 
60
57
  const toString = Object.prototype.toString
61
58
 
@@ -80,14 +77,6 @@ Object.defineProperties(Reply.prototype, {
80
77
  return this.request[kRouteContext]
81
78
  }
82
79
  },
83
- // TODO: remove once v5 is done
84
- // Is temporary to avoid constant conflicts between `next` and `main`
85
- context: {
86
- get () {
87
- FSTDEP019()
88
- return this.request[kRouteContext]
89
- }
90
- },
91
80
  elapsedTime: {
92
81
  get () {
93
82
  if (this[kReplyStartTime] === undefined) {
@@ -106,20 +95,6 @@ Object.defineProperties(Reply.prototype, {
106
95
  get () {
107
96
  // We are checking whether reply was hijacked or the response has ended.
108
97
  return (this[kReplyHijacked] || this.raw.writableEnded) === true
109
- },
110
- set (value) {
111
- FSTDEP010()
112
-
113
- if (value !== true) {
114
- throw new FST_ERR_REP_SENT_VALUE()
115
- }
116
-
117
- // We throw only if sent was overwritten from Fastify
118
- if (this.sent && this[kReplyHijacked]) {
119
- throw new FST_ERR_REP_ALREADY_SENT(this.request.url, this.request.method)
120
- }
121
-
122
- this[kReplyHijacked] = true
123
98
  }
124
99
  },
125
100
  statusCode: {
@@ -130,9 +105,9 @@ Object.defineProperties(Reply.prototype, {
130
105
  this.code(value)
131
106
  }
132
107
  },
133
- [kPublicRouteContext]: {
108
+ routeOptions: {
134
109
  get () {
135
- return this.request[kPublicRouteContext]
110
+ return this.request.routeOptions
136
111
  }
137
112
  }
138
113
  })
@@ -208,7 +183,7 @@ Reply.prototype.send = function (payload) {
208
183
  payload = this[kReplySerializer](payload)
209
184
  }
210
185
 
211
- // The indexOf below also matches custom json mimetypes such as 'application/hal+json' or 'application/ld+json'
186
+ // The indexOf below also matches custom json mimetypes such as 'application/hal+json' or 'application/ld+json'
212
187
  } else if (hasContentType === false || contentType.indexOf('json') > -1) {
213
188
  if (hasContentType === false) {
214
189
  this[kReplyHeaders]['content-type'] = CONTENT_TYPE.JSON
@@ -376,13 +351,13 @@ Reply.prototype.compileSerializationSchema = function (schema, httpStatus = null
376
351
  }
377
352
 
378
353
  const serializerCompiler = this[kRouteContext].serializerCompiler ||
379
- this.server[kSchemaController].serializerCompiler ||
380
- (
381
- // We compile the schemas if no custom serializerCompiler is provided
382
- // nor set
383
- this.server[kSchemaController].setupSerializer(this.server[kOptions]) ||
384
- this.server[kSchemaController].serializerCompiler
385
- )
354
+ this.server[kSchemaController].serializerCompiler ||
355
+ (
356
+ // We compile the schemas if no custom serializerCompiler is provided
357
+ // nor set
358
+ this.server[kSchemaController].setupSerializer(this.server[kOptions]) ||
359
+ this.server[kSchemaController].serializerCompiler
360
+ )
386
361
 
387
362
  const serializeFn = serializerCompiler({
388
363
  schema,
@@ -463,13 +438,6 @@ Reply.prototype.type = function (type) {
463
438
  }
464
439
 
465
440
  Reply.prototype.redirect = function (url, code) {
466
- if (typeof url === 'number') {
467
- FSTDEP021()
468
- const temp = code
469
- code = url
470
- url = temp
471
- }
472
-
473
441
  if (!code) {
474
442
  code = this[kReplyHasStatusCode] ? this.raw.statusCode : 302
475
443
  }
@@ -600,14 +568,30 @@ function onSendEnd (reply, payload) {
600
568
  reply.header('Trailer', header.trim())
601
569
  }
602
570
 
603
- // since Response contain status code, we need to update before
604
- // any action that used statusCode
605
- const isResponse = toString.call(payload) === '[object Response]'
606
- if (isResponse) {
571
+ // since Response contain status code, headers and body,
572
+ // we need to update the status, add the headers and use it's body as payload
573
+ // before continuing
574
+ if (toString.call(payload) === '[object Response]') {
607
575
  // https://developer.mozilla.org/en-US/docs/Web/API/Response/status
608
576
  if (typeof payload.status === 'number') {
609
577
  reply.code(payload.status)
610
578
  }
579
+
580
+ // https://developer.mozilla.org/en-US/docs/Web/API/Response/headers
581
+ if (typeof payload.headers === 'object' && typeof payload.headers.forEach === 'function') {
582
+ for (const [headerName, headerValue] of payload.headers) {
583
+ reply.header(headerName, headerValue)
584
+ }
585
+ }
586
+
587
+ // https://developer.mozilla.org/en-US/docs/Web/API/Response/body
588
+ if (payload.body !== null) {
589
+ if (payload.bodyUsed) {
590
+ throw new FST_ERR_REP_RESPONSE_BODY_CONSUMED()
591
+ }
592
+ }
593
+ // Keep going, body is either null or ReadableStream
594
+ payload = payload.body
611
595
  }
612
596
  const statusCode = res.statusCode
613
597
 
@@ -654,26 +638,6 @@ function onSendEnd (reply, payload) {
654
638
  return
655
639
  }
656
640
 
657
- // Response
658
- if (isResponse) {
659
- // https://developer.mozilla.org/en-US/docs/Web/API/Response/headers
660
- if (typeof payload.headers === 'object' && typeof payload.headers.forEach === 'function') {
661
- for (const [headerName, headerValue] of payload.headers) {
662
- reply.header(headerName, headerValue)
663
- }
664
- }
665
-
666
- // https://developer.mozilla.org/en-US/docs/Web/API/Response/body
667
- if (payload.body != null) {
668
- if (payload.bodyUsed) {
669
- throw new FST_ERR_REP_RESPONSE_BODY_CONSUMED()
670
- }
671
- // Response.body always a ReadableStream
672
- sendWebStream(payload.body, res, reply)
673
- }
674
- return
675
- }
676
-
677
641
  if (typeof payload !== 'string' && !Buffer.isBuffer(payload)) {
678
642
  throw new FST_ERR_REP_INVALID_PAYLOAD_TYPE(typeof payload)
679
643
  }
@@ -681,9 +645,9 @@ function onSendEnd (reply, payload) {
681
645
  if (reply[kReplyTrailers] === null) {
682
646
  const contentLength = reply[kReplyHeaders]['content-length']
683
647
  if (!contentLength ||
684
- (req.raw.method !== 'HEAD' &&
685
- Number(contentLength) !== Buffer.byteLength(payload)
686
- )
648
+ (req.raw.method !== 'HEAD' &&
649
+ Number(contentLength) !== Buffer.byteLength(payload)
650
+ )
687
651
  ) {
688
652
  reply[kReplyHeaders]['content-length'] = '' + Buffer.byteLength(payload)
689
653
  }
@@ -724,7 +688,7 @@ function sendStream (payload, res, reply) {
724
688
  if (res.headersSent || reply.request.raw.aborted === true) {
725
689
  if (!errorLogged) {
726
690
  errorLogged = true
727
- logStreamError(reply.log, err, res)
691
+ logStreamError(reply.log, err, reply)
728
692
  }
729
693
  res.destroy()
730
694
  } else {
@@ -812,10 +776,6 @@ function sendTrailer (payload, res, reply) {
812
776
  const result = reply[kReplyTrailers][trailerName](reply, payload, cb)
813
777
  if (typeof result === 'object' && typeof result.then === 'function') {
814
778
  result.then((v) => cb(null, v), cb)
815
- } else if (result !== null && result !== undefined) {
816
- // TODO: should be removed in fastify@5
817
- FSTDEP013()
818
- cb(null, result)
819
779
  }
820
780
  }
821
781
 
package/lib/request.js CHANGED
@@ -1,14 +1,6 @@
1
1
  'use strict'
2
2
 
3
3
  const proxyAddr = require('proxy-addr')
4
- const {
5
- FSTDEP005,
6
- FSTDEP012,
7
- FSTDEP015,
8
- FSTDEP016,
9
- FSTDEP017,
10
- FSTDEP018
11
- } = require('./warnings')
12
4
  const {
13
5
  kHasBeenDecorated,
14
6
  kSchemaBody,
@@ -19,7 +11,6 @@ const {
19
11
  kOptions,
20
12
  kRequestCacheValidateFns,
21
13
  kRouteContext,
22
- kPublicRouteContext,
23
14
  kRequestOriginalUrl
24
15
  } = require('./symbols')
25
16
  const { FST_ERR_REQ_INVALID_VALIDATION_INVOCATION } = require('./errors')
@@ -171,18 +162,6 @@ Object.defineProperties(Request.prototype, {
171
162
  return this.raw.method
172
163
  }
173
164
  },
174
- context: {
175
- get () {
176
- FSTDEP012()
177
- return this[kRouteContext]
178
- }
179
- },
180
- routerPath: {
181
- get () {
182
- FSTDEP017()
183
- return this[kRouteContext].config?.url
184
- }
185
- },
186
165
  routeOptions: {
187
166
  get () {
188
167
  const context = this[kRouteContext]
@@ -213,35 +192,11 @@ Object.defineProperties(Request.prototype, {
213
192
  return Object.freeze(options)
214
193
  }
215
194
  },
216
- routerMethod: {
217
- get () {
218
- FSTDEP018()
219
- return this[kRouteContext].config?.method
220
- }
221
- },
222
- routeConfig: {
223
- get () {
224
- FSTDEP016()
225
- return this[kRouteContext][kPublicRouteContext]?.config
226
- }
227
- },
228
- routeSchema: {
229
- get () {
230
- FSTDEP015()
231
- return this[kRouteContext][kPublicRouteContext].schema
232
- }
233
- },
234
195
  is404: {
235
196
  get () {
236
197
  return this[kRouteContext].config?.url === undefined
237
198
  }
238
199
  },
239
- connection: {
240
- get () {
241
- FSTDEP005()
242
- return this.raw.connection
243
- }
244
- },
245
200
  socket: {
246
201
  get () {
247
202
  return this.raw.socket
package/lib/route.js CHANGED
@@ -4,13 +4,8 @@ const FindMyWay = require('find-my-way')
4
4
  const Context = require('./context')
5
5
  const handleRequest = require('./handleRequest')
6
6
  const { onRequestAbortHookRunner, lifecycleHooks, preParsingHookRunner, onTimeoutHookRunner, onRequestHookRunner } = require('./hooks')
7
- const { supportedMethods } = require('./httpMethods')
8
7
  const { normalizeSchema } = require('./schemas')
9
8
  const { parseHeadOnSendHandlers } = require('./headRoute')
10
- const {
11
- FSTDEP007,
12
- FSTDEP008
13
- } = require('./warnings')
14
9
 
15
10
  const {
16
11
  compileSchemasForValidation,
@@ -36,6 +31,7 @@ const {
36
31
 
37
32
  const {
38
33
  kRoutePrefix,
34
+ kSupportedHTTPMethods,
39
35
  kLogLevel,
40
36
  kLogSerializers,
41
37
  kHooks,
@@ -91,7 +87,7 @@ function buildRouting (options) {
91
87
  disableRequestLogging = options.disableRequestLogging
92
88
  ignoreTrailingSlash = options.ignoreTrailingSlash
93
89
  ignoreDuplicateSlashes = options.ignoreDuplicateSlashes
94
- return503OnClosing = Object.prototype.hasOwnProperty.call(options, 'return503OnClosing') ? options.return503OnClosing : true
90
+ return503OnClosing = Object.hasOwn(options, 'return503OnClosing') ? options.return503OnClosing : true
95
91
  keepAliveConnections = fastifyArgs.keepAliveConnections
96
92
  },
97
93
  routing: router.lookup.bind(router), // router func to find the right handler to call
@@ -207,12 +203,12 @@ function buildRouting (options) {
207
203
  if (Array.isArray(opts.method)) {
208
204
  // eslint-disable-next-line no-var
209
205
  for (var i = 0; i < opts.method.length; ++i) {
210
- opts.method[i] = normalizeAndValidateMethod(opts.method[i])
211
- validateSchemaBodyOption(opts.method[i], path, opts.schema)
206
+ opts.method[i] = normalizeAndValidateMethod.call(this, opts.method[i])
207
+ validateSchemaBodyOption.call(this, opts.method[i], path, opts.schema)
212
208
  }
213
209
  } else {
214
- opts.method = normalizeAndValidateMethod(opts.method)
215
- validateSchemaBodyOption(opts.method, path, opts.schema)
210
+ opts.method = normalizeAndValidateMethod.call(this, opts.method)
211
+ validateSchemaBodyOption.call(this, opts.method, path, opts.schema)
216
212
  }
217
213
 
218
214
  if (!opts.handler) {
@@ -332,26 +328,16 @@ function buildRouting (options) {
332
328
  isFastify
333
329
  })
334
330
 
335
- if (opts.version) {
336
- FSTDEP008()
337
- constraints.version = opts.version
338
- }
339
-
340
331
  const headHandler = router.findRoute('HEAD', opts.url, constraints)
341
332
  const hasHEADHandler = headHandler !== null
342
333
 
343
- // remove the head route created by fastify
344
- if (isHeadRoute && hasHEADHandler && !context[kRouteByFastify] && headHandler.store[kRouteByFastify]) {
345
- router.off('HEAD', opts.url, constraints)
346
- }
347
-
348
334
  try {
349
335
  router.on(opts.method, opts.url, { constraints }, routeHandler, context)
350
336
  } catch (error) {
351
337
  // any route insertion error created by fastify can be safely ignore
352
338
  // because it only duplicate route for head
353
339
  if (!context[kRouteByFastify]) {
354
- const isDuplicatedRoute = error.message.includes(`Method '${opts.method}' already declared for route '${opts.url}'`)
340
+ const isDuplicatedRoute = error.message.includes(`Method '${opts.method}' already declared for route`)
355
341
  if (isDuplicatedRoute) {
356
342
  throw new FST_ERR_DUPLICATED_ROUTE(opts.method, opts.url)
357
343
  }
@@ -425,8 +411,6 @@ function buildRouting (options) {
425
411
  if (shouldExposeHead && isGetRoute && !isHeadRoute && !hasHEADHandler) {
426
412
  const onSendHandlers = parseHeadOnSendHandlers(headOpts.onSend)
427
413
  prepareRoute.call(this, { method: 'HEAD', url: path, options: { ...headOpts, onSend: onSendHandlers }, isFastify: true })
428
- } else if (hasHEADHandler && exposeHeadRoute) {
429
- FSTDEP007()
430
414
  }
431
415
  }
432
416
  }
@@ -549,7 +533,8 @@ function normalizeAndValidateMethod (method) {
549
533
  throw new FST_ERR_ROUTE_METHOD_INVALID()
550
534
  }
551
535
  method = method.toUpperCase()
552
- if (supportedMethods.indexOf(method) === -1) {
536
+ if (!this[kSupportedHTTPMethods].bodyless.has(method) &&
537
+ !this[kSupportedHTTPMethods].bodywith.has(method)) {
553
538
  throw new FST_ERR_ROUTE_METHOD_NOT_SUPPORTED(method)
554
539
  }
555
540
 
@@ -557,7 +542,7 @@ function normalizeAndValidateMethod (method) {
557
542
  }
558
543
 
559
544
  function validateSchemaBodyOption (method, path, schema) {
560
- if ((method === 'GET' || method === 'HEAD') && schema && schema.body) {
545
+ if (this[kSupportedHTTPMethods].bodyless.has(method) && schema?.body) {
561
546
  throw new FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED(method, path)
562
547
  }
563
548
  }
@@ -580,9 +565,9 @@ function runPreParsing (err, request, reply) {
580
565
  request[kRequestPayloadStream] = request.raw
581
566
 
582
567
  if (request[kRouteContext].preParsing !== null) {
583
- preParsingHookRunner(request[kRouteContext].preParsing, request, reply, handleRequest)
568
+ preParsingHookRunner(request[kRouteContext].preParsing, request, reply, handleRequest.bind(request.server))
584
569
  } else {
585
- handleRequest(null, request, reply)
570
+ handleRequest.call(request.server, null, request, reply)
586
571
  }
587
572
  }
588
573
 
package/lib/schemas.js CHANGED
@@ -73,7 +73,18 @@ function normalizeSchema (routeSchemas, serverOptions) {
73
73
  for (const key of SCHEMAS_SOURCE) {
74
74
  const schema = routeSchemas[key]
75
75
  if (schema && !isCustomSchemaPrototype(schema)) {
76
- routeSchemas[key] = getSchemaAnyway(schema, serverOptions.jsonShorthand)
76
+ if (key === 'body' && schema.content) {
77
+ const contentProperty = schema.content
78
+ const keys = Object.keys(contentProperty)
79
+ for (let i = 0; i < keys.length; i++) {
80
+ const contentType = keys[i]
81
+ const contentSchema = contentProperty[contentType].schema
82
+ if (!contentSchema) {
83
+ throw new FST_ERR_SCH_CONTENT_MISSING_SCHEMA(contentType)
84
+ }
85
+ }
86
+ continue
87
+ }
77
88
  }
78
89
  }
79
90
 
@@ -86,25 +97,15 @@ function normalizeSchema (routeSchemas, serverOptions) {
86
97
 
87
98
  const contentProperty = routeSchemas.response[code].content
88
99
 
89
- let hasContentMultipleContentTypes = false
90
100
  if (contentProperty) {
91
101
  const keys = Object.keys(contentProperty)
92
102
  for (let i = 0; i < keys.length; i++) {
93
103
  const mediaName = keys[i]
94
104
  if (!contentProperty[mediaName].schema) {
95
- if (keys.length === 1) { break }
96
105
  throw new FST_ERR_SCH_CONTENT_MISSING_SCHEMA(mediaName)
97
106
  }
98
- routeSchemas.response[code].content[mediaName].schema = getSchemaAnyway(contentProperty[mediaName].schema, serverOptions.jsonShorthand)
99
- if (i === keys.length - 1) {
100
- hasContentMultipleContentTypes = true
101
- }
102
107
  }
103
108
  }
104
-
105
- if (!hasContentMultipleContentTypes) {
106
- routeSchemas.response[code] = getSchemaAnyway(routeSchemas.response[code], serverOptions.jsonShorthand)
107
- }
108
109
  }
109
110
  }
110
111
 
@@ -129,17 +130,6 @@ function generateFluentSchema (schema) {
129
130
  }
130
131
  }
131
132
 
132
- function getSchemaAnyway (schema, jsonShorthand) {
133
- if (!jsonShorthand || schema.$ref || schema.oneOf || schema.allOf || schema.anyOf || schema.$merge || schema.$patch) return schema
134
- if (!schema.type && !schema.properties) {
135
- return {
136
- type: 'object',
137
- properties: schema
138
- }
139
- }
140
- return schema
141
- }
142
-
143
133
  /**
144
134
  * Search for the right JSON schema compiled function in the request context
145
135
  * setup by the route configuration `schema.response`.
@@ -163,6 +153,11 @@ function getSchemaSerializer (context, statusCode, contentType) {
163
153
  return responseSchemaDef[statusCode][mediaName]
164
154
  }
165
155
 
156
+ // fallback to match all media-type
157
+ if (responseSchemaDef[statusCode]['*/*']) {
158
+ return responseSchemaDef[statusCode]['*/*']
159
+ }
160
+
166
161
  return false
167
162
  }
168
163
  return responseSchemaDef[statusCode]
@@ -175,6 +170,11 @@ function getSchemaSerializer (context, statusCode, contentType) {
175
170
  return responseSchemaDef[fallbackStatusCode][mediaName]
176
171
  }
177
172
 
173
+ // fallback to match all media-type
174
+ if (responseSchemaDef[fallbackStatusCode]['*/*']) {
175
+ return responseSchemaDef[fallbackStatusCode]['*/*']
176
+ }
177
+
178
178
  return false
179
179
  }
180
180
 
@@ -187,6 +187,11 @@ function getSchemaSerializer (context, statusCode, contentType) {
187
187
  return responseSchemaDef.default[mediaName]
188
188
  }
189
189
 
190
+ // fallback to match all media-type
191
+ if (responseSchemaDef.default['*/*']) {
192
+ return responseSchemaDef.default['*/*']
193
+ }
194
+
190
195
  return false
191
196
  }
192
197
 
package/lib/server.js CHANGED
@@ -55,7 +55,8 @@ function createServer (options, httpHandler) {
55
55
  } else {
56
56
  host = listenOptions.host
57
57
  }
58
- if (Object.prototype.hasOwnProperty.call(listenOptions, 'host') === false) {
58
+ if (!Object.hasOwn(listenOptions, 'host') ||
59
+ listenOptions.host == null) {
59
60
  listenOptions.host = host
60
61
  }
61
62
  if (host === 'localhost') {
@@ -143,7 +144,6 @@ function multipleBindings (mainServer, httpHandler, serverOpts, listenOptions, o
143
144
  cb: (_ignoreErr) => {
144
145
  bound++
145
146
 
146
- /* istanbul ignore next: the else won't be taken unless listening fails */
147
147
  if (!_ignoreErr) {
148
148
  this[kServerBindings].push(secondaryServer)
149
149
  }
@@ -157,18 +157,13 @@ function multipleBindings (mainServer, httpHandler, serverOpts, listenOptions, o
157
157
 
158
158
  const secondaryServer = getServerInstance(serverOpts, httpHandler)
159
159
  const closeSecondary = () => {
160
- // To avoid fall into situations where the close of the
160
+ // To avoid falling into situations where the close of the
161
161
  // secondary server is triggered before the preClose hook
162
- // is done running, we better wait until the main server
163
- // is closed.
162
+ // is done running, we better wait until the main server is closed.
164
163
  // No new TCP connections are accepted
165
- // We swallow any error from the secondary
166
- // server
164
+ // We swallow any error from the secondary server
167
165
  secondaryServer.close(() => {})
168
- if (serverOpts.forceCloseConnections === 'idle') {
169
- // Not needed in Node 19
170
- secondaryServer.closeIdleConnections()
171
- } else if (typeof secondaryServer.closeAllConnections === 'function' && serverOpts.forceCloseConnections) {
166
+ if (typeof secondaryServer.closeAllConnections === 'function' && serverOpts.forceCloseConnections === true) {
172
167
  secondaryServer.closeAllConnections()
173
168
  }
174
169
  }
package/lib/symbols.js CHANGED
@@ -5,6 +5,7 @@ const keys = {
5
5
  kChildren: Symbol('fastify.children'),
6
6
  kServerBindings: Symbol('fastify.serverBindings'),
7
7
  kBodyLimit: Symbol('fastify.bodyLimit'),
8
+ kSupportedHTTPMethods: Symbol('fastify.acceptedHTTPMethods'),
8
9
  kRoutePrefix: Symbol('fastify.routePrefix'),
9
10
  kLogLevel: Symbol('fastify.logLevel'),
10
11
  kLogSerializers: Symbol('fastify.logSerializers'),
@@ -15,7 +16,6 @@ const keys = {
15
16
  kDisableRequestLogging: Symbol('fastify.disableRequestLogging'),
16
17
  kPluginNameChain: Symbol('fastify.pluginNameChain'),
17
18
  kRouteContext: Symbol('fastify.context'),
18
- kPublicRouteContext: Symbol('fastify.routeOptions'),
19
19
  kGenReqId: Symbol('fastify.genReqId'),
20
20
  // Schema
21
21
  kSchemaController: Symbol('fastify.schemaController'),
package/lib/validation.js CHANGED
@@ -82,25 +82,35 @@ function compileSchemasForValidation (context, compile, isCustom) {
82
82
  })
83
83
  }
84
84
  context[headersSchema] = compile({ schema: headersSchemaLowerCase, method, url, httpPart: 'headers' })
85
- } else if (Object.prototype.hasOwnProperty.call(schema, 'headers')) {
85
+ } else if (Object.hasOwn(schema, 'headers')) {
86
86
  FSTWRN001('headers', method, url)
87
87
  }
88
88
 
89
89
  if (schema.body) {
90
- context[bodySchema] = compile({ schema: schema.body, method, url, httpPart: 'body' })
91
- } else if (Object.prototype.hasOwnProperty.call(schema, 'body')) {
90
+ const contentProperty = schema.body.content
91
+ if (contentProperty) {
92
+ const contentTypeSchemas = {}
93
+ for (const contentType of Object.keys(contentProperty)) {
94
+ const contentSchema = contentProperty[contentType].schema
95
+ contentTypeSchemas[contentType] = compile({ schema: contentSchema, method, url, httpPart: 'body', contentType })
96
+ }
97
+ context[bodySchema] = contentTypeSchemas
98
+ } else {
99
+ context[bodySchema] = compile({ schema: schema.body, method, url, httpPart: 'body' })
100
+ }
101
+ } else if (Object.hasOwn(schema, 'body')) {
92
102
  FSTWRN001('body', method, url)
93
103
  }
94
104
 
95
105
  if (schema.querystring) {
96
106
  context[querystringSchema] = compile({ schema: schema.querystring, method, url, httpPart: 'querystring' })
97
- } else if (Object.prototype.hasOwnProperty.call(schema, 'querystring')) {
107
+ } else if (Object.hasOwn(schema, 'querystring')) {
98
108
  FSTWRN001('querystring', method, url)
99
109
  }
100
110
 
101
111
  if (schema.params) {
102
112
  context[paramsSchema] = compile({ schema: schema.params, method, url, httpPart: 'params' })
103
- } else if (Object.prototype.hasOwnProperty.call(schema, 'params')) {
113
+ } else if (Object.hasOwn(schema, 'params')) {
104
114
  FSTWRN001('params', method, url)
105
115
  }
106
116
  }
@@ -140,7 +150,18 @@ function validate (context, request, execution) {
140
150
  }
141
151
 
142
152
  if (runExecution || !execution.skipBody) {
143
- const body = validateParam(context[bodySchema], request, 'body')
153
+ let validatorFunction = null
154
+ if (typeof context[bodySchema] === 'function') {
155
+ validatorFunction = context[bodySchema]
156
+ } else if (context[bodySchema]) {
157
+ // TODO: add request.contentType and reuse it here
158
+ const contentType = request.headers['content-type']?.split(';', 1)[0]
159
+ const contentSchema = context[bodySchema][contentType]
160
+ if (contentSchema) {
161
+ validatorFunction = contentSchema
162
+ }
163
+ }
164
+ const body = validateParam(validatorFunction, request, 'body')
144
165
  if (body) {
145
166
  if (typeof body.then !== 'function') {
146
167
  return wrapValidationError(body, 'body', context.schemaErrorFormatter)