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,335 @@
1
+ 'use strict'
2
+
3
+ const Ajv = require('ajv')
4
+ const Joi = require('joi')
5
+ const yup = require('yup')
6
+ const assert = require('node:assert')
7
+
8
+ module.exports.payloadMethod = function (method, t) {
9
+ const test = t.test
10
+ const fastify = require('..')()
11
+ const upMethod = method.toUpperCase()
12
+ const loMethod = method.toLowerCase()
13
+
14
+ const opts = {
15
+ schema: {
16
+ body: {
17
+ type: 'object',
18
+ properties: {
19
+ hello: {
20
+ type: 'integer'
21
+ }
22
+ }
23
+ }
24
+ }
25
+ }
26
+
27
+ const ajv = new Ajv({ coerceTypes: true, removeAdditional: true })
28
+ const optsWithCustomValidator = {
29
+ schema: {
30
+ body: {
31
+ type: 'object',
32
+ properties: {
33
+ hello: {
34
+ type: 'integer'
35
+ }
36
+ },
37
+ additionalProperties: false
38
+ }
39
+ },
40
+ validatorCompiler: function ({ schema, method, url, httpPart }) {
41
+ return ajv.compile(schema)
42
+ }
43
+ }
44
+
45
+ const optsWithJoiValidator = {
46
+ schema: {
47
+ body: Joi.object().keys({
48
+ hello: Joi.string().required()
49
+ }).required()
50
+ },
51
+ validatorCompiler: function ({ schema, method, url, httpPart }) {
52
+ return schema.validate.bind(schema)
53
+ }
54
+ }
55
+
56
+ const yupOptions = {
57
+ strict: true, // don't coerce
58
+ abortEarly: false, // return all errors
59
+ stripUnknown: true, // remove additional properties
60
+ recursive: true
61
+ }
62
+
63
+ const optsWithYupValidator = {
64
+ schema: {
65
+ body: yup.object().shape({
66
+ hello: yup.string().required()
67
+ }).required()
68
+ },
69
+ validatorCompiler: function ({ schema, method, url, httpPart }) {
70
+ return data => {
71
+ try {
72
+ const result = schema.validateSync(data, yupOptions)
73
+ return { value: result }
74
+ } catch (e) {
75
+ return { error: [e] }
76
+ }
77
+ }
78
+ }
79
+ }
80
+
81
+ test(`${upMethod} can be created`, t => {
82
+ t.plan(1)
83
+ try {
84
+ fastify[loMethod]('/', opts, function (req, reply) {
85
+ reply.send(req.body)
86
+ })
87
+ fastify[loMethod]('/custom', optsWithCustomValidator, function (req, reply) {
88
+ reply.send(req.body)
89
+ })
90
+ fastify[loMethod]('/joi', optsWithJoiValidator, function (req, reply) {
91
+ reply.send(req.body)
92
+ })
93
+ fastify[loMethod]('/yup', optsWithYupValidator, function (req, reply) {
94
+ reply.send(req.body)
95
+ })
96
+
97
+ fastify.register(function (fastify2, opts, done) {
98
+ fastify2.setValidatorCompiler(function schema ({ schema, method, url, httpPart }) {
99
+ return body => ({ error: new Error('From custom schema compiler!') })
100
+ })
101
+ const withInstanceCustomCompiler = {
102
+ schema: {
103
+ body: {
104
+ type: 'object',
105
+ properties: { },
106
+ additionalProperties: false
107
+ }
108
+ }
109
+ }
110
+ fastify2[loMethod]('/plugin', withInstanceCustomCompiler, (req, reply) => reply.send({ hello: 'never here!' }))
111
+
112
+ const optsWithCustomValidator2 = {
113
+ schema: {
114
+ body: {
115
+ type: 'object',
116
+ properties: { },
117
+ additionalProperties: false
118
+ }
119
+ },
120
+ validatorCompiler: function ({ schema, method, url, httpPart }) {
121
+ return function (body) {
122
+ return { error: new Error('Always fail!') }
123
+ }
124
+ }
125
+ }
126
+ fastify2[loMethod]('/plugin/custom', optsWithCustomValidator2, (req, reply) => reply.send({ hello: 'never here!' }))
127
+
128
+ done()
129
+ })
130
+ t.assert.ok(true)
131
+ } catch (e) {
132
+ t.assert.fail()
133
+ }
134
+ })
135
+
136
+ fastify.listen({ port: 0 }, function (err) {
137
+ assert.ifError(err)
138
+
139
+ t.after(() => { fastify.close() })
140
+
141
+ test(`${upMethod} - correctly replies`, async (t) => {
142
+ if (upMethod === 'HEAD') {
143
+ t.plan(2)
144
+ const result = await fetch('http://localhost:' + fastify.server.address().port, {
145
+ method: upMethod
146
+ })
147
+ t.assert.ok(result.ok)
148
+ t.assert.strictEqual(result.status, 200)
149
+ } else {
150
+ t.plan(3)
151
+
152
+ const result = await fetch('http://localhost:' + fastify.server.address().port, {
153
+ method: upMethod,
154
+ body: JSON.stringify({ hello: 42 }),
155
+ headers: {
156
+ 'Content-Type': 'application/json'
157
+ }
158
+ })
159
+
160
+ t.assert.ok(result.ok)
161
+ t.assert.strictEqual(result.status, 200)
162
+ t.assert.deepStrictEqual(await result.json(), { hello: 42 })
163
+ }
164
+ })
165
+
166
+ test(`${upMethod} - 400 on bad parameters`, async (t) => {
167
+ t.plan(3)
168
+
169
+ const result = await fetch('http://localhost:' + fastify.server.address().port, {
170
+ method: upMethod,
171
+ body: JSON.stringify({ hello: 'world' }),
172
+ headers: {
173
+ 'Content-Type': 'application/json'
174
+ }
175
+ })
176
+
177
+ t.assert.ok(!result.ok)
178
+ t.assert.strictEqual(result.status, 400)
179
+ t.assert.deepStrictEqual(await result.json(), {
180
+ error: 'Bad Request',
181
+ message: 'body/hello must be integer',
182
+ statusCode: 400,
183
+ code: 'FST_ERR_VALIDATION'
184
+ })
185
+ })
186
+
187
+ test(`${upMethod} - input-validation coerce`, async (t) => {
188
+ t.plan(3)
189
+
190
+ const restult = await fetch('http://localhost:' + fastify.server.address().port, {
191
+ method: upMethod,
192
+ body: JSON.stringify({ hello: '42' }),
193
+ headers: {
194
+ 'Content-Type': 'application/json'
195
+ }
196
+ })
197
+
198
+ t.assert.ok(restult.ok)
199
+ t.assert.strictEqual(restult.status, 200)
200
+ t.assert.deepStrictEqual(await restult.json(), { hello: 42 })
201
+ })
202
+
203
+ test(`${upMethod} - input-validation custom schema compiler`, async (t) => {
204
+ t.plan(3)
205
+
206
+ const result = await fetch('http://localhost:' + fastify.server.address().port + '/custom', {
207
+ method: upMethod,
208
+ body: JSON.stringify({ hello: '42', world: 55 }),
209
+ headers: {
210
+ 'Content-Type': 'application/json'
211
+ }
212
+ })
213
+
214
+ t.assert.ok(result.ok)
215
+ t.assert.strictEqual(result.status, 200)
216
+ t.assert.deepStrictEqual(await result.json(), { hello: 42 })
217
+ })
218
+
219
+ test(`${upMethod} - input-validation joi schema compiler ok`, async (t) => {
220
+ t.plan(3)
221
+
222
+ const result = await fetch('http://localhost:' + fastify.server.address().port + '/joi', {
223
+ method: upMethod,
224
+ body: JSON.stringify({ hello: '42' }),
225
+ headers: {
226
+ 'Content-Type': 'application/json'
227
+ }
228
+ })
229
+
230
+ t.assert.ok(result.ok)
231
+ t.assert.strictEqual(result.status, 200)
232
+ t.assert.deepStrictEqual(await result.json(), { hello: '42' })
233
+ })
234
+
235
+ test(`${upMethod} - input-validation joi schema compiler ko`, async (t) => {
236
+ t.plan(3)
237
+
238
+ const result = await fetch('http://localhost:' + fastify.server.address().port + '/joi', {
239
+ method: upMethod,
240
+ body: JSON.stringify({ hello: 44 }),
241
+ headers: {
242
+ 'Content-Type': 'application/json'
243
+ }
244
+ })
245
+
246
+ t.assert.ok(!result.ok)
247
+ t.assert.strictEqual(result.status, 400)
248
+ t.assert.deepStrictEqual(await result.json(), {
249
+ error: 'Bad Request',
250
+ message: '"hello" must be a string',
251
+ statusCode: 400,
252
+ code: 'FST_ERR_VALIDATION'
253
+ })
254
+ })
255
+
256
+ test(`${upMethod} - input-validation yup schema compiler ok`, async (t) => {
257
+ t.plan(3)
258
+
259
+ const result = await fetch('http://localhost:' + fastify.server.address().port + '/yup', {
260
+ method: upMethod,
261
+ body: JSON.stringify({ hello: '42' }),
262
+ headers: {
263
+ 'Content-Type': 'application/json'
264
+ }
265
+ })
266
+
267
+ t.assert.ok(result.ok)
268
+ t.assert.strictEqual(result.status, 200)
269
+ t.assert.deepStrictEqual(await result.json(), { hello: '42' })
270
+ })
271
+
272
+ test(`${upMethod} - input-validation yup schema compiler ko`, async (t) => {
273
+ t.plan(3)
274
+
275
+ const result = await fetch('http://localhost:' + fastify.server.address().port + '/yup', {
276
+ method: upMethod,
277
+ body: JSON.stringify({ hello: 44 }),
278
+ headers: {
279
+ 'Content-Type': 'application/json'
280
+ }
281
+ })
282
+
283
+ t.assert.ok(!result.ok)
284
+ t.assert.strictEqual(result.status, 400)
285
+ t.assert.deepStrictEqual(await result.json(), {
286
+ error: 'Bad Request',
287
+ message: 'body hello must be a `string` type, but the final value was: `44`.',
288
+ statusCode: 400,
289
+ code: 'FST_ERR_VALIDATION'
290
+ })
291
+ })
292
+
293
+ test(`${upMethod} - input-validation instance custom schema compiler encapsulated`, async (t) => {
294
+ t.plan(3)
295
+
296
+ const result = await fetch('http://localhost:' + fastify.server.address().port + '/plugin', {
297
+ method: upMethod,
298
+ body: JSON.stringify({}),
299
+ headers: {
300
+ 'Content-Type': 'application/json'
301
+ }
302
+ })
303
+
304
+ t.assert.ok(!result.ok)
305
+ t.assert.strictEqual(result.status, 400)
306
+ t.assert.deepStrictEqual(await result.json(), {
307
+ error: 'Bad Request',
308
+ message: 'From custom schema compiler!',
309
+ statusCode: 400,
310
+ code: 'FST_ERR_VALIDATION'
311
+ })
312
+ })
313
+
314
+ test(`${upMethod} - input-validation custom schema compiler encapsulated`, async (t) => {
315
+ t.plan(3)
316
+
317
+ const result = await fetch('http://localhost:' + fastify.server.address().port + '/plugin/custom', {
318
+ method: upMethod,
319
+ body: JSON.stringify({}),
320
+ headers: {
321
+ 'Content-Type': 'application/json'
322
+ }
323
+ })
324
+
325
+ t.assert.ok(!result.ok)
326
+ t.assert.strictEqual(result.status, 400)
327
+ t.assert.deepStrictEqual(await result.json(), {
328
+ error: 'Bad Request',
329
+ message: 'Always fail!',
330
+ statusCode: 400,
331
+ code: 'FST_ERR_VALIDATION'
332
+ })
333
+ })
334
+ })
335
+ }
@@ -0,0 +1,38 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('node:test')
4
+ const Fastify = require('../..')
5
+
6
+ test('fastify.all should add all the methods to the same url', async t => {
7
+ const fastify = Fastify()
8
+
9
+ const requirePayload = [
10
+ 'POST',
11
+ 'PUT',
12
+ 'PATCH'
13
+ ]
14
+
15
+ const supportedMethods = fastify.supportedMethods
16
+ t.plan(supportedMethods.length)
17
+
18
+ fastify.all('/', (req, reply) => {
19
+ reply.send({ method: req.raw.method })
20
+ })
21
+
22
+ await Promise.all(supportedMethods.map(async method => injectRequest(method)))
23
+
24
+ async function injectRequest (method) {
25
+ const options = {
26
+ url: '/',
27
+ method
28
+ }
29
+
30
+ if (requirePayload.includes(method)) {
31
+ options.payload = { hello: 'world' }
32
+ }
33
+
34
+ const res = await fastify.inject(options)
35
+ const payload = JSON.parse(res.payload)
36
+ t.assert.deepStrictEqual(payload, { method })
37
+ }
38
+ })
@@ -0,0 +1,111 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('node:test')
4
+ const proxyquire = require('proxyquire')
5
+ const { Readable } = require('node:stream')
6
+ const { kTestInternals, kRouteContext } = require('../../lib/symbols')
7
+ const Request = require('../../lib/request')
8
+ const Reply = require('../../lib/reply')
9
+
10
+ test('rawBody function', t => {
11
+ t.plan(2)
12
+
13
+ const internals = require('../../lib/content-type-parser')[kTestInternals]
14
+ const body = Buffer.from('你好 世界')
15
+ const parser = {
16
+ asString: true,
17
+ asBuffer: false,
18
+ fn (req, bodyInString, done) {
19
+ t.assert.strictEqual(bodyInString, body.toString())
20
+ t.assert.strictEqual(typeof done, 'function')
21
+ return {
22
+ then (cb) {
23
+ cb()
24
+ }
25
+ }
26
+ }
27
+ }
28
+ const res = {}
29
+ res.end = () => { }
30
+ res.writeHead = () => { }
31
+
32
+ res.log = { error: () => { }, info: () => { } }
33
+ const context = {
34
+ Reply,
35
+ Request,
36
+ preHandler: [],
37
+ onSend: [],
38
+ _parserOptions: {
39
+ limit: 1024
40
+ }
41
+ }
42
+ const rs = new Readable()
43
+ rs._read = function () { }
44
+ rs.headers = { 'content-length': body.length }
45
+ const request = new Request('id', 'params', rs, 'query', 'log', context)
46
+ const reply = new Reply(res, request)
47
+ const done = () => { }
48
+
49
+ internals.rawBody(
50
+ request,
51
+ reply,
52
+ reply[kRouteContext]._parserOptions,
53
+ parser,
54
+ done
55
+ )
56
+ rs.emit('data', body.toString())
57
+ rs.emit('end')
58
+ })
59
+
60
+ test('Should support Webpack and faux modules', t => {
61
+ t.plan(2)
62
+
63
+ const internals = proxyquire('../../lib/content-type-parser', {
64
+ 'toad-cache': { default: () => { } }
65
+ })[kTestInternals]
66
+
67
+ const body = Buffer.from('你好 世界')
68
+ const parser = {
69
+ asString: true,
70
+ asBuffer: false,
71
+ fn (req, bodyInString, done) {
72
+ t.assert.strictEqual(bodyInString, body.toString())
73
+ t.assert.strictEqual(typeof done, 'function')
74
+ return {
75
+ then (cb) {
76
+ cb()
77
+ }
78
+ }
79
+ }
80
+ }
81
+ const res = {}
82
+ res.end = () => { }
83
+ res.writeHead = () => { }
84
+
85
+ res.log = { error: () => { }, info: () => { } }
86
+ const context = {
87
+ Reply,
88
+ Request,
89
+ preHandler: [],
90
+ onSend: [],
91
+ _parserOptions: {
92
+ limit: 1024
93
+ }
94
+ }
95
+ const rs = new Readable()
96
+ rs._read = function () { }
97
+ rs.headers = { 'content-length': body.length }
98
+ const request = new Request('id', 'params', rs, 'query', 'log', context)
99
+ const reply = new Reply(res, request)
100
+ const done = () => { }
101
+
102
+ internals.rawBody(
103
+ request,
104
+ reply,
105
+ reply[kRouteContext]._parserOptions,
106
+ parser,
107
+ done
108
+ )
109
+ rs.emit('data', body.toString())
110
+ rs.emit('end')
111
+ })
@@ -0,0 +1,31 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('node:test')
4
+ const { kRouteContext } = require('../../lib/symbols')
5
+ const Context = require('../../lib/context')
6
+
7
+ const Fastify = require('../..')
8
+
9
+ test('context', async context => {
10
+ context.plan(1)
11
+
12
+ await context.test('Should not contain undefined as key prop', async t => {
13
+ t.plan(4)
14
+ const app = Fastify()
15
+
16
+ app.get('/', (req, reply) => {
17
+ t.assert.ok(req[kRouteContext] instanceof Context)
18
+ t.assert.ok(reply[kRouteContext] instanceof Context)
19
+ t.assert.ok(!('undefined' in reply[kRouteContext]))
20
+ t.assert.ok(!('undefined' in req[kRouteContext]))
21
+
22
+ reply.send('hello world!')
23
+ })
24
+
25
+ try {
26
+ await app.inject('/')
27
+ } catch (e) {
28
+ t.assert.fail(e)
29
+ }
30
+ })
31
+ })
@@ -0,0 +1,156 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('node:test')
4
+ const decorator = require('../../lib/decorate')
5
+ const {
6
+ kState
7
+ } = require('../../lib/symbols')
8
+
9
+ test('decorate should add the given method to its instance', t => {
10
+ t.plan(1)
11
+ function build () {
12
+ server.add = decorator.add
13
+ server[kState] = {
14
+ listening: false,
15
+ closing: false,
16
+ started: false
17
+ }
18
+ return server
19
+ function server () {}
20
+ }
21
+
22
+ const server = build()
23
+ server.add('test', () => {})
24
+ t.assert.ok(server.test)
25
+ })
26
+
27
+ test('decorate is chainable', t => {
28
+ t.plan(3)
29
+ function build () {
30
+ server.add = decorator.add
31
+ server[kState] = {
32
+ listening: false,
33
+ closing: false,
34
+ started: false
35
+ }
36
+ return server
37
+ function server () {}
38
+ }
39
+
40
+ const server = build()
41
+ server
42
+ .add('test1', () => {})
43
+ .add('test2', () => {})
44
+ .add('test3', () => {})
45
+
46
+ t.assert.ok(server.test1)
47
+ t.assert.ok(server.test2)
48
+ t.assert.ok(server.test3)
49
+ })
50
+
51
+ test('checkExistence should check if a property is part of the given instance', t => {
52
+ t.plan(1)
53
+ const instance = { test: () => {} }
54
+ t.assert.ok(decorator.exist(instance, 'test'))
55
+ })
56
+
57
+ test('checkExistence should find the instance if not given', t => {
58
+ t.plan(1)
59
+ function build () {
60
+ server.add = decorator.add
61
+ server.check = decorator.exist
62
+ server[kState] = {
63
+ listening: false,
64
+ closing: false,
65
+ started: false
66
+ }
67
+ return server
68
+ function server () {}
69
+ }
70
+
71
+ const server = build()
72
+ server.add('test', () => {})
73
+ t.assert.ok(server.check('test'))
74
+ })
75
+
76
+ test('checkExistence should check the prototype as well', t => {
77
+ t.plan(1)
78
+ function Instance () {}
79
+ Instance.prototype.test = () => {}
80
+
81
+ const instance = new Instance()
82
+ t.assert.ok(decorator.exist(instance, 'test'))
83
+ })
84
+
85
+ test('checkDependencies should throw if a dependency is not present', t => {
86
+ t.plan(2)
87
+ const instance = {}
88
+ try {
89
+ decorator.dependencies(instance, 'foo', ['test'])
90
+ t.assert.fail()
91
+ } catch (e) {
92
+ t.assert.strictEqual(e.code, 'FST_ERR_DEC_MISSING_DEPENDENCY')
93
+ t.assert.strictEqual(e.message, 'The decorator is missing dependency \'test\'.')
94
+ }
95
+ })
96
+
97
+ test('decorate should internally call checkDependencies', t => {
98
+ t.plan(2)
99
+ function build () {
100
+ server.add = decorator.add
101
+ server[kState] = {
102
+ listening: false,
103
+ closing: false,
104
+ started: false
105
+ }
106
+ return server
107
+ function server () {}
108
+ }
109
+
110
+ const server = build()
111
+
112
+ try {
113
+ server.add('method', () => {}, ['test'])
114
+ t.assert.fail()
115
+ } catch (e) {
116
+ t.assert.strictEqual(e.code, 'FST_ERR_DEC_MISSING_DEPENDENCY')
117
+ t.assert.strictEqual(e.message, 'The decorator is missing dependency \'test\'.')
118
+ }
119
+ })
120
+
121
+ test('decorate should recognize getter/setter objects', t => {
122
+ t.plan(6)
123
+
124
+ const one = {
125
+ [kState]: {
126
+ listening: false,
127
+ closing: false,
128
+ started: false
129
+ }
130
+ }
131
+ decorator.add.call(one, 'foo', {
132
+ getter: () => this._a,
133
+ setter: (val) => {
134
+ t.assert.ok(true)
135
+ this._a = val
136
+ }
137
+ })
138
+ t.assert.strictEqual(Object.hasOwn(one, 'foo'), true)
139
+ t.assert.strictEqual(one.foo, undefined)
140
+ one.foo = 'a'
141
+ t.assert.strictEqual(one.foo, 'a')
142
+
143
+ // getter only
144
+ const two = {
145
+ [kState]: {
146
+ listening: false,
147
+ closing: false,
148
+ started: false
149
+ }
150
+ }
151
+ decorator.add.call(two, 'foo', {
152
+ getter: () => 'a getter'
153
+ })
154
+ t.assert.strictEqual(Object.hasOwn(two, 'foo'), true)
155
+ t.assert.strictEqual(two.foo, 'a getter')
156
+ })