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,705 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('node:test')
4
+ const Fastify = require('..')
5
+ const split = require('split2')
6
+ const pino = require('pino')
7
+ const { sleep } = require('./helper')
8
+ const statusCodes = require('node:http').STATUS_CODES
9
+
10
+ const opts = {
11
+ schema: {
12
+ response: {
13
+ '2xx': {
14
+ type: 'object',
15
+ properties: {
16
+ hello: {
17
+ type: 'string'
18
+ }
19
+ }
20
+ }
21
+ }
22
+ }
23
+ }
24
+
25
+ const optsWithHostnameAndPort = {
26
+ schema: {
27
+ response: {
28
+ '2xx': {
29
+ type: 'object',
30
+ properties: {
31
+ hello: {
32
+ type: 'string'
33
+ },
34
+ hostname: {
35
+ type: 'string'
36
+ },
37
+ port: {
38
+ type: 'string'
39
+ }
40
+ }
41
+ }
42
+ }
43
+ }
44
+ }
45
+ test('async await', async t => {
46
+ t.plan(15)
47
+ const fastify = Fastify()
48
+ try {
49
+ fastify.get('/', opts, async function awaitMyFunc (req, reply) {
50
+ await sleep(200)
51
+ return { hello: 'world' }
52
+ })
53
+ t.assert.ok(true)
54
+ } catch (e) {
55
+ t.assert.fail()
56
+ }
57
+
58
+ try {
59
+ fastify.get('/no-await', opts, async function (req, reply) {
60
+ return { hello: 'world' }
61
+ })
62
+ t.assert.ok(true)
63
+ } catch (e) {
64
+ t.assert.fail()
65
+ }
66
+
67
+ try {
68
+ fastify.get('/await/hostname_port', optsWithHostnameAndPort, async function awaitMyFunc (req, reply) {
69
+ await sleep(200)
70
+ return { hello: 'world', hostname: req.hostname, port: req.port }
71
+ })
72
+ t.assert.ok(true)
73
+ } catch (e) {
74
+ t.assert.fail()
75
+ }
76
+
77
+ const fastifyServer = await fastify.listen({ port: 0 })
78
+
79
+ t.after(() => { fastify.close() })
80
+
81
+ const result = await fetch(fastifyServer)
82
+
83
+ const body = await result.text()
84
+ t.assert.ok(result.ok)
85
+ t.assert.strictEqual(result.status, 200)
86
+ t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
87
+ t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
88
+
89
+ const result1 = await fetch(`${fastifyServer}/no-await`)
90
+
91
+ const body1 = await result1.text()
92
+ t.assert.ok(result1.ok)
93
+ t.assert.strictEqual(result1.status, 200)
94
+ t.assert.strictEqual(result1.headers.get('content-length'), '' + body1.length)
95
+ t.assert.deepStrictEqual(JSON.parse(body1), { hello: 'world' })
96
+
97
+ const result2 = await fetch(`http://localhost:${fastify.server.address().port}/await/hostname_port`)
98
+
99
+ const parsedBody = await result2.json()
100
+ t.assert.ok(result2.ok)
101
+ t.assert.strictEqual(result2.status, 200)
102
+ t.assert.strictEqual(parsedBody.hostname, 'localhost')
103
+ t.assert.strictEqual(parseInt(parsedBody.port), fastify.server.address().port)
104
+ })
105
+
106
+ test('ignore the result of the promise if reply.send is called beforehand (undefined)', async (t) => {
107
+ t.plan(3)
108
+
109
+ const server = Fastify()
110
+ const payload = { hello: 'world' }
111
+
112
+ server.get('/', async function awaitMyFunc (req, reply) {
113
+ await reply.send(payload)
114
+ })
115
+
116
+ t.after(() => { server.close() })
117
+
118
+ const fastifyServer = await server.listen({ port: 0 })
119
+
120
+ const result = await fetch(fastifyServer)
121
+
122
+ t.assert.ok(result.ok)
123
+ t.assert.deepStrictEqual(payload, await result.json())
124
+ t.assert.strictEqual(result.status, 200)
125
+ })
126
+
127
+ test('ignore the result of the promise if reply.send is called beforehand (object)', async (t) => {
128
+ t.plan(3)
129
+
130
+ const server = Fastify()
131
+ const payload = { hello: 'world2' }
132
+
133
+ server.get('/', async function awaitMyFunc (req, reply) {
134
+ await reply.send(payload)
135
+ return { hello: 'world' }
136
+ })
137
+
138
+ t.after(() => { server.close() })
139
+
140
+ const fastifyServer = await server.listen({ port: 0 })
141
+
142
+ const result = await fetch(fastifyServer)
143
+
144
+ t.assert.ok(result.ok)
145
+ t.assert.deepStrictEqual(payload, await result.json())
146
+ t.assert.strictEqual(result.status, 200)
147
+ })
148
+
149
+ test('server logs an error if reply.send is called and a value is returned via async/await', (t, done) => {
150
+ const lines = ['incoming request', 'request completed', 'Reply was already sent, did you forget to "return reply" in "/" (GET)?']
151
+ t.plan(lines.length + 2)
152
+
153
+ const splitStream = split(JSON.parse)
154
+ splitStream.on('data', (line) => {
155
+ t.assert.strictEqual(line.msg, lines.shift())
156
+ })
157
+
158
+ const logger = pino(splitStream)
159
+
160
+ const fastify = Fastify({
161
+ loggerInstance: logger
162
+ })
163
+
164
+ fastify.get('/', async (req, reply) => {
165
+ await reply.send({ hello: 'world' })
166
+ return { hello: 'world2' }
167
+ })
168
+
169
+ fastify.inject({
170
+ method: 'GET',
171
+ url: '/'
172
+ }, (err, res) => {
173
+ t.assert.ifError(err)
174
+ const payload = JSON.parse(res.payload)
175
+ t.assert.deepStrictEqual(payload, { hello: 'world' })
176
+ done()
177
+ })
178
+ })
179
+
180
+ test('ignore the result of the promise if reply.send is called beforehand (undefined)', async (t) => {
181
+ t.plan(3)
182
+
183
+ const server = Fastify()
184
+ const payload = { hello: 'world' }
185
+
186
+ server.get('/', async function awaitMyFunc (req, reply) {
187
+ await reply.send(payload)
188
+ })
189
+
190
+ t.after(() => { server.close() })
191
+
192
+ const fastifyServer = await server.listen({ port: 0 })
193
+
194
+ const result = await fetch(fastifyServer)
195
+
196
+ t.assert.ok(result.ok)
197
+ t.assert.deepStrictEqual(payload, await result.json())
198
+ t.assert.strictEqual(result.status, 200)
199
+ })
200
+
201
+ test('ignore the result of the promise if reply.send is called beforehand (object)', async (t) => {
202
+ t.plan(3)
203
+
204
+ const server = Fastify()
205
+ const payload = { hello: 'world2' }
206
+
207
+ server.get('/', async function awaitMyFunc (req, reply) {
208
+ await reply.send(payload)
209
+ return { hello: 'world' }
210
+ })
211
+
212
+ t.after(() => { server.close() })
213
+
214
+ const fastifyServer = await server.listen({ port: 0 })
215
+
216
+ const result = await fetch(fastifyServer)
217
+
218
+ t.assert.ok(result.ok)
219
+ t.assert.deepStrictEqual(payload, await result.json())
220
+ t.assert.strictEqual(result.status, 200)
221
+ })
222
+
223
+ test('await reply if we will be calling reply.send in the future', (t, done) => {
224
+ const lines = ['incoming request', 'request completed']
225
+ t.plan(lines.length + 2)
226
+
227
+ const splitStream = split(JSON.parse)
228
+ splitStream.on('data', (line) => {
229
+ t.assert.strictEqual(line.msg, lines.shift())
230
+ })
231
+
232
+ const server = Fastify({
233
+ logger: {
234
+ stream: splitStream
235
+ }
236
+ })
237
+ const payload = { hello: 'world' }
238
+
239
+ server.get('/', async function awaitMyFunc (req, reply) {
240
+ setImmediate(function () {
241
+ reply.send(payload)
242
+ })
243
+
244
+ await reply
245
+ })
246
+
247
+ server.inject({
248
+ method: 'GET',
249
+ url: '/'
250
+ }, (err, res) => {
251
+ t.assert.ifError(err)
252
+ const payload = JSON.parse(res.payload)
253
+ t.assert.deepStrictEqual(payload, { hello: 'world' })
254
+ done()
255
+ })
256
+ })
257
+
258
+ test('await reply if we will be calling reply.send in the future (error case)', (t, done) => {
259
+ const lines = ['incoming request', 'kaboom', 'request completed']
260
+ t.plan(lines.length + 2)
261
+
262
+ const splitStream = split(JSON.parse)
263
+ splitStream.on('data', (line) => {
264
+ t.assert.strictEqual(line.msg, lines.shift())
265
+ })
266
+
267
+ const server = Fastify({
268
+ logger: {
269
+ stream: splitStream
270
+ }
271
+ })
272
+
273
+ server.get('/', async function awaitMyFunc (req, reply) {
274
+ setImmediate(function () {
275
+ reply.send(new Error('kaboom'))
276
+ })
277
+
278
+ await reply
279
+ })
280
+
281
+ server.inject({
282
+ method: 'GET',
283
+ url: '/'
284
+ }, (err, res) => {
285
+ t.assert.ifError(err)
286
+ t.assert.strictEqual(res.statusCode, 500)
287
+ done()
288
+ })
289
+ })
290
+
291
+ test('support reply decorators with await', (t, done) => {
292
+ t.plan(2)
293
+
294
+ const fastify = Fastify()
295
+
296
+ fastify.decorateReply('wow', function () {
297
+ setImmediate(() => {
298
+ this.send({ hello: 'world' })
299
+ })
300
+
301
+ return this
302
+ })
303
+
304
+ fastify.get('/', async (req, reply) => {
305
+ await sleep(1)
306
+ await reply.wow()
307
+ })
308
+
309
+ fastify.inject({
310
+ method: 'GET',
311
+ url: '/'
312
+ }, (err, res) => {
313
+ t.assert.ifError(err)
314
+ const payload = JSON.parse(res.payload)
315
+ t.assert.deepStrictEqual(payload, { hello: 'world' })
316
+ done()
317
+ })
318
+ })
319
+
320
+ test('inject async await', async t => {
321
+ t.plan(1)
322
+
323
+ const fastify = Fastify()
324
+
325
+ fastify.get('/', (req, reply) => {
326
+ reply.send({ hello: 'world' })
327
+ })
328
+
329
+ try {
330
+ const res = await fastify.inject({ method: 'GET', url: '/' })
331
+ t.assert.deepStrictEqual({ hello: 'world' }, JSON.parse(res.payload))
332
+ } catch (err) {
333
+ t.assert.fail(err)
334
+ }
335
+ })
336
+
337
+ test('inject async await - when the server equal up', async t => {
338
+ t.plan(2)
339
+
340
+ const fastify = Fastify()
341
+
342
+ fastify.get('/', (req, reply) => {
343
+ reply.send({ hello: 'world' })
344
+ })
345
+
346
+ try {
347
+ const res = await fastify.inject({ method: 'GET', url: '/' })
348
+ t.assert.deepStrictEqual({ hello: 'world' }, JSON.parse(res.payload))
349
+ } catch (err) {
350
+ t.assert.fail(err)
351
+ }
352
+
353
+ await sleep(200)
354
+
355
+ try {
356
+ const res2 = await fastify.inject({ method: 'GET', url: '/' })
357
+ t.assert.deepStrictEqual({ hello: 'world' }, JSON.parse(res2.payload))
358
+ } catch (err) {
359
+ t.assert.fail(err)
360
+ }
361
+ })
362
+
363
+ test('async await plugin', async t => {
364
+ t.plan(1)
365
+
366
+ const fastify = Fastify()
367
+
368
+ fastify.register(async (fastify, opts) => {
369
+ fastify.get('/', (req, reply) => {
370
+ reply.send({ hello: 'world' })
371
+ })
372
+
373
+ await sleep(200)
374
+ })
375
+
376
+ try {
377
+ const res = await fastify.inject({ method: 'GET', url: '/' })
378
+ t.assert.deepStrictEqual({ hello: 'world' }, JSON.parse(res.payload))
379
+ } catch (err) {
380
+ t.assert.fail(err)
381
+ }
382
+ })
383
+
384
+ test('does not call reply.send() twice if 204 response equal already sent', (t, done) => {
385
+ t.plan(2)
386
+
387
+ const fastify = Fastify()
388
+
389
+ fastify.get('/', async (req, reply) => {
390
+ reply.code(204).send()
391
+ reply.send = () => {
392
+ throw new Error('reply.send() was called twice')
393
+ }
394
+ })
395
+
396
+ fastify.inject({
397
+ method: 'GET',
398
+ url: '/'
399
+ }, (err, res) => {
400
+ t.assert.ifError(err)
401
+ t.assert.strictEqual(res.statusCode, 204)
402
+ done()
403
+ })
404
+ })
405
+
406
+ test('promise was fulfilled with undefined', async (t) => {
407
+ t.plan(3)
408
+
409
+ let fastify = null
410
+ const stream = split(JSON.parse)
411
+ try {
412
+ fastify = Fastify({
413
+ logger: {
414
+ stream,
415
+ level: 'error'
416
+ }
417
+ })
418
+ } catch (e) {
419
+ t.assert.fail()
420
+ }
421
+
422
+ t.after(() => { fastify.close() })
423
+
424
+ fastify.get('/', async (req, reply) => {
425
+ })
426
+
427
+ stream.once('data', line => {
428
+ t.assert.fail('should not log an error')
429
+ })
430
+
431
+ const fastifyServer = await fastify.listen({ port: 0 })
432
+
433
+ const result = await fetch(fastifyServer)
434
+
435
+ t.assert.ok(result.ok)
436
+ t.assert.strictEqual(await result.text(), '')
437
+ t.assert.strictEqual(result.status, 200)
438
+ })
439
+
440
+ test('promise was fulfilled with undefined using inject', async (t) => {
441
+ const stream = split(JSON.parse)
442
+ const fastify = Fastify({
443
+ logger: {
444
+ stream,
445
+ level: 'error'
446
+ }
447
+ })
448
+
449
+ fastify.get('/', async (req, reply) => {
450
+ })
451
+
452
+ stream.once('data', line => {
453
+ t.assert.fail('should not log an error')
454
+ })
455
+
456
+ const res = await fastify.inject('/')
457
+
458
+ t.assert.strictEqual(res.body, '')
459
+ t.assert.strictEqual(res.statusCode, 200)
460
+ })
461
+
462
+ test('error is not logged because promise was fulfilled with undefined but response was sent before promise resolution', async (t) => {
463
+ t.plan(3)
464
+
465
+ let fastify = null
466
+ const stream = split(JSON.parse)
467
+ const payload = { hello: 'world' }
468
+ try {
469
+ fastify = Fastify({
470
+ logger: {
471
+ stream,
472
+ level: 'error'
473
+ }
474
+ })
475
+ } catch (e) {
476
+ t.assert.fail()
477
+ }
478
+
479
+ t.after(() => { fastify.close() })
480
+
481
+ fastify.get('/', async (req, reply) => {
482
+ reply.send(payload)
483
+ })
484
+
485
+ stream.once('data', line => {
486
+ t.assert.fail('should not log an error')
487
+ })
488
+
489
+ const fastifyServer = await fastify.listen({ port: 0 })
490
+
491
+ const result = await fetch(fastifyServer)
492
+
493
+ t.assert.ok(result.ok)
494
+ t.assert.strictEqual(result.status, 200)
495
+ t.assert.deepStrictEqual(
496
+ payload,
497
+ await result.json()
498
+ )
499
+ })
500
+
501
+ test('Thrown Error instance sets HTTP status code', (t, done) => {
502
+ t.plan(3)
503
+
504
+ const fastify = Fastify()
505
+
506
+ const err = new Error('winter is coming')
507
+ err.statusCode = 418
508
+
509
+ fastify.get('/', async (req, reply) => {
510
+ throw err
511
+ })
512
+
513
+ fastify.inject({
514
+ method: 'GET',
515
+ url: '/'
516
+ }, (error, res) => {
517
+ t.assert.ifError(error)
518
+ t.assert.strictEqual(res.statusCode, 418)
519
+ t.assert.deepStrictEqual(
520
+ {
521
+ error: statusCodes['418'],
522
+ message: err.message,
523
+ statusCode: 418
524
+ },
525
+ JSON.parse(res.payload)
526
+ )
527
+ done()
528
+ })
529
+ })
530
+
531
+ test('customErrorHandler support', (t, done) => {
532
+ t.plan(4)
533
+
534
+ const fastify = Fastify()
535
+
536
+ fastify.get('/', async (req, reply) => {
537
+ const error = new Error('ouch')
538
+ error.statusCode = 400
539
+ throw error
540
+ })
541
+
542
+ fastify.setErrorHandler(async err => {
543
+ t.assert.strictEqual(err.message, 'ouch')
544
+ const error = new Error('kaboom')
545
+ error.statusCode = 401
546
+ throw error
547
+ })
548
+
549
+ fastify.inject({
550
+ method: 'GET',
551
+ url: '/'
552
+ }, (err, res) => {
553
+ t.assert.ifError(err)
554
+ t.assert.strictEqual(res.statusCode, 401)
555
+ t.assert.deepStrictEqual(
556
+ {
557
+ error: statusCodes['401'],
558
+ message: 'kaboom',
559
+ statusCode: 401
560
+ },
561
+ JSON.parse(res.payload)
562
+ )
563
+ done()
564
+ })
565
+ })
566
+
567
+ test('customErrorHandler support without throwing', (t, done) => {
568
+ t.plan(4)
569
+
570
+ const fastify = Fastify()
571
+
572
+ fastify.get('/', async (req, reply) => {
573
+ const error = new Error('ouch')
574
+ error.statusCode = 400
575
+ throw error
576
+ })
577
+
578
+ fastify.setErrorHandler(async (err, req, reply) => {
579
+ t.assert.strictEqual(err.message, 'ouch')
580
+ await reply.code(401).send('kaboom')
581
+ reply.send = t.assert.fail.bind(t, 'should not be called')
582
+ })
583
+
584
+ fastify.inject({
585
+ method: 'GET',
586
+ url: '/'
587
+ }, (err, res) => {
588
+ t.assert.ifError(err)
589
+ t.assert.strictEqual(res.statusCode, 401)
590
+ t.assert.deepStrictEqual(
591
+ 'kaboom',
592
+ res.payload
593
+ )
594
+ done()
595
+ })
596
+ })
597
+
598
+ // See https://github.com/fastify/fastify/issues/2653
599
+ test('customErrorHandler only called if reply not already sent', (t, done) => {
600
+ t.plan(3)
601
+
602
+ const fastify = Fastify()
603
+
604
+ fastify.get('/', async (req, reply) => {
605
+ await reply.send('success')
606
+ const error = new Error('ouch')
607
+ error.statusCode = 400
608
+ throw error
609
+ })
610
+
611
+ fastify.setErrorHandler(t.assert.fail.bind(t, 'should not be called'))
612
+
613
+ fastify.inject({
614
+ method: 'GET',
615
+ url: '/'
616
+ }, (err, res) => {
617
+ t.assert.ifError(err)
618
+ t.assert.strictEqual(res.statusCode, 200)
619
+ t.assert.deepStrictEqual(
620
+ 'success',
621
+ res.payload
622
+ )
623
+ done()
624
+ })
625
+ })
626
+
627
+ // See https://github.com/fastify/fastify/issues/3209
628
+ test('setNotFoundHandler should accept return value', (t, done) => {
629
+ t.plan(3)
630
+
631
+ const fastify = Fastify()
632
+
633
+ fastify.get('/', async () => ({ hello: 'world' }))
634
+
635
+ fastify.setNotFoundHandler((req, reply) => {
636
+ reply.code(404)
637
+ return {
638
+ error: statusCodes['404'],
639
+ message: 'lost',
640
+ statusCode: 404
641
+ }
642
+ })
643
+
644
+ fastify.inject({
645
+ method: 'GET',
646
+ url: '/elsewhere'
647
+ }, (err, res) => {
648
+ t.assert.ifError(err)
649
+ t.assert.strictEqual(res.statusCode, 404)
650
+ t.assert.deepStrictEqual(
651
+ {
652
+ error: statusCodes['404'],
653
+ message: 'lost',
654
+ statusCode: 404
655
+ },
656
+ JSON.parse(res.payload)
657
+ )
658
+ done()
659
+ })
660
+ })
661
+
662
+ // See https://github.com/fastify/fastify/issues/3209
663
+ test('customErrorHandler should accept return value', (t, done) => {
664
+ t.plan(4)
665
+
666
+ const fastify = Fastify()
667
+
668
+ fastify.get('/', async (req, reply) => {
669
+ const error = new Error('ouch')
670
+ error.statusCode = 400
671
+ throw error
672
+ })
673
+
674
+ fastify.setErrorHandler((err, req, reply) => {
675
+ t.assert.strictEqual(err.message, 'ouch')
676
+ reply.code(401)
677
+ return {
678
+ error: statusCodes['401'],
679
+ message: 'kaboom',
680
+ statusCode: 401
681
+ }
682
+ })
683
+
684
+ fastify.inject({
685
+ method: 'GET',
686
+ url: '/'
687
+ }, (err, res) => {
688
+ t.assert.ifError(err)
689
+ t.assert.strictEqual(res.statusCode, 401)
690
+ t.assert.deepStrictEqual(
691
+ {
692
+ error: statusCodes['401'],
693
+ message: 'kaboom',
694
+ statusCode: 401
695
+ },
696
+ JSON.parse(res.payload)
697
+ )
698
+ done()
699
+ })
700
+ })
701
+
702
+ test('await self', async t => {
703
+ const app = Fastify()
704
+ t.assert.strictEqual(await app, app)
705
+ })
@@ -0,0 +1,20 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('node:test')
4
+ const Fastify = require('../fastify')
5
+
6
+ // asyncDispose doesn't exist in node <= 16
7
+ test('async dispose should close fastify', { skip: !('asyncDispose' in Symbol) }, async t => {
8
+ t.plan(2)
9
+
10
+ const fastify = Fastify()
11
+
12
+ await fastify.listen({ port: 0 })
13
+
14
+ t.assert.strictEqual(fastify.server.listening, true)
15
+
16
+ // the same as syntax sugar for
17
+ // await using app = fastify()
18
+ await fastify[Symbol.asyncDispose]()
19
+ t.assert.strictEqual(fastify.server.listening, false)
20
+ })