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,1213 @@
1
+ import fastify, {
2
+ FastifyTypeProvider,
3
+ HookHandlerDoneFunction,
4
+ FastifyRequest,
5
+ FastifyReply,
6
+ FastifyInstance,
7
+ FastifyError,
8
+ SafePromiseLike
9
+ } from '../../fastify'
10
+ import { expectAssignable, expectError, expectType } from 'tsd'
11
+ import { IncomingHttpHeaders } from 'node:http'
12
+ import { Type, TSchema, Static } from 'typebox'
13
+ import { FromSchema, JSONSchema } from 'json-schema-to-ts'
14
+
15
+ const server = fastify()
16
+
17
+ // -------------------------------------------------------------------
18
+ // Default (unknown)
19
+ // -------------------------------------------------------------------
20
+
21
+ expectAssignable(server.get('/', (req) => expectType<unknown>(req.body)))
22
+
23
+ // -------------------------------------------------------------------
24
+ // Remapping
25
+ // -------------------------------------------------------------------
26
+
27
+ interface NumberProvider extends FastifyTypeProvider {
28
+ validator: number
29
+ serializer: number
30
+ } // remap all schemas to numbers
31
+
32
+ expectAssignable(server.withTypeProvider<NumberProvider>().get(
33
+ '/',
34
+ {
35
+ schema: {
36
+ body: { type: 'string' },
37
+ querystring: { type: 'string' },
38
+ headers: { type: 'string' },
39
+ params: { type: 'string' }
40
+ }
41
+ },
42
+ (req) => {
43
+ expectType<number & IncomingHttpHeaders>(req.headers)
44
+ expectType<number>(req.body)
45
+ expectType<number>(req.query)
46
+ expectType<number>(req.params)
47
+ }
48
+ ))
49
+
50
+ // -------------------------------------------------------------------
51
+ // Override
52
+ // -------------------------------------------------------------------
53
+
54
+ interface OverriddenProvider extends FastifyTypeProvider { validator: 'inferenced' }
55
+
56
+ expectAssignable(server.withTypeProvider<OverriddenProvider>().get<{ Body: 'override' }>(
57
+ '/',
58
+ {
59
+ schema: {
60
+ body: Type.Object({
61
+ x: Type.Number(),
62
+ y: Type.Number(),
63
+ z: Type.Number()
64
+ })
65
+ }
66
+ },
67
+ (req) => {
68
+ expectType<'override'>(req.body)
69
+ }
70
+ ))
71
+
72
+ // -------------------------------------------------------------------
73
+ // TypeBox
74
+ // -------------------------------------------------------------------
75
+
76
+ interface TypeBoxProvider extends FastifyTypeProvider {
77
+ validator: this['schema'] extends TSchema ? Static<this['schema']> : unknown
78
+ serializer: this['schema'] extends TSchema ? Static<this['schema']> : unknown
79
+ }
80
+
81
+ expectAssignable(server.withTypeProvider<TypeBoxProvider>().get(
82
+ '/',
83
+ {
84
+ schema: {
85
+ body: Type.Object({
86
+ x: Type.Number(),
87
+ y: Type.Number(),
88
+ z: Type.Number()
89
+ })
90
+ },
91
+ errorHandler: (error, request, reply) => {
92
+ expectType<FastifyError>(error)
93
+ expectAssignable<FastifyRequest>(request)
94
+ expectType<number>(request.body.x)
95
+ expectType<number>(request.body.y)
96
+ expectType<number>(request.body.z)
97
+ expectAssignable<FastifyReply>(reply)
98
+ }
99
+ },
100
+ (req) => {
101
+ expectType<number>(req.body.x)
102
+ expectType<number>(req.body.y)
103
+ expectType<number>(req.body.z)
104
+ }
105
+ ))
106
+
107
+ expectAssignable<FastifyInstance>(server.withTypeProvider<TypeBoxProvider>())
108
+
109
+ // -------------------------------------------------------------------
110
+ // JsonSchemaToTs
111
+ // -------------------------------------------------------------------
112
+
113
+ interface JsonSchemaToTsProvider extends FastifyTypeProvider {
114
+ validator: this['schema'] extends JSONSchema ? FromSchema<this['schema']> : unknown
115
+ serializer: this['schema'] extends JSONSchema ? FromSchema<this['schema']> : unknown
116
+ }
117
+
118
+ // explicitly setting schema `as const`
119
+
120
+ expectAssignable(server.withTypeProvider<JsonSchemaToTsProvider>().get(
121
+ '/',
122
+ {
123
+ schema: {
124
+ body: {
125
+ type: 'object',
126
+ properties: {
127
+ x: { type: 'number' },
128
+ y: { type: 'string' },
129
+ z: { type: 'boolean' }
130
+ }
131
+ } as const
132
+ },
133
+ errorHandler: (error, request, reply) => {
134
+ expectType<FastifyError>(error)
135
+ expectAssignable<FastifyRequest>(request)
136
+ expectType<number | undefined>(request.body.x)
137
+ expectType<string | undefined>(request.body.y)
138
+ expectType<boolean | undefined>(request.body.z)
139
+ expectAssignable<FastifyReply>(reply)
140
+ }
141
+ },
142
+ (req) => {
143
+ expectType<number | undefined>(req.body.x)
144
+ expectType<string | undefined>(req.body.y)
145
+ expectType<boolean | undefined>(req.body.z)
146
+ }
147
+ ))
148
+
149
+ expectAssignable(server.withTypeProvider<JsonSchemaToTsProvider>().route({
150
+ url: '/',
151
+ method: 'POST',
152
+ schema: {
153
+ body: {
154
+ type: 'object',
155
+ properties: {
156
+ x: { type: 'number' },
157
+ y: { type: 'string' },
158
+ z: { type: 'boolean' }
159
+ }
160
+ }
161
+ } as const,
162
+ errorHandler: (error, request, reply) => {
163
+ expectType<FastifyError>(error)
164
+ expectAssignable<FastifyRequest>(request)
165
+ expectType<number | undefined>(request.body.x)
166
+ expectType<string | undefined>(request.body.y)
167
+ expectType<boolean | undefined>(request.body.z)
168
+ expectAssignable<FastifyReply>(reply)
169
+ },
170
+ handler: (req) => {
171
+ expectType<number | undefined>(req.body.x)
172
+ expectType<string | undefined>(req.body.y)
173
+ expectType<boolean | undefined>(req.body.z)
174
+ }
175
+ }))
176
+
177
+ // inferring schema `as const`
178
+
179
+ expectAssignable(server.withTypeProvider<JsonSchemaToTsProvider>().get(
180
+ '/',
181
+ {
182
+ schema: {
183
+ body: {
184
+ type: 'object',
185
+ properties: {
186
+ x: { type: 'number' },
187
+ y: { type: 'string' },
188
+ z: { type: 'boolean' }
189
+ }
190
+ }
191
+ },
192
+ errorHandler: (error, request, reply) => {
193
+ expectType<FastifyError>(error)
194
+ expectAssignable<FastifyRequest>(request)
195
+ expectType<number | undefined>(request.body.x)
196
+ expectType<string | undefined>(request.body.y)
197
+ expectType<boolean | undefined>(request.body.z)
198
+ expectAssignable<FastifyReply>(reply)
199
+ }
200
+ },
201
+ (req) => {
202
+ expectType<number | undefined>(req.body.x)
203
+ expectType<string | undefined>(req.body.y)
204
+ expectType<boolean | undefined>(req.body.z)
205
+ }
206
+ ))
207
+
208
+ expectAssignable(server.withTypeProvider<JsonSchemaToTsProvider>().route({
209
+ url: '/',
210
+ method: 'POST',
211
+ schema: {
212
+ body: {
213
+ type: 'object',
214
+ properties: {
215
+ x: { type: 'number' },
216
+ y: { type: 'string' },
217
+ z: { type: 'boolean' }
218
+ }
219
+ }
220
+ },
221
+ errorHandler: (error, request, reply) => {
222
+ expectType<FastifyError>(error)
223
+ expectAssignable<FastifyRequest>(request)
224
+ expectType<number | undefined>(request.body.x)
225
+ expectType<string | undefined>(request.body.y)
226
+ expectType<boolean | undefined>(request.body.z)
227
+ expectAssignable<FastifyReply>(reply)
228
+ },
229
+ handler: (req) => {
230
+ expectType<number | undefined>(req.body.x)
231
+ expectType<string | undefined>(req.body.y)
232
+ expectType<boolean | undefined>(req.body.z)
233
+ }
234
+ }))
235
+
236
+ expectAssignable<FastifyInstance>(server.withTypeProvider<JsonSchemaToTsProvider>())
237
+
238
+ // -------------------------------------------------------------------
239
+ // Instance Type Remappable
240
+ // -------------------------------------------------------------------
241
+
242
+ expectAssignable(server.withTypeProvider<TypeBoxProvider>().withTypeProvider<JsonSchemaToTsProvider>().get(
243
+ '/',
244
+ {
245
+ schema: {
246
+ body: {
247
+ type: 'object',
248
+ properties: {
249
+ x: { type: 'number' },
250
+ y: { type: 'string' },
251
+ z: { type: 'boolean' }
252
+ }
253
+ } as const
254
+ },
255
+ errorHandler: (error, request, reply) => {
256
+ expectType<FastifyError>(error)
257
+ expectAssignable<FastifyRequest>(request)
258
+ expectType<number | undefined>(request.body.x)
259
+ expectType<string | undefined>(request.body.y)
260
+ expectType<boolean | undefined>(request.body.z)
261
+ expectAssignable<FastifyReply>(reply)
262
+ }
263
+ },
264
+ (req) => {
265
+ expectType<number | undefined>(req.body.x)
266
+ expectType<string | undefined>(req.body.y)
267
+ expectType<boolean | undefined>(req.body.z)
268
+ }
269
+ ))
270
+
271
+ // -------------------------------------------------------------------
272
+ // Request Hooks
273
+ // -------------------------------------------------------------------
274
+
275
+ // Sync handlers
276
+
277
+ expectAssignable(server.withTypeProvider<TypeBoxProvider>().get(
278
+ '/',
279
+ {
280
+ schema: {
281
+ body: Type.Object({
282
+ x: Type.Number(),
283
+ y: Type.String(),
284
+ z: Type.Boolean()
285
+ })
286
+ },
287
+ preHandler: (req, reply, done) => {
288
+ expectType<number>(req.body.x)
289
+ expectType<string>(req.body.y)
290
+ expectType<boolean>(req.body.z)
291
+ },
292
+ preParsing: (req, reply, payload, done) => {
293
+ expectType<number>(req.body.x)
294
+ expectType<string>(req.body.y)
295
+ expectType<boolean>(req.body.z)
296
+ },
297
+ preSerialization: (req, reply, payload, done) => {
298
+ expectType<number>(req.body.x)
299
+ expectType<string>(req.body.y)
300
+ expectType<boolean>(req.body.z)
301
+ },
302
+ preValidation: (req, reply, done) => {
303
+ expectType<number>(req.body.x)
304
+ expectType<string>(req.body.y)
305
+ expectType<boolean>(req.body.z)
306
+ },
307
+ onError: (req, reply, error, done) => {
308
+ expectType<number>(req.body.x)
309
+ expectType<string>(req.body.y)
310
+ expectType<boolean>(req.body.z)
311
+ },
312
+ onRequest: (req, reply, done) => {
313
+ expectType<number>(req.body.x)
314
+ expectType<string>(req.body.y)
315
+ expectType<boolean>(req.body.z)
316
+ },
317
+ onResponse: (req, reply, done) => {
318
+ expectType<number>(req.body.x)
319
+ expectType<string>(req.body.y)
320
+ expectType<boolean>(req.body.z)
321
+ },
322
+ onTimeout: (req, reply, done) => {
323
+ expectType<number>(req.body.x)
324
+ expectType<string>(req.body.y)
325
+ expectType<boolean>(req.body.z)
326
+ },
327
+ onSend: (req, reply, payload, done) => {
328
+ expectType<number>(req.body.x)
329
+ expectType<string>(req.body.y)
330
+ expectType<boolean>(req.body.z)
331
+ }
332
+ },
333
+ req => {
334
+ expectType<number>(req.body.x)
335
+ expectType<string>(req.body.y)
336
+ expectType<boolean>(req.body.z)
337
+ }
338
+ ))
339
+
340
+ // Async handlers
341
+
342
+ expectAssignable(server.withTypeProvider<TypeBoxProvider>().get(
343
+ '/',
344
+ {
345
+ schema: {
346
+ body: Type.Object({
347
+ x: Type.Number(),
348
+ y: Type.String(),
349
+ z: Type.Boolean()
350
+ })
351
+ },
352
+ preHandler: async (req, reply, done) => {
353
+ expectType<number>(req.body.x)
354
+ expectType<string>(req.body.y)
355
+ expectType<boolean>(req.body.z)
356
+ },
357
+ preParsing: async (req, reply, payload, done) => {
358
+ expectType<number>(req.body.x)
359
+ expectType<string>(req.body.y)
360
+ expectType<boolean>(req.body.z)
361
+ },
362
+ preSerialization: async (req, reply, payload, done) => {
363
+ expectType<number>(req.body.x)
364
+ expectType<string>(req.body.y)
365
+ expectType<boolean>(req.body.z)
366
+ },
367
+ preValidation: async (req, reply, done) => {
368
+ expectType<number>(req.body.x)
369
+ expectType<string>(req.body.y)
370
+ expectType<boolean>(req.body.z)
371
+ },
372
+ onError: async (req, reply, error, done) => {
373
+ expectType<number>(req.body.x)
374
+ expectType<string>(req.body.y)
375
+ expectType<boolean>(req.body.z)
376
+ },
377
+ onRequest: async (req, reply, done) => {
378
+ expectType<number>(req.body.x)
379
+ expectType<string>(req.body.y)
380
+ expectType<boolean>(req.body.z)
381
+ },
382
+ onResponse: async (req, reply, done) => {
383
+ expectType<number>(req.body.x)
384
+ expectType<string>(req.body.y)
385
+ expectType<boolean>(req.body.z)
386
+ },
387
+ onTimeout: async (req, reply, done) => {
388
+ expectType<number>(req.body.x)
389
+ expectType<string>(req.body.y)
390
+ expectType<boolean>(req.body.z)
391
+ },
392
+ onSend: async (req, reply, payload, done) => {
393
+ expectType<number>(req.body.x)
394
+ expectType<string>(req.body.y)
395
+ expectType<boolean>(req.body.z)
396
+ }
397
+ },
398
+ req => {
399
+ expectType<number>(req.body.x)
400
+ expectType<string>(req.body.y)
401
+ expectType<boolean>(req.body.z)
402
+ }
403
+ ))
404
+
405
+ // -------------------------------------------------------------------
406
+ // Request headers
407
+ // -------------------------------------------------------------------
408
+
409
+ // JsonSchemaToTsProvider
410
+ expectAssignable(server.withTypeProvider<JsonSchemaToTsProvider>().get(
411
+ '/',
412
+ {
413
+ schema: {
414
+ headers: {
415
+ type: 'object',
416
+ properties: {
417
+ lowercase: { type: 'string' },
418
+ UPPERCASE: { type: 'number' },
419
+ camelCase: { type: 'boolean' },
420
+ 'KEBAB-case': { type: 'boolean' },
421
+ PRESERVE_OPTIONAL: { type: 'number' }
422
+ },
423
+ required: ['lowercase', 'UPPERCASE', 'camelCase', 'KEBAB-case']
424
+ } as const
425
+ }
426
+ },
427
+ (req) => {
428
+ expectType<string>(req.headers.lowercase)
429
+ expectType<string | string[] | undefined>(req.headers.UPPERCASE)
430
+ expectType<number>(req.headers.uppercase)
431
+ expectType<boolean>(req.headers.camelcase)
432
+ expectType<boolean>(req.headers['kebab-case'])
433
+ expectType<number | undefined>(req.headers.preserve_optional)
434
+ }
435
+ ))
436
+
437
+ // TypeBoxProvider
438
+ expectAssignable(server.withTypeProvider<TypeBoxProvider>().get(
439
+ '/',
440
+ {
441
+ schema: {
442
+ headers: Type.Object({
443
+ lowercase: Type.String(),
444
+ UPPERCASE: Type.Number(),
445
+ camelCase: Type.Boolean(),
446
+ 'KEBAB-case': Type.Boolean(),
447
+ PRESERVE_OPTIONAL: Type.Optional(Type.Number())
448
+ })
449
+ }
450
+ },
451
+ (req) => {
452
+ expectType<string>(req.headers.lowercase)
453
+ expectType<string | string[] | undefined>(req.headers.UPPERCASE)
454
+ expectType<number>(req.headers.uppercase)
455
+ expectType<boolean>(req.headers.camelcase)
456
+ expectType<boolean>(req.headers['kebab-case'])
457
+ expectType<number | undefined>(req.headers.preserve_optional)
458
+ }
459
+ ))
460
+
461
+ // -------------------------------------------------------------------
462
+ // TypeBox Reply Type
463
+ // -------------------------------------------------------------------
464
+
465
+ expectAssignable(server.withTypeProvider<TypeBoxProvider>().get(
466
+ '/',
467
+ {
468
+ schema: {
469
+ response: {
470
+ 200: Type.String(),
471
+ 400: Type.Number(),
472
+ 500: Type.Object({
473
+ error: Type.String()
474
+ })
475
+ }
476
+ }
477
+ },
478
+ async (_, res) => {
479
+ res.send('hello')
480
+ res.send(42)
481
+ res.send({ error: 'error' })
482
+ expectType<((...args: [payload: string]) => typeof res)>(res.code(200).send)
483
+ expectType<((...args: [payload: number]) => typeof res)>(res.code(400).send)
484
+ expectType<((...args: [payload: { error: string }]) => typeof res)>(res.code(500).send)
485
+ expectError<(payload?: unknown) => typeof res>(res.code(200).send)
486
+ }
487
+ ))
488
+
489
+ // -------------------------------------------------------------------
490
+ // TypeBox Reply Type (Different Content-types)
491
+ // -------------------------------------------------------------------
492
+
493
+ expectAssignable(server.withTypeProvider<TypeBoxProvider>().get(
494
+ '/',
495
+ {
496
+ schema: {
497
+ response: {
498
+ 200: {
499
+ content: {
500
+ 'text/string': {
501
+ schema: Type.String()
502
+ },
503
+ 'application/json': {
504
+ schema: Type.Object({
505
+ msg: Type.String()
506
+ })
507
+ }
508
+ }
509
+ },
510
+ 500: Type.Object({
511
+ error: Type.String()
512
+ })
513
+ }
514
+ }
515
+ },
516
+ async (_, res) => {
517
+ res.send('hello')
518
+ res.send({ msg: 'hello' })
519
+ res.send({ error: 'error' })
520
+ }
521
+ ))
522
+
523
+ // -------------------------------------------------------------------
524
+ // TypeBox Reply Type: Non Assignable
525
+ // -------------------------------------------------------------------
526
+
527
+ expectError(server.withTypeProvider<TypeBoxProvider>().get(
528
+ '/',
529
+ {
530
+ schema: {
531
+ response: {
532
+ 200: Type.String(),
533
+ 400: Type.Number(),
534
+ 500: Type.Object({
535
+ error: Type.String()
536
+ })
537
+ }
538
+ }
539
+ },
540
+ async (_, res) => {
541
+ res.send(false)
542
+ }
543
+ ))
544
+
545
+ // -------------------------------------------------------------------
546
+ // TypeBox Reply Type: Non Assignable (Different Content-types)
547
+ // -------------------------------------------------------------------
548
+
549
+ expectError(server.withTypeProvider<TypeBoxProvider>().get(
550
+ '/',
551
+ {
552
+ schema: {
553
+ response: {
554
+ 200: {
555
+ content: {
556
+ 'text/string': {
557
+ schema: Type.String()
558
+ },
559
+ 'application/json': {
560
+ schema: Type.Object({
561
+ msg: Type.String()
562
+ })
563
+ }
564
+ }
565
+ },
566
+ 500: Type.Object({
567
+ error: Type.String()
568
+ })
569
+ }
570
+ }
571
+ },
572
+ async (_, res) => {
573
+ res.send(false)
574
+ }
575
+ ))
576
+
577
+ // -------------------------------------------------------------------
578
+ // TypeBox Reply Return Type
579
+ // -------------------------------------------------------------------
580
+
581
+ expectAssignable(server.withTypeProvider<TypeBoxProvider>().get(
582
+ '/',
583
+ {
584
+ schema: {
585
+ response: {
586
+ 200: Type.String(),
587
+ 400: Type.Number(),
588
+ 500: Type.Object({
589
+ error: Type.String()
590
+ })
591
+ }
592
+ }
593
+ },
594
+ async (_, res) => {
595
+ const option = 1 as 1 | 2 | 3
596
+ switch (option) {
597
+ case 1: return 'hello'
598
+ case 2: return 42
599
+ case 3: return { error: 'error' }
600
+ }
601
+ }
602
+ ))
603
+
604
+ // -------------------------------------------------------------------
605
+ // TypeBox Reply Return Type (Different Content-types)
606
+ // -------------------------------------------------------------------
607
+
608
+ expectAssignable(server.withTypeProvider<TypeBoxProvider>().get(
609
+ '/',
610
+ {
611
+ schema: {
612
+ response: {
613
+ 200: {
614
+ content: {
615
+ 'text/string': {
616
+ schema: Type.String()
617
+ },
618
+ 'application/json': {
619
+ schema: Type.Object({
620
+ msg: Type.String()
621
+ })
622
+ }
623
+ }
624
+ },
625
+ 500: Type.Object({
626
+ error: Type.String()
627
+ })
628
+ }
629
+ }
630
+ },
631
+ async (_, res) => {
632
+ const option = 1 as 1 | 2 | 3
633
+ switch (option) {
634
+ case 1: return 'hello'
635
+ case 2: return { msg: 'hello' }
636
+ case 3: return { error: 'error' }
637
+ }
638
+ }
639
+ ))
640
+
641
+ // -------------------------------------------------------------------
642
+ // TypeBox Reply Return Type: Non Assignable
643
+ // -------------------------------------------------------------------
644
+
645
+ expectError(server.withTypeProvider<TypeBoxProvider>().get(
646
+ '/',
647
+ {
648
+ schema: {
649
+ response: {
650
+ 200: Type.String(),
651
+ 400: Type.Number(),
652
+ 500: Type.Object({
653
+ error: Type.String()
654
+ })
655
+ }
656
+ }
657
+ },
658
+ async (_, res) => {
659
+ return false
660
+ }
661
+ ))
662
+
663
+ // -------------------------------------------------------------------
664
+ // TypeBox Reply Return Type: Non Assignable (Different Content-types)
665
+ // -------------------------------------------------------------------
666
+
667
+ expectError(server.withTypeProvider<TypeBoxProvider>().get(
668
+ '/',
669
+ {
670
+ schema: {
671
+ response: {
672
+ 200: {
673
+ content: {
674
+ 'text/string': {
675
+ schema: Type.String()
676
+ },
677
+ 'application/json': {
678
+ schema: Type.Object({
679
+ msg: Type.String()
680
+ })
681
+ }
682
+ }
683
+ },
684
+ 500: Type.Object({
685
+ error: Type.String()
686
+ })
687
+ }
688
+ }
689
+ },
690
+ async (_, res) => {
691
+ return false
692
+ }
693
+ ))
694
+
695
+ // -------------------------------------------------------------------
696
+ // JsonSchemaToTs Reply Type
697
+ // -------------------------------------------------------------------
698
+
699
+ expectAssignable(server.withTypeProvider<JsonSchemaToTsProvider>().get(
700
+ '/',
701
+ {
702
+ schema: {
703
+ response: {
704
+ 200: { type: 'string' },
705
+ 400: { type: 'number' },
706
+ 500: { type: 'object', properties: { error: { type: 'string' } } }
707
+ } as const
708
+ }
709
+ },
710
+ (_, res) => {
711
+ res.send('hello')
712
+ res.send(42)
713
+ res.send({ error: 'error' })
714
+ expectType<((...args: [payload: string]) => typeof res)>(res.code(200).send)
715
+ expectType<((...args: [payload: number]) => typeof res)>(res.code(400).send)
716
+ expectType<((...args: [payload: { [x: string]: unknown; error?: string }]) => typeof res)>(res.code(500).send)
717
+ expectError<(payload?: unknown) => typeof res>(res.code(200).send)
718
+ }
719
+ ))
720
+
721
+ // -------------------------------------------------------------------
722
+ // JsonSchemaToTs Reply Type (Different Content-types)
723
+ // -------------------------------------------------------------------
724
+
725
+ expectAssignable(server.withTypeProvider<JsonSchemaToTsProvider>().get(
726
+ '/',
727
+ {
728
+ schema: {
729
+ response: {
730
+ 200: {
731
+ content: {
732
+ 'text/string': {
733
+ schema: { type: 'string' }
734
+ },
735
+ 'application/json': {
736
+ schema: { type: 'object', properties: { msg: { type: 'string' } } }
737
+ }
738
+ }
739
+ },
740
+ 500: { type: 'object', properties: { error: { type: 'string' } } }
741
+ } as const
742
+ }
743
+ },
744
+ (_, res) => {
745
+ res.send('hello')
746
+ res.send({ msg: 'hello' })
747
+ res.send({ error: 'error' })
748
+ }
749
+ ))
750
+
751
+ // -------------------------------------------------------------------
752
+ // JsonSchemaToTs Reply Type: Non Assignable
753
+ // -------------------------------------------------------------------
754
+
755
+ expectError(server.withTypeProvider<JsonSchemaToTsProvider>().get(
756
+ '/',
757
+ {
758
+ schema: {
759
+ response: {
760
+ 200: { type: 'string' },
761
+ 400: { type: 'number' },
762
+ 500: { type: 'object', properties: { error: { type: 'string' } } }
763
+ } as const
764
+ }
765
+ },
766
+ async (_, res) => {
767
+ res.send(false)
768
+ }
769
+ ))
770
+
771
+ // -------------------------------------------------------------------
772
+ // JsonSchemaToTs Reply Type: Non Assignable (Different Content-types)
773
+ // -------------------------------------------------------------------
774
+
775
+ expectError(server.withTypeProvider<JsonSchemaToTsProvider>().get(
776
+ '/',
777
+ {
778
+ schema: {
779
+ response: {
780
+ 200: {
781
+ content: {
782
+ 'text/string': {
783
+ schema: { type: 'string' }
784
+ },
785
+ 'application/json': {
786
+ schema: { type: 'object', properties: { msg: { type: 'string' } } }
787
+ }
788
+ }
789
+ },
790
+ 500: { type: 'object', properties: { error: { type: 'string' } } }
791
+ } as const
792
+ }
793
+ },
794
+ async (_, res) => {
795
+ res.send(false)
796
+ }
797
+ ))
798
+
799
+ // -------------------------------------------------------------------
800
+ // JsonSchemaToTs Reply Type Return
801
+ // -------------------------------------------------------------------
802
+
803
+ expectAssignable(server.withTypeProvider<JsonSchemaToTsProvider>().get(
804
+ '/',
805
+ {
806
+ schema: {
807
+ response: {
808
+ 200: { type: 'string' },
809
+ 400: { type: 'number' },
810
+ 500: { type: 'object', properties: { error: { type: 'string' } } }
811
+ } as const
812
+ }
813
+ },
814
+ async (_, res) => {
815
+ const option = 1 as 1 | 2 | 3
816
+ switch (option) {
817
+ case 1: return 'hello'
818
+ case 2: return 42
819
+ case 3: return { error: 'error' }
820
+ }
821
+ }
822
+ ))
823
+
824
+ // -------------------------------------------------------------------
825
+ // JsonSchemaToTs Reply Type Return (Different Content-types)
826
+ // -------------------------------------------------------------------
827
+
828
+ expectAssignable(server.withTypeProvider<JsonSchemaToTsProvider>().get(
829
+ '/',
830
+ {
831
+ schema: {
832
+ response: {
833
+ 200: {
834
+ content: {
835
+ 'text/string': {
836
+ schema: { type: 'string' }
837
+ },
838
+ 'application/json': {
839
+ schema: { type: 'object', properties: { msg: { type: 'string' } } }
840
+ }
841
+ }
842
+ },
843
+ 500: { type: 'object', properties: { error: { type: 'string' } } }
844
+ } as const
845
+ }
846
+ },
847
+ async (_, res) => {
848
+ const option = 1 as 1 | 2 | 3
849
+ switch (option) {
850
+ case 1: return 'hello'
851
+ case 2: return { msg: 'hello' }
852
+ case 3: return { error: 'error' }
853
+ }
854
+ }
855
+ ))
856
+
857
+ // -------------------------------------------------------------------
858
+ // JsonSchemaToTs Reply Type Return: Non Assignable
859
+ // -------------------------------------------------------------------
860
+
861
+ expectError(server.withTypeProvider<JsonSchemaToTsProvider>().get(
862
+ '/',
863
+ {
864
+ schema: {
865
+ response: {
866
+ 200: { type: 'string' },
867
+ 400: { type: 'number' },
868
+ 500: { type: 'object', properties: { error: { type: 'string' } } }
869
+ } as const
870
+ }
871
+ },
872
+ async (_, res) => {
873
+ return false
874
+ }
875
+ ))
876
+
877
+ // https://github.com/fastify/fastify/issues/4088
878
+ expectError(server.withTypeProvider<JsonSchemaToTsProvider>().get('/', {
879
+ schema: {
880
+ response: {
881
+ 200: { type: 'string' }
882
+ }
883
+ } as const
884
+ }, (_, res) => {
885
+ return { foo: 555 }
886
+ }))
887
+
888
+ // -------------------------------------------------------------------
889
+ // JsonSchemaToTs Reply Type Return: Non Assignable (Different Content-types)
890
+ // -------------------------------------------------------------------
891
+
892
+ expectError(server.withTypeProvider<JsonSchemaToTsProvider>().get(
893
+ '/',
894
+ {
895
+ schema: {
896
+ response: {
897
+ 200: {
898
+ content: {
899
+ 'text/string': {
900
+ schema: { type: 'string' }
901
+ },
902
+ 'application/json': {
903
+ schema: { type: 'object', properties: { msg: { type: 'string' } } }
904
+ }
905
+ }
906
+ },
907
+ 500: { type: 'object', properties: { error: { type: 'string' } } }
908
+ } as const
909
+ }
910
+ },
911
+ async (_, res) => {
912
+ return false
913
+ }
914
+ ))
915
+
916
+ // -------------------------------------------------------------------
917
+ // Reply Type Override
918
+ // -------------------------------------------------------------------
919
+
920
+ expectAssignable(server.withTypeProvider<JsonSchemaToTsProvider>().get<{ Reply: boolean }>(
921
+ '/',
922
+ {
923
+ schema: {
924
+ response: {
925
+ 200: { type: 'string' },
926
+ 400: { type: 'number' },
927
+ 500: { type: 'object', properties: { error: { type: 'string' } } }
928
+ } as const
929
+ }
930
+ },
931
+ async (_, res) => {
932
+ res.send(true)
933
+ }
934
+ ))
935
+
936
+ // -------------------------------------------------------------------
937
+ // Reply Type Override (Different Content-types)
938
+ // -------------------------------------------------------------------
939
+
940
+ expectAssignable(server.withTypeProvider<JsonSchemaToTsProvider>().get<{ Reply: boolean }>(
941
+ '/',
942
+ {
943
+ schema: {
944
+ response: {
945
+ 200: {
946
+ content: {
947
+ 'text/string': {
948
+ schema: { type: 'string' }
949
+ },
950
+ 'application/json': {
951
+ schema: { type: 'object', properties: { msg: { type: 'string' } } }
952
+ }
953
+ }
954
+ },
955
+ 500: { type: 'object', properties: { error: { type: 'string' } } }
956
+ } as const
957
+ }
958
+ },
959
+ async (_, res) => {
960
+ res.send(true)
961
+ }
962
+ ))
963
+
964
+ // -------------------------------------------------------------------
965
+ // Reply Type Return Override
966
+ // -------------------------------------------------------------------
967
+
968
+ expectAssignable(server.withTypeProvider<JsonSchemaToTsProvider>().get<{ Reply: boolean }>(
969
+ '/',
970
+ {
971
+ schema: {
972
+ response: {
973
+ 200: { type: 'string' },
974
+ 400: { type: 'number' },
975
+ 500: { type: 'object', properties: { error: { type: 'string' } } }
976
+ } as const
977
+ }
978
+ },
979
+ async (_, res) => {
980
+ return true
981
+ }
982
+ ))
983
+
984
+ // -------------------------------------------------------------------
985
+ // Reply Type Return Override (Different Content-types)
986
+ // -------------------------------------------------------------------
987
+
988
+ expectAssignable(server.withTypeProvider<JsonSchemaToTsProvider>().get<{ Reply: boolean }>(
989
+ '/',
990
+ {
991
+ schema: {
992
+ response: {
993
+ 200: {
994
+ content: {
995
+ 'text/string': {
996
+ schema: { type: 'string' }
997
+ },
998
+ 'application/json': {
999
+ schema: { type: 'object', properties: { msg: { type: 'string' } } }
1000
+ }
1001
+ }
1002
+ },
1003
+ 500: { type: 'object', properties: { error: { type: 'string' } } }
1004
+ } as const
1005
+ }
1006
+ },
1007
+ async (_, res) => {
1008
+ return true
1009
+ }
1010
+ ))
1011
+
1012
+ // -------------------------------------------------------------------
1013
+ // Reply Status Code (Different Status Codes)
1014
+ // -------------------------------------------------------------------
1015
+
1016
+ expectAssignable(server.withTypeProvider<JsonSchemaToTsProvider>().get(
1017
+ '/',
1018
+ {
1019
+ schema: {
1020
+ response: {
1021
+ 200: {
1022
+ content: {
1023
+ 'text/string': {
1024
+ schema: { type: 'string' }
1025
+ },
1026
+ 'application/json': {
1027
+ schema: { type: 'object', properties: { msg: { type: 'string' } } }
1028
+ }
1029
+ }
1030
+ },
1031
+ 500: { type: 'object', properties: { error: { type: 'string' } } }
1032
+ } as const
1033
+ }
1034
+ },
1035
+ async (_, res) => {
1036
+ res.code(200)
1037
+ res.code(500)
1038
+ expectError(() => res.code(201))
1039
+ expectError(() => res.code(400))
1040
+ }
1041
+ ))
1042
+
1043
+ // -------------------------------------------------------------------
1044
+ // RouteGeneric Reply Type Return (Different Status Codes)
1045
+ // -------------------------------------------------------------------
1046
+
1047
+ expectAssignable(server.get<{
1048
+ Reply: {
1049
+ 200: string | { msg: string }
1050
+ 400: number
1051
+ '5xx': { error: string }
1052
+ }
1053
+ }>(
1054
+ '/',
1055
+ async (_, res) => {
1056
+ const option = 1 as 1 | 2 | 3 | 4
1057
+ switch (option) {
1058
+ case 1: return 'hello'
1059
+ case 2: return { msg: 'hello' }
1060
+ case 3: return 400
1061
+ case 4: return { error: 'error' }
1062
+ }
1063
+ }
1064
+ ))
1065
+
1066
+ // -------------------------------------------------------------------
1067
+ // RouteGeneric Status Code (Different Status Codes)
1068
+ // -------------------------------------------------------------------
1069
+
1070
+ expectAssignable(server.get<{
1071
+ Reply: {
1072
+ 200: string | { msg: string }
1073
+ 400: number
1074
+ '5xx': { error: string }
1075
+ }
1076
+ }>(
1077
+ '/',
1078
+ async (_, res) => {
1079
+ res.code(200)
1080
+ res.code(400)
1081
+ res.code(500)
1082
+ res.code(502)
1083
+ expectError(() => res.code(201))
1084
+ expectError(() => res.code(300))
1085
+ expectError(() => res.code(404))
1086
+ return 'hello'
1087
+ }
1088
+ ))
1089
+
1090
+ // -------------------------------------------------------------------
1091
+ // RouteGeneric Reply Type Return: Non Assignable (Different Status Codes)
1092
+ // -------------------------------------------------------------------
1093
+
1094
+ expectError(server.get<{
1095
+ Reply: {
1096
+ 200: string | { msg: string }
1097
+ 400: number
1098
+ '5xx': { error: string }
1099
+ }
1100
+ }>(
1101
+ '/',
1102
+ async (_, res) => {
1103
+ return true
1104
+ }
1105
+ ))
1106
+
1107
+ // -------------------------------------------------------------------
1108
+ // FastifyPlugin: Auxiliary
1109
+ // -------------------------------------------------------------------
1110
+
1111
+ interface AuxiliaryPluginProvider extends FastifyTypeProvider { validator: 'plugin-auxiliary' }
1112
+
1113
+ // Auxiliary plugins may have varying server types per application. Recommendation would be to explicitly remap instance provider context within plugin if required.
1114
+ function plugin<T extends FastifyInstance> (instance: T) {
1115
+ expectAssignable(instance.withTypeProvider<AuxiliaryPluginProvider>().get(
1116
+ '/',
1117
+ {
1118
+ schema: { body: null }
1119
+ },
1120
+ (req) => {
1121
+ expectType<'plugin-auxiliary'>(req.body)
1122
+ }
1123
+ ))
1124
+ }
1125
+
1126
+ expectAssignable(server.withTypeProvider<AuxiliaryPluginProvider>().register(plugin).get(
1127
+ '/',
1128
+ {
1129
+ schema: { body: null }
1130
+ },
1131
+ (req) => {
1132
+ expectType<'plugin-auxiliary'>(req.body)
1133
+ }
1134
+ ))
1135
+
1136
+ // -------------------------------------------------------------------
1137
+ // Handlers: Inline
1138
+ // -------------------------------------------------------------------
1139
+
1140
+ interface InlineHandlerProvider extends FastifyTypeProvider { validator: 'handler-inline' }
1141
+
1142
+ // Inline handlers should infer for the request parameters (non-shared)
1143
+ expectAssignable(server.withTypeProvider<InlineHandlerProvider>().get(
1144
+ '/',
1145
+ {
1146
+ onRequest: (req, res, done) => {
1147
+ expectType<'handler-inline'>(req.body)
1148
+ },
1149
+ schema: { body: null }
1150
+ },
1151
+ (req) => {
1152
+ expectType<'handler-inline'>(req.body)
1153
+ }
1154
+ ))
1155
+
1156
+ // -------------------------------------------------------------------
1157
+ // Handlers: Auxiliary
1158
+ // -------------------------------------------------------------------
1159
+
1160
+ interface AuxiliaryHandlerProvider extends FastifyTypeProvider { validator: 'handler-auxiliary' }
1161
+
1162
+ // Auxiliary handlers are likely shared for multiple routes and thus should infer as unknown due to potential varying parameters
1163
+ function auxiliaryHandler (request: FastifyRequest, reply: FastifyReply, done: HookHandlerDoneFunction): void {
1164
+ expectType<unknown>(request.body)
1165
+ }
1166
+
1167
+ expectAssignable(server.withTypeProvider<AuxiliaryHandlerProvider>().get(
1168
+ '/',
1169
+ {
1170
+ onRequest: auxiliaryHandler,
1171
+ schema: { body: 'handler-auxiliary' }
1172
+ },
1173
+ (req) => {
1174
+ expectType<'handler-auxiliary'>(req.body)
1175
+ }
1176
+ ))
1177
+
1178
+ // -------------------------------------------------------------------
1179
+ // SafePromiseLike
1180
+ // -------------------------------------------------------------------
1181
+ const safePromiseLike = {
1182
+ then: new Promise<string>(resolve => resolve('')).then,
1183
+ __linterBrands: 'SafePromiseLike' as const
1184
+ }
1185
+ expectAssignable<SafePromiseLike<string>>(safePromiseLike)
1186
+ expectAssignable<PromiseLike<string>>(safePromiseLike)
1187
+ expectError<Promise<string>>(safePromiseLike)
1188
+
1189
+ // -------------------------------------------------------------------
1190
+ // Separate Providers
1191
+ // -------------------------------------------------------------------
1192
+
1193
+ interface SeparateProvider extends FastifyTypeProvider {
1194
+ validator: string
1195
+ serializer: Date
1196
+ }
1197
+
1198
+ expectAssignable(server.withTypeProvider<SeparateProvider>().get(
1199
+ '/',
1200
+ {
1201
+ schema: {
1202
+ body: null,
1203
+ response: {
1204
+ 200: { type: 'string' }
1205
+ }
1206
+ }
1207
+ },
1208
+ (req, res) => {
1209
+ expectType<string>(req.body)
1210
+
1211
+ res.send(new Date())
1212
+ }
1213
+ ))