node-fastify 5.8.3

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 (354) hide show
  1. package/.borp.yaml +3 -0
  2. package/.markdownlint-cli2.yaml +22 -0
  3. package/.prettierignore +1 -0
  4. package/GOVERNANCE.md +4 -0
  5. package/LICENSE +21 -0
  6. package/PROJECT_CHARTER.md +126 -0
  7. package/README.md +423 -0
  8. package/SECURITY.md +220 -0
  9. package/SPONSORS.md +24 -0
  10. package/build/build-error-serializer.js +35 -0
  11. package/build/build-validation.js +169 -0
  12. package/build/sync-version.js +11 -0
  13. package/docs/Guides/Benchmarking.md +60 -0
  14. package/docs/Guides/Database.md +321 -0
  15. package/docs/Guides/Delay-Accepting-Requests.md +608 -0
  16. package/docs/Guides/Detecting-When-Clients-Abort.md +172 -0
  17. package/docs/Guides/Ecosystem.md +726 -0
  18. package/docs/Guides/Fluent-Schema.md +127 -0
  19. package/docs/Guides/Getting-Started.md +620 -0
  20. package/docs/Guides/Index.md +43 -0
  21. package/docs/Guides/Migration-Guide-V3.md +287 -0
  22. package/docs/Guides/Migration-Guide-V4.md +267 -0
  23. package/docs/Guides/Migration-Guide-V5.md +727 -0
  24. package/docs/Guides/Plugins-Guide.md +520 -0
  25. package/docs/Guides/Prototype-Poisoning.md +383 -0
  26. package/docs/Guides/Recommendations.md +378 -0
  27. package/docs/Guides/Serverless.md +604 -0
  28. package/docs/Guides/Style-Guide.md +246 -0
  29. package/docs/Guides/Testing.md +481 -0
  30. package/docs/Guides/Write-Plugin.md +103 -0
  31. package/docs/Guides/Write-Type-Provider.md +34 -0
  32. package/docs/Reference/ContentTypeParser.md +271 -0
  33. package/docs/Reference/Decorators.md +436 -0
  34. package/docs/Reference/Encapsulation.md +194 -0
  35. package/docs/Reference/Errors.md +377 -0
  36. package/docs/Reference/HTTP2.md +94 -0
  37. package/docs/Reference/Hooks.md +958 -0
  38. package/docs/Reference/Index.md +73 -0
  39. package/docs/Reference/LTS.md +86 -0
  40. package/docs/Reference/Lifecycle.md +99 -0
  41. package/docs/Reference/Logging.md +268 -0
  42. package/docs/Reference/Middleware.md +79 -0
  43. package/docs/Reference/Plugins.md +245 -0
  44. package/docs/Reference/Principles.md +73 -0
  45. package/docs/Reference/Reply.md +1001 -0
  46. package/docs/Reference/Request.md +295 -0
  47. package/docs/Reference/Routes.md +802 -0
  48. package/docs/Reference/Server.md +2389 -0
  49. package/docs/Reference/Type-Providers.md +256 -0
  50. package/docs/Reference/TypeScript.md +1729 -0
  51. package/docs/Reference/Validation-and-Serialization.md +1130 -0
  52. package/docs/Reference/Warnings.md +58 -0
  53. package/docs/index.md +24 -0
  54. package/docs/resources/encapsulation_context.drawio +1 -0
  55. package/docs/resources/encapsulation_context.svg +3 -0
  56. package/eslint.config.js +35 -0
  57. package/examples/asyncawait.js +38 -0
  58. package/examples/benchmark/body.json +3 -0
  59. package/examples/benchmark/hooks-benchmark-async-await.js +44 -0
  60. package/examples/benchmark/hooks-benchmark.js +52 -0
  61. package/examples/benchmark/parser.js +47 -0
  62. package/examples/benchmark/simple.js +30 -0
  63. package/examples/benchmark/webstream.js +27 -0
  64. package/examples/hooks.js +91 -0
  65. package/examples/http2.js +39 -0
  66. package/examples/https.js +38 -0
  67. package/examples/parser.js +53 -0
  68. package/examples/plugin.js +12 -0
  69. package/examples/route-prefix.js +38 -0
  70. package/examples/shared-schema.js +38 -0
  71. package/examples/simple-stream.js +20 -0
  72. package/examples/simple.js +32 -0
  73. package/examples/simple.mjs +27 -0
  74. package/examples/typescript-server.ts +79 -0
  75. package/examples/use-plugin.js +29 -0
  76. package/fastify.d.ts +253 -0
  77. package/fastify.js +985 -0
  78. package/integration/server.js +29 -0
  79. package/integration/test.sh +23 -0
  80. package/lib/config-validator.js +1266 -0
  81. package/lib/content-type-parser.js +413 -0
  82. package/lib/content-type.js +160 -0
  83. package/lib/context.js +98 -0
  84. package/lib/decorate.js +152 -0
  85. package/lib/error-handler.js +173 -0
  86. package/lib/error-serializer.js +134 -0
  87. package/lib/error-status.js +14 -0
  88. package/lib/errors.js +516 -0
  89. package/lib/four-oh-four.js +190 -0
  90. package/lib/handle-request.js +195 -0
  91. package/lib/head-route.js +45 -0
  92. package/lib/hooks.js +429 -0
  93. package/lib/initial-config-validation.js +37 -0
  94. package/lib/logger-factory.js +136 -0
  95. package/lib/logger-pino.js +68 -0
  96. package/lib/noop-set.js +10 -0
  97. package/lib/plugin-override.js +90 -0
  98. package/lib/plugin-utils.js +169 -0
  99. package/lib/promise.js +23 -0
  100. package/lib/reply.js +1030 -0
  101. package/lib/req-id-gen-factory.js +52 -0
  102. package/lib/request.js +391 -0
  103. package/lib/route.js +686 -0
  104. package/lib/schema-controller.js +164 -0
  105. package/lib/schemas.js +207 -0
  106. package/lib/server.js +441 -0
  107. package/lib/symbols.js +71 -0
  108. package/lib/validation.js +280 -0
  109. package/lib/warnings.js +57 -0
  110. package/lib/wrap-thenable.js +84 -0
  111. package/package.json +225 -0
  112. package/scripts/validate-ecosystem-links.js +179 -0
  113. package/test/404s.test.js +2035 -0
  114. package/test/500s.test.js +422 -0
  115. package/test/allow-unsafe-regex.test.js +92 -0
  116. package/test/als.test.js +65 -0
  117. package/test/async-await.test.js +705 -0
  118. package/test/async-dispose.test.js +20 -0
  119. package/test/async_hooks.test.js +52 -0
  120. package/test/body-limit.test.js +224 -0
  121. package/test/buffer.test.js +74 -0
  122. package/test/build/error-serializer.test.js +36 -0
  123. package/test/build/version.test.js +14 -0
  124. package/test/build-certificate.js +109 -0
  125. package/test/bundler/README.md +29 -0
  126. package/test/bundler/esbuild/bundler-test.js +32 -0
  127. package/test/bundler/esbuild/package.json +10 -0
  128. package/test/bundler/esbuild/src/fail-plugin-version.js +14 -0
  129. package/test/bundler/esbuild/src/index.js +9 -0
  130. package/test/bundler/webpack/bundler-test.js +32 -0
  131. package/test/bundler/webpack/package.json +11 -0
  132. package/test/bundler/webpack/src/fail-plugin-version.js +14 -0
  133. package/test/bundler/webpack/src/index.js +9 -0
  134. package/test/bundler/webpack/webpack.config.js +15 -0
  135. package/test/case-insensitive.test.js +102 -0
  136. package/test/chainable.test.js +40 -0
  137. package/test/child-logger-factory.test.js +128 -0
  138. package/test/client-timeout.test.js +38 -0
  139. package/test/close-pipelining.test.js +78 -0
  140. package/test/close.test.js +706 -0
  141. package/test/conditional-pino.test.js +47 -0
  142. package/test/connection-timeout.test.js +42 -0
  143. package/test/constrained-routes.test.js +1138 -0
  144. package/test/content-length.test.js +174 -0
  145. package/test/content-parser.test.js +739 -0
  146. package/test/content-type.test.js +181 -0
  147. package/test/context-config.test.js +164 -0
  148. package/test/custom-http-server.test.js +118 -0
  149. package/test/custom-parser-async.test.js +59 -0
  150. package/test/custom-parser.0.test.js +701 -0
  151. package/test/custom-parser.1.test.js +266 -0
  152. package/test/custom-parser.2.test.js +91 -0
  153. package/test/custom-parser.3.test.js +208 -0
  154. package/test/custom-parser.4.test.js +218 -0
  155. package/test/custom-parser.5.test.js +130 -0
  156. package/test/custom-querystring-parser.test.js +129 -0
  157. package/test/decorator.test.js +1330 -0
  158. package/test/delete.test.js +344 -0
  159. package/test/diagnostics-channel/404.test.js +49 -0
  160. package/test/diagnostics-channel/async-delay-request.test.js +65 -0
  161. package/test/diagnostics-channel/async-request.test.js +64 -0
  162. package/test/diagnostics-channel/error-before-handler.test.js +35 -0
  163. package/test/diagnostics-channel/error-request.test.js +53 -0
  164. package/test/diagnostics-channel/error-status.test.js +123 -0
  165. package/test/diagnostics-channel/init.test.js +50 -0
  166. package/test/diagnostics-channel/sync-delay-request.test.js +49 -0
  167. package/test/diagnostics-channel/sync-request-reply.test.js +51 -0
  168. package/test/diagnostics-channel/sync-request.test.js +54 -0
  169. package/test/encapsulated-child-logger-factory.test.js +69 -0
  170. package/test/encapsulated-error-handler.test.js +237 -0
  171. package/test/esm/errorCodes.test.mjs +10 -0
  172. package/test/esm/esm.test.mjs +13 -0
  173. package/test/esm/index.test.js +8 -0
  174. package/test/esm/named-exports.mjs +14 -0
  175. package/test/esm/other.mjs +8 -0
  176. package/test/esm/plugin.mjs +8 -0
  177. package/test/fastify-instance.test.js +300 -0
  178. package/test/find-route.test.js +152 -0
  179. package/test/fluent-schema.test.js +209 -0
  180. package/test/genReqId.test.js +426 -0
  181. package/test/handler-context.test.js +45 -0
  182. package/test/handler-timeout.test.js +367 -0
  183. package/test/has-route.test.js +88 -0
  184. package/test/header-overflow.test.js +55 -0
  185. package/test/helper.js +496 -0
  186. package/test/hooks-async.test.js +1099 -0
  187. package/test/hooks.on-listen.test.js +1162 -0
  188. package/test/hooks.on-ready.test.js +421 -0
  189. package/test/hooks.test.js +3578 -0
  190. package/test/http-methods/copy.test.js +35 -0
  191. package/test/http-methods/custom-http-methods.test.js +114 -0
  192. package/test/http-methods/get.test.js +412 -0
  193. package/test/http-methods/head.test.js +263 -0
  194. package/test/http-methods/lock.test.js +108 -0
  195. package/test/http-methods/mkcalendar.test.js +143 -0
  196. package/test/http-methods/mkcol.test.js +35 -0
  197. package/test/http-methods/move.test.js +42 -0
  198. package/test/http-methods/propfind.test.js +136 -0
  199. package/test/http-methods/proppatch.test.js +105 -0
  200. package/test/http-methods/report.test.js +142 -0
  201. package/test/http-methods/search.test.js +233 -0
  202. package/test/http-methods/trace.test.js +21 -0
  203. package/test/http-methods/unlock.test.js +38 -0
  204. package/test/http2/closing.test.js +270 -0
  205. package/test/http2/constraint.test.js +109 -0
  206. package/test/http2/head.test.js +34 -0
  207. package/test/http2/plain.test.js +68 -0
  208. package/test/http2/secure-with-fallback.test.js +113 -0
  209. package/test/http2/secure.test.js +67 -0
  210. package/test/http2/unknown-http-method.test.js +34 -0
  211. package/test/https/custom-https-server.test.js +58 -0
  212. package/test/https/https.test.js +136 -0
  213. package/test/imports.test.js +17 -0
  214. package/test/inject.test.js +502 -0
  215. package/test/input-validation.js +335 -0
  216. package/test/internals/all.test.js +38 -0
  217. package/test/internals/content-type-parser.test.js +111 -0
  218. package/test/internals/context.test.js +31 -0
  219. package/test/internals/decorator.test.js +156 -0
  220. package/test/internals/errors.test.js +982 -0
  221. package/test/internals/handle-request.test.js +270 -0
  222. package/test/internals/hook-runner.test.js +449 -0
  223. package/test/internals/hooks.test.js +96 -0
  224. package/test/internals/initial-config.test.js +383 -0
  225. package/test/internals/logger.test.js +163 -0
  226. package/test/internals/plugin.test.js +170 -0
  227. package/test/internals/promise.test.js +63 -0
  228. package/test/internals/reply-serialize.test.js +714 -0
  229. package/test/internals/reply.test.js +1920 -0
  230. package/test/internals/req-id-gen-factory.test.js +133 -0
  231. package/test/internals/request-validate.test.js +1402 -0
  232. package/test/internals/request.test.js +506 -0
  233. package/test/internals/schema-controller-perf.test.js +40 -0
  234. package/test/internals/server.test.js +91 -0
  235. package/test/internals/validation.test.js +352 -0
  236. package/test/issue-4959.test.js +118 -0
  237. package/test/keep-alive-timeout.test.js +42 -0
  238. package/test/listen.1.test.js +154 -0
  239. package/test/listen.2.test.js +113 -0
  240. package/test/listen.3.test.js +83 -0
  241. package/test/listen.4.test.js +168 -0
  242. package/test/listen.5.test.js +122 -0
  243. package/test/logger/instantiation.test.js +341 -0
  244. package/test/logger/logger-test-utils.js +47 -0
  245. package/test/logger/logging.test.js +460 -0
  246. package/test/logger/options.test.js +579 -0
  247. package/test/logger/request.test.js +292 -0
  248. package/test/logger/response.test.js +183 -0
  249. package/test/logger/tap-parallel-not-ok +0 -0
  250. package/test/max-requests-per-socket.test.js +113 -0
  251. package/test/middleware.test.js +37 -0
  252. package/test/noop-set.test.js +19 -0
  253. package/test/nullable-validation.test.js +187 -0
  254. package/test/options.error-handler.test.js +5 -0
  255. package/test/options.test.js +5 -0
  256. package/test/output-validation.test.js +140 -0
  257. package/test/patch.error-handler.test.js +5 -0
  258. package/test/patch.test.js +5 -0
  259. package/test/plugin.1.test.js +230 -0
  260. package/test/plugin.2.test.js +314 -0
  261. package/test/plugin.3.test.js +287 -0
  262. package/test/plugin.4.test.js +504 -0
  263. package/test/plugin.helper.js +8 -0
  264. package/test/plugin.name.display.js +10 -0
  265. package/test/post-empty-body.test.js +38 -0
  266. package/test/pretty-print.test.js +366 -0
  267. package/test/promises.test.js +125 -0
  268. package/test/proto-poisoning.test.js +145 -0
  269. package/test/put.error-handler.test.js +5 -0
  270. package/test/put.test.js +5 -0
  271. package/test/register.test.js +184 -0
  272. package/test/reply-code.test.js +148 -0
  273. package/test/reply-early-hints.test.js +100 -0
  274. package/test/reply-error.test.js +815 -0
  275. package/test/reply-trailers.test.js +445 -0
  276. package/test/reply-web-stream-locked.test.js +37 -0
  277. package/test/request-error.test.js +624 -0
  278. package/test/request-header-host.test.js +339 -0
  279. package/test/request-id.test.js +118 -0
  280. package/test/request-timeout.test.js +53 -0
  281. package/test/route-hooks.test.js +635 -0
  282. package/test/route-prefix.test.js +904 -0
  283. package/test/route-shorthand.test.js +48 -0
  284. package/test/route.1.test.js +259 -0
  285. package/test/route.2.test.js +100 -0
  286. package/test/route.3.test.js +213 -0
  287. package/test/route.4.test.js +127 -0
  288. package/test/route.5.test.js +211 -0
  289. package/test/route.6.test.js +306 -0
  290. package/test/route.7.test.js +406 -0
  291. package/test/route.8.test.js +225 -0
  292. package/test/router-options.test.js +1108 -0
  293. package/test/same-shape.test.js +124 -0
  294. package/test/schema-examples.test.js +661 -0
  295. package/test/schema-feature.test.js +2198 -0
  296. package/test/schema-serialization.test.js +1171 -0
  297. package/test/schema-special-usage.test.js +1348 -0
  298. package/test/schema-validation.test.js +1572 -0
  299. package/test/scripts/validate-ecosystem-links.test.js +339 -0
  300. package/test/serialize-response.test.js +186 -0
  301. package/test/server.test.js +347 -0
  302. package/test/set-error-handler.test.js +69 -0
  303. package/test/skip-reply-send.test.js +317 -0
  304. package/test/stream-serializers.test.js +40 -0
  305. package/test/stream.1.test.js +94 -0
  306. package/test/stream.2.test.js +129 -0
  307. package/test/stream.3.test.js +198 -0
  308. package/test/stream.4.test.js +176 -0
  309. package/test/stream.5.test.js +188 -0
  310. package/test/sync-routes.test.js +32 -0
  311. package/test/throw.test.js +359 -0
  312. package/test/toolkit.js +63 -0
  313. package/test/trust-proxy.test.js +162 -0
  314. package/test/type-provider.test.js +22 -0
  315. package/test/types/content-type-parser.test-d.ts +72 -0
  316. package/test/types/decorate-request-reply.test-d.ts +18 -0
  317. package/test/types/dummy-plugin.ts +9 -0
  318. package/test/types/errors.test-d.ts +90 -0
  319. package/test/types/fastify.test-d.ts +352 -0
  320. package/test/types/hooks.test-d.ts +550 -0
  321. package/test/types/import.ts +2 -0
  322. package/test/types/instance.test-d.ts +588 -0
  323. package/test/types/logger.test-d.ts +277 -0
  324. package/test/types/plugin.test-d.ts +97 -0
  325. package/test/types/register.test-d.ts +237 -0
  326. package/test/types/reply.test-d.ts +254 -0
  327. package/test/types/request.test-d.ts +188 -0
  328. package/test/types/route.test-d.ts +553 -0
  329. package/test/types/schema.test-d.ts +135 -0
  330. package/test/types/serverFactory.test-d.ts +37 -0
  331. package/test/types/type-provider.test-d.ts +1213 -0
  332. package/test/types/using.test-d.ts +17 -0
  333. package/test/upgrade.test.js +52 -0
  334. package/test/url-rewriting.test.js +122 -0
  335. package/test/use-semicolon-delimiter.test.js +168 -0
  336. package/test/validation-error-handling.test.js +900 -0
  337. package/test/versioned-routes.test.js +603 -0
  338. package/test/web-api.test.js +616 -0
  339. package/test/wrap-thenable.test.js +30 -0
  340. package/types/content-type-parser.d.ts +75 -0
  341. package/types/context.d.ts +22 -0
  342. package/types/errors.d.ts +92 -0
  343. package/types/hooks.d.ts +875 -0
  344. package/types/instance.d.ts +609 -0
  345. package/types/logger.d.ts +107 -0
  346. package/types/plugin.d.ts +44 -0
  347. package/types/register.d.ts +42 -0
  348. package/types/reply.d.ts +81 -0
  349. package/types/request.d.ts +95 -0
  350. package/types/route.d.ts +199 -0
  351. package/types/schema.d.ts +61 -0
  352. package/types/server-factory.d.ts +19 -0
  353. package/types/type-provider.d.ts +130 -0
  354. package/types/utils.d.ts +98 -0
@@ -0,0 +1,280 @@
1
+ 'use strict'
2
+
3
+ const {
4
+ kSchemaHeaders: headersSchema,
5
+ kSchemaParams: paramsSchema,
6
+ kSchemaQuerystring: querystringSchema,
7
+ kSchemaBody: bodySchema,
8
+ kSchemaResponse: responseSchema
9
+ } = require('./symbols')
10
+ const scChecker = /^[1-5](?:\d{2}|xx)$|^default$/
11
+
12
+ const {
13
+ FST_ERR_SCH_RESPONSE_SCHEMA_NOT_NESTED_2XX
14
+ } = require('./errors')
15
+
16
+ const { FSTWRN001 } = require('./warnings')
17
+
18
+ function compileSchemasForSerialization (context, compile) {
19
+ if (!context.schema || !context.schema.response) {
20
+ return
21
+ }
22
+ const { method, url } = context.config || {}
23
+ context[responseSchema] = Object.keys(context.schema.response)
24
+ .reduce(function (acc, statusCode) {
25
+ const schema = context.schema.response[statusCode]
26
+ statusCode = statusCode.toLowerCase()
27
+ if (!scChecker.test(statusCode)) {
28
+ throw new FST_ERR_SCH_RESPONSE_SCHEMA_NOT_NESTED_2XX()
29
+ }
30
+
31
+ if (schema.content) {
32
+ const contentTypesSchemas = {}
33
+ for (const mediaName of Object.keys(schema.content)) {
34
+ const contentSchema = schema.content[mediaName].schema
35
+ contentTypesSchemas[mediaName] = compile({
36
+ schema: contentSchema,
37
+ url,
38
+ method,
39
+ httpStatus: statusCode,
40
+ contentType: mediaName
41
+ })
42
+ }
43
+ acc[statusCode] = contentTypesSchemas
44
+ } else {
45
+ acc[statusCode] = compile({
46
+ schema,
47
+ url,
48
+ method,
49
+ httpStatus: statusCode
50
+ })
51
+ }
52
+
53
+ return acc
54
+ }, {})
55
+ }
56
+
57
+ function compileSchemasForValidation (context, compile, isCustom) {
58
+ const { schema } = context
59
+ if (!schema) {
60
+ return
61
+ }
62
+
63
+ const { method, url } = context.config || {}
64
+
65
+ const headers = schema.headers
66
+ // the or part is used for backward compatibility
67
+ if (headers && (isCustom || Object.getPrototypeOf(headers) !== Object.prototype)) {
68
+ // do not mess with schema when custom validator applied, e.g. Joi, Typebox
69
+ context[headersSchema] = compile({ schema: headers, method, url, httpPart: 'headers' })
70
+ } else if (headers) {
71
+ // The header keys are case insensitive
72
+ // https://datatracker.ietf.org/doc/html/rfc2616#section-4.2
73
+ const headersSchemaLowerCase = {}
74
+ Object.keys(headers).forEach(k => { headersSchemaLowerCase[k] = headers[k] })
75
+ if (headersSchemaLowerCase.required instanceof Array) {
76
+ headersSchemaLowerCase.required = headersSchemaLowerCase.required.map(h => h.toLowerCase())
77
+ }
78
+ if (headers.properties) {
79
+ headersSchemaLowerCase.properties = {}
80
+ Object.keys(headers.properties).forEach(k => {
81
+ headersSchemaLowerCase.properties[k.toLowerCase()] = headers.properties[k]
82
+ })
83
+ }
84
+ context[headersSchema] = compile({ schema: headersSchemaLowerCase, method, url, httpPart: 'headers' })
85
+ } else if (Object.hasOwn(schema, 'headers')) {
86
+ FSTWRN001('headers', method, url)
87
+ }
88
+
89
+ if (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')) {
102
+ FSTWRN001('body', method, url)
103
+ }
104
+
105
+ if (schema.querystring) {
106
+ context[querystringSchema] = compile({ schema: schema.querystring, method, url, httpPart: 'querystring' })
107
+ } else if (Object.hasOwn(schema, 'querystring')) {
108
+ FSTWRN001('querystring', method, url)
109
+ }
110
+
111
+ if (schema.params) {
112
+ context[paramsSchema] = compile({ schema: schema.params, method, url, httpPart: 'params' })
113
+ } else if (Object.hasOwn(schema, 'params')) {
114
+ FSTWRN001('params', method, url)
115
+ }
116
+ }
117
+
118
+ function validateParam (validatorFunction, request, paramName) {
119
+ const isUndefined = request[paramName] === undefined
120
+ let ret
121
+
122
+ try {
123
+ ret = validatorFunction?.(isUndefined ? null : request[paramName])
124
+ } catch (err) {
125
+ // If validator throws synchronously, ensure it propagates as an internal error
126
+ err.statusCode = 500
127
+ return err
128
+ }
129
+
130
+ if (ret && typeof ret.then === 'function') {
131
+ return ret
132
+ .then((res) => { return answer(res) })
133
+ .catch(err => { return err }) // return as simple error (not throw)
134
+ }
135
+
136
+ return answer(ret)
137
+
138
+ function answer (ret) {
139
+ if (ret === false) return validatorFunction.errors
140
+ if (ret && ret.error) return ret.error
141
+ if (ret && ret.value) request[paramName] = ret.value
142
+ return false
143
+ }
144
+ }
145
+
146
+ function validate (context, request, execution) {
147
+ const runExecution = execution === undefined
148
+
149
+ if (runExecution || !execution.skipParams) {
150
+ const params = validateParam(context[paramsSchema], request, 'params')
151
+ if (params) {
152
+ if (typeof params.then !== 'function') {
153
+ return wrapValidationError(params, 'params', context.schemaErrorFormatter)
154
+ } else {
155
+ return validateAsyncParams(params, context, request)
156
+ }
157
+ }
158
+ }
159
+
160
+ if (runExecution || !execution.skipBody) {
161
+ let validatorFunction = null
162
+ if (typeof context[bodySchema] === 'function') {
163
+ validatorFunction = context[bodySchema]
164
+ } else if (context[bodySchema]) {
165
+ // TODO: add request.contentType and reuse it here
166
+ const contentType = getEssenceMediaType(request.headers['content-type'])
167
+ const contentSchema = context[bodySchema][contentType]
168
+ if (contentSchema) {
169
+ validatorFunction = contentSchema
170
+ }
171
+ }
172
+ const body = validateParam(validatorFunction, request, 'body')
173
+ if (body) {
174
+ if (typeof body.then !== 'function') {
175
+ return wrapValidationError(body, 'body', context.schemaErrorFormatter)
176
+ } else {
177
+ return validateAsyncBody(body, context, request)
178
+ }
179
+ }
180
+ }
181
+
182
+ if (runExecution || !execution.skipQuery) {
183
+ const query = validateParam(context[querystringSchema], request, 'query')
184
+ if (query) {
185
+ if (typeof query.then !== 'function') {
186
+ return wrapValidationError(query, 'querystring', context.schemaErrorFormatter)
187
+ } else {
188
+ return validateAsyncQuery(query, context, request)
189
+ }
190
+ }
191
+ }
192
+
193
+ const headers = validateParam(context[headersSchema], request, 'headers')
194
+ if (headers) {
195
+ if (typeof headers.then !== 'function') {
196
+ return wrapValidationError(headers, 'headers', context.schemaErrorFormatter)
197
+ } else {
198
+ return validateAsyncHeaders(headers, context, request)
199
+ }
200
+ }
201
+
202
+ return false
203
+ }
204
+
205
+ function validateAsyncParams (validatePromise, context, request) {
206
+ return validatePromise
207
+ .then((paramsResult) => {
208
+ if (paramsResult) {
209
+ return wrapValidationError(paramsResult, 'params', context.schemaErrorFormatter)
210
+ }
211
+
212
+ return validate(context, request, { skipParams: true })
213
+ })
214
+ }
215
+
216
+ function validateAsyncBody (validatePromise, context, request) {
217
+ return validatePromise
218
+ .then((bodyResult) => {
219
+ if (bodyResult) {
220
+ return wrapValidationError(bodyResult, 'body', context.schemaErrorFormatter)
221
+ }
222
+
223
+ return validate(context, request, { skipParams: true, skipBody: true })
224
+ })
225
+ }
226
+
227
+ function validateAsyncQuery (validatePromise, context, request) {
228
+ return validatePromise
229
+ .then((queryResult) => {
230
+ if (queryResult) {
231
+ return wrapValidationError(queryResult, 'querystring', context.schemaErrorFormatter)
232
+ }
233
+
234
+ return validate(context, request, { skipParams: true, skipBody: true, skipQuery: true })
235
+ })
236
+ }
237
+
238
+ function validateAsyncHeaders (validatePromise, context, request) {
239
+ return validatePromise
240
+ .then((headersResult) => {
241
+ if (headersResult) {
242
+ return wrapValidationError(headersResult, 'headers', context.schemaErrorFormatter)
243
+ }
244
+
245
+ return false
246
+ })
247
+ }
248
+
249
+ function wrapValidationError (result, dataVar, schemaErrorFormatter) {
250
+ if (result instanceof Error) {
251
+ result.statusCode = result.statusCode || 400
252
+ result.code = result.code || 'FST_ERR_VALIDATION'
253
+ result.validationContext = result.validationContext || dataVar
254
+ return result
255
+ }
256
+
257
+ const error = schemaErrorFormatter(result, dataVar)
258
+ error.statusCode = error.statusCode || 400
259
+ error.code = error.code || 'FST_ERR_VALIDATION'
260
+ error.validation = result
261
+ error.validationContext = dataVar
262
+ return error
263
+ }
264
+
265
+ /**
266
+ * simple function to retrieve the essence media type
267
+ * @param {string} header
268
+ * @returns {string} Mimetype string.
269
+ */
270
+ function getEssenceMediaType (header) {
271
+ if (!header) return ''
272
+ return header.split(/[ ;]/, 1)[0].trim().toLowerCase()
273
+ }
274
+
275
+ module.exports = {
276
+ symbols: { bodySchema, querystringSchema, responseSchema, paramsSchema, headersSchema },
277
+ compileSchemasForValidation,
278
+ compileSchemasForSerialization,
279
+ validate
280
+ }
@@ -0,0 +1,57 @@
1
+ 'use strict'
2
+
3
+ const { createWarning } = require('process-warning')
4
+
5
+ /**
6
+ * Deprecation codes:
7
+ * - FSTWRN001
8
+ * - FSTSEC001
9
+ * - FSTDEP022
10
+ *
11
+ * Deprecation Codes FSTDEP001 - FSTDEP021 were used by v4 and MUST NOT not be reused.
12
+ * - FSTDEP022 is used by v5 and MUST NOT be reused.
13
+ * Warning Codes FSTWRN001 - FSTWRN002 were used by v4 and MUST NOT not be reused.
14
+ */
15
+
16
+ const FSTWRN001 = createWarning({
17
+ name: 'FastifyWarning',
18
+ code: 'FSTWRN001',
19
+ message: 'The %s schema for %s: %s is missing. This may indicate the schema is not well specified.',
20
+ unlimited: true
21
+ })
22
+
23
+ const FSTWRN003 = createWarning({
24
+ name: 'FastifyWarning',
25
+ code: 'FSTWRN003',
26
+ message: 'The %s mixes async and callback styles that may lead to unhandled rejections. Please use only one of them.',
27
+ unlimited: true
28
+ })
29
+
30
+ const FSTWRN004 = createWarning({
31
+ name: 'FastifyWarning',
32
+ code: 'FSTWRN004',
33
+ message: 'It seems that you are overriding an errorHandler in the same scope, which can lead to subtle bugs.',
34
+ unlimited: true
35
+ })
36
+
37
+ const FSTSEC001 = createWarning({
38
+ name: 'FastifySecurity',
39
+ code: 'FSTSEC001',
40
+ message: 'You are using /%s/ Content-Type which may be vulnerable to CORS attack. Please make sure your RegExp start with "^" or include ";?" to proper detection of the essence MIME type.',
41
+ unlimited: true
42
+ })
43
+
44
+ const FSTDEP022 = createWarning({
45
+ name: 'FastifyWarning',
46
+ code: 'FSTDEP022',
47
+ 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`.',
48
+ unlimited: true
49
+ })
50
+
51
+ module.exports = {
52
+ FSTWRN001,
53
+ FSTWRN003,
54
+ FSTWRN004,
55
+ FSTSEC001,
56
+ FSTDEP022
57
+ }
@@ -0,0 +1,84 @@
1
+ 'use strict'
2
+
3
+ const {
4
+ kReplyIsError,
5
+ kReplyHijacked
6
+ } = require('./symbols')
7
+ const { setErrorStatusCode } = require('./error-status')
8
+
9
+ const diagnostics = require('node:diagnostics_channel')
10
+ const channels = diagnostics.tracingChannel('fastify.request.handler')
11
+
12
+ function wrapThenable (thenable, reply, store) {
13
+ if (store) store.async = true
14
+ thenable.then(function (payload) {
15
+ if (reply[kReplyHijacked] === true) {
16
+ return
17
+ }
18
+
19
+ if (store) {
20
+ channels.asyncStart.publish(store)
21
+ }
22
+
23
+ try {
24
+ // this is for async functions that are using reply.send directly
25
+ //
26
+ // since wrap-thenable will be called when using reply.send directly
27
+ // without actual return. the response can be sent already or
28
+ // the request may be terminated during the reply. in this situation,
29
+ // it require an extra checking of request.aborted to see whether
30
+ // the request is killed by client.
31
+ if (payload !== undefined || //
32
+ (reply.sent === false && //
33
+ reply.raw.headersSent === false &&
34
+ reply.request.raw.aborted === false &&
35
+ reply.request.socket &&
36
+ !reply.request.socket.destroyed
37
+ )
38
+ ) {
39
+ // we use a try-catch internally to avoid adding a catch to another
40
+ // promise, increase promise perf by 10%
41
+ try {
42
+ reply.send(payload)
43
+ } catch (err) {
44
+ reply[kReplyIsError] = true
45
+ reply.send(err)
46
+ }
47
+ }
48
+ } finally {
49
+ if (store) {
50
+ channels.asyncEnd.publish(store)
51
+ }
52
+ }
53
+ }, function (err) {
54
+ if (store) {
55
+ store.error = err
56
+ // Set status code before publishing so subscribers see the correct value
57
+ setErrorStatusCode(reply, err)
58
+ channels.error.publish(store) // note that error happens before asyncStart
59
+ channels.asyncStart.publish(store)
60
+ }
61
+
62
+ try {
63
+ if (reply.sent === true) {
64
+ reply.log.error({ err }, 'Promise errored, but reply.sent = true was set')
65
+ return
66
+ }
67
+
68
+ reply[kReplyIsError] = true
69
+
70
+ reply.send(err)
71
+ // The following should not happen
72
+ /* c8 ignore next 3 */
73
+ } catch (err) {
74
+ // try-catch allow to re-throw error in error handler for async handler
75
+ reply.send(err)
76
+ } finally {
77
+ if (store) {
78
+ channels.asyncEnd.publish(store)
79
+ }
80
+ }
81
+ })
82
+ }
83
+
84
+ module.exports = wrapThenable
package/package.json ADDED
@@ -0,0 +1,225 @@
1
+ {
2
+ "name": "node-fastify",
3
+ "version": "5.8.3",
4
+ "description": "Fast and low overhead web framework, for Node.js",
5
+ "main": "fastify.js",
6
+ "type": "commonjs",
7
+ "types": "fastify.d.ts",
8
+ "scripts": {
9
+ "bench": "branchcmp -r 2 -g -s \"npm run benchmark\"",
10
+ "benchmark": "concurrently -k -s first \"node ./examples/benchmark/simple.js\" \"autocannon -c 100 -d 30 -p 10 localhost:3000/\"",
11
+ "benchmark:parser": "concurrently -k -s first \"node ./examples/benchmark/parser.js\" \"autocannon -c 100 -d 30 -p 10 -i ./examples/benchmark/body.json -H \"content-type:application/jsoff\" -m POST localhost:3000/\"",
12
+ "benchmark:parser:error": "concurrently -k -s first \"node ./examples/benchmark/parser.js\" \"autocannon -c 100 -d 30 -p 10 -i ./examples/benchmark/body.json -H \"content-type:application/jsoff\" -H \"content-length:123\" -m POST localhost:3000/\"",
13
+ "build:validation": "node build/build-error-serializer.js && node build/build-validation.js",
14
+ "build:sync-version": "node build/sync-version.js",
15
+ "coverage": "c8 --reporter html borp --reporter=@jsumners/line-reporter",
16
+ "coverage:ci-check-coverage": "borp --reporter=@jsumners/line-reporter --coverage --check-coverage --lines 100",
17
+ "lint": "npm run lint:eslint",
18
+ "lint:fix": "eslint --fix",
19
+ "lint:markdown": "markdownlint-cli2",
20
+ "lint:eslint": "eslint",
21
+ "prepublishOnly": "cross-env PREPUBLISH=true borp --reporter=@jsumners/line-reporter && npm run test:validator:integrity && npm run build:sync-version",
22
+ "test": "npm run lint && npm run unit && npm run test:typescript",
23
+ "test:ci": "npm run unit && npm run test:typescript",
24
+ "test:report": "npm run lint && npm run unit:report && npm run test:typescript",
25
+ "test:validator:integrity": "npm run build:validation && git diff --quiet --ignore-all-space --ignore-blank-lines --ignore-cr-at-eol lib/error-serializer.js && git diff --quiet --ignore-all-space --ignore-blank-lines --ignore-cr-at-eol lib/config-validator.js",
26
+ "test:typescript": "tsc test/types/import.ts --target es2022 --moduleResolution node16 --module node16 --noEmit && tsd",
27
+ "test:watch": "npm run unit -- --watch --coverage-report=none --reporter=terse",
28
+ "unit": "borp",
29
+ "unit:report": "c8 --reporter html borp --reporter=@jsumners/line-reporter",
30
+ "citgm": "borp --reporter=@jsumners/line-reporter --coverage --check-coverage --concurrency=1"
31
+ },
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/fastify/fastify.git"
35
+ },
36
+ "keywords": [
37
+ "web",
38
+ "framework",
39
+ "json",
40
+ "schema",
41
+ "open",
42
+ "api"
43
+ ],
44
+ "author": "Matteo Collina <hello@matteocollina.com>",
45
+ "contributors": [
46
+ {
47
+ "name": "Tomas Della Vedova",
48
+ "url": "http://delved.org",
49
+ "author": true
50
+ },
51
+ {
52
+ "name": "Tommaso Allevi",
53
+ "email": "tomallevi@gmail.com"
54
+ },
55
+ {
56
+ "name": "Ethan Arrowood",
57
+ "url": "https://github.com/Ethan-Arrowood",
58
+ "email": "ethan.arrowood@gmail.com"
59
+ },
60
+ {
61
+ "name": "Dustin Deus",
62
+ "url": "http://starptech.de",
63
+ "email": "deusdustin@gmail.com"
64
+ },
65
+ {
66
+ "name": "Ayoub El Khattabi",
67
+ "url": "https://github.com/AyoubElk",
68
+ "email": "elkhattabi.ayoub@gmail.com"
69
+ },
70
+ {
71
+ "name": "Rafael Gonzaga",
72
+ "email": "rafael.nunu@hotmail.com",
73
+ "url": "https://github.com/rafaelgss"
74
+ },
75
+ {
76
+ "name": "Trivikram Kamat",
77
+ "url": "http://trivikr.github.io",
78
+ "email": "trivikr.dev@gmail.com"
79
+ },
80
+ {
81
+ "name": "Luciano Mammino",
82
+ "url": "https://loige.co"
83
+ },
84
+ {
85
+ "name": "Cemre Mengu",
86
+ "email": "cemremengu@gmail.com"
87
+ },
88
+ {
89
+ "name": "Evan Shortiss",
90
+ "email": "evanshortiss@gmail.com"
91
+ },
92
+ {
93
+ "name": "Maksim Sinik",
94
+ "url": "https://maksim.dev"
95
+ },
96
+ {
97
+ "name": "Manuel Spigolon",
98
+ "email": "behemoth89@gmail.com"
99
+ },
100
+ {
101
+ "name": "James Sumners",
102
+ "url": "https://james.sumners.info"
103
+ },
104
+ {
105
+ "name": "Denis Fäcke",
106
+ "url": "https://github.com/SerayaEryn"
107
+ },
108
+ {
109
+ "name": "Igor Savin",
110
+ "email": "kibertoad@gmail.com",
111
+ "url": "https://github.com/kibertoad"
112
+ },
113
+ {
114
+ "name": "Vincent Le Goff",
115
+ "email": "vince.legoff@gmail.com",
116
+ "url": "https://github.com/zekth"
117
+ },
118
+ {
119
+ "name": "Luis Orbaiceta",
120
+ "email": "luisorbaiceta@gmail.com",
121
+ "url": "https://luisorbaiceta.com"
122
+ },
123
+ {
124
+ "name": "Carlos Fuentes",
125
+ "email": "me@metcoder.dev",
126
+ "url": "https://metcoder.dev"
127
+ },
128
+ {
129
+ "name": "Gürgün Dayıoğlu",
130
+ "email": "hey@gurgun.day",
131
+ "url": "https://heyhey.to/G"
132
+ },
133
+ {
134
+ "name": "Aras Abbasi",
135
+ "email": "aras.abbasi@gmail.com"
136
+ },
137
+ {
138
+ "name": "Frazer Smith",
139
+ "email": "frazer.dev@icloud.com",
140
+ "url": "https://github.com/fdawgs"
141
+ },
142
+ {
143
+ "name": "KaKa Ng",
144
+ "email": "kaka@kakang.dev",
145
+ "url": "https://github.com/climba03003"
146
+ },
147
+ {
148
+ "name": "Jean Michelet",
149
+ "email": "jean.antoine.michelet@gmail.com",
150
+ "url": "https://github.com/jean-michelet"
151
+ }
152
+ ],
153
+ "license": "MIT",
154
+ "bugs": {
155
+ "url": "https://github.com/fastify/fastify/issues"
156
+ },
157
+ "homepage": "https://fastify.dev/",
158
+ "funding": [
159
+ {
160
+ "type": "github",
161
+ "url": "https://github.com/sponsors/fastify"
162
+ },
163
+ {
164
+ "type": "opencollective",
165
+ "url": "https://opencollective.com/fastify"
166
+ }
167
+ ],
168
+ "devDependencies": {
169
+ "@jsumners/line-reporter": "^1.0.1",
170
+ "@sinonjs/fake-timers": "^11.2.2",
171
+ "@stylistic/eslint-plugin": "^5.1.0",
172
+ "@stylistic/eslint-plugin-js": "^4.1.0",
173
+ "@types/node": "^25.0.3",
174
+ "ajv": "^8.12.0",
175
+ "ajv-errors": "^3.0.0",
176
+ "ajv-formats": "^3.0.1",
177
+ "ajv-i18n": "^4.2.0",
178
+ "ajv-merge-patch": "^5.0.1",
179
+ "autocannon": "^8.0.0",
180
+ "borp": "^0.21.0",
181
+ "branch-comparer": "^1.1.0",
182
+ "concurrently": "^9.1.2",
183
+ "cross-env": "^10.0.0",
184
+ "eslint": "^9.0.0",
185
+ "fast-json-body": "^1.1.0",
186
+ "fastify-plugin": "^5.0.0",
187
+ "fluent-json-schema": "^6.0.0",
188
+ "h2url": "^0.2.0",
189
+ "http-errors": "^2.0.0",
190
+ "joi": "^18.0.1",
191
+ "json-schema-to-ts": "^3.0.1",
192
+ "JSONStream": "^1.3.5",
193
+ "markdownlint-cli2": "^0.21.0",
194
+ "neostandard": "^0.12.0",
195
+ "node-forge": "^1.3.1",
196
+ "proxyquire": "^2.1.3",
197
+ "split2": "^4.2.0",
198
+ "tsd": "^0.33.0",
199
+ "typebox": "^1.0.81",
200
+ "typescript": "~5.9.2",
201
+ "undici": "^7.11.0",
202
+ "vary": "^1.1.2",
203
+ "yup": "^1.4.0"
204
+ },
205
+ "dependencies": {
206
+ "@fastify/ajv-compiler": "^4.0.5",
207
+ "@fastify/error": "^4.0.0",
208
+ "@fastify/fast-json-stringify-compiler": "^5.0.0",
209
+ "@fastify/proxy-addr": "^5.0.0",
210
+ "abstract-logging": "^2.0.1",
211
+ "avvio": "^9.0.0",
212
+ "fast-json-stringify": "^6.0.0",
213
+ "find-my-way": "^9.0.0",
214
+ "light-my-request": "^6.0.0",
215
+ "pino": "^9.14.0 || ^10.1.0",
216
+ "process-warning": "^5.0.0",
217
+ "rfdc": "^1.3.1",
218
+ "secure-json-parse": "^4.0.0",
219
+ "semver": "^7.6.0",
220
+ "toad-cache": "^3.7.0"
221
+ },
222
+ "tsd": {
223
+ "directory": "test/types"
224
+ }
225
+ }