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,714 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('node:test')
4
+ const { kReplyCacheSerializeFns, kRouteContext } = require('../../lib/symbols')
5
+ const Fastify = require('../../fastify')
6
+
7
+ function getDefaultSchema () {
8
+ return {
9
+ type: 'object',
10
+ required: ['hello'],
11
+ properties: {
12
+ hello: { type: 'string' },
13
+ world: { type: 'string' }
14
+ }
15
+ }
16
+ }
17
+
18
+ function getResponseSchema () {
19
+ return {
20
+ 201: {
21
+ type: 'object',
22
+ required: ['status'],
23
+ properties: {
24
+ status: {
25
+ type: 'string',
26
+ enum: ['ok']
27
+ },
28
+ message: {
29
+ type: 'string'
30
+ }
31
+ }
32
+ },
33
+ '4xx': {
34
+ type: 'object',
35
+ properties: {
36
+ status: {
37
+ type: 'string',
38
+ enum: ['error']
39
+ },
40
+ code: {
41
+ type: 'integer',
42
+ minimum: 1
43
+ },
44
+ message: {
45
+ type: 'string'
46
+ }
47
+ }
48
+ },
49
+ '3xx': {
50
+ content: {
51
+ 'application/json': {
52
+ schema: {
53
+ type: 'object',
54
+ properties: {
55
+ fullName: { type: 'string' },
56
+ phone: { type: 'number' }
57
+ }
58
+ }
59
+ }
60
+ }
61
+ }
62
+ }
63
+ }
64
+
65
+ test('Reply#compileSerializationSchema', async t => {
66
+ t.plan(4)
67
+
68
+ await t.test('Should return a serialization function', async t => {
69
+ const fastify = Fastify()
70
+
71
+ t.plan(4)
72
+
73
+ fastify.get('/', (req, reply) => {
74
+ const serialize = reply.compileSerializationSchema(getDefaultSchema())
75
+ const input = { hello: 'world' }
76
+ t.assert.ok(serialize instanceof Function)
77
+ t.assert.ok(typeof serialize(input) === 'string')
78
+ t.assert.strictEqual(serialize(input), JSON.stringify(input))
79
+
80
+ try {
81
+ serialize({ world: 'foo' })
82
+ } catch (err) {
83
+ t.assert.strictEqual(err.message, '"hello" is required!')
84
+ }
85
+
86
+ reply.send({ hello: 'world' })
87
+ })
88
+
89
+ await fastify.inject({
90
+ path: '/',
91
+ method: 'GET'
92
+ })
93
+ })
94
+
95
+ await t.test('Should reuse the serialize fn across multiple invocations - Route without schema',
96
+ async t => {
97
+ const fastify = Fastify()
98
+ let serialize = null
99
+ let counter = 0
100
+
101
+ t.plan(17)
102
+
103
+ const schemaObj = getDefaultSchema()
104
+
105
+ fastify.get('/', (req, reply) => {
106
+ const input = { hello: 'world' }
107
+ counter++
108
+ if (counter > 1) {
109
+ const newSerialize = reply.compileSerializationSchema(schemaObj)
110
+ t.assert.strictEqual(serialize, newSerialize, 'Are the same validate function')
111
+ serialize = newSerialize
112
+ } else {
113
+ t.assert.ok(true, 'build the schema compilation function')
114
+ serialize = reply.compileSerializationSchema(schemaObj)
115
+ }
116
+
117
+ t.assert.ok(serialize instanceof Function)
118
+ t.assert.strictEqual(serialize(input), JSON.stringify(input))
119
+
120
+ try {
121
+ serialize({ world: 'foo' })
122
+ } catch (err) {
123
+ t.assert.strictEqual(err.message, '"hello" is required!')
124
+ }
125
+
126
+ reply.send({ hello: 'world' })
127
+ })
128
+
129
+ await Promise.all([
130
+ fastify.inject('/'),
131
+ fastify.inject('/'),
132
+ fastify.inject('/'),
133
+ fastify.inject('/')
134
+ ])
135
+
136
+ t.assert.strictEqual(counter, 4)
137
+ }
138
+ )
139
+
140
+ await t.test('Should use the custom serializer compiler for the route',
141
+ async t => {
142
+ const fastify = Fastify()
143
+ let called = 0
144
+ const custom = ({ schema, httpStatus, url, method }) => {
145
+ t.assert.strictEqual(schema, schemaObj)
146
+ t.assert.strictEqual(url, '/')
147
+ t.assert.strictEqual(method, 'GET')
148
+ t.assert.strictEqual(httpStatus, '201')
149
+
150
+ return input => {
151
+ called++
152
+ t.assert.deepStrictEqual(input, { hello: 'world' })
153
+ return JSON.stringify(input)
154
+ }
155
+ }
156
+
157
+ const custom2 = ({ schema, httpStatus, url, method, contentType }) => {
158
+ t.assert.strictEqual(schema, schemaObj)
159
+ t.assert.strictEqual(url, '/user')
160
+ t.assert.strictEqual(method, 'GET')
161
+ t.assert.strictEqual(httpStatus, '3xx')
162
+ t.assert.strictEqual(contentType, 'application/json')
163
+
164
+ return input => {
165
+ t.assert.deepStrictEqual(input, { fullName: 'Jone', phone: 1090243795 })
166
+ return JSON.stringify(input)
167
+ }
168
+ }
169
+
170
+ t.plan(17)
171
+ const schemaObj = getDefaultSchema()
172
+
173
+ fastify.get('/', { serializerCompiler: custom }, (req, reply) => {
174
+ const input = { hello: 'world' }
175
+ const first = reply.compileSerializationSchema(schemaObj, '201')
176
+ const second = reply.compileSerializationSchema(schemaObj, '201')
177
+
178
+ t.assert.strictEqual(first, second)
179
+ t.assert.ok(first(input), JSON.stringify(input))
180
+ t.assert.ok(second(input), JSON.stringify(input))
181
+ t.assert.strictEqual(called, 2)
182
+
183
+ reply.send({ hello: 'world' })
184
+ })
185
+
186
+ fastify.get('/user', { serializerCompiler: custom2 }, (req, reply) => {
187
+ const input = { fullName: 'Jone', phone: 1090243795 }
188
+ const first = reply.compileSerializationSchema(schemaObj, '3xx', 'application/json')
189
+ t.assert.ok(first(input), JSON.stringify(input))
190
+ reply.send(input)
191
+ })
192
+
193
+ await fastify.inject({
194
+ path: '/',
195
+ method: 'GET'
196
+ })
197
+
198
+ await fastify.inject({
199
+ path: '/user',
200
+ method: 'GET'
201
+ })
202
+ }
203
+ )
204
+
205
+ await t.test('Should build a WeakMap for cache when called', async t => {
206
+ const fastify = Fastify()
207
+
208
+ t.plan(4)
209
+
210
+ fastify.get('/', (req, reply) => {
211
+ const input = { hello: 'world' }
212
+
213
+ t.assert.strictEqual(reply[kRouteContext][kReplyCacheSerializeFns], null)
214
+ t.assert.strictEqual(reply.compileSerializationSchema(getDefaultSchema())(input), JSON.stringify(input))
215
+ t.assert.ok(reply[kRouteContext][kReplyCacheSerializeFns] instanceof WeakMap)
216
+ t.assert.strictEqual(reply.compileSerializationSchema(getDefaultSchema())(input), JSON.stringify(input))
217
+
218
+ reply.send({ hello: 'world' })
219
+ })
220
+
221
+ await fastify.inject({
222
+ path: '/',
223
+ method: 'GET'
224
+ })
225
+ })
226
+ })
227
+
228
+ test('Reply#getSerializationFunction', async t => {
229
+ t.plan(3)
230
+
231
+ await t.test('Should retrieve the serialization function from the Schema definition',
232
+ async t => {
233
+ const fastify = Fastify()
234
+ const okInput201 = {
235
+ status: 'ok',
236
+ message: 'done!'
237
+ }
238
+ const notOkInput201 = {
239
+ message: 'created'
240
+ }
241
+ const okInput4xx = {
242
+ status: 'error',
243
+ code: 2,
244
+ message: 'oops!'
245
+ }
246
+ const notOkInput4xx = {
247
+ status: 'error',
248
+ code: 'something'
249
+ }
250
+ const okInput3xx = {
251
+ fullName: 'Jone',
252
+ phone: 0
253
+ }
254
+ const noOkInput3xx = {
255
+ fullName: 'Jone',
256
+ phone: 'phone'
257
+ }
258
+ let cached4xx
259
+ let cached201
260
+ let cachedJson3xx
261
+
262
+ t.plan(13)
263
+
264
+ const responseSchema = getResponseSchema()
265
+
266
+ fastify.get(
267
+ '/:id',
268
+ {
269
+ params: {
270
+ type: 'object',
271
+ properties: {
272
+ id: {
273
+ type: 'integer'
274
+ }
275
+ }
276
+ },
277
+ schema: {
278
+ response: responseSchema
279
+ }
280
+ },
281
+ (req, reply) => {
282
+ const { id } = req.params
283
+
284
+ if (Number(id) === 1) {
285
+ const serialize4xx = reply.getSerializationFunction('4xx')
286
+ const serialize201 = reply.getSerializationFunction(201)
287
+ const serializeJson3xx = reply.getSerializationFunction('3xx', 'application/json')
288
+ const serializeUndefined = reply.getSerializationFunction(undefined)
289
+
290
+ cached4xx = serialize4xx
291
+ cached201 = serialize201
292
+ cachedJson3xx = serializeJson3xx
293
+
294
+ t.assert.ok(serialize4xx instanceof Function)
295
+ t.assert.ok(serialize201 instanceof Function)
296
+ t.assert.ok(serializeJson3xx instanceof Function)
297
+ t.assert.strictEqual(serialize4xx(okInput4xx), JSON.stringify(okInput4xx))
298
+ t.assert.strictEqual(serialize201(okInput201), JSON.stringify(okInput201))
299
+ t.assert.strictEqual(serializeJson3xx(okInput3xx), JSON.stringify(okInput3xx))
300
+ t.assert.ok(!serializeUndefined)
301
+
302
+ try {
303
+ serialize4xx(notOkInput4xx)
304
+ } catch (err) {
305
+ t.assert.strictEqual(
306
+ err.message,
307
+ 'The value "something" cannot be converted to an integer.'
308
+ )
309
+ }
310
+
311
+ try {
312
+ serialize201(notOkInput201)
313
+ } catch (err) {
314
+ t.assert.strictEqual(err.message, '"status" is required!')
315
+ }
316
+
317
+ try {
318
+ serializeJson3xx(noOkInput3xx)
319
+ } catch (err) {
320
+ t.assert.strictEqual(err.message, 'The value "phone" cannot be converted to a number.')
321
+ }
322
+
323
+ reply.status(201).send(okInput201)
324
+ } else {
325
+ const serialize201 = reply.getSerializationFunction(201)
326
+ const serialize4xx = reply.getSerializationFunction('4xx')
327
+ const serializeJson3xx = reply.getSerializationFunction('3xx', 'application/json')
328
+
329
+ t.assert.strictEqual(serialize4xx, cached4xx)
330
+ t.assert.strictEqual(serialize201, cached201)
331
+ t.assert.strictEqual(serializeJson3xx, cachedJson3xx)
332
+ reply.status(401).send(okInput4xx)
333
+ }
334
+ }
335
+ )
336
+
337
+ await Promise.all([
338
+ fastify.inject('/1'),
339
+ fastify.inject('/2')
340
+ ])
341
+ }
342
+ )
343
+
344
+ await t.test('Should retrieve the serialization function from the cached one',
345
+ async t => {
346
+ const fastify = Fastify()
347
+
348
+ const schemaObj = getDefaultSchema()
349
+
350
+ const okInput = {
351
+ hello: 'world',
352
+ world: 'done!'
353
+ }
354
+ const notOkInput = {
355
+ world: 'done!'
356
+ }
357
+ let cached
358
+
359
+ t.plan(6)
360
+
361
+ fastify.get(
362
+ '/:id',
363
+ {
364
+ params: {
365
+ type: 'object',
366
+ properties: {
367
+ id: {
368
+ type: 'integer'
369
+ }
370
+ }
371
+ }
372
+ },
373
+ (req, reply) => {
374
+ const { id } = req.params
375
+
376
+ if (Number(id) === 1) {
377
+ const serialize = reply.compileSerializationSchema(schemaObj)
378
+
379
+ t.assert.ok(serialize instanceof Function)
380
+ t.assert.strictEqual(serialize(okInput), JSON.stringify(okInput))
381
+
382
+ try {
383
+ serialize(notOkInput)
384
+ } catch (err) {
385
+ t.assert.strictEqual(err.message, '"hello" is required!')
386
+ }
387
+
388
+ cached = serialize
389
+ } else {
390
+ const serialize = reply.getSerializationFunction(schemaObj)
391
+
392
+ t.assert.strictEqual(serialize, cached)
393
+ t.assert.strictEqual(serialize(okInput), JSON.stringify(okInput))
394
+
395
+ try {
396
+ serialize(notOkInput)
397
+ } catch (err) {
398
+ t.assert.strictEqual(err.message, '"hello" is required!')
399
+ }
400
+ }
401
+
402
+ reply.status(201).send(okInput)
403
+ }
404
+ )
405
+
406
+ await Promise.all([
407
+ fastify.inject('/1'),
408
+ fastify.inject('/2')
409
+ ])
410
+ }
411
+ )
412
+
413
+ await t.test('Should not instantiate a WeakMap if it is not needed', async t => {
414
+ const fastify = Fastify()
415
+
416
+ t.plan(4)
417
+
418
+ fastify.get('/', (req, reply) => {
419
+ t.assert.ok(!reply.getSerializationFunction(getDefaultSchema()))
420
+ t.assert.strictEqual(reply[kRouteContext][kReplyCacheSerializeFns], null)
421
+ t.assert.ok(!reply.getSerializationFunction('200'))
422
+ t.assert.strictEqual(reply[kRouteContext][kReplyCacheSerializeFns], null)
423
+
424
+ reply.send({ hello: 'world' })
425
+ })
426
+
427
+ await fastify.inject({
428
+ path: '/',
429
+ method: 'GET'
430
+ })
431
+ })
432
+ })
433
+
434
+ test('Reply#serializeInput', async t => {
435
+ t.plan(6)
436
+
437
+ await t.test(
438
+ 'Should throw if missed serialization function from HTTP status',
439
+ async t => {
440
+ const fastify = Fastify()
441
+
442
+ t.plan(2)
443
+
444
+ fastify.get('/', (req, reply) => {
445
+ reply.serializeInput({}, 201)
446
+ })
447
+
448
+ const result = await fastify.inject({
449
+ path: '/',
450
+ method: 'GET'
451
+ })
452
+
453
+ t.assert.strictEqual(result.statusCode, 500)
454
+ t.assert.deepStrictEqual(result.json(), {
455
+ statusCode: 500,
456
+ code: 'FST_ERR_MISSING_SERIALIZATION_FN',
457
+ error: 'Internal Server Error',
458
+ message: 'Missing serialization function. Key "201"'
459
+ })
460
+ }
461
+ )
462
+
463
+ await t.test(
464
+ 'Should throw if missed serialization function from HTTP status with specific content type',
465
+ async t => {
466
+ const fastify = Fastify()
467
+
468
+ t.plan(2)
469
+
470
+ fastify.get('/', {
471
+ schema: {
472
+ response: {
473
+ '3xx': {
474
+ content: {
475
+ 'application/json': {
476
+ schema: {
477
+ type: 'object',
478
+ properties: {
479
+ fullName: { type: 'string' },
480
+ phone: { type: 'number' }
481
+ }
482
+ }
483
+ }
484
+ }
485
+ }
486
+ }
487
+ }
488
+ }, (req, reply) => {
489
+ reply.serializeInput({}, '3xx', 'application/vnd.v1+json')
490
+ })
491
+
492
+ const result = await fastify.inject({
493
+ path: '/',
494
+ method: 'GET'
495
+ })
496
+
497
+ t.assert.strictEqual(result.statusCode, 500)
498
+ t.assert.deepStrictEqual(result.json(), {
499
+ statusCode: 500,
500
+ code: 'FST_ERR_MISSING_CONTENTTYPE_SERIALIZATION_FN',
501
+ error: 'Internal Server Error',
502
+ message: 'Missing serialization function. Key "3xx:application/vnd.v1+json"'
503
+ })
504
+ }
505
+ )
506
+
507
+ await t.test('Should use a serializer fn from HTTP status', async t => {
508
+ const fastify = Fastify()
509
+ const okInput201 = {
510
+ status: 'ok',
511
+ message: 'done!'
512
+ }
513
+ const notOkInput201 = {
514
+ message: 'created'
515
+ }
516
+ const okInput4xx = {
517
+ status: 'error',
518
+ code: 2,
519
+ message: 'oops!'
520
+ }
521
+ const notOkInput4xx = {
522
+ status: 'error',
523
+ code: 'something'
524
+ }
525
+ const okInput3xx = {
526
+ fullName: 'Jone',
527
+ phone: 0
528
+ }
529
+ const noOkInput3xx = {
530
+ fullName: 'Jone',
531
+ phone: 'phone'
532
+ }
533
+
534
+ t.plan(6)
535
+
536
+ fastify.get(
537
+ '/',
538
+ {
539
+ params: {
540
+ type: 'object',
541
+ properties: {
542
+ id: {
543
+ type: 'integer'
544
+ }
545
+ }
546
+ },
547
+ schema: {
548
+ response: getResponseSchema()
549
+ }
550
+ },
551
+ (req, reply) => {
552
+ t.assert.strictEqual(
553
+ reply.serializeInput(okInput4xx, '4xx'),
554
+ JSON.stringify(okInput4xx)
555
+ )
556
+ t.assert.strictEqual(
557
+ reply.serializeInput(okInput201, 201),
558
+ JSON.stringify(okInput201)
559
+ )
560
+
561
+ t.assert.strictEqual(
562
+ reply.serializeInput(okInput3xx, {}, '3xx', 'application/json'),
563
+ JSON.stringify(okInput3xx)
564
+ )
565
+
566
+ try {
567
+ reply.serializeInput(noOkInput3xx, '3xx', 'application/json')
568
+ } catch (err) {
569
+ t.assert.strictEqual(err.message, 'The value "phone" cannot be converted to a number.')
570
+ }
571
+
572
+ try {
573
+ reply.serializeInput(notOkInput4xx, '4xx')
574
+ } catch (err) {
575
+ t.assert.strictEqual(
576
+ err.message,
577
+ 'The value "something" cannot be converted to an integer.'
578
+ )
579
+ }
580
+
581
+ try {
582
+ reply.serializeInput(notOkInput201, 201)
583
+ } catch (err) {
584
+ t.assert.strictEqual(err.message, '"status" is required!')
585
+ }
586
+
587
+ reply.status(204).send('')
588
+ }
589
+ )
590
+
591
+ await fastify.inject({
592
+ path: '/',
593
+ method: 'GET'
594
+ })
595
+ })
596
+
597
+ await t.test(
598
+ 'Should compile a serializer out of a schema if serializer fn missed',
599
+ async t => {
600
+ let compilerCalled = 0
601
+ let serializerCalled = 0
602
+ const testInput = { hello: 'world' }
603
+ const schemaObj = getDefaultSchema()
604
+ const fastify = Fastify()
605
+ const serializerCompiler = ({ schema, httpStatus, method, url }) => {
606
+ t.assert.strictEqual(schema, schemaObj)
607
+ t.assert.ok(!httpStatus)
608
+ t.assert.strictEqual(method, 'GET')
609
+ t.assert.strictEqual(url, '/')
610
+
611
+ compilerCalled++
612
+ return input => {
613
+ t.assert.strictEqual(input, testInput)
614
+ serializerCalled++
615
+ return JSON.stringify(input)
616
+ }
617
+ }
618
+
619
+ t.plan(10)
620
+
621
+ fastify.get('/', { serializerCompiler }, (req, reply) => {
622
+ t.assert.strictEqual(
623
+ reply.serializeInput(testInput, schemaObj),
624
+ JSON.stringify(testInput)
625
+ )
626
+
627
+ t.assert.strictEqual(
628
+ reply.serializeInput(testInput, schemaObj),
629
+ JSON.stringify(testInput)
630
+ )
631
+
632
+ reply.status(201).send(testInput)
633
+ })
634
+
635
+ await fastify.inject({
636
+ path: '/',
637
+ method: 'GET'
638
+ })
639
+
640
+ t.assert.strictEqual(compilerCalled, 1)
641
+ t.assert.strictEqual(serializerCalled, 2)
642
+ }
643
+ )
644
+
645
+ await t.test('Should use a cached serializer fn', async t => {
646
+ let compilerCalled = 0
647
+ let serializerCalled = 0
648
+ let cached
649
+ const testInput = { hello: 'world' }
650
+ const schemaObj = getDefaultSchema()
651
+ const fastify = Fastify()
652
+ const serializer = input => {
653
+ t.assert.strictEqual(input, testInput)
654
+ serializerCalled++
655
+ return JSON.stringify(input)
656
+ }
657
+ const serializerCompiler = ({ schema, httpStatus, method, url }) => {
658
+ t.assert.strictEqual(schema, schemaObj)
659
+ t.assert.ok(!httpStatus)
660
+ t.assert.strictEqual(method, 'GET')
661
+ t.assert.strictEqual(url, '/')
662
+
663
+ compilerCalled++
664
+ return serializer
665
+ }
666
+
667
+ t.plan(12)
668
+
669
+ fastify.get('/', { serializerCompiler }, (req, reply) => {
670
+ t.assert.strictEqual(
671
+ reply.serializeInput(testInput, schemaObj),
672
+ JSON.stringify(testInput)
673
+ )
674
+
675
+ cached = reply.getSerializationFunction(schemaObj)
676
+
677
+ t.assert.strictEqual(
678
+ reply.serializeInput(testInput, schemaObj),
679
+ cached(testInput)
680
+ )
681
+
682
+ reply.status(201).send(testInput)
683
+ })
684
+
685
+ await fastify.inject({
686
+ path: '/',
687
+ method: 'GET'
688
+ })
689
+
690
+ t.assert.strictEqual(cached, serializer)
691
+ t.assert.strictEqual(compilerCalled, 1)
692
+ t.assert.strictEqual(serializerCalled, 3)
693
+ })
694
+
695
+ await t.test('Should instantiate a WeakMap after first call', async t => {
696
+ const fastify = Fastify()
697
+
698
+ t.plan(3)
699
+
700
+ fastify.get('/', (req, reply) => {
701
+ const input = { hello: 'world' }
702
+ t.assert.strictEqual(reply[kRouteContext][kReplyCacheSerializeFns], null)
703
+ t.assert.strictEqual(reply.serializeInput(input, getDefaultSchema()), JSON.stringify(input))
704
+ t.assert.ok(reply[kRouteContext][kReplyCacheSerializeFns] instanceof WeakMap)
705
+
706
+ reply.send({ hello: 'world' })
707
+ })
708
+
709
+ await fastify.inject({
710
+ path: '/',
711
+ method: 'GET'
712
+ })
713
+ })
714
+ })