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,96 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('node:test')
4
+ const { Hooks } = require('../../lib/hooks')
5
+ const { default: fastify } = require('../../fastify')
6
+ const noop = () => {}
7
+
8
+ test('hooks should have 4 array with the registered hooks', t => {
9
+ const hooks = new Hooks()
10
+ t.assert.strictEqual(typeof hooks, 'object')
11
+ t.assert.ok(Array.isArray(hooks.onRequest))
12
+ t.assert.ok(Array.isArray(hooks.onSend))
13
+ t.assert.ok(Array.isArray(hooks.preParsing))
14
+ t.assert.ok(Array.isArray(hooks.preValidation))
15
+ t.assert.ok(Array.isArray(hooks.preHandler))
16
+ t.assert.ok(Array.isArray(hooks.onResponse))
17
+ t.assert.ok(Array.isArray(hooks.onError))
18
+ })
19
+
20
+ test('hooks.add should add a hook to the given hook', t => {
21
+ const hooks = new Hooks()
22
+ hooks.add('onRequest', noop)
23
+ t.assert.strictEqual(hooks.onRequest.length, 1)
24
+ t.assert.strictEqual(typeof hooks.onRequest[0], 'function')
25
+
26
+ hooks.add('preParsing', noop)
27
+ t.assert.strictEqual(hooks.preParsing.length, 1)
28
+ t.assert.strictEqual(typeof hooks.preParsing[0], 'function')
29
+
30
+ hooks.add('preValidation', noop)
31
+ t.assert.strictEqual(hooks.preValidation.length, 1)
32
+ t.assert.strictEqual(typeof hooks.preValidation[0], 'function')
33
+
34
+ hooks.add('preHandler', noop)
35
+ t.assert.strictEqual(hooks.preHandler.length, 1)
36
+ t.assert.strictEqual(typeof hooks.preHandler[0], 'function')
37
+
38
+ hooks.add('onResponse', noop)
39
+ t.assert.strictEqual(hooks.onResponse.length, 1)
40
+ t.assert.strictEqual(typeof hooks.onResponse[0], 'function')
41
+
42
+ hooks.add('onSend', noop)
43
+ t.assert.strictEqual(hooks.onSend.length, 1)
44
+ t.assert.strictEqual(typeof hooks.onSend[0], 'function')
45
+
46
+ hooks.add('onError', noop)
47
+ t.assert.strictEqual(hooks.onError.length, 1)
48
+ t.assert.strictEqual(typeof hooks.onError[0], 'function')
49
+ })
50
+
51
+ test('hooks should throw on unexisting handler', t => {
52
+ t.plan(1)
53
+ const hooks = new Hooks()
54
+ try {
55
+ hooks.add('onUnexistingHook', noop)
56
+ t.assert.fail()
57
+ } catch (e) {
58
+ t.assert.ok(true)
59
+ }
60
+ })
61
+
62
+ test('should throw on wrong parameters', t => {
63
+ const hooks = new Hooks()
64
+ t.plan(4)
65
+ try {
66
+ hooks.add(null, () => {})
67
+ t.assert.fail()
68
+ } catch (e) {
69
+ t.assert.strictEqual(e.code, 'FST_ERR_HOOK_INVALID_TYPE')
70
+ t.assert.strictEqual(e.message, 'The hook name must be a string')
71
+ }
72
+
73
+ try {
74
+ hooks.add('onSend', null)
75
+ t.assert.fail()
76
+ } catch (e) {
77
+ t.assert.strictEqual(e.code, 'FST_ERR_HOOK_INVALID_HANDLER')
78
+ t.assert.strictEqual(e.message, 'onSend hook should be a function, instead got [object Null]')
79
+ }
80
+ })
81
+
82
+ test('Integration test: internal function _addHook should be turned into app.ready() rejection', async (t) => {
83
+ const app = fastify()
84
+
85
+ app.register(async function () {
86
+ app.addHook('notRealHook', async () => {})
87
+ })
88
+
89
+ try {
90
+ await app.ready()
91
+ t.assert.fail('Expected ready() to throw')
92
+ } catch (err) {
93
+ t.assert.strictEqual(err.code, 'FST_ERR_HOOK_NOT_SUPPORTED')
94
+ t.assert.match(err.message, /hook not supported/i)
95
+ }
96
+ })
@@ -0,0 +1,383 @@
1
+ 'use strict'
2
+
3
+ const { test, before } = require('node:test')
4
+ const Fastify = require('../..')
5
+ const helper = require('../helper')
6
+ const http = require('node:http')
7
+ const pino = require('pino')
8
+ const split = require('split2')
9
+ const deepClone = require('rfdc')({ circles: true, proto: false })
10
+ const { deepFreezeObject } = require('../../lib/initial-config-validation').utils
11
+
12
+ const { buildCertificate } = require('../build-certificate')
13
+
14
+ process.removeAllListeners('warning')
15
+
16
+ let localhost
17
+ let localhostForURL
18
+
19
+ before(async function () {
20
+ await buildCertificate();
21
+ [localhost, localhostForURL] = await helper.getLoopbackHost()
22
+ })
23
+
24
+ test('Fastify.initialConfig is an object', t => {
25
+ t.plan(1)
26
+ t.assert.ok(typeof Fastify().initialConfig === 'object')
27
+ })
28
+
29
+ test('without options passed to Fastify, initialConfig should expose default values', t => {
30
+ t.plan(1)
31
+
32
+ const fastifyDefaultOptions = {
33
+ connectionTimeout: 0,
34
+ keepAliveTimeout: 72000,
35
+ maxRequestsPerSocket: 0,
36
+ requestTimeout: 0,
37
+ handlerTimeout: 0,
38
+ bodyLimit: 1024 * 1024,
39
+ caseSensitive: true,
40
+ allowUnsafeRegex: false,
41
+ disableRequestLogging: false,
42
+ ignoreTrailingSlash: false,
43
+ ignoreDuplicateSlashes: false,
44
+ maxParamLength: 100,
45
+ onProtoPoisoning: 'error',
46
+ onConstructorPoisoning: 'error',
47
+ pluginTimeout: 10000,
48
+ requestIdHeader: false,
49
+ requestIdLogLabel: 'reqId',
50
+ http2SessionTimeout: 72000,
51
+ exposeHeadRoutes: true,
52
+ useSemicolonDelimiter: false
53
+ }
54
+
55
+ t.assert.deepStrictEqual(Fastify().initialConfig, fastifyDefaultOptions)
56
+ })
57
+
58
+ test('Fastify.initialConfig should expose all options', t => {
59
+ t.plan(22)
60
+
61
+ const serverFactory = (handler, opts) => {
62
+ const server = http.createServer((req, res) => {
63
+ handler(req, res)
64
+ })
65
+
66
+ return server
67
+ }
68
+
69
+ const versionStrategy = {
70
+ name: 'version',
71
+ storage: function () {
72
+ const versions = {}
73
+ return {
74
+ get: (version) => { return versions[version] || null },
75
+ set: (version, store) => { versions[version] = store }
76
+ }
77
+ },
78
+ deriveConstraint: (req, ctx) => {
79
+ return req.headers.accept
80
+ },
81
+ validate () { return true }
82
+ }
83
+
84
+ let reqId = 0
85
+ const options = {
86
+ http2: true,
87
+ https: {
88
+ key: global.context.key,
89
+ cert: global.context.cert
90
+ },
91
+ ignoreTrailingSlash: true,
92
+ ignoreDuplicateSlashes: true,
93
+ maxParamLength: 200,
94
+ connectionTimeout: 0,
95
+ keepAliveTimeout: 72000,
96
+ bodyLimit: 1049600,
97
+ onProtoPoisoning: 'remove',
98
+ serverFactory,
99
+ caseSensitive: true,
100
+ allowUnsafeRegex: false,
101
+ requestIdHeader: 'request-id-alt',
102
+ pluginTimeout: 20000,
103
+ useSemicolonDelimiter: false,
104
+ querystringParser: str => str,
105
+ genReqId: function (req) {
106
+ return reqId++
107
+ },
108
+ loggerInstance: pino({ level: 'info' }),
109
+ constraints: {
110
+ version: versionStrategy
111
+ },
112
+ trustProxy: function myTrustFn (address, hop) {
113
+ return address === '1.2.3.4' || hop === 1
114
+ }
115
+ }
116
+
117
+ const fastify = Fastify(options)
118
+ t.assert.strictEqual(fastify.initialConfig.http2, true)
119
+ t.assert.strictEqual(fastify.initialConfig.https, true, 'for security reason the key cert is hidden')
120
+ t.assert.strictEqual(fastify.initialConfig.ignoreTrailingSlash, true)
121
+ t.assert.strictEqual(fastify.initialConfig.ignoreDuplicateSlashes, true)
122
+ t.assert.strictEqual(fastify.initialConfig.maxParamLength, 200)
123
+ t.assert.strictEqual(fastify.initialConfig.connectionTimeout, 0)
124
+ t.assert.strictEqual(fastify.initialConfig.keepAliveTimeout, 72000)
125
+ t.assert.strictEqual(fastify.initialConfig.bodyLimit, 1049600)
126
+ t.assert.strictEqual(fastify.initialConfig.onProtoPoisoning, 'remove')
127
+ t.assert.strictEqual(fastify.initialConfig.caseSensitive, true)
128
+ t.assert.strictEqual(fastify.initialConfig.useSemicolonDelimiter, false)
129
+ t.assert.strictEqual(fastify.initialConfig.allowUnsafeRegex, false)
130
+ t.assert.strictEqual(fastify.initialConfig.requestIdHeader, 'request-id-alt')
131
+ t.assert.strictEqual(fastify.initialConfig.pluginTimeout, 20000)
132
+ t.assert.ok(fastify.initialConfig.constraints.version)
133
+
134
+ // obfuscated options:
135
+ t.assert.strictEqual(fastify.initialConfig.serverFactory, undefined)
136
+ t.assert.strictEqual(fastify.initialConfig.trustProxy, undefined)
137
+ t.assert.strictEqual(fastify.initialConfig.genReqId, undefined)
138
+ t.assert.strictEqual(fastify.initialConfig.childLoggerFactory, undefined)
139
+ t.assert.strictEqual(fastify.initialConfig.querystringParser, undefined)
140
+ t.assert.strictEqual(fastify.initialConfig.logger, undefined)
141
+ t.assert.strictEqual(fastify.initialConfig.trustProxy, undefined)
142
+ })
143
+
144
+ test('Should throw if you try to modify Fastify.initialConfig', t => {
145
+ t.plan(4)
146
+
147
+ const fastify = Fastify({ ignoreTrailingSlash: true })
148
+ try {
149
+ fastify.initialConfig.ignoreTrailingSlash = false
150
+ t.assert.fail()
151
+ } catch (error) {
152
+ t.assert.ok(error instanceof TypeError)
153
+ t.assert.strictEqual(error.message, "Cannot assign to read only property 'ignoreTrailingSlash' of object '#<Object>'")
154
+ t.assert.ok(error.stack)
155
+ t.assert.ok(true)
156
+ }
157
+ })
158
+
159
+ test('We must avoid shallow freezing and ensure that the whole object is freezed', t => {
160
+ t.plan(4)
161
+
162
+ const fastify = Fastify({
163
+ https: {
164
+ allowHTTP1: true,
165
+ key: global.context.key,
166
+ cert: global.context.cert
167
+ }
168
+ })
169
+
170
+ try {
171
+ fastify.initialConfig.https.allowHTTP1 = false
172
+ t.assert.fail()
173
+ } catch (error) {
174
+ t.assert.ok(error instanceof TypeError)
175
+ t.assert.strictEqual(error.message, "Cannot assign to read only property 'allowHTTP1' of object '#<Object>'")
176
+ t.assert.ok(error.stack)
177
+ t.assert.deepStrictEqual(fastify.initialConfig.https, {
178
+ allowHTTP1: true
179
+ }, 'key cert removed')
180
+ }
181
+ })
182
+
183
+ test('https value check', t => {
184
+ t.plan(1)
185
+
186
+ const fastify = Fastify({})
187
+ t.assert.ok(!fastify.initialConfig.https)
188
+ })
189
+
190
+ test('Return an error if options do not match the validation schema', t => {
191
+ t.plan(6)
192
+
193
+ try {
194
+ Fastify({ ignoreTrailingSlash: 'string instead of boolean' })
195
+
196
+ t.assert.fail()
197
+ } catch (error) {
198
+ t.assert.ok(error instanceof Error)
199
+ t.assert.strictEqual(error.name, 'FastifyError')
200
+ t.assert.strictEqual(error.message, 'Invalid initialization options: \'["must be boolean"]\'')
201
+ t.assert.strictEqual(error.code, 'FST_ERR_INIT_OPTS_INVALID')
202
+ t.assert.ok(error.stack)
203
+ t.assert.ok(true)
204
+ }
205
+ })
206
+
207
+ test('Original options must not be frozen', t => {
208
+ t.plan(4)
209
+
210
+ const originalOptions = {
211
+ https: {
212
+ allowHTTP1: true,
213
+ key: global.context.key,
214
+ cert: global.context.cert
215
+ }
216
+ }
217
+
218
+ const fastify = Fastify(originalOptions)
219
+
220
+ t.assert.strictEqual(Object.isFrozen(originalOptions), false)
221
+ t.assert.strictEqual(Object.isFrozen(originalOptions.https), false)
222
+ t.assert.strictEqual(Object.isFrozen(fastify.initialConfig), true)
223
+ t.assert.strictEqual(Object.isFrozen(fastify.initialConfig.https), true)
224
+ })
225
+
226
+ test('Original options must not be altered (test deep cloning)', t => {
227
+ t.plan(3)
228
+
229
+ const originalOptions = {
230
+ https: {
231
+ allowHTTP1: true,
232
+ key: global.context.key,
233
+ cert: global.context.cert
234
+ }
235
+ }
236
+
237
+ const originalOptionsClone = deepClone(originalOptions)
238
+
239
+ const fastify = Fastify(originalOptions)
240
+
241
+ // initialConfig has been triggered
242
+ t.assert.strictEqual(Object.isFrozen(fastify.initialConfig), true)
243
+
244
+ // originalOptions must not have been altered
245
+ t.assert.deepStrictEqual(originalOptions.https.key, originalOptionsClone.https.key)
246
+ t.assert.deepStrictEqual(originalOptions.https.cert, originalOptionsClone.https.cert)
247
+ })
248
+
249
+ test('Should not have issues when passing stream options to Pino.js', (t, done) => {
250
+ t.plan(17)
251
+
252
+ const stream = split(JSON.parse)
253
+
254
+ const originalOptions = {
255
+ ignoreTrailingSlash: true,
256
+ logger: {
257
+ level: 'trace',
258
+ stream
259
+ }
260
+ }
261
+
262
+ let fastify
263
+
264
+ try {
265
+ fastify = Fastify(originalOptions)
266
+ fastify.setChildLoggerFactory(function (logger, bindings, opts) {
267
+ bindings.someBinding = 'value'
268
+ return logger.child(bindings, opts)
269
+ })
270
+
271
+ t.assert.ok(typeof fastify === 'object')
272
+ t.assert.deepStrictEqual(fastify.initialConfig, {
273
+ connectionTimeout: 0,
274
+ keepAliveTimeout: 72000,
275
+ maxRequestsPerSocket: 0,
276
+ requestTimeout: 0,
277
+ handlerTimeout: 0,
278
+ bodyLimit: 1024 * 1024,
279
+ caseSensitive: true,
280
+ allowUnsafeRegex: false,
281
+ disableRequestLogging: false,
282
+ ignoreTrailingSlash: true,
283
+ ignoreDuplicateSlashes: false,
284
+ maxParamLength: 100,
285
+ onProtoPoisoning: 'error',
286
+ onConstructorPoisoning: 'error',
287
+ pluginTimeout: 10000,
288
+ requestIdHeader: false,
289
+ requestIdLogLabel: 'reqId',
290
+ http2SessionTimeout: 72000,
291
+ exposeHeadRoutes: true,
292
+ useSemicolonDelimiter: false
293
+ })
294
+ } catch (error) {
295
+ t.assert.fail()
296
+ }
297
+
298
+ fastify.get('/', function (req, reply) {
299
+ t.assert.ok(req.log)
300
+ reply.send({ hello: 'world' })
301
+ })
302
+
303
+ stream.once('data', listenAtLogLine => {
304
+ t.assert.ok(listenAtLogLine, 'listen at log message is ok')
305
+
306
+ stream.once('data', line => {
307
+ const id = line.reqId
308
+ t.assert.ok(line.reqId, 'reqId is defined')
309
+ t.assert.strictEqual(line.someBinding, 'value', 'child logger binding is set')
310
+ t.assert.ok(line.req, 'req is defined')
311
+ t.assert.strictEqual(line.msg, 'incoming request', 'message is set')
312
+ t.assert.strictEqual(line.req.method, 'GET', 'method is get')
313
+
314
+ stream.once('data', line => {
315
+ t.assert.strictEqual(line.reqId, id)
316
+ t.assert.ok(line.reqId, 'reqId is defined')
317
+ t.assert.strictEqual(line.someBinding, 'value', 'child logger binding is set')
318
+ t.assert.ok(line.res, 'res is defined')
319
+ t.assert.strictEqual(line.msg, 'request completed', 'message is set')
320
+ t.assert.strictEqual(line.res.statusCode, 200, 'statusCode is 200')
321
+ t.assert.ok(line.responseTime, 'responseTime is defined')
322
+ })
323
+ })
324
+ })
325
+
326
+ fastify.listen({ port: 0, host: localhost }, err => {
327
+ t.assert.ifError(err)
328
+ t.after(() => { fastify.close() })
329
+
330
+ http.get(`http://${localhostForURL}:${fastify.server.address().port}`, () => {
331
+ done()
332
+ })
333
+ })
334
+ })
335
+
336
+ test('deepFreezeObject() should not throw on TypedArray', t => {
337
+ t.plan(5)
338
+
339
+ const object = {
340
+ buffer: Buffer.from(global.context.key),
341
+ dataView: new DataView(new ArrayBuffer(16)),
342
+ float: 1.1,
343
+ integer: 1,
344
+ object: {
345
+ nested: { string: 'string' }
346
+ },
347
+ stream: split(JSON.parse),
348
+ string: 'string'
349
+ }
350
+
351
+ try {
352
+ const frozenObject = deepFreezeObject(object)
353
+
354
+ // Buffers should not be frozen, as they are Uint8Array inherited instances
355
+ t.assert.strictEqual(Object.isFrozen(frozenObject.buffer), false)
356
+
357
+ t.assert.strictEqual(Object.isFrozen(frozenObject), true)
358
+ t.assert.strictEqual(Object.isFrozen(frozenObject.object), true)
359
+ t.assert.strictEqual(Object.isFrozen(frozenObject.object.nested), true)
360
+
361
+ t.assert.ok(true)
362
+ } catch (error) {
363
+ t.assert.fail()
364
+ }
365
+ })
366
+
367
+ test('pluginTimeout should be parsed correctly', t => {
368
+ const withDisabledTimeout = Fastify({ pluginTimeout: '0' })
369
+ t.assert.strictEqual(withDisabledTimeout.initialConfig.pluginTimeout, 0)
370
+ const withInvalidTimeout = Fastify({ pluginTimeout: undefined })
371
+ t.assert.strictEqual(withInvalidTimeout.initialConfig.pluginTimeout, 10000)
372
+ })
373
+
374
+ test('Should not mutate the options object outside Fastify', async t => {
375
+ const options = Object.freeze({})
376
+
377
+ try {
378
+ Fastify(options)
379
+ t.assert.ok(true)
380
+ } catch (error) {
381
+ t.assert.fail(error.message)
382
+ }
383
+ })
@@ -0,0 +1,163 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('node:test')
4
+ const Fastify = require('../..')
5
+ const loggerUtils = require('../../lib/logger-factory')
6
+ const { serializers } = require('../../lib/logger-pino')
7
+
8
+ test('time resolution', t => {
9
+ t.plan(2)
10
+ t.assert.strictEqual(typeof loggerUtils.now, 'function')
11
+ t.assert.strictEqual(typeof loggerUtils.now(), 'number')
12
+ })
13
+
14
+ test('The logger should add a unique id for every request', (t, done) => {
15
+ const ids = []
16
+
17
+ const fastify = Fastify()
18
+ fastify.get('/', (req, reply) => {
19
+ t.assert.ok(req.id)
20
+ reply.send({ id: req.id })
21
+ })
22
+
23
+ fastify.listen({ port: 0 }, err => {
24
+ t.assert.ifError(err)
25
+ const queue = new Queue()
26
+ for (let i = 0; i < 10; i++) {
27
+ queue.add(checkId)
28
+ }
29
+ queue.add(() => {
30
+ fastify.close()
31
+ done()
32
+ })
33
+ })
34
+
35
+ function checkId (done) {
36
+ fastify.inject({
37
+ method: 'GET',
38
+ url: 'http://localhost:' + fastify.server.address().port
39
+ }, (err, res) => {
40
+ t.assert.ifError(err)
41
+ const payload = JSON.parse(res.payload)
42
+ t.assert.ok(ids.indexOf(payload.id) === -1, 'the id should not be duplicated')
43
+ ids.push(payload.id)
44
+ done()
45
+ })
46
+ }
47
+ })
48
+
49
+ test('The logger should not reuse request id header for req.id', (t, done) => {
50
+ const fastify = Fastify()
51
+ fastify.get('/', (req, reply) => {
52
+ t.assert.ok(req.id)
53
+ reply.send({ id: req.id })
54
+ })
55
+
56
+ fastify.listen({ port: 0 }, err => {
57
+ t.assert.ifError(err)
58
+
59
+ fastify.inject({
60
+ method: 'GET',
61
+ url: 'http://localhost:' + fastify.server.address().port,
62
+ headers: {
63
+ 'Request-Id': 'request-id-1'
64
+ }
65
+ }, (err, res) => {
66
+ t.assert.ifError(err)
67
+ const payload = JSON.parse(res.payload)
68
+ t.assert.ok(payload.id !== 'request-id-1', 'the request id from the header should not be returned with default configuration')
69
+ t.assert.ok(payload.id === 'req-1') // first request id when using the default configuration
70
+ fastify.close()
71
+ done()
72
+ })
73
+ })
74
+ })
75
+
76
+ test('The logger should reuse request id header for req.id if requestIdHeader is set', (t, done) => {
77
+ const fastify = Fastify({
78
+ requestIdHeader: 'request-id'
79
+ })
80
+ fastify.get('/', (req, reply) => {
81
+ t.assert.ok(req.id)
82
+ reply.send({ id: req.id })
83
+ })
84
+
85
+ fastify.listen({ port: 0 }, err => {
86
+ t.assert.ifError(err)
87
+
88
+ fastify.inject({
89
+ method: 'GET',
90
+ url: 'http://localhost:' + fastify.server.address().port,
91
+ headers: {
92
+ 'Request-Id': 'request-id-1'
93
+ }
94
+ }, (err, res) => {
95
+ t.assert.ifError(err)
96
+ const payload = JSON.parse(res.payload)
97
+ t.assert.ok(payload.id === 'request-id-1', 'the request id from the header should be returned')
98
+ fastify.close()
99
+ done()
100
+ })
101
+ })
102
+ })
103
+
104
+ function Queue () {
105
+ this.q = []
106
+ this.running = false
107
+ }
108
+
109
+ Queue.prototype.add = function add (job) {
110
+ this.q.push(job)
111
+ if (!this.running) this.run()
112
+ }
113
+
114
+ Queue.prototype.run = function run () {
115
+ this.running = true
116
+ const job = this.q.shift()
117
+ job(() => {
118
+ if (this.q.length) {
119
+ this.run()
120
+ } else {
121
+ this.running = false
122
+ }
123
+ })
124
+ }
125
+
126
+ test('The logger should error if both stream and file destination are given', t => {
127
+ t.plan(2)
128
+
129
+ const stream = require('node:stream').Writable
130
+
131
+ try {
132
+ Fastify({
133
+ logger: {
134
+ level: 'info',
135
+ stream,
136
+ file: '/test'
137
+ }
138
+ })
139
+ } catch (err) {
140
+ t.assert.strictEqual(err.code, 'FST_ERR_LOG_INVALID_DESTINATION')
141
+ t.assert.strictEqual(err.message, 'Cannot specify both logger.stream and logger.file options')
142
+ }
143
+ })
144
+
145
+ test('The serializer prevent fails if the request socket is undefined', t => {
146
+ t.plan(1)
147
+
148
+ const serialized = serializers.req({
149
+ method: 'GET',
150
+ url: '/',
151
+ socket: undefined,
152
+ headers: {}
153
+ })
154
+
155
+ t.assert.deepStrictEqual(serialized, {
156
+ method: 'GET',
157
+ url: '/',
158
+ version: undefined,
159
+ host: undefined,
160
+ remoteAddress: undefined,
161
+ remotePort: undefined
162
+ })
163
+ })