acrylblends 0.3.0 → 0.4.1

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 (341) hide show
  1. package/dist/index.d.ts +6 -1
  2. package/dist/index.js +84 -5
  3. package/dist/index.js.map +1 -1
  4. package/docs/framework/LICENSE +23 -0
  5. package/docs/framework/README.md +63 -0
  6. package/docs/framework/UPSTREAM.md +3 -0
  7. package/docs/framework/api-summary.md +138 -0
  8. package/docs/framework/concepts.md +105 -0
  9. package/docs/framework/context.md +129 -0
  10. package/docs/framework/getting-started.md +146 -0
  11. package/docs/framework/helpers.md +118 -0
  12. package/docs/framework/middleware.md +154 -0
  13. package/docs/framework/migration-from-0-3.md +108 -0
  14. package/docs/framework/migration-from-dsh-webserver.md +103 -0
  15. package/docs/framework/node-server.md +75 -0
  16. package/docs/framework/plugin-lifecycle.md +111 -0
  17. package/docs/framework/request.md +106 -0
  18. package/docs/framework/routing.md +188 -0
  19. package/docs/framework/surfaces.md +71 -0
  20. package/docs/framework/upstream.json +9 -0
  21. package/docs/framework/validation.md +135 -0
  22. package/package.json +4 -2
  23. package/plugins/acryl-inspect/plugin.json +2 -1
  24. package/plugins/acryl-inspect/src/report.ts +21 -13
  25. package/plugins/acryl-inspect/src/service.ts +9 -7
  26. package/plugins/acryl-inspect/tests/report.spec.ts +13 -8
  27. package/plugins/acryl-inspect/tests/service.spec.ts +11 -10
  28. package/plugins/acryl-inspect/upstream.json +2 -2
  29. package/plugins/acryl-node-server/LICENSE +26 -0
  30. package/plugins/acryl-node-server/plugin.json +2 -1
  31. package/plugins/acryl-node-server/src/adapter/conninfo.ts +23 -0
  32. package/plugins/acryl-node-server/src/adapter/early-hints.ts +55 -0
  33. package/plugins/acryl-node-server/src/adapter/error.ts +11 -0
  34. package/plugins/acryl-node-server/src/adapter/headers.ts +249 -0
  35. package/plugins/acryl-node-server/src/adapter/index.ts +6 -0
  36. package/plugins/acryl-node-server/src/adapter/listener.ts +540 -0
  37. package/plugins/acryl-node-server/src/adapter/request.ts +797 -0
  38. package/plugins/acryl-node-server/src/adapter/response.ts +186 -0
  39. package/plugins/acryl-node-server/src/adapter/serve-static.ts +238 -0
  40. package/plugins/acryl-node-server/src/adapter/server.ts +36 -0
  41. package/plugins/acryl-node-server/src/adapter/types.ts +82 -0
  42. package/plugins/acryl-node-server/src/adapter/url.ts +42 -0
  43. package/plugins/acryl-node-server/src/adapter/utils/response/constants.ts +1 -0
  44. package/plugins/acryl-node-server/src/adapter/utils/response.ts +4 -0
  45. package/plugins/acryl-node-server/src/adapter/utils/stream.ts +88 -0
  46. package/plugins/acryl-node-server/src/adapter/utils.ts +96 -0
  47. package/plugins/acryl-node-server/src/adapter/websocket-types.ts +45 -0
  48. package/plugins/acryl-node-server/src/adapter/websocket.ts +375 -0
  49. package/plugins/acryl-node-server/src/index.ts +19 -6
  50. package/plugins/acryl-node-server/tests/adapter/app.ts +76 -0
  51. package/plugins/acryl-node-server/tests/adapter/assets/.static/plain.txt +1 -0
  52. package/plugins/acryl-node-server/tests/adapter/assets/favicon.ico +0 -0
  53. package/plugins/acryl-node-server/tests/adapter/assets/secret.txt +1 -0
  54. package/plugins/acryl-node-server/tests/adapter/assets/static/acryl.html +1 -0
  55. package/plugins/acryl-node-server/tests/adapter/assets/static/admin/secret.txt +1 -0
  56. package/plugins/acryl-node-server/tests/adapter/assets/static/data.json +5 -0
  57. package/plugins/acryl-node-server/tests/adapter/assets/static/extensionless +1 -0
  58. package/plugins/acryl-node-server/tests/adapter/assets/static/foo..bar.txt +0 -0
  59. package/plugins/acryl-node-server/tests/adapter/assets/static/index.html +1 -0
  60. package/plugins/acryl-node-server/tests/adapter/assets/static/plain.txt +1 -0
  61. package/plugins/acryl-node-server/tests/adapter/assets/static-with-context-path/plain.txt +1 -0
  62. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.bin +1 -0
  63. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.bin.br +1 -0
  64. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt +1 -0
  65. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt.br +1 -0
  66. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt.zst +1 -0
  67. package/plugins/acryl-node-server/tests/adapter/conninfo.test.ts +34 -0
  68. package/plugins/acryl-node-server/tests/adapter/content-length.test.ts +248 -0
  69. package/plugins/acryl-node-server/tests/adapter/early-hints.test.ts +457 -0
  70. package/plugins/acryl-node-server/tests/adapter/fixtures/keys/agent1-cert.pem +23 -0
  71. package/plugins/acryl-node-server/tests/adapter/fixtures/keys/agent1-key.pem +27 -0
  72. package/plugins/acryl-node-server/tests/adapter/headers.test.ts +252 -0
  73. package/plugins/acryl-node-server/tests/adapter/helpers/request.ts +190 -0
  74. package/plugins/acryl-node-server/tests/adapter/listener.test.ts +727 -0
  75. package/plugins/acryl-node-server/tests/adapter/request-client-disconnect.test.ts +189 -0
  76. package/plugins/acryl-node-server/tests/adapter/request.test.ts +1230 -0
  77. package/plugins/acryl-node-server/tests/adapter/response.test.ts +285 -0
  78. package/plugins/acryl-node-server/tests/adapter/serve-static.test.ts +849 -0
  79. package/plugins/acryl-node-server/tests/adapter/server-socket.test.ts +790 -0
  80. package/plugins/acryl-node-server/tests/adapter/server.test.ts +1302 -0
  81. package/plugins/acryl-node-server/tests/adapter/setup.ts +16 -0
  82. package/plugins/acryl-node-server/tests/adapter/url.test.ts +76 -0
  83. package/plugins/acryl-node-server/tests/adapter/utils/response.test.ts +23 -0
  84. package/plugins/acryl-node-server/tests/adapter/utils/stream.test.ts +75 -0
  85. package/plugins/acryl-node-server/tests/adapter/utils.test.ts +235 -0
  86. package/plugins/acryl-node-server/tests/adapter/websocket.test.ts +260 -0
  87. package/plugins/acryl-node-server/tests/server.spec.ts +44 -8
  88. package/plugins/acryl-node-server/upstream.json +3 -3
  89. package/plugins/acryl-router/LICENSE +26 -0
  90. package/plugins/acryl-router/plugin.json +2 -1
  91. package/plugins/acryl-router/src/client.ts +2 -0
  92. package/plugins/acryl-router/src/connection.ts +42 -0
  93. package/plugins/acryl-router/src/core/app-base.ts +544 -0
  94. package/plugins/acryl-router/src/core/app.test.ts +3833 -0
  95. package/plugins/acryl-router/src/core/app.ts +34 -0
  96. package/plugins/acryl-router/src/core/client/client.test.ts +2313 -0
  97. package/plugins/acryl-router/src/core/client/client.ts +249 -0
  98. package/plugins/acryl-router/src/core/client/fetch-result-please.ts +95 -0
  99. package/plugins/acryl-router/src/core/client/index.ts +17 -0
  100. package/plugins/acryl-router/src/core/client/types.test.ts +149 -0
  101. package/plugins/acryl-router/src/core/client/types.ts +393 -0
  102. package/plugins/acryl-router/src/core/client/utils.test.ts +405 -0
  103. package/plugins/acryl-router/src/core/client/utils.ts +117 -0
  104. package/plugins/acryl-router/src/core/compose.test.ts +751 -0
  105. package/plugins/acryl-router/src/core/compose.ts +73 -0
  106. package/plugins/acryl-router/src/core/context.test.ts +629 -0
  107. package/plugins/acryl-router/src/core/context.ts +781 -0
  108. package/plugins/acryl-router/src/core/helper/accepts/accepts.test.ts +344 -0
  109. package/plugins/acryl-router/src/core/helper/accepts/accepts.ts +96 -0
  110. package/plugins/acryl-router/src/core/helper/accepts/index.ts +6 -0
  111. package/plugins/acryl-router/src/core/helper/adapter/index.test.ts +55 -0
  112. package/plugins/acryl-router/src/core/helper/adapter/index.ts +90 -0
  113. package/plugins/acryl-router/src/core/helper/conninfo/index.ts +6 -0
  114. package/plugins/acryl-router/src/core/helper/conninfo/types.ts +45 -0
  115. package/plugins/acryl-router/src/core/helper/cookie/index.test.ts +510 -0
  116. package/plugins/acryl-router/src/core/helper/cookie/index.ts +146 -0
  117. package/plugins/acryl-router/src/core/helper/factory/index.test.ts +434 -0
  118. package/plugins/acryl-router/src/core/helper/factory/index.ts +375 -0
  119. package/plugins/acryl-router/src/core/helper/html/index.test.ts +78 -0
  120. package/plugins/acryl-router/src/core/helper/html/index.ts +57 -0
  121. package/plugins/acryl-router/src/core/helper/route/index.test.ts +259 -0
  122. package/plugins/acryl-router/src/core/helper/route/index.ts +144 -0
  123. package/plugins/acryl-router/src/core/helper/streaming/index.ts +9 -0
  124. package/plugins/acryl-router/src/core/helper/streaming/sse.ts +101 -0
  125. package/plugins/acryl-router/src/core/helper/streaming/stream.test.ts +128 -0
  126. package/plugins/acryl-router/src/core/helper/streaming/stream.ts +45 -0
  127. package/plugins/acryl-router/src/core/helper/streaming/text.test.ts +34 -0
  128. package/plugins/acryl-router/src/core/helper/streaming/text.ts +15 -0
  129. package/plugins/acryl-router/src/core/helper/streaming/utils.ts +11 -0
  130. package/plugins/acryl-router/src/core/helper/testing/index.test.ts +42 -0
  131. package/plugins/acryl-router/src/core/helper/testing/index.ts +28 -0
  132. package/plugins/acryl-router/src/core/helper/websocket/index.test.ts +124 -0
  133. package/plugins/acryl-router/src/core/helper/websocket/index.ts +140 -0
  134. package/plugins/acryl-router/src/core/http-exception.test.ts +45 -0
  135. package/plugins/acryl-router/src/core/http-exception.ts +77 -0
  136. package/plugins/acryl-router/src/core/index.ts +53 -0
  137. package/plugins/acryl-router/src/core/middleware/basic-auth/index.test.ts +434 -0
  138. package/plugins/acryl-router/src/core/middleware/basic-auth/index.ts +152 -0
  139. package/plugins/acryl-router/src/core/middleware/bearer-auth/index.test.ts +1018 -0
  140. package/plugins/acryl-router/src/core/middleware/bearer-auth/index.ts +220 -0
  141. package/plugins/acryl-router/src/core/middleware/body-limit/index.test.ts +273 -0
  142. package/plugins/acryl-router/src/core/middleware/body-limit/index.ts +110 -0
  143. package/plugins/acryl-router/src/core/middleware/combine/index.test.ts +355 -0
  144. package/plugins/acryl-router/src/core/middleware/combine/index.ts +171 -0
  145. package/plugins/acryl-router/src/core/middleware/compress/index.test.ts +535 -0
  146. package/plugins/acryl-router/src/core/middleware/compress/index.ts +142 -0
  147. package/plugins/acryl-router/src/core/middleware/context-storage/index.test.ts +43 -0
  148. package/plugins/acryl-router/src/core/middleware/context-storage/index.ts +58 -0
  149. package/plugins/acryl-router/src/core/middleware/cors/index.test.ts +532 -0
  150. package/plugins/acryl-router/src/core/middleware/cors/index.ts +163 -0
  151. package/plugins/acryl-router/src/core/middleware/csrf/index.test.ts +534 -0
  152. package/plugins/acryl-router/src/core/middleware/csrf/index.ts +150 -0
  153. package/plugins/acryl-router/src/core/middleware/etag/digest.test.ts +21 -0
  154. package/plugins/acryl-router/src/core/middleware/etag/digest.ts +95 -0
  155. package/plugins/acryl-router/src/core/middleware/etag/index.test.ts +503 -0
  156. package/plugins/acryl-router/src/core/middleware/etag/index.ts +135 -0
  157. package/plugins/acryl-router/src/core/middleware/ip-restriction/index.test.ts +217 -0
  158. package/plugins/acryl-router/src/core/middleware/ip-restriction/index.ts +278 -0
  159. package/plugins/acryl-router/src/core/middleware/jwk/index.test.ts +1135 -0
  160. package/plugins/acryl-router/src/core/middleware/jwk/index.ts +1 -0
  161. package/plugins/acryl-router/src/core/middleware/jwk/jwk.ts +195 -0
  162. package/plugins/acryl-router/src/core/middleware/jwk/keys.test.json +48 -0
  163. package/plugins/acryl-router/src/core/middleware/jwt/index.test.ts +802 -0
  164. package/plugins/acryl-router/src/core/middleware/jwt/index.ts +9 -0
  165. package/plugins/acryl-router/src/core/middleware/jwt/jwt.ts +189 -0
  166. package/plugins/acryl-router/src/core/middleware/language/index.test.ts +552 -0
  167. package/plugins/acryl-router/src/core/middleware/language/index.ts +12 -0
  168. package/plugins/acryl-router/src/core/middleware/language/language.ts +314 -0
  169. package/plugins/acryl-router/src/core/middleware/logger/index.test.ts +204 -0
  170. package/plugins/acryl-router/src/core/middleware/logger/index.ts +95 -0
  171. package/plugins/acryl-router/src/core/middleware/method-not-allowed/index.test.ts +292 -0
  172. package/plugins/acryl-router/src/core/middleware/method-not-allowed/index.ts +141 -0
  173. package/plugins/acryl-router/src/core/middleware/method-override/index.test.ts +221 -0
  174. package/plugins/acryl-router/src/core/middleware/method-override/index.ts +147 -0
  175. package/plugins/acryl-router/src/core/middleware/powered-by/index.test.ts +37 -0
  176. package/plugins/acryl-router/src/core/middleware/powered-by/index.ts +35 -0
  177. package/plugins/acryl-router/src/core/middleware/pretty-json/index.test.ts +218 -0
  178. package/plugins/acryl-router/src/core/middleware/pretty-json/index.ts +67 -0
  179. package/plugins/acryl-router/src/core/middleware/request-id/index.test.ts +168 -0
  180. package/plugins/acryl-router/src/core/middleware/request-id/index.ts +7 -0
  181. package/plugins/acryl-router/src/core/middleware/request-id/request-id.ts +59 -0
  182. package/plugins/acryl-router/src/core/middleware/secure-headers/index.test.ts +671 -0
  183. package/plugins/acryl-router/src/core/middleware/secure-headers/index.ts +8 -0
  184. package/plugins/acryl-router/src/core/middleware/secure-headers/permissions-policy.ts +101 -0
  185. package/plugins/acryl-router/src/core/middleware/secure-headers/secure-headers.ts +340 -0
  186. package/plugins/acryl-router/src/core/middleware/serve-static/index.test.ts +450 -0
  187. package/plugins/acryl-router/src/core/middleware/serve-static/index.ts +135 -0
  188. package/plugins/acryl-router/src/core/middleware/serve-static/path.test.ts +50 -0
  189. package/plugins/acryl-router/src/core/middleware/serve-static/path.ts +25 -0
  190. package/plugins/acryl-router/src/core/middleware/timeout/index.test.ts +70 -0
  191. package/plugins/acryl-router/src/core/middleware/timeout/index.ts +58 -0
  192. package/plugins/acryl-router/src/core/middleware/timing/index.test.ts +310 -0
  193. package/plugins/acryl-router/src/core/middleware/timing/index.ts +8 -0
  194. package/plugins/acryl-router/src/core/middleware/timing/timing.ts +256 -0
  195. package/plugins/acryl-router/src/core/middleware/trailing-slash/index.test.ts +381 -0
  196. package/plugins/acryl-router/src/core/middleware/trailing-slash/index.ts +156 -0
  197. package/plugins/acryl-router/src/core/request/constants.ts +1 -0
  198. package/plugins/acryl-router/src/core/request.test.ts +751 -0
  199. package/plugins/acryl-router/src/core/request.ts +502 -0
  200. package/plugins/acryl-router/src/core/router/common.case.test.ts +1132 -0
  201. package/plugins/acryl-router/src/core/router/linear-router/index.ts +6 -0
  202. package/plugins/acryl-router/src/core/router/linear-router/router.test.ts +80 -0
  203. package/plugins/acryl-router/src/core/router/linear-router/router.ts +152 -0
  204. package/plugins/acryl-router/src/core/router/pattern-router/index.ts +6 -0
  205. package/plugins/acryl-router/src/core/router/pattern-router/router.test.ts +46 -0
  206. package/plugins/acryl-router/src/core/router/pattern-router/router.ts +54 -0
  207. package/plugins/acryl-router/src/core/router/reg-exp-router/index.ts +7 -0
  208. package/plugins/acryl-router/src/core/router/reg-exp-router/matcher.ts +33 -0
  209. package/plugins/acryl-router/src/core/router/reg-exp-router/node.ts +170 -0
  210. package/plugins/acryl-router/src/core/router/reg-exp-router/prepared-router.test.ts +154 -0
  211. package/plugins/acryl-router/src/core/router/reg-exp-router/prepared-router.ts +165 -0
  212. package/plugins/acryl-router/src/core/router/reg-exp-router/router.test.ts +265 -0
  213. package/plugins/acryl-router/src/core/router/reg-exp-router/router.ts +174 -0
  214. package/plugins/acryl-router/src/core/router/reg-exp-router/trie.ts +84 -0
  215. package/plugins/acryl-router/src/core/router/smart-router/index.ts +6 -0
  216. package/plugins/acryl-router/src/core/router/smart-router/router.test.ts +13 -0
  217. package/plugins/acryl-router/src/core/router/smart-router/router.ts +70 -0
  218. package/plugins/acryl-router/src/core/router/trie-router/index.ts +6 -0
  219. package/plugins/acryl-router/src/core/router/trie-router/node.test.ts +896 -0
  220. package/plugins/acryl-router/src/core/router/trie-router/node.ts +221 -0
  221. package/plugins/acryl-router/src/core/router/trie-router/router.test.ts +8 -0
  222. package/plugins/acryl-router/src/core/router/trie-router/router.ts +18 -0
  223. package/plugins/acryl-router/src/core/router/utils.ts +1 -0
  224. package/plugins/acryl-router/src/core/router.ts +103 -0
  225. package/plugins/acryl-router/src/core/types.test.ts +4134 -0
  226. package/plugins/acryl-router/src/core/types.ts +2782 -0
  227. package/plugins/acryl-router/src/core/utils/accept.test.ts +299 -0
  228. package/plugins/acryl-router/src/core/utils/accept.ts +268 -0
  229. package/plugins/acryl-router/src/core/utils/basic-auth.test.ts +56 -0
  230. package/plugins/acryl-router/src/core/utils/basic-auth.ts +26 -0
  231. package/plugins/acryl-router/src/core/utils/body.test.ts +496 -0
  232. package/plugins/acryl-router/src/core/utils/body.ts +263 -0
  233. package/plugins/acryl-router/src/core/utils/buffer.test.ts +147 -0
  234. package/plugins/acryl-router/src/core/utils/buffer.ts +117 -0
  235. package/plugins/acryl-router/src/core/utils/color.test.ts +23 -0
  236. package/plugins/acryl-router/src/core/utils/color.ts +56 -0
  237. package/plugins/acryl-router/src/core/utils/compress.ts +10 -0
  238. package/plugins/acryl-router/src/core/utils/concurrent.test.ts +69 -0
  239. package/plugins/acryl-router/src/core/utils/concurrent.ts +55 -0
  240. package/plugins/acryl-router/src/core/utils/constants.ts +4 -0
  241. package/plugins/acryl-router/src/core/utils/cookie.test.ts +460 -0
  242. package/plugins/acryl-router/src/core/utils/cookie.ts +281 -0
  243. package/plugins/acryl-router/src/core/utils/crypto.test.ts +43 -0
  244. package/plugins/acryl-router/src/core/utils/crypto.ts +58 -0
  245. package/plugins/acryl-router/src/core/utils/encode.test.ts +61 -0
  246. package/plugins/acryl-router/src/core/utils/encode.ts +34 -0
  247. package/plugins/acryl-router/src/core/utils/filepath.test.ts +78 -0
  248. package/plugins/acryl-router/src/core/utils/filepath.ts +60 -0
  249. package/plugins/acryl-router/src/core/utils/handler.ts +15 -0
  250. package/plugins/acryl-router/src/core/utils/headers.ts +355 -0
  251. package/plugins/acryl-router/src/core/utils/html.test.ts +16 -0
  252. package/plugins/acryl-router/src/core/utils/html.ts +182 -0
  253. package/plugins/acryl-router/src/core/utils/http-status.ts +72 -0
  254. package/plugins/acryl-router/src/core/utils/ipaddr.test.ts +149 -0
  255. package/plugins/acryl-router/src/core/utils/ipaddr.ts +374 -0
  256. package/plugins/acryl-router/src/core/utils/jwt/index.ts +7 -0
  257. package/plugins/acryl-router/src/core/utils/jwt/jwa.test.ts +86 -0
  258. package/plugins/acryl-router/src/core/utils/jwt/jwa.ts +38 -0
  259. package/plugins/acryl-router/src/core/utils/jwt/jws.ts +235 -0
  260. package/plugins/acryl-router/src/core/utils/jwt/jwt.test.ts +1788 -0
  261. package/plugins/acryl-router/src/core/utils/jwt/jwt.ts +292 -0
  262. package/plugins/acryl-router/src/core/utils/jwt/types.ts +157 -0
  263. package/plugins/acryl-router/src/core/utils/jwt/utf8.ts +7 -0
  264. package/plugins/acryl-router/src/core/utils/mime.test.ts +64 -0
  265. package/plugins/acryl-router/src/core/utils/mime.ts +94 -0
  266. package/plugins/acryl-router/src/core/utils/stream.test.ts +212 -0
  267. package/plugins/acryl-router/src/core/utils/stream.ts +107 -0
  268. package/plugins/acryl-router/src/core/utils/types.test.ts +295 -0
  269. package/plugins/acryl-router/src/core/utils/types.ts +123 -0
  270. package/plugins/acryl-router/src/core/utils/url.test.ts +384 -0
  271. package/plugins/acryl-router/src/core/utils/url.ts +337 -0
  272. package/plugins/acryl-router/src/core/validator/index.ts +8 -0
  273. package/plugins/acryl-router/src/core/validator/utils.test.ts +82 -0
  274. package/plugins/acryl-router/src/core/validator/utils.ts +69 -0
  275. package/plugins/acryl-router/src/core/validator/validator.test.ts +1488 -0
  276. package/plugins/acryl-router/src/core/validator/validator.ts +185 -0
  277. package/plugins/acryl-router/src/helpers/index.ts +13 -3
  278. package/plugins/acryl-router/src/helpers/schema-validator.ts +35 -0
  279. package/plugins/acryl-router/src/index.ts +6 -6
  280. package/plugins/acryl-router/src/middleware/index.ts +25 -6
  281. package/plugins/acryl-router/src/middleware/loopback-only.ts +53 -14
  282. package/plugins/acryl-router/src/service.ts +262 -64
  283. package/plugins/acryl-router/tests/docs-examples.spec.ts +152 -0
  284. package/plugins/acryl-router/tests/live-install.spec.ts +2 -2
  285. package/plugins/acryl-router/tests/loader.spec.ts +3 -2
  286. package/plugins/acryl-router/tests/loopback-only.spec.ts +80 -0
  287. package/plugins/acryl-router/tests/service.spec.ts +281 -66
  288. package/plugins/acryl-router/tests/shadow-state.spec.ts +79 -0
  289. package/plugins/acryl-router/upstream.json +2 -2
  290. package/plugins/acryl-router-http/plugin.json +2 -1
  291. package/plugins/acryl-router-http/src/bridge.ts +3 -1
  292. package/plugins/acryl-router-http/tests/adapter.spec.ts +16 -12
  293. package/plugins/acryl-router-http/tests/surfaces.spec.ts +12 -10
  294. package/plugins/acryl-router-http/upstream.json +3 -3
  295. package/plugins/acryl-state/NOTICE +30 -0
  296. package/plugins/acryl-state/plugin.json +1 -1
  297. package/plugins/acryl-state/src/cell.ts +1 -1
  298. package/plugins/acryl-state/src/service.ts +33 -15
  299. package/plugins/acryl-state/upstream.json +6 -4
  300. package/plugins/builder/script.ts +4 -7
  301. package/plugins/extensions/index.ts +6 -5
  302. package/plugins/extensions/resolve.ts +28 -3
  303. package/plugins/openai/prompt.ts +9 -7
  304. package/plugins/tsconfig.json +5 -4
  305. package/shared/AGENTS.core.md +54 -0
  306. package/templates/agent/AGENTS.md +6 -0
  307. package/templates/agent/README.md +2 -2
  308. package/templates/agent/blend.yaml +1 -1
  309. package/templates/agent/src/routes.ts +37 -27
  310. package/templates/agent/test/model.test.ts +2 -1
  311. package/templates/agent/tsconfig.json +8 -5
  312. package/templates/blank/AGENTS.md +5 -0
  313. package/templates/blank/README.md +1 -1
  314. package/templates/blank/src/routes.ts +6 -4
  315. package/templates/blank/tsconfig.json +2 -4
  316. package/templates/todo/AGENTS.md +5 -0
  317. package/templates/todo/src/page.ts +1 -1
  318. package/templates/todo/src/todo.ts +58 -29
  319. package/templates/todo/tsconfig.json +2 -4
  320. package/templates/todo/upstream.json +2 -2
  321. package/plugins/acryl-router/src/compose.ts +0 -21
  322. package/plugins/acryl-router/src/context.ts +0 -59
  323. package/plugins/acryl-router/src/helpers/read-body.ts +0 -59
  324. package/plugins/acryl-router/src/helpers/sse.ts +0 -83
  325. package/plugins/acryl-router/src/helpers/validate.ts +0 -44
  326. package/plugins/acryl-router/src/http-error.ts +0 -21
  327. package/plugins/acryl-router/src/middleware/body-limit.ts +0 -25
  328. package/plugins/acryl-router/src/middleware/cors.ts +0 -46
  329. package/plugins/acryl-router/src/middleware/logger.ts +0 -21
  330. package/plugins/acryl-router/src/middleware/request-id.ts +0 -24
  331. package/plugins/acryl-router/src/middleware/secure-headers.ts +0 -23
  332. package/plugins/acryl-router/src/pattern.ts +0 -89
  333. package/plugins/acryl-router/src/registrar.ts +0 -90
  334. package/plugins/acryl-router/src/table.ts +0 -156
  335. package/plugins/acryl-router/src/types.ts +0 -73
  336. package/plugins/acryl-router/src/util.ts +0 -23
  337. package/plugins/acryl-router/tests/api.spec.ts +0 -223
  338. package/plugins/acryl-router/tests/conformance.ts +0 -170
  339. package/plugins/acryl-router/tests/helpers.spec.ts +0 -172
  340. package/plugins/acryl-router/tests/middleware.spec.ts +0 -183
  341. package/plugins/acryl-router/tests/table.spec.ts +0 -47
@@ -0,0 +1,4134 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ import { expectTypeOf } from 'vitest'
3
+ import { hc } from './client/index.ts'
4
+ import { Context } from './context.ts'
5
+ import { createMiddleware } from './helper/factory/index.ts'
6
+ import { testClient } from './helper/testing/index.ts'
7
+ import { App } from './app.ts'
8
+ import { poweredBy } from './middleware/powered-by/index.ts'
9
+ import type {
10
+ AddParam,
11
+ Env,
12
+ ExtractSchema,
13
+ Handler,
14
+ InputToDataByTarget,
15
+ MergePath,
16
+ MergeSchemaPath,
17
+ MiddlewareHandler,
18
+ ParamKeyToRecord,
19
+ ParamKeys,
20
+ RemoveQuestion,
21
+ ResponseFormat,
22
+ ToSchema,
23
+ TypedResponse,
24
+ } from './types.ts'
25
+ import type { ContentfulStatusCode, StatusCode } from './utils/http-status.ts'
26
+ import type { Equal, Expect } from './utils/types.ts'
27
+ import { validator } from './validator/index.ts'
28
+
29
+ describe('Env', () => {
30
+ test('Env', () => {
31
+ type E = {
32
+ Variables: {
33
+ foo: string
34
+ }
35
+ Bindings: {
36
+ FLAG: boolean
37
+ }
38
+ }
39
+ const app = new App<E>()
40
+ app.use('*', poweredBy())
41
+ app.get('/', (c) => {
42
+ const foo = c.get('foo')
43
+ expectTypeOf(foo).toEqualTypeOf<string>()
44
+ const FLAG = c.env.FLAG
45
+ expectTypeOf(FLAG).toEqualTypeOf<boolean>()
46
+ return c.text('foo')
47
+ })
48
+ })
49
+ })
50
+
51
+ describe('HandlerInterface', () => {
52
+ type Env = {}
53
+
54
+ type Payload = { foo: string; bar: boolean }
55
+
56
+ describe('no path pattern', () => {
57
+ const app = new App<Env>()
58
+ const middleware: MiddlewareHandler<
59
+ Env,
60
+ '/',
61
+ {
62
+ in: { json: Payload }
63
+ out: { json: Payload }
64
+ }
65
+ > = async (_c, next) => {
66
+ await next()
67
+ }
68
+ test('Context', () => {
69
+ const route = app.get(middleware, (c) => {
70
+ type Expected = Context<
71
+ Env,
72
+ '/',
73
+ {
74
+ in: { json: Payload }
75
+ out: { json: Payload }
76
+ }
77
+ >
78
+ expectTypeOf(c).toEqualTypeOf<Expected>()
79
+ return c.json({
80
+ message: 'Hello!',
81
+ })
82
+ })
83
+ app.get(middleware, (c) => {
84
+ const data = c.req.valid('json')
85
+ expectTypeOf(data).toEqualTypeOf<Payload>()
86
+ return c.json({
87
+ message: 'Hello!',
88
+ })
89
+ })
90
+ type Actual = ExtractSchema<typeof route>
91
+ type Expected = {
92
+ '/': {
93
+ $get: {
94
+ input: {
95
+ json: Payload
96
+ }
97
+ output: {
98
+ message: string
99
+ }
100
+ outputFormat: 'json'
101
+ status: ContentfulStatusCode
102
+ }
103
+ }
104
+ }
105
+ type verify = Expect<Equal<Expected, Actual>>
106
+ })
107
+ })
108
+
109
+ describe('path pattern', () => {
110
+ const app = new App<Env>()
111
+ const middleware: MiddlewareHandler<
112
+ Env,
113
+ '/foo',
114
+ { in: { json: Payload }; out: { json: Payload } },
115
+ never
116
+ > = async (_c, next) => {
117
+ await next()
118
+ }
119
+
120
+ test('Context and AppType', () => {
121
+ const route = app.get('/foo', middleware, (c) => {
122
+ type Expected = Context<Env, '/foo', { in: { json: Payload }; out: { json: Payload } }>
123
+ expectTypeOf(c).toEqualTypeOf<Expected>()
124
+ return c.json({
125
+ message: 'Hello!',
126
+ })
127
+ })
128
+ type Actual = ExtractSchema<typeof route>
129
+ type Expected = {
130
+ '/foo': {
131
+ $get: {
132
+ input: {
133
+ json: {
134
+ foo: string
135
+ bar: boolean
136
+ }
137
+ }
138
+ output: {
139
+ message: string
140
+ }
141
+ outputFormat: 'json'
142
+ status: ContentfulStatusCode
143
+ }
144
+ }
145
+ }
146
+ type verify = Expect<Equal<Expected, Actual>>
147
+ })
148
+ })
149
+
150
+ describe('With path parameters', () => {
151
+ const app = new App<Env>()
152
+ const middleware: MiddlewareHandler<Env, '/post/:id', {}, never> = async (_c, next) => {
153
+ await next()
154
+ }
155
+ it('Should have the `param` type', () => {
156
+ const route = app.get('/post/:id', middleware, (c) => {
157
+ return c.text('foo')
158
+ })
159
+ type Actual = ExtractSchema<typeof route>
160
+ type Expected = {
161
+ '/post/:id': {
162
+ $get: {
163
+ input: {
164
+ param: {
165
+ id: string
166
+ }
167
+ }
168
+ output: 'foo'
169
+ outputFormat: 'text'
170
+ status: ContentfulStatusCode
171
+ }
172
+ }
173
+ }
174
+ type verify = Expect<Equal<Expected, Actual>>
175
+ })
176
+ })
177
+
178
+ describe('Without path', () => {
179
+ const app = new App<Env>().basePath('/foo/:foo')
180
+
181
+ it('With basePath and path params', () => {
182
+ const route = app.get(async (c) => {
183
+ const foo = c.req.param('foo')
184
+ expect(typeof foo).toBe('string')
185
+ return c.text(foo)
186
+ })
187
+ type Actual = ExtractSchema<typeof route>
188
+
189
+ type Expected = {
190
+ '/foo/:foo': {
191
+ $get: {
192
+ input: {
193
+ param: {
194
+ foo: string
195
+ }
196
+ }
197
+ output: string
198
+ outputFormat: 'text'
199
+ status: ContentfulStatusCode
200
+ }
201
+ }
202
+ }
203
+ type verify = Expect<Equal<Expected, Actual>>
204
+ })
205
+
206
+ it('Chained', () => {
207
+ const route = app.post('/books/:id').get((c) => {
208
+ const id = c.req.param('id')
209
+ return c.text(id)
210
+ })
211
+ type Actual = ExtractSchema<typeof route>
212
+ type Expected = {
213
+ '/foo/:foo/books/:id': {
214
+ $get: {
215
+ input: {
216
+ param: {
217
+ id: string
218
+ } & {
219
+ foo: string
220
+ }
221
+ }
222
+ output: string
223
+ outputFormat: 'text'
224
+ status: ContentfulStatusCode
225
+ }
226
+ }
227
+ } & {
228
+ '/foo/:foo/books/:id': {
229
+ $post: {
230
+ input: {
231
+ param: {
232
+ id: string
233
+ } & {
234
+ foo: string
235
+ }
236
+ }
237
+ output: {}
238
+ outputFormat: ResponseFormat
239
+ status: StatusCode
240
+ }
241
+ }
242
+ }
243
+ type verify = Expect<Equal<Expected, Actual>>
244
+ })
245
+
246
+ it('should use the last registered path for a pathless handler', () => {
247
+ const app = new App()
248
+ .get('/foo', (c) => c.text('foo'))
249
+ .get('/bar', (c) => c.text('bar'))
250
+ .post((c) => c.text('baz'))
251
+
252
+ type Actual = ExtractSchema<typeof app>
253
+ type Expected = {
254
+ '/foo': {
255
+ $get: {
256
+ input: {}
257
+ output: 'foo'
258
+ outputFormat: 'text'
259
+ status: ContentfulStatusCode
260
+ }
261
+ }
262
+ } & {
263
+ '/bar': {
264
+ $get: {
265
+ input: {}
266
+ output: 'bar'
267
+ outputFormat: 'text'
268
+ status: ContentfulStatusCode
269
+ }
270
+ }
271
+ } & {
272
+ '/bar': {
273
+ $post: {
274
+ input: {}
275
+ output: 'baz'
276
+ outputFormat: 'text'
277
+ status: ContentfulStatusCode
278
+ }
279
+ }
280
+ }
281
+ type verify = Expect<Equal<Expected, Actual>>
282
+ })
283
+ })
284
+ })
285
+
286
+ describe('OnHandlerInterface', () => {
287
+ const app = new App()
288
+ test('Context', () => {
289
+ const middleware: MiddlewareHandler<
290
+ Env,
291
+ '/purge',
292
+ { in: { form: { id: string } }; out: { form: { id: number } } }
293
+ > = async (_c, next) => {
294
+ await next()
295
+ }
296
+ const route = app.on('PURGE', '/purge', middleware, (c) => {
297
+ const data = c.req.valid('form')
298
+ expectTypeOf(data).toEqualTypeOf<{ id: number }>()
299
+ return c.json({
300
+ success: true,
301
+ })
302
+ })
303
+ type Actual = ExtractSchema<typeof route>
304
+ type Expected = {
305
+ '/purge': {
306
+ $purge: {
307
+ input: {
308
+ form: {
309
+ id: string
310
+ }
311
+ }
312
+ output: {
313
+ success: true
314
+ }
315
+ outputFormat: 'json'
316
+ status: ContentfulStatusCode
317
+ }
318
+ }
319
+ }
320
+ type verify = Expect<Equal<Expected, Actual>>
321
+ })
322
+
323
+ test('app.on(method[], path, middleware, handler)', () => {
324
+ const middleware: MiddlewareHandler<{ Variables: { foo: string } }> = async () => {}
325
+ const route = app.on(['GET', 'POST'], '/multi-method', middleware, (c) => {
326
+ return c.json({ success: true })
327
+ })
328
+ type Actual = ExtractSchema<typeof route>
329
+ type Expected = {
330
+ '/multi-method': {
331
+ $get: {
332
+ input: {}
333
+ output: {
334
+ success: true
335
+ }
336
+ outputFormat: 'json'
337
+ status: ContentfulStatusCode
338
+ }
339
+ $post: {
340
+ input: {}
341
+ output: {
342
+ success: true
343
+ }
344
+ outputFormat: 'json'
345
+ status: ContentfulStatusCode
346
+ }
347
+ }
348
+ }
349
+ type verify = Expect<Equal<Expected, Actual>>
350
+ })
351
+
352
+ test('app.on(method, path[], middleware, handler) should not throw a type error', () => {
353
+ const middleware: MiddlewareHandler<{ Variables: { foo: string } }> = async () => {}
354
+ app.on('GET', ['/a', '/b'], middleware, (c) => {
355
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
356
+ return c.json({})
357
+ })
358
+ })
359
+
360
+ test('app.on(method, path[], handler).get(pathless handler) - pathless handler should use last path', () => {
361
+ const route = app
362
+ .on('GET', ['/a', '/b'], (c) => {
363
+ return c.json({ first: true })
364
+ })
365
+ .get((c) => {
366
+ return c.json({ second: true })
367
+ })
368
+ type Actual = ExtractSchema<typeof route>
369
+ type Expected = {
370
+ '/a': {
371
+ $get: {
372
+ input: {}
373
+ output: {
374
+ first: true
375
+ }
376
+ outputFormat: 'json'
377
+ status: ContentfulStatusCode
378
+ }
379
+ }
380
+ '/b': {
381
+ $get: {
382
+ input: {}
383
+ output: {
384
+ first: true
385
+ }
386
+ outputFormat: 'json'
387
+ status: ContentfulStatusCode
388
+ }
389
+ }
390
+ } & {
391
+ '/b': {
392
+ $get: {
393
+ input: {}
394
+ output: {
395
+ second: true
396
+ }
397
+ outputFormat: 'json'
398
+ status: ContentfulStatusCode
399
+ }
400
+ }
401
+ }
402
+ type verify = Expect<Equal<Expected, Actual>>
403
+ })
404
+
405
+ test('app.on(method, path, ...10 handlers) - last handler response type should be inferred', () => {
406
+ const noop: MiddlewareHandler = async (_c, next) => {
407
+ await next()
408
+ }
409
+ const route = app.on('GET', '/x10', noop, noop, noop, noop, noop, noop, noop, noop, noop, (c) =>
410
+ c.json({ success: true })
411
+ )
412
+ type Actual = ExtractSchema<typeof route>['/x10']['$get']['output']
413
+ type Expected = { success: true }
414
+ type verify = Expect<Equal<Expected, Actual>>
415
+ })
416
+
417
+ test('app.on(method[], path, ...9 handlers) - last handler response type should be inferred', () => {
418
+ const noop: MiddlewareHandler = async (_c, next) => {
419
+ await next()
420
+ }
421
+ const route = app.on(['GET'], '/x9-arr', noop, noop, noop, noop, noop, noop, noop, noop, (c) =>
422
+ c.json({ success: true })
423
+ )
424
+ type Actual = ExtractSchema<typeof route>['/x9-arr']['$get']['output']
425
+ type Expected = { success: true }
426
+ type verify = Expect<Equal<Expected, Actual>>
427
+ })
428
+
429
+ test('app.on(method[], path, ...10 handlers) - last handler response type should be inferred', () => {
430
+ const noop: MiddlewareHandler = async (_c, next) => {
431
+ await next()
432
+ }
433
+ const route = app.on(
434
+ ['GET'],
435
+ '/x10-arr',
436
+ noop,
437
+ noop,
438
+ noop,
439
+ noop,
440
+ noop,
441
+ noop,
442
+ noop,
443
+ noop,
444
+ noop,
445
+ (c) => c.json({ success: true })
446
+ )
447
+ type Actual = ExtractSchema<typeof route>['/x10-arr']['$get']['output']
448
+ type Expected = { success: true }
449
+ type verify = Expect<Equal<Expected, Actual>>
450
+ })
451
+ })
452
+
453
+ describe('TypedResponse', () => {
454
+ test('unknown', () => {
455
+ type Actual = TypedResponse
456
+ type Expected = {
457
+ _data: unknown
458
+ _status: StatusCode
459
+ _format: ResponseFormat
460
+ }
461
+ type verify = Expect<Equal<Expected, Actual>>
462
+ })
463
+
464
+ test('text auto infer', () => {
465
+ type Actual = TypedResponse<string>
466
+ type Expected = {
467
+ _data: string
468
+ _status: StatusCode
469
+ _format: 'text'
470
+ }
471
+ type verify = Expect<Equal<Expected, Actual>>
472
+ })
473
+
474
+ test('json auto infer', () => {
475
+ type Actual = TypedResponse<{ ok: true }>
476
+ type Expected = {
477
+ _data: { ok: true }
478
+ _status: StatusCode
479
+ _format: 'json'
480
+ }
481
+ type verify = Expect<Equal<Expected, Actual>>
482
+ })
483
+ })
484
+
485
+ describe('Schema', () => {
486
+ test('Schema', () => {
487
+ type AppType = App<
488
+ Env,
489
+ ToSchema<
490
+ 'post',
491
+ '/api/posts/:id',
492
+ {
493
+ in: {
494
+ json: {
495
+ id: number
496
+ title: string
497
+ }
498
+ }
499
+ },
500
+ TypedResponse<
501
+ {
502
+ message: string
503
+ success: boolean
504
+ },
505
+ StatusCode,
506
+ 'json'
507
+ >
508
+ >
509
+ >
510
+
511
+ type Actual = ExtractSchema<AppType>
512
+ type Expected = {
513
+ '/api/posts/:id': {
514
+ $post: {
515
+ input: {
516
+ json: {
517
+ id: number
518
+ title: string
519
+ }
520
+ } & {
521
+ param: {
522
+ id: string
523
+ }
524
+ }
525
+ output: {
526
+ message: string
527
+ success: boolean
528
+ }
529
+ outputFormat: 'json'
530
+ status: StatusCode
531
+ }
532
+ }
533
+ }
534
+ type verify = Expect<Equal<Expected, Actual>>
535
+ })
536
+ })
537
+
538
+ describe('Support c.json(undefined)', () => {
539
+ it('Should return a correct type', () => {
540
+ const app = new App().get('/this/is/a/test', async (c) => {
541
+ return c.json(undefined)
542
+ })
543
+ type Actual = ExtractSchema<typeof app>
544
+ type Expected = {
545
+ '/this/is/a/test': {
546
+ $get: {
547
+ input: {}
548
+ output: never
549
+ outputFormat: 'json'
550
+ status: ContentfulStatusCode
551
+ }
552
+ }
553
+ }
554
+ type verify = Expect<Equal<Expected, Actual>>
555
+ })
556
+ })
557
+
558
+ describe('Test types of Handler', () => {
559
+ type E = {
560
+ Variables: {
561
+ foo: number
562
+ }
563
+ }
564
+
565
+ const url = 'http://localhost/'
566
+
567
+ test('Env', async () => {
568
+ const app = new App<E>()
569
+ const handler: Handler<E> = (c) => {
570
+ const foo = c.get('foo')
571
+ expectTypeOf(foo).toEqualTypeOf<number>()
572
+ const id = c.req.param('id')
573
+ expectTypeOf(id).toEqualTypeOf<string | undefined>()
574
+ return c.text('Hi')
575
+ }
576
+ app.get('/', handler)
577
+ const res = await app.request(url)
578
+ expect(res.status).toBe(200)
579
+ })
580
+
581
+ test('Env, Path', async () => {
582
+ const app = new App<E>()
583
+ const handler: Handler<E, '/'> = (c) => {
584
+ const foo = c.get('foo')
585
+ expectTypeOf(foo).toEqualTypeOf<number>()
586
+ return c.text('Hi')
587
+ }
588
+ app.get('/', handler)
589
+
590
+ const res = await app.request(url)
591
+ expect(res.status).toBe(200)
592
+ })
593
+
594
+ type User = {
595
+ name: string
596
+ age: number
597
+ }
598
+
599
+ test('Env, Path, Type', async () => {
600
+ const app = new App<E>()
601
+ const handler: Handler<E, '/', { in: { json: User }; out: { json: User } }> = (c) => {
602
+ const foo = c.get('foo')
603
+ expectTypeOf(foo).toEqualTypeOf<number>()
604
+ const { name } = c.req.valid('json')
605
+ expectTypeOf(name).toEqualTypeOf<string>()
606
+ return c.text('Hi')
607
+ }
608
+ })
609
+ })
610
+
611
+ describe('`json()`', () => {
612
+ const app = new App<{ Variables: { foo: string } }>()
613
+ app.get('/post/:id', (c) => {
614
+ c.req.param('id')
615
+ const id = c.req.param('id')
616
+ return c.text('foo')
617
+ })
618
+
619
+ test('json', () => {
620
+ const route = app.get('/hello', (c) => {
621
+ return c.json({
622
+ message: 'Hello!',
623
+ })
624
+ })
625
+ type Actual = ExtractSchema<typeof route>
626
+ type Expected = {
627
+ '/hello': {
628
+ $get: {
629
+ input: {}
630
+ output: {
631
+ message: string
632
+ }
633
+ outputFormat: 'json'
634
+ status: ContentfulStatusCode
635
+ }
636
+ }
637
+ }
638
+ type verify = Expect<Equal<Expected, Actual>>
639
+ })
640
+
641
+ test('json with specific status code', () => {
642
+ const route = app.get('/hello', (c) => {
643
+ return c.json(
644
+ {
645
+ message: 'Hello!',
646
+ },
647
+ 200
648
+ )
649
+ })
650
+ type Actual = ExtractSchema<typeof route>
651
+ type Expected = {
652
+ '/hello': {
653
+ $get: {
654
+ input: {}
655
+ output: {
656
+ message: string
657
+ }
658
+ outputFormat: 'json'
659
+ status: 200
660
+ }
661
+ }
662
+ }
663
+ type verify = Expect<Equal<Expected, Actual>>
664
+ })
665
+ })
666
+
667
+ describe('Path parameters', () => {
668
+ test('ParamKeys', () => {
669
+ type Actual = ParamKeys<'/posts/:postId/comment/:commentId'>
670
+ type Expected = 'postId' | 'commentId'
671
+ type verify = Expect<Equal<Expected, Actual>>
672
+ })
673
+
674
+ describe('ParamKeyToRecord', () => {
675
+ test('With ?', () => {
676
+ type Actual = ParamKeyToRecord<'/animal/type?'>
677
+ type Expected = { [K in '/animal/type']: string | undefined }
678
+ type verify = Expect<Equal<Expected, Actual>>
679
+ })
680
+ test('Without ?', () => {
681
+ type Actual = ParamKeyToRecord<'/animal/type'>
682
+ type Expected = { [K in '/animal/type']: string }
683
+ type verify = Expect<Equal<Expected, Actual>>
684
+ })
685
+ })
686
+
687
+ describe('Path parameters in app', () => {
688
+ test('Optional parameters - /api/:a/:b?', () => {
689
+ const app = new App()
690
+ const routes = app.get('/api/:a/:b?', (c) => {
691
+ const a = c.req.param('a')
692
+ const b = c.req.param('b')
693
+ expectTypeOf(a).toEqualTypeOf<string>()
694
+ expectTypeOf(b).toEqualTypeOf<string | undefined>()
695
+ return c.json({ a, b })
696
+ })
697
+ type T = ExtractSchema<typeof routes>
698
+ type Output = T['/api/:a/:b?']['$get']['output']
699
+ type Expected = {
700
+ a: string
701
+ b: string | undefined
702
+ }
703
+ type verify = Expect<Equal<Expected, Output>>
704
+ })
705
+ })
706
+ })
707
+
708
+ describe('For AppRequest', () => {
709
+ type Input = {
710
+ json: {
711
+ id: number
712
+ title: string
713
+ }
714
+ query: {
715
+ page: string
716
+ }
717
+ }
718
+
719
+ test('InputToDataByType with value', () => {
720
+ type Actual = InputToDataByTarget<Input, 'json'>
721
+ type Expected = {
722
+ id: number
723
+ title: string
724
+ }
725
+ type verify = Expect<Equal<Expected, Actual>>
726
+ })
727
+
728
+ test('InputToDataByType without value', () => {
729
+ type Actual = InputToDataByTarget<Input, 'form'>
730
+ type verify = Expect<Equal<never, Actual>>
731
+ })
732
+
733
+ test('RemoveQuestion', () => {
734
+ type Actual = RemoveQuestion<'/animal/type?'>
735
+ type verify = Expect<Equal<'/animal/type', Actual>>
736
+ })
737
+ })
738
+
739
+ describe('AddParam', () => {
740
+ it('Should add params to input correctly', () => {
741
+ type Actual = AddParam<
742
+ {
743
+ param: {
744
+ id: string
745
+ }
746
+ } & {
747
+ query: {
748
+ page: string
749
+ }
750
+ },
751
+ '/:id'
752
+ >
753
+ type Expected = {
754
+ query: {
755
+ page: string
756
+ }
757
+ } & {
758
+ param: {
759
+ id: string
760
+ }
761
+ }
762
+ type verify = Expect<Equal<Expected, Actual>>
763
+ })
764
+ })
765
+
766
+ describe('ToSchema', () => {
767
+ it('Should convert parameters to schema correctly', () => {
768
+ type Actual = ToSchema<
769
+ 'get',
770
+ '/:id',
771
+ { in: { param: { id: string }; query: { page: string } } },
772
+ TypedResponse<{}>
773
+ >
774
+ type Expected = {
775
+ '/:id': {
776
+ $get: {
777
+ input: {
778
+ param: {
779
+ id: string
780
+ }
781
+ query: {
782
+ page: string
783
+ }
784
+ }
785
+ output: {}
786
+ outputFormat: 'json'
787
+ status: StatusCode
788
+ }
789
+ }
790
+ }
791
+ type verify = Expect<Equal<Expected, Actual>>
792
+ })
793
+ })
794
+
795
+ describe('MergePath', () => {
796
+ it('Should merge paths correctly', () => {
797
+ type path1 = MergePath<'/api', '/book'>
798
+ type verify1 = Expect<Equal<'/api/book', path1>>
799
+ type path2 = MergePath<'/api/', '/book'>
800
+ type verify2 = Expect<Equal<'/api/book', path2>>
801
+ type path3 = MergePath<'/api/', '/'>
802
+ type verify3 = Expect<Equal<'/api/', path3>>
803
+ type path4 = MergePath<'/api', '/'>
804
+ type verify4 = Expect<Equal<'/api', path4>>
805
+ type path5 = MergePath<'/', ''>
806
+ type verify5 = Expect<Equal<'/', path5>>
807
+ type path6 = MergePath<'', '/'>
808
+ type verify6 = Expect<Equal<'/', path6>>
809
+ type path7 = MergePath<'/', '/'>
810
+ type verify7 = Expect<Equal<'/', path7>>
811
+ type path8 = MergePath<'', ''>
812
+ type verify8 = Expect<Equal<'/', path8>>
813
+ })
814
+ })
815
+
816
+ describe('MergeSchemaPath', () => {
817
+ it('Should merge schema and sub path correctly', () => {
818
+ type Sub = ToSchema<
819
+ 'post',
820
+ '/posts',
821
+ {
822
+ in: {
823
+ json: {
824
+ id: number
825
+ title: string
826
+ }
827
+ }
828
+ },
829
+ TypedResponse<{
830
+ message: string
831
+ }>
832
+ > &
833
+ ToSchema<
834
+ 'get',
835
+ '/posts',
836
+ {},
837
+ TypedResponse<{
838
+ ok: boolean
839
+ }>
840
+ >
841
+
842
+ type Actual = MergeSchemaPath<Sub, '/api'>
843
+
844
+ type Expected = {
845
+ '/api/posts': {
846
+ $post: {
847
+ input: {
848
+ json: {
849
+ id: number
850
+ title: string
851
+ }
852
+ }
853
+ output: {
854
+ message: string
855
+ }
856
+ outputFormat: 'json'
857
+ status: StatusCode
858
+ }
859
+ $get: {
860
+ input: {}
861
+ output: {
862
+ ok: boolean
863
+ }
864
+ outputFormat: 'json'
865
+ status: StatusCode
866
+ }
867
+ }
868
+ }
869
+
870
+ type verify = Expect<Equal<Expected, Actual>>
871
+ })
872
+
873
+ it('Should merge schema which has params and sub path does not have params', () => {
874
+ type Actual = MergeSchemaPath<
875
+ {
876
+ '/': {
877
+ $get: {
878
+ input: {
879
+ param: {
880
+ id: string
881
+ }
882
+ query: {
883
+ page: string
884
+ }
885
+ }
886
+ output: {}
887
+ outputFormat: 'json'
888
+ status: StatusCode
889
+ }
890
+ }
891
+ },
892
+ '/something'
893
+ >
894
+ type Expected = {
895
+ '/something': {
896
+ $get: {
897
+ input: {
898
+ param: {
899
+ id: string
900
+ }
901
+ query: {
902
+ page: string
903
+ }
904
+ }
905
+ output: {}
906
+ outputFormat: 'json'
907
+ status: StatusCode
908
+ }
909
+ }
910
+ }
911
+ type verify = Expect<Equal<Expected, Actual>>
912
+ })
913
+
914
+ type GetKey<T> = T extends Record<infer K, unknown> ? K : never
915
+
916
+ it('Should remove a slash - `/` + `/`', () => {
917
+ type Sub = ToSchema<'get', '/', {}, TypedResponse<{}>>
918
+ type Actual = MergeSchemaPath<Sub, '/'>
919
+ type verify = Expect<Equal<'/', GetKey<Actual>>>
920
+ })
921
+
922
+ it('Should remove a slash - `/tags` + `/`', () => {
923
+ type Sub = ToSchema<'get', '/tags', {}, TypedResponse<{}>>
924
+ type Actual = MergeSchemaPath<Sub, '/'>
925
+ type verify = Expect<Equal<'/tags', GetKey<Actual>>>
926
+ })
927
+
928
+ it('Should remove a slash - `/` + `/tags`', () => {
929
+ type Sub = ToSchema<'get', '/', {}, TypedResponse<{}>>
930
+ type Actual = MergeSchemaPath<Sub, '/tags'>
931
+ type verify = Expect<Equal<'/tags', GetKey<Actual>>>
932
+ })
933
+
934
+ test('MergeSchemaPath - SubPath has path params', () => {
935
+ type Actual = MergeSchemaPath<ToSchema<'get', '/', {}, TypedResponse>, '/a/:b'>
936
+ type Expected = {
937
+ '/a/:b': {
938
+ $get: {
939
+ input: {
940
+ param: {
941
+ b: string
942
+ }
943
+ }
944
+ output: {}
945
+ outputFormat: ResponseFormat
946
+ status: StatusCode
947
+ }
948
+ }
949
+ }
950
+ type verify = Expect<Equal<Expected, Actual>>
951
+ })
952
+
953
+ test('MergeSchemaPath - Path and SubPath have path params', () => {
954
+ type Actual = MergeSchemaPath<ToSchema<'get', '/c/:d', {}, TypedResponse<{}>>, '/a/:b'>
955
+ type Expected = {
956
+ '/a/:b/c/:d': {
957
+ $get: {
958
+ input: {
959
+ param: {
960
+ d: string
961
+ } & {
962
+ b: string
963
+ }
964
+ }
965
+ output: {}
966
+ outputFormat: 'json'
967
+ status: StatusCode
968
+ }
969
+ }
970
+ }
971
+ type verify = Expect<Equal<Expected, Actual>>
972
+ })
973
+
974
+ test('MergeSchemaPath - Path and SubPath have regexp path params', () => {
975
+ type Actual = MergeSchemaPath<ToSchema<'get', '/c/:d{.+}', {}, TypedResponse<{}>>, '/a/:b{.+}'>
976
+ type Expected = {
977
+ '/a/:b{.+}/c/:d{.+}': {
978
+ $get: {
979
+ input: {
980
+ param: {
981
+ d: string
982
+ } & {
983
+ b: string
984
+ }
985
+ }
986
+ output: {}
987
+ outputFormat: 'json'
988
+ status: StatusCode
989
+ }
990
+ }
991
+ }
992
+ type verify = Expect<Equal<Expected, Actual>>
993
+ })
994
+
995
+ test('MergeSchemaPath - Method has Endpoints as Union', () => {
996
+ type Actual = MergeSchemaPath<
997
+ {
998
+ '/': {
999
+ $get:
1000
+ | {
1001
+ input: {}
1002
+ output: {
1003
+ error: string
1004
+ }
1005
+ outputFormat: 'json'
1006
+ status: 404
1007
+ }
1008
+ | {
1009
+ input: {}
1010
+ output: {
1011
+ success: boolean
1012
+ }
1013
+ outputFormat: 'json'
1014
+ status: 200
1015
+ }
1016
+ }
1017
+ },
1018
+ '/api/hello'
1019
+ >
1020
+ type Expected = {
1021
+ '/api/hello': {
1022
+ $get:
1023
+ | {
1024
+ input: {}
1025
+ output: {
1026
+ error: string
1027
+ }
1028
+ outputFormat: 'json'
1029
+ status: 404
1030
+ }
1031
+ | {
1032
+ input: {}
1033
+ output: {
1034
+ success: boolean
1035
+ }
1036
+ outputFormat: 'json'
1037
+ status: 200
1038
+ }
1039
+ }
1040
+ }
1041
+ type verify = Expect<Equal<Expected, Actual>>
1042
+ })
1043
+ })
1044
+
1045
+ describe('Different types using json()', () => {
1046
+ describe('no path pattern', () => {
1047
+ const app = new App()
1048
+
1049
+ test('Three different types', () => {
1050
+ const route = app.get((c) => {
1051
+ const flag = false
1052
+ if (flag) {
1053
+ return c.json({
1054
+ ng: true,
1055
+ })
1056
+ }
1057
+ if (!flag) {
1058
+ return c.json({
1059
+ ok: true,
1060
+ })
1061
+ }
1062
+ return c.json({
1063
+ default: true,
1064
+ })
1065
+ })
1066
+ type Actual = ExtractSchema<typeof route>
1067
+ type Expected = {
1068
+ '/': {
1069
+ $get:
1070
+ | {
1071
+ input: {}
1072
+ output: {
1073
+ ng: true
1074
+ }
1075
+ outputFormat: 'json'
1076
+ status: ContentfulStatusCode
1077
+ }
1078
+ | {
1079
+ input: {}
1080
+ output: {
1081
+ ok: true
1082
+ }
1083
+ outputFormat: 'json'
1084
+ status: ContentfulStatusCode
1085
+ }
1086
+ | {
1087
+ input: {}
1088
+ output: {
1089
+ default: true
1090
+ }
1091
+ outputFormat: 'json'
1092
+ status: ContentfulStatusCode
1093
+ }
1094
+ }
1095
+ }
1096
+ type verify = Expect<Equal<Expected, Actual>>
1097
+ })
1098
+
1099
+ test('Three different types and status codes', () => {
1100
+ const route = app.get((c) => {
1101
+ const flag = false
1102
+ if (flag) {
1103
+ return c.json(
1104
+ {
1105
+ ng: true,
1106
+ },
1107
+ 400
1108
+ )
1109
+ }
1110
+ if (!flag) {
1111
+ return c.json(
1112
+ {
1113
+ ok: true,
1114
+ },
1115
+ 200
1116
+ )
1117
+ }
1118
+ return c.json({
1119
+ default: true,
1120
+ })
1121
+ })
1122
+ type Actual = ExtractSchema<typeof route>
1123
+ type Expected = {
1124
+ '/': {
1125
+ $get:
1126
+ | {
1127
+ input: {}
1128
+ output: {
1129
+ ng: true
1130
+ }
1131
+ outputFormat: 'json'
1132
+ status: 400
1133
+ }
1134
+ | {
1135
+ input: {}
1136
+ output: {
1137
+ ok: true
1138
+ }
1139
+ outputFormat: 'json'
1140
+ status: 200
1141
+ }
1142
+ | {
1143
+ input: {}
1144
+ output: {
1145
+ default: true
1146
+ }
1147
+ outputFormat: 'json'
1148
+ status: ContentfulStatusCode
1149
+ }
1150
+ }
1151
+ }
1152
+ type verify = Expect<Equal<Expected, Actual>>
1153
+ })
1154
+ })
1155
+
1156
+ describe('path pattern', () => {
1157
+ const app = new App()
1158
+
1159
+ test('Three different types', () => {
1160
+ const route = app.get('/foo', (c) => {
1161
+ const flag = false
1162
+ if (flag) {
1163
+ return c.json({
1164
+ ng: true,
1165
+ })
1166
+ }
1167
+ if (!flag) {
1168
+ return c.json({
1169
+ ok: true,
1170
+ })
1171
+ }
1172
+ return c.json({
1173
+ default: true,
1174
+ })
1175
+ })
1176
+ type Actual = ExtractSchema<typeof route>
1177
+ type Expected = {
1178
+ '/foo': {
1179
+ $get:
1180
+ | {
1181
+ input: {}
1182
+ output: {
1183
+ ng: true
1184
+ }
1185
+ outputFormat: 'json'
1186
+ status: ContentfulStatusCode
1187
+ }
1188
+ | {
1189
+ input: {}
1190
+ output: {
1191
+ ok: true
1192
+ }
1193
+ outputFormat: 'json'
1194
+ status: ContentfulStatusCode
1195
+ }
1196
+ | {
1197
+ input: {}
1198
+ output: {
1199
+ default: true
1200
+ }
1201
+ outputFormat: 'json'
1202
+ status: ContentfulStatusCode
1203
+ }
1204
+ }
1205
+ }
1206
+ type verify = Expect<Equal<Expected, Actual>>
1207
+ })
1208
+
1209
+ test('Three different types and status codes', () => {
1210
+ const route = app.get('/foo', (c) => {
1211
+ const flag = false
1212
+ if (flag) {
1213
+ return c.json(
1214
+ {
1215
+ ng: true,
1216
+ },
1217
+ 400
1218
+ )
1219
+ }
1220
+ if (!flag) {
1221
+ return c.json(
1222
+ {
1223
+ ok: true,
1224
+ },
1225
+ 200
1226
+ )
1227
+ }
1228
+ return c.json({
1229
+ default: true,
1230
+ })
1231
+ })
1232
+ type Actual = ExtractSchema<typeof route>
1233
+ type Expected = {
1234
+ '/foo': {
1235
+ $get:
1236
+ | {
1237
+ input: {}
1238
+ output: {
1239
+ ng: true
1240
+ }
1241
+ outputFormat: 'json'
1242
+ status: 400
1243
+ }
1244
+ | {
1245
+ input: {}
1246
+ output: {
1247
+ ok: true
1248
+ }
1249
+ outputFormat: 'json'
1250
+ status: 200
1251
+ }
1252
+ | {
1253
+ input: {}
1254
+ output: {
1255
+ default: true
1256
+ }
1257
+ outputFormat: 'json'
1258
+ status: ContentfulStatusCode
1259
+ }
1260
+ }
1261
+ }
1262
+ type verify = Expect<Equal<Expected, Actual>>
1263
+ })
1264
+ })
1265
+ })
1266
+
1267
+ describe('json() in an async handler', () => {
1268
+ const app = new App()
1269
+
1270
+ test('json', () => {
1271
+ const route = app.get(async (c) => {
1272
+ return c.json({
1273
+ ok: true,
1274
+ })
1275
+ })
1276
+ type Actual = ExtractSchema<typeof route>
1277
+ type Expected = {
1278
+ '/': {
1279
+ $get: {
1280
+ input: {}
1281
+ output: {
1282
+ ok: true
1283
+ }
1284
+ outputFormat: 'json'
1285
+ status: ContentfulStatusCode
1286
+ }
1287
+ }
1288
+ }
1289
+ type verify = Expect<Equal<Expected, Actual>>
1290
+ })
1291
+
1292
+ test('json with specific status code', () => {
1293
+ const route = app.get(async (c) => {
1294
+ return c.json(
1295
+ {
1296
+ ok: true,
1297
+ },
1298
+ 200
1299
+ )
1300
+ })
1301
+ type Actual = ExtractSchema<typeof route>
1302
+ type Expected = {
1303
+ '/': {
1304
+ $get: {
1305
+ input: {}
1306
+ output: {
1307
+ ok: true
1308
+ }
1309
+ outputFormat: 'json'
1310
+ status: 200
1311
+ }
1312
+ }
1313
+ }
1314
+ type verify = Expect<Equal<Expected, Actual>>
1315
+ })
1316
+ })
1317
+
1318
+ /**
1319
+ * Other tests for `c.var` are written in `acryl.test.ts`.
1320
+ * This tests are only for types.
1321
+ */
1322
+ describe('c.var with chaining - test only types', () => {
1323
+ const mw1 = createMiddleware<
1324
+ { Variables: { foo1: string } },
1325
+ string,
1326
+ { out: { query: { bar1: number } } }
1327
+ >(async () => {})
1328
+ const mw2 = createMiddleware<
1329
+ { Variables: { foo2: string } },
1330
+ string,
1331
+ { out: { query: { bar2: number } } }
1332
+ >(async () => {})
1333
+ const mw3 = createMiddleware<
1334
+ { Variables: { foo3: string } },
1335
+ string,
1336
+ { out: { query: { bar3: number } } }
1337
+ >(async () => {})
1338
+ const mw4 = createMiddleware<
1339
+ { Variables: { foo4: string } },
1340
+ string,
1341
+ { out: { query: { bar4: number } } }
1342
+ >(async () => {})
1343
+ const mw5 = createMiddleware<
1344
+ { Variables: { foo5: string } },
1345
+ string,
1346
+ { out: { query: { bar5: number } } }
1347
+ >(async () => {})
1348
+ const mw6 = createMiddleware<
1349
+ { Variables: { foo6: string } },
1350
+ string,
1351
+ { out: { query: { bar6: number } } }
1352
+ >(async () => {})
1353
+ const mw7 = createMiddleware<
1354
+ { Variables: { foo7: string } },
1355
+ string,
1356
+ { out: { query: { bar7: number } } }
1357
+ >(async () => {})
1358
+ const mw8 = createMiddleware<
1359
+ { Variables: { foo8: string } },
1360
+ string,
1361
+ { out: { query: { bar8: number } } }
1362
+ >(async () => {})
1363
+ const mw9 = createMiddleware<
1364
+ { Variables: { foo9: string } },
1365
+ string,
1366
+ { out: { query: { bar9: number } } }
1367
+ >(async () => {})
1368
+ const mw10 = createMiddleware<
1369
+ { Variables: { foo10: string } },
1370
+ string,
1371
+ { out: { query: { bar10: number } } }
1372
+ >(async () => {})
1373
+
1374
+ it('Should not throw type errors', () => {
1375
+ // app.get(handler...)
1376
+
1377
+ new App().get(mw1).get('/', (c) => {
1378
+ expectTypeOf(c.var.foo1).toEqualTypeOf<string>()
1379
+ return c.json(0)
1380
+ })
1381
+
1382
+ new App().get(mw1, mw2).get('/', (c) => {
1383
+ expectTypeOf(c.var.foo1).toEqualTypeOf<string>()
1384
+ expectTypeOf(c.var.foo2).toEqualTypeOf<string>()
1385
+ return c.json(0)
1386
+ })
1387
+
1388
+ new App().get(mw1, mw2, mw3).get('/', (c) => {
1389
+ expectTypeOf(c.var.foo1).toEqualTypeOf<string>()
1390
+ expectTypeOf(c.var.foo2).toEqualTypeOf<string>()
1391
+ expectTypeOf(c.var.foo3).toEqualTypeOf<string>()
1392
+ return c.json(0)
1393
+ })
1394
+
1395
+ new App().get(mw1, mw2, mw3, mw4).get('/', (c) => {
1396
+ expectTypeOf(c.var.foo1).toEqualTypeOf<string>()
1397
+ expectTypeOf(c.var.foo2).toEqualTypeOf<string>()
1398
+ expectTypeOf(c.var.foo3).toEqualTypeOf<string>()
1399
+ expectTypeOf(c.var.foo4).toEqualTypeOf<string>()
1400
+ return c.json(0)
1401
+ })
1402
+
1403
+ new App().get(mw1, mw2, mw3, mw4, mw5).get('/', (c) => {
1404
+ expectTypeOf(c.var.foo1).toEqualTypeOf<string>()
1405
+ expectTypeOf(c.var.foo2).toEqualTypeOf<string>()
1406
+ expectTypeOf(c.var.foo3).toEqualTypeOf<string>()
1407
+ expectTypeOf(c.var.foo4).toEqualTypeOf<string>()
1408
+ expectTypeOf(c.var.foo5).toEqualTypeOf<string>()
1409
+ return c.json(0)
1410
+ })
1411
+
1412
+ new App().get(mw1, mw2, mw3, mw4, mw5, mw6).get('/', (c) => {
1413
+ expectTypeOf(c.var.foo1).toEqualTypeOf<string>()
1414
+ expectTypeOf(c.var.foo2).toEqualTypeOf<string>()
1415
+ expectTypeOf(c.var.foo3).toEqualTypeOf<string>()
1416
+ expectTypeOf(c.var.foo4).toEqualTypeOf<string>()
1417
+ expectTypeOf(c.var.foo5).toEqualTypeOf<string>()
1418
+ expectTypeOf(c.var.foo6).toEqualTypeOf<string>()
1419
+ return c.json(0)
1420
+ })
1421
+
1422
+ new App().get(mw1, mw2, mw3, mw4, mw5, mw6, mw7).get('/', (c) => {
1423
+ expectTypeOf(c.var.foo1).toEqualTypeOf<string>()
1424
+ expectTypeOf(c.var.foo2).toEqualTypeOf<string>()
1425
+ expectTypeOf(c.var.foo3).toEqualTypeOf<string>()
1426
+ expectTypeOf(c.var.foo4).toEqualTypeOf<string>()
1427
+ expectTypeOf(c.var.foo5).toEqualTypeOf<string>()
1428
+ expectTypeOf(c.var.foo6).toEqualTypeOf<string>()
1429
+ expectTypeOf(c.var.foo7).toEqualTypeOf<string>()
1430
+ return c.json(0)
1431
+ })
1432
+
1433
+ new App().get(mw1, mw2, mw3, mw4, mw5, mw6, mw7, mw8).get('/', (c) => {
1434
+ expectTypeOf(c.var.foo1).toEqualTypeOf<string>()
1435
+ expectTypeOf(c.var.foo2).toEqualTypeOf<string>()
1436
+ expectTypeOf(c.var.foo3).toEqualTypeOf<string>()
1437
+ expectTypeOf(c.var.foo4).toEqualTypeOf<string>()
1438
+ expectTypeOf(c.var.foo5).toEqualTypeOf<string>()
1439
+ expectTypeOf(c.var.foo6).toEqualTypeOf<string>()
1440
+ expectTypeOf(c.var.foo7).toEqualTypeOf<string>()
1441
+ expectTypeOf(c.var.foo8).toEqualTypeOf<string>()
1442
+ return c.json(0)
1443
+ })
1444
+
1445
+ new App().get(mw1, mw2, mw3, mw4, mw5, mw6, mw7, mw8, mw9).get('/', (c) => {
1446
+ expectTypeOf(c.var.foo1).toEqualTypeOf<string>()
1447
+ expectTypeOf(c.var.foo2).toEqualTypeOf<string>()
1448
+ expectTypeOf(c.var.foo3).toEqualTypeOf<string>()
1449
+ expectTypeOf(c.var.foo4).toEqualTypeOf<string>()
1450
+ expectTypeOf(c.var.foo5).toEqualTypeOf<string>()
1451
+ expectTypeOf(c.var.foo6).toEqualTypeOf<string>()
1452
+ expectTypeOf(c.var.foo7).toEqualTypeOf<string>()
1453
+ expectTypeOf(c.var.foo8).toEqualTypeOf<string>()
1454
+ expectTypeOf(c.var.foo9).toEqualTypeOf<string>()
1455
+ return c.json(0)
1456
+ })
1457
+
1458
+ new App().get(mw1, mw2, mw3, mw4, mw5, mw6, mw7, mw8, mw9, mw10).get('/', (c) => {
1459
+ expectTypeOf(c.var.foo1).toEqualTypeOf<string>()
1460
+ expectTypeOf(c.var.foo2).toEqualTypeOf<string>()
1461
+ expectTypeOf(c.var.foo3).toEqualTypeOf<string>()
1462
+ expectTypeOf(c.var.foo4).toEqualTypeOf<string>()
1463
+ expectTypeOf(c.var.foo5).toEqualTypeOf<string>()
1464
+ expectTypeOf(c.var.foo6).toEqualTypeOf<string>()
1465
+ expectTypeOf(c.var.foo7).toEqualTypeOf<string>()
1466
+ expectTypeOf(c.var.foo8).toEqualTypeOf<string>()
1467
+ expectTypeOf(c.var.foo9).toEqualTypeOf<string>()
1468
+ expectTypeOf(c.var.foo10).toEqualTypeOf<string>()
1469
+ return c.json(0)
1470
+ })
1471
+
1472
+ new App().get(
1473
+ mw1,
1474
+ mw2,
1475
+ mw3,
1476
+ mw4,
1477
+ mw5,
1478
+ mw6,
1479
+ mw7,
1480
+ mw8,
1481
+ async (c) => {
1482
+ expectTypeOf(c.var.foo1).toEqualTypeOf<string>()
1483
+ expectTypeOf(c.var.foo2).toEqualTypeOf<string>()
1484
+ expectTypeOf(c.var.foo3).toEqualTypeOf<string>()
1485
+ expectTypeOf(c.var.foo4).toEqualTypeOf<string>()
1486
+ expectTypeOf(c.var.foo5).toEqualTypeOf<string>()
1487
+ expectTypeOf(c.var.foo6).toEqualTypeOf<string>()
1488
+ expectTypeOf(c.var.foo7).toEqualTypeOf<string>()
1489
+ expectTypeOf(c.var.foo8).toEqualTypeOf<string>()
1490
+ },
1491
+ (c) => c.json(0)
1492
+ )
1493
+
1494
+ new App().get(mw1, mw2, mw3, mw4, mw5, mw6, mw7, mw8, mw9, (c) => {
1495
+ expectTypeOf(c.req.valid('query')).toMatchTypeOf<{
1496
+ bar1: number
1497
+ bar2: number
1498
+ bar3: number
1499
+ bar4: number
1500
+ bar5: number
1501
+ bar6: number
1502
+ bar7: number
1503
+ bar8: number
1504
+ bar9: number
1505
+ }>()
1506
+
1507
+ return c.json(0)
1508
+ })
1509
+
1510
+ new App().get(
1511
+ '/',
1512
+ mw1,
1513
+ mw2,
1514
+ mw3,
1515
+ mw4,
1516
+ mw5,
1517
+ mw6,
1518
+ mw7,
1519
+ mw8,
1520
+ async (c) => {
1521
+ expectTypeOf(c.var.foo1).toEqualTypeOf<string>()
1522
+ expectTypeOf(c.var.foo2).toEqualTypeOf<string>()
1523
+ expectTypeOf(c.var.foo3).toEqualTypeOf<string>()
1524
+ expectTypeOf(c.var.foo4).toEqualTypeOf<string>()
1525
+ expectTypeOf(c.var.foo5).toEqualTypeOf<string>()
1526
+ expectTypeOf(c.var.foo6).toEqualTypeOf<string>()
1527
+ expectTypeOf(c.var.foo7).toEqualTypeOf<string>()
1528
+ expectTypeOf(c.var.foo8).toEqualTypeOf<string>()
1529
+ },
1530
+ (c) => c.json(0)
1531
+ )
1532
+
1533
+ new App().get('/', mw1, mw2, mw3, mw4, mw5, mw6, mw7, mw8, mw9, (c) => {
1534
+ expectTypeOf(c.req.valid('query')).toMatchTypeOf<{
1535
+ bar1: number
1536
+ bar2: number
1537
+ bar3: number
1538
+ bar4: number
1539
+ bar5: number
1540
+ bar6: number
1541
+ bar7: number
1542
+ bar8: number
1543
+ bar9: number
1544
+ }>()
1545
+
1546
+ return c.json(0)
1547
+ })
1548
+
1549
+ type Env = {
1550
+ Variables: {
1551
+ init: number
1552
+ }
1553
+ }
1554
+
1555
+ new App<Env>()
1556
+ .get('/', mw1, (c) => {
1557
+ expectTypeOf(c.get('init')).toEqualTypeOf<number>()
1558
+ expectTypeOf(c.var.init).toEqualTypeOf<number>()
1559
+ expectTypeOf(c.get('foo1')).toEqualTypeOf<string>()
1560
+ expectTypeOf(c.var.foo1).toEqualTypeOf<string>()
1561
+ return c.json(0)
1562
+ })
1563
+ .get('/', (c) => {
1564
+ expectTypeOf(c.get('init')).toEqualTypeOf<number>()
1565
+ expectTypeOf(c.var.init).toEqualTypeOf<number>()
1566
+ // @ts-expect-error foo1 is not typed
1567
+ c.get('foo1')
1568
+ // @ts-expect-error foo1 is not typed
1569
+ c.var.foo1
1570
+ return c.json(0)
1571
+ })
1572
+ new App<Env>()
1573
+ .get('/', mw1, mw2, (c) => {
1574
+ expectTypeOf(c.get('init')).toEqualTypeOf<number>()
1575
+ expectTypeOf(c.var.init).toEqualTypeOf<number>()
1576
+ expectTypeOf(c.get('foo1')).toEqualTypeOf<string>()
1577
+ expectTypeOf(c.var.foo1).toEqualTypeOf<string>()
1578
+ expectTypeOf(c.get('foo2')).toEqualTypeOf<string>()
1579
+ expectTypeOf(c.var.foo2).toEqualTypeOf<string>()
1580
+ return c.json(0)
1581
+ })
1582
+ .get('/', (c) => {
1583
+ expectTypeOf(c.get('init')).toEqualTypeOf<number>()
1584
+ expectTypeOf(c.var.init).toEqualTypeOf<number>()
1585
+ // @ts-expect-error foo1 is not typed
1586
+ c.get('foo1')
1587
+ // @ts-expect-error foo1 is not typed
1588
+ c.var.foo1
1589
+ // @ts-expect-error foo2 is not typed
1590
+ c.get('foo2')
1591
+ // @ts-expect-error foo2 is not typed
1592
+ c.var.foo2
1593
+ return c.json(0)
1594
+ })
1595
+ new App<Env>()
1596
+ .get('/', mw1, mw2, mw3, (c) => {
1597
+ expectTypeOf(c.get('init')).toEqualTypeOf<number>()
1598
+ expectTypeOf(c.var.init).toEqualTypeOf<number>()
1599
+ expectTypeOf(c.get('foo1')).toEqualTypeOf<string>()
1600
+ expectTypeOf(c.var.foo1).toEqualTypeOf<string>()
1601
+ expectTypeOf(c.get('foo2')).toEqualTypeOf<string>()
1602
+ expectTypeOf(c.var.foo2).toEqualTypeOf<string>()
1603
+ expectTypeOf(c.get('foo3')).toEqualTypeOf<string>()
1604
+ expectTypeOf(c.var.foo3).toEqualTypeOf<string>()
1605
+ return c.json(0)
1606
+ })
1607
+ .get('/', (c) => {
1608
+ expectTypeOf(c.get('init')).toEqualTypeOf<number>()
1609
+ expectTypeOf(c.var.init).toEqualTypeOf<number>()
1610
+ // @ts-expect-error foo1 is not typed
1611
+ c.get('foo1')
1612
+ // @ts-expect-error foo1 is not typed
1613
+ c.var.foo1
1614
+ // @ts-expect-error foo2 is not typed
1615
+ c.get('foo2')
1616
+ // @ts-expect-error foo2 is not typed
1617
+ c.var.foo2
1618
+ // @ts-expect-error foo3 is not typed
1619
+ c.get('foo3')
1620
+ // @ts-expect-error foo3 is not typed
1621
+ c.var.foo3
1622
+ return c.json(0)
1623
+ })
1624
+ new App<Env>()
1625
+ .get('/', mw1, mw2, mw3, mw4, (c) => {
1626
+ expectTypeOf(c.get('init')).toEqualTypeOf<number>()
1627
+ expectTypeOf(c.var.init).toEqualTypeOf<number>()
1628
+ expectTypeOf(c.get('foo1')).toEqualTypeOf<string>()
1629
+ expectTypeOf(c.var.foo1).toEqualTypeOf<string>()
1630
+ expectTypeOf(c.get('foo2')).toEqualTypeOf<string>()
1631
+ expectTypeOf(c.var.foo2).toEqualTypeOf<string>()
1632
+ expectTypeOf(c.get('foo3')).toEqualTypeOf<string>()
1633
+ expectTypeOf(c.var.foo3).toEqualTypeOf<string>()
1634
+ expectTypeOf(c.get('foo4')).toEqualTypeOf<string>()
1635
+ expectTypeOf(c.var.foo4).toEqualTypeOf<string>()
1636
+ return c.json(0)
1637
+ })
1638
+ .get('/', (c) => {
1639
+ expectTypeOf(c.get('init')).toEqualTypeOf<number>()
1640
+ expectTypeOf(c.var.init).toEqualTypeOf<number>()
1641
+ // @ts-expect-error foo1 is not typed
1642
+ c.get('foo1')
1643
+ // @ts-expect-error foo1 is not typed
1644
+ c.var.foo1
1645
+ // @ts-expect-error foo2 is not typed
1646
+ c.get('foo2')
1647
+ // @ts-expect-error foo2 is not typed
1648
+ c.var.foo2
1649
+ // @ts-expect-error foo3 is not typed
1650
+ c.get('foo3')
1651
+ // @ts-expect-error foo3 is not typed
1652
+ c.var.foo3
1653
+ // @ts-expect-error foo4 is not typed
1654
+ c.get('foo4')
1655
+ // @ts-expect-error foo4 is not typed
1656
+ c.var.foo4
1657
+ return c.json(0)
1658
+ })
1659
+
1660
+ new App<Env>()
1661
+ .get('/', mw1, mw2, mw3, mw4, mw5, (c) => {
1662
+ expectTypeOf(c.get('init')).toEqualTypeOf<number>()
1663
+ expectTypeOf(c.var.init).toEqualTypeOf<number>()
1664
+ expectTypeOf(c.get('foo1')).toEqualTypeOf<string>()
1665
+ expectTypeOf(c.var.foo1).toEqualTypeOf<string>()
1666
+ expectTypeOf(c.get('foo2')).toEqualTypeOf<string>()
1667
+ expectTypeOf(c.var.foo2).toEqualTypeOf<string>()
1668
+ expectTypeOf(c.get('foo3')).toEqualTypeOf<string>()
1669
+ expectTypeOf(c.var.foo3).toEqualTypeOf<string>()
1670
+ expectTypeOf(c.get('foo4')).toEqualTypeOf<string>()
1671
+ expectTypeOf(c.var.foo4).toEqualTypeOf<string>()
1672
+ expectTypeOf(c.get('foo5')).toEqualTypeOf<string>()
1673
+ expectTypeOf(c.var.foo5).toEqualTypeOf<string>()
1674
+ return c.json(0)
1675
+ })
1676
+ .get('/', (c) => {
1677
+ expectTypeOf(c.get('init')).toEqualTypeOf<number>()
1678
+ expectTypeOf(c.var.init).toEqualTypeOf<number>()
1679
+ // @ts-expect-error foo1 is not typed
1680
+ c.get('foo1')
1681
+ // @ts-expect-error foo1 is not typed
1682
+ c.var.foo1
1683
+ // @ts-expect-error foo2 is not typed
1684
+ c.get('foo2')
1685
+ // @ts-expect-error foo2 is not typed
1686
+ c.var.foo2
1687
+ // @ts-expect-error foo3 is not typed
1688
+ c.get('foo3')
1689
+ // @ts-expect-error foo3 is not typed
1690
+ c.var.foo3
1691
+ // @ts-expect-error foo4 is not typed
1692
+ c.get('foo4')
1693
+ // @ts-expect-error foo4 is not typed
1694
+ c.var.foo4
1695
+ // @ts-expect-error foo5 is not typed
1696
+ c.get('foo5')
1697
+ // @ts-expect-error foo5 is not typed
1698
+ c.var.foo5
1699
+ return c.json(0)
1700
+ })
1701
+ new App<Env>()
1702
+ .get('/', mw1, mw2, mw3, mw4, mw5, mw6, (c) => {
1703
+ expectTypeOf(c.get('init')).toEqualTypeOf<number>()
1704
+ expectTypeOf(c.var.init).toEqualTypeOf<number>()
1705
+ expectTypeOf(c.get('foo1')).toEqualTypeOf<string>()
1706
+ expectTypeOf(c.var.foo1).toEqualTypeOf<string>()
1707
+ expectTypeOf(c.get('foo2')).toEqualTypeOf<string>()
1708
+ expectTypeOf(c.var.foo2).toEqualTypeOf<string>()
1709
+ expectTypeOf(c.get('foo3')).toEqualTypeOf<string>()
1710
+ expectTypeOf(c.var.foo3).toEqualTypeOf<string>()
1711
+ expectTypeOf(c.get('foo4')).toEqualTypeOf<string>()
1712
+ expectTypeOf(c.var.foo4).toEqualTypeOf<string>()
1713
+ expectTypeOf(c.get('foo5')).toEqualTypeOf<string>()
1714
+ expectTypeOf(c.var.foo5).toEqualTypeOf<string>()
1715
+ expectTypeOf(c.get('foo6')).toEqualTypeOf<string>()
1716
+ expectTypeOf(c.var.foo6).toEqualTypeOf<string>()
1717
+ return c.json(0)
1718
+ })
1719
+ .get('/', (c) => {
1720
+ expectTypeOf(c.get('init')).toEqualTypeOf<number>()
1721
+ expectTypeOf(c.var.init).toEqualTypeOf<number>()
1722
+ // @ts-expect-error foo1 is not typed
1723
+ c.get('foo1')
1724
+ // @ts-expect-error foo1 is not typed
1725
+ c.var.foo1
1726
+ // @ts-expect-error foo2 is not typed
1727
+ c.get('foo2')
1728
+ // @ts-expect-error foo2 is not typed
1729
+ c.var.foo2
1730
+ // @ts-expect-error foo3 is not typed
1731
+ c.get('foo3')
1732
+ // @ts-expect-error foo3 is not typed
1733
+ c.var.foo3
1734
+ // @ts-expect-error foo4 is not typed
1735
+ c.get('foo4')
1736
+ // @ts-expect-error foo4 is not typed
1737
+ c.var.foo4
1738
+ // @ts-expect-error foo5 is not typed
1739
+ c.get('foo5')
1740
+ // @ts-expect-error foo5 is not typed
1741
+ c.var.foo5
1742
+ // @ts-expect-error foo6 is not typed
1743
+ c.get('foo6')
1744
+ // @ts-expect-error foo6 is not typed
1745
+ c.var.foo6
1746
+ return c.json(0)
1747
+ })
1748
+ new App<Env>()
1749
+ .get('/', mw1, mw2, mw3, mw4, mw5, mw6, mw7, (c) => {
1750
+ expectTypeOf(c.get('init')).toEqualTypeOf<number>()
1751
+ expectTypeOf(c.var.init).toEqualTypeOf<number>()
1752
+ expectTypeOf(c.get('foo1')).toEqualTypeOf<string>()
1753
+ expectTypeOf(c.var.foo1).toEqualTypeOf<string>()
1754
+ expectTypeOf(c.get('foo2')).toEqualTypeOf<string>()
1755
+ expectTypeOf(c.var.foo2).toEqualTypeOf<string>()
1756
+ expectTypeOf(c.get('foo3')).toEqualTypeOf<string>()
1757
+ expectTypeOf(c.var.foo3).toEqualTypeOf<string>()
1758
+ expectTypeOf(c.get('foo4')).toEqualTypeOf<string>()
1759
+ expectTypeOf(c.var.foo4).toEqualTypeOf<string>()
1760
+ expectTypeOf(c.get('foo5')).toEqualTypeOf<string>()
1761
+ expectTypeOf(c.var.foo5).toEqualTypeOf<string>()
1762
+ expectTypeOf(c.get('foo6')).toEqualTypeOf<string>()
1763
+ expectTypeOf(c.var.foo6).toEqualTypeOf<string>()
1764
+ expectTypeOf(c.get('foo7')).toEqualTypeOf<string>()
1765
+ expectTypeOf(c.var.foo7).toEqualTypeOf<string>()
1766
+ return c.json(0)
1767
+ })
1768
+ .get('/', (c) => {
1769
+ expectTypeOf(c.get('init')).toEqualTypeOf<number>()
1770
+ expectTypeOf(c.var.init).toEqualTypeOf<number>()
1771
+ // @ts-expect-error foo1 is not typed
1772
+ c.get('foo1')
1773
+ // @ts-expect-error foo1 is not typed
1774
+ c.var.foo1
1775
+ // @ts-expect-error foo2 is not typed
1776
+ c.get('foo2')
1777
+ // @ts-expect-error foo2 is not typed
1778
+ c.var.foo2
1779
+ // @ts-expect-error foo3 is not typed
1780
+ c.get('foo3')
1781
+ // @ts-expect-error foo3 is not typed
1782
+ c.var.foo3
1783
+ // @ts-expect-error foo4 is not typed
1784
+ c.get('foo4')
1785
+ // @ts-expect-error foo4 is not typed
1786
+ c.var.foo4
1787
+ // @ts-expect-error foo5 is not typed
1788
+ c.get('foo5')
1789
+ // @ts-expect-error foo5 is not typed
1790
+ c.var.foo5
1791
+ // @ts-expect-error foo6 is not typed
1792
+ c.get('foo6')
1793
+ // @ts-expect-error foo6 is not typed
1794
+ c.var.foo6
1795
+ // @ts-expect-error foo7 is not typed
1796
+ c.get('foo7')
1797
+ // @ts-expect-error foo7 is not typed
1798
+ c.var.foo7
1799
+ return c.json(0)
1800
+ })
1801
+ new App<Env>()
1802
+ .get('/', mw1, mw2, mw3, mw4, mw5, mw6, mw7, mw8, (c) => {
1803
+ expectTypeOf(c.get('init')).toEqualTypeOf<number>()
1804
+ expectTypeOf(c.var.init).toEqualTypeOf<number>()
1805
+ expectTypeOf(c.get('foo1')).toEqualTypeOf<string>()
1806
+ expectTypeOf(c.var.foo1).toEqualTypeOf<string>()
1807
+ expectTypeOf(c.get('foo2')).toEqualTypeOf<string>()
1808
+ expectTypeOf(c.var.foo2).toEqualTypeOf<string>()
1809
+ expectTypeOf(c.get('foo3')).toEqualTypeOf<string>()
1810
+ expectTypeOf(c.var.foo3).toEqualTypeOf<string>()
1811
+ expectTypeOf(c.get('foo4')).toEqualTypeOf<string>()
1812
+ expectTypeOf(c.var.foo4).toEqualTypeOf<string>()
1813
+ expectTypeOf(c.get('foo5')).toEqualTypeOf<string>()
1814
+ expectTypeOf(c.var.foo5).toEqualTypeOf<string>()
1815
+ expectTypeOf(c.get('foo6')).toEqualTypeOf<string>()
1816
+ expectTypeOf(c.var.foo6).toEqualTypeOf<string>()
1817
+ expectTypeOf(c.get('foo7')).toEqualTypeOf<string>()
1818
+ expectTypeOf(c.var.foo7).toEqualTypeOf<string>()
1819
+ expectTypeOf(c.get('foo8')).toEqualTypeOf<string>()
1820
+ expectTypeOf(c.var.foo8).toEqualTypeOf<string>()
1821
+ return c.json(0)
1822
+ })
1823
+ .get('/', (c) => {
1824
+ expectTypeOf(c.get('init')).toEqualTypeOf<number>()
1825
+ expectTypeOf(c.var.init).toEqualTypeOf<number>()
1826
+ // @ts-expect-error foo1 is not typed
1827
+ c.get('foo1')
1828
+ // @ts-expect-error foo1 is not typed
1829
+ c.var.foo1
1830
+ // @ts-expect-error foo2 is not typed
1831
+ c.get('foo2')
1832
+ // @ts-expect-error foo2 is not typed
1833
+ c.var.foo2
1834
+ // @ts-expect-error foo3 is not typed
1835
+ c.get('foo3')
1836
+ // @ts-expect-error foo3 is not typed
1837
+ c.var.foo3
1838
+ // @ts-expect-error foo4 is not typed
1839
+ c.get('foo4')
1840
+ // @ts-expect-error foo4 is not typed
1841
+ c.var.foo4
1842
+ // @ts-expect-error foo5 is not typed
1843
+ c.get('foo5')
1844
+ // @ts-expect-error foo5 is not typed
1845
+ c.var.foo5
1846
+ // @ts-expect-error foo6 is not typed
1847
+ c.get('foo6')
1848
+ // @ts-expect-error foo6 is not typed
1849
+ c.var.foo6
1850
+ // @ts-expect-error foo7 is not typed
1851
+ c.get('foo7')
1852
+ // @ts-expect-error foo7 is not typed
1853
+ c.var.foo7
1854
+ // @ts-expect-error foo8 is not typed
1855
+ c.get('foo8')
1856
+ // @ts-expect-error foo8 is not typed
1857
+ c.var.foo8
1858
+ return c.json(0)
1859
+ })
1860
+
1861
+ new App<Env>()
1862
+ .get('/', mw1, mw2, mw3, mw4, mw5, mw6, mw7, mw8, mw9, (c) => {
1863
+ expectTypeOf(c.get('init')).toEqualTypeOf<number>()
1864
+ expectTypeOf(c.var.init).toEqualTypeOf<number>()
1865
+ expectTypeOf(c.get('foo1')).toEqualTypeOf<string>()
1866
+ expectTypeOf(c.var.foo1).toEqualTypeOf<string>()
1867
+ expectTypeOf(c.get('foo2')).toEqualTypeOf<string>()
1868
+ expectTypeOf(c.var.foo2).toEqualTypeOf<string>()
1869
+ expectTypeOf(c.get('foo3')).toEqualTypeOf<string>()
1870
+ expectTypeOf(c.var.foo3).toEqualTypeOf<string>()
1871
+ expectTypeOf(c.get('foo4')).toEqualTypeOf<string>()
1872
+ expectTypeOf(c.var.foo4).toEqualTypeOf<string>()
1873
+ expectTypeOf(c.get('foo5')).toEqualTypeOf<string>()
1874
+ expectTypeOf(c.var.foo5).toEqualTypeOf<string>()
1875
+ expectTypeOf(c.get('foo6')).toEqualTypeOf<string>()
1876
+ expectTypeOf(c.var.foo6).toEqualTypeOf<string>()
1877
+ expectTypeOf(c.get('foo7')).toEqualTypeOf<string>()
1878
+ expectTypeOf(c.var.foo7).toEqualTypeOf<string>()
1879
+ expectTypeOf(c.get('foo8')).toEqualTypeOf<string>()
1880
+ expectTypeOf(c.var.foo8).toEqualTypeOf<string>()
1881
+ expectTypeOf(c.get('foo9')).toEqualTypeOf<string>()
1882
+ expectTypeOf(c.var.foo9).toEqualTypeOf<string>()
1883
+ return c.json(0)
1884
+ })
1885
+ .get('/', (c) => {
1886
+ expectTypeOf(c.get('init')).toEqualTypeOf<number>()
1887
+ expectTypeOf(c.var.init).toEqualTypeOf<number>()
1888
+ // @ts-expect-error foo1 is not typed
1889
+ c.get('foo1')
1890
+ // @ts-expect-error foo1 is not typed
1891
+ c.var.foo1
1892
+ // @ts-expect-error foo2 is not typed
1893
+ c.get('foo2')
1894
+ // @ts-expect-error foo2 is not typed
1895
+ c.var.foo2
1896
+ // @ts-expect-error foo3 is not typed
1897
+ c.get('foo3')
1898
+ // @ts-expect-error foo3 is not typed
1899
+ c.var.foo3
1900
+ // @ts-expect-error foo4 is not typed
1901
+ c.get('foo4')
1902
+ // @ts-expect-error foo4 is not typed
1903
+ c.var.foo4
1904
+ // @ts-expect-error foo5 is not typed
1905
+ c.get('foo5')
1906
+ // @ts-expect-error foo5 is not typed
1907
+ c.var.foo5
1908
+ // @ts-expect-error foo6 is not typed
1909
+ c.get('foo6')
1910
+ // @ts-expect-error foo6 is not typed
1911
+ c.var.foo6
1912
+ // @ts-expect-error foo7 is not typed
1913
+ c.get('foo7')
1914
+ // @ts-expect-error foo7 is not typed
1915
+ c.var.foo7
1916
+ // @ts-expect-error foo8 is not typed
1917
+ c.get('foo8')
1918
+ // @ts-expect-error foo8 is not typed
1919
+ c.var.foo8
1920
+ // @ts-expect-error foo9 is not typed
1921
+ c.get('foo9')
1922
+ // @ts-expect-error foo9 is not typed
1923
+ c.var.foo9
1924
+ return c.json(0)
1925
+ })
1926
+ })
1927
+ })
1928
+
1929
+ /**
1930
+ *
1931
+ * Declaring a ContextVariableMap for testing.
1932
+ */
1933
+ declare module './context.ts' {
1934
+ interface ContextVariableMap {
1935
+ payload: string
1936
+ }
1937
+ }
1938
+
1939
+ describe('ContextVariableMap type tests', () => {
1940
+ it('Should not throw type errors with c.var', () => {
1941
+ new App().get((c) => {
1942
+ expectTypeOf(c.get('payload')).toEqualTypeOf<string>()
1943
+ return c.json(0)
1944
+ })
1945
+ new App().get((c) => {
1946
+ expectTypeOf(c.var.payload).toEqualTypeOf<string>()
1947
+ return c.json(0)
1948
+ })
1949
+ })
1950
+
1951
+ it('Should override ContextVariableMap with env variables', () => {
1952
+ const middleware = createMiddleware<{
1953
+ Variables: {
1954
+ payload: number
1955
+ }
1956
+ }>(async (c, next) => {
1957
+ c.set('payload', 123)
1958
+ await next()
1959
+ })
1960
+
1961
+ new App().get(middleware, (c) => {
1962
+ expectTypeOf(c.get('payload')).toEqualTypeOf<number>()
1963
+ return c.json(0)
1964
+ })
1965
+ })
1966
+
1967
+ it('Should use ContextVariableMap when c is Context<any>', () => {
1968
+ const c = new Context(new Request('http://localhost'))
1969
+ expectTypeOf(c.get('payload')).toEqualTypeOf<string>()
1970
+ expectTypeOf(c.var.payload).toEqualTypeOf<string>()
1971
+ // @ts-expect-error the value of payload should be string
1972
+ expectTypeOf(c.set('payload', 123))
1973
+ })
1974
+ })
1975
+
1976
+ /**
1977
+ * It's challenge to test all cases. This is a minimal pattern.
1978
+ */
1979
+ describe('Env types with chained routes - test only types', () => {
1980
+ const app = new App<{ Variables: { testVar: string } }>()
1981
+ it('Should not throw a type error', () => {
1982
+ app
1983
+ .post(
1984
+ '/',
1985
+ validator('json', (v) => v),
1986
+ async (c) => {
1987
+ expectTypeOf(c.get('testVar')).toEqualTypeOf<string>()
1988
+ return c.json({ success: true })
1989
+ }
1990
+ )
1991
+ .patch(
1992
+ '/',
1993
+ validator('json', (v) => v),
1994
+ async (c) => {
1995
+ expectTypeOf(c.get('testVar')).toEqualTypeOf<string>()
1996
+ return c.json({ success: true })
1997
+ }
1998
+ )
1999
+ })
2000
+ })
2001
+
2002
+ /**
2003
+ */
2004
+ describe('Env types with validator as first middleware - test only types', () => {
2005
+ const app = new App<{ Variables: { testVar: string } }>()
2006
+ it('Should not throw a type error', () => {
2007
+ const testApp = app.get(
2008
+ validator('json', () => {
2009
+ return {
2010
+ cd: 'bar',
2011
+ }
2012
+ }),
2013
+ async (c) => {
2014
+ const foo = c.req.valid('json') // Error here
2015
+ return c.json(1)
2016
+ }
2017
+ )
2018
+
2019
+ const dummyMiddleware1 = createMiddleware(async (c, next) => {
2020
+ await next()
2021
+ })
2022
+ // Multiple levels of middleware
2023
+ const testApp2 = app.post(
2024
+ validator('json', () => {
2025
+ return {
2026
+ cd: 'bar',
2027
+ }
2028
+ }),
2029
+ dummyMiddleware1,
2030
+ createMiddleware(async (c, next) => {
2031
+ await next()
2032
+ }),
2033
+ async (c) => {
2034
+ const foo = c.req.valid('json') // Error here also
2035
+ return c.json(1)
2036
+ }
2037
+ )
2038
+ })
2039
+ })
2040
+ describe('c.req.valid() in non-last handler after validator middleware - test only types', () => {
2041
+ it('Should not throw a type error', () => {
2042
+ const app = new App()
2043
+ app.get(
2044
+ '/',
2045
+ validator('query', () => {
2046
+ return {
2047
+ test: 'hello',
2048
+ }
2049
+ }),
2050
+ async (c, next) => {
2051
+ const { test } = c.req.valid('query')
2052
+ expectTypeOf(test).toEqualTypeOf<string>()
2053
+ await next()
2054
+ },
2055
+ async (c) => {
2056
+ const { test } = c.req.valid('query')
2057
+ expectTypeOf(test).toEqualTypeOf<string>()
2058
+ return c.json({ ok: true })
2059
+ }
2060
+ )
2061
+ })
2062
+ })
2063
+
2064
+ describe('Env types with `use` middleware - test only types', () => {
2065
+ const app = new App()
2066
+
2067
+ const mw1 = createMiddleware<{ Variables: { foo1: string } }>(async () => {})
2068
+ const mw2 = createMiddleware<{ Variables: { foo2: string } }>(async () => {})
2069
+
2070
+ it('Should not throw a type error', () => {
2071
+ app
2072
+ .use(mw1)
2073
+ .use(mw2)
2074
+ .get('/', (c) => {
2075
+ expectTypeOf(c.get('foo1')).toEqualTypeOf<string>()
2076
+ expectTypeOf(c.get('foo2')).toEqualTypeOf<string>()
2077
+ return c.json({ success: true })
2078
+ })
2079
+ app.use(mw1, mw2).get('/', (c) => {
2080
+ expectTypeOf(c.get('foo1')).toEqualTypeOf<string>()
2081
+ expectTypeOf(c.get('foo2')).toEqualTypeOf<string>()
2082
+ return c.json({ success: true })
2083
+ })
2084
+ })
2085
+ })
2086
+
2087
+ describe('Env types and a path type with `app.use(path, handler...)` - test only types', () => {
2088
+ it('Should not throw a type error', () => {
2089
+ type Env = {
2090
+ Variables: {
2091
+ foo: string
2092
+ }
2093
+ }
2094
+
2095
+ // app.use(path, handler)
2096
+ new App<Env>()
2097
+ .use('/:id', async (c, next) => {
2098
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2099
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2100
+ await next()
2101
+ })
2102
+ .get((c) => {
2103
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2104
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2105
+ return c.json(0)
2106
+ })
2107
+
2108
+ // app.use(path, handler x2)
2109
+ new App<Env>()
2110
+ .use(
2111
+ '/:id',
2112
+ async (c, next) => {
2113
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2114
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2115
+ await next()
2116
+ },
2117
+ async (c, next) => {
2118
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2119
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2120
+ await next()
2121
+ }
2122
+ )
2123
+ .get((c) => {
2124
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2125
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2126
+ return c.json(0)
2127
+ })
2128
+
2129
+ // app.use(path, handler x3)
2130
+ new App<Env>()
2131
+ .use(
2132
+ '/:id',
2133
+ async (c, next) => {
2134
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2135
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2136
+ await next()
2137
+ },
2138
+ async (c, next) => {
2139
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2140
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2141
+ await next()
2142
+ },
2143
+ async (c, next) => {
2144
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2145
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2146
+ await next()
2147
+ }
2148
+ )
2149
+ .get((c) => {
2150
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2151
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2152
+ return c.json(0)
2153
+ })
2154
+
2155
+ // app.use(path, handler x4)
2156
+ new App<Env>()
2157
+ .use(
2158
+ '/:id',
2159
+ async (c, next) => {
2160
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2161
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2162
+ await next()
2163
+ },
2164
+ async (c, next) => {
2165
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2166
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2167
+ await next()
2168
+ },
2169
+ async (c, next) => {
2170
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2171
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2172
+ await next()
2173
+ },
2174
+ async (c, next) => {
2175
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2176
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2177
+ await next()
2178
+ }
2179
+ )
2180
+ .get((c) => {
2181
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2182
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2183
+ return c.json(0)
2184
+ })
2185
+
2186
+ // app.use(path, handler x5)
2187
+ new App<Env>()
2188
+ .use(
2189
+ '/:id',
2190
+ async (c, next) => {
2191
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2192
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2193
+ await next()
2194
+ },
2195
+ async (c, next) => {
2196
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2197
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2198
+ await next()
2199
+ },
2200
+ async (c, next) => {
2201
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2202
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2203
+ await next()
2204
+ },
2205
+ async (c, next) => {
2206
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2207
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2208
+ await next()
2209
+ },
2210
+ async (c, next) => {
2211
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2212
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2213
+ await next()
2214
+ }
2215
+ )
2216
+ .get((c) => {
2217
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2218
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2219
+ return c.json(0)
2220
+ })
2221
+
2222
+ // app.use(path, handler x6)
2223
+ new App<Env>()
2224
+ .use(
2225
+ '/:id',
2226
+ async (c, next) => {
2227
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2228
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2229
+ await next()
2230
+ },
2231
+ async (c, next) => {
2232
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2233
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2234
+ await next()
2235
+ },
2236
+ async (c, next) => {
2237
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2238
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2239
+ await next()
2240
+ },
2241
+ async (c, next) => {
2242
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2243
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2244
+ await next()
2245
+ },
2246
+ async (c, next) => {
2247
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2248
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2249
+ await next()
2250
+ },
2251
+ async (c, next) => {
2252
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2253
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2254
+ await next()
2255
+ }
2256
+ )
2257
+ .get((c) => {
2258
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2259
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2260
+ return c.json(0)
2261
+ })
2262
+
2263
+ // app.use(path, handler x7)
2264
+ new App<Env>()
2265
+ .use(
2266
+ '/:id',
2267
+ async (c, next) => {
2268
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2269
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2270
+ await next()
2271
+ },
2272
+ async (c, next) => {
2273
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2274
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2275
+ await next()
2276
+ },
2277
+ async (c, next) => {
2278
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2279
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2280
+ await next()
2281
+ },
2282
+ async (c, next) => {
2283
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2284
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2285
+ await next()
2286
+ },
2287
+ async (c, next) => {
2288
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2289
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2290
+ await next()
2291
+ },
2292
+ async (c, next) => {
2293
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2294
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2295
+ await next()
2296
+ },
2297
+ async (c, next) => {
2298
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2299
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2300
+ await next()
2301
+ }
2302
+ )
2303
+ .get((c) => {
2304
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2305
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2306
+ return c.json(0)
2307
+ })
2308
+
2309
+ // app.use(path, handler x8)
2310
+ new App<Env>()
2311
+ .use(
2312
+ '/:id',
2313
+ async (c, next) => {
2314
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2315
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2316
+ await next()
2317
+ },
2318
+ async (c, next) => {
2319
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2320
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2321
+ await next()
2322
+ },
2323
+ async (c, next) => {
2324
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2325
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2326
+ await next()
2327
+ },
2328
+ async (c, next) => {
2329
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2330
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2331
+ await next()
2332
+ },
2333
+ async (c, next) => {
2334
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2335
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2336
+ await next()
2337
+ },
2338
+ async (c, next) => {
2339
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2340
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2341
+ await next()
2342
+ },
2343
+ async (c, next) => {
2344
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2345
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2346
+ await next()
2347
+ },
2348
+ async (c, next) => {
2349
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2350
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2351
+ await next()
2352
+ }
2353
+ )
2354
+ .get((c) => {
2355
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2356
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2357
+ return c.json(0)
2358
+ })
2359
+
2360
+ // app.use(path, handler x9)
2361
+ new App<Env>()
2362
+ .use(
2363
+ '/:id',
2364
+ async (c, next) => {
2365
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2366
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2367
+ await next()
2368
+ },
2369
+ async (c, next) => {
2370
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2371
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2372
+ await next()
2373
+ },
2374
+ async (c, next) => {
2375
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2376
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2377
+ await next()
2378
+ },
2379
+ async (c, next) => {
2380
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2381
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2382
+ await next()
2383
+ },
2384
+ async (c, next) => {
2385
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2386
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2387
+ await next()
2388
+ },
2389
+ async (c, next) => {
2390
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2391
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2392
+ await next()
2393
+ },
2394
+ async (c, next) => {
2395
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2396
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2397
+ await next()
2398
+ },
2399
+ async (c, next) => {
2400
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2401
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2402
+ await next()
2403
+ },
2404
+ async (c, next) => {
2405
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2406
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2407
+ await next()
2408
+ }
2409
+ )
2410
+ .get((c) => {
2411
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2412
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2413
+ return c.json(0)
2414
+ })
2415
+
2416
+ // app.use(path, handler x10)
2417
+ new App<Env>()
2418
+ .use(
2419
+ '/:id',
2420
+ async (c, next) => {
2421
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2422
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2423
+ await next()
2424
+ },
2425
+ async (c, next) => {
2426
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2427
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2428
+ await next()
2429
+ },
2430
+ async (c, next) => {
2431
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2432
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2433
+ await next()
2434
+ },
2435
+ async (c, next) => {
2436
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2437
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2438
+ await next()
2439
+ },
2440
+ async (c, next) => {
2441
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2442
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2443
+ await next()
2444
+ },
2445
+ async (c, next) => {
2446
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2447
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2448
+ await next()
2449
+ },
2450
+ async (c, next) => {
2451
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2452
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2453
+ await next()
2454
+ },
2455
+ async (c, next) => {
2456
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2457
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2458
+ await next()
2459
+ },
2460
+ async (c, next) => {
2461
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2462
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2463
+ await next()
2464
+ }
2465
+ )
2466
+ .get((c) => {
2467
+ expectTypeOf(c.var.foo).toEqualTypeOf<string>()
2468
+ expectTypeOf(c.req.param('id')).toEqualTypeOf<string>()
2469
+ return c.json(0)
2470
+ })
2471
+ })
2472
+ })
2473
+ describe('Returning type from `app.use(path, mw)`', () => {
2474
+ const mw = createMiddleware(async (c, next) => {
2475
+ await next()
2476
+ })
2477
+ it('Should ignore the `*` wildcard when building the schema', () => {
2478
+ const app = new App().use('*', mw)
2479
+ type Actual = ExtractSchema<typeof app>
2480
+ type Expected = {}
2481
+ type verify = Expect<Equal<Expected, Actual>>
2482
+ })
2483
+ })
2484
+
2485
+ describe('generic typed variables', () => {
2486
+ const okHelper = (c: Context) => {
2487
+ return <TData>(data: TData) => c.json({ data })
2488
+ }
2489
+ type Variables = {
2490
+ ok: ReturnType<typeof okHelper>
2491
+ }
2492
+ const app = new App<{ Variables: Variables }>()
2493
+
2494
+ it('Should set and get variables with correct types', async () => {
2495
+ const route = app
2496
+ .use('*', async (c, next) => {
2497
+ c.set('ok', okHelper(c))
2498
+ await next()
2499
+ })
2500
+ .get('/', (c) => {
2501
+ const ok = c.get('ok')
2502
+ return ok('Hello')
2503
+ })
2504
+ type Actual = ExtractSchema<typeof route>['/']['$get']['output']
2505
+ type Expected = { data: string }
2506
+ expectTypeOf<Actual>().toEqualTypeOf<Expected>()
2507
+ })
2508
+ })
2509
+
2510
+ describe('status code', () => {
2511
+ const app = new App()
2512
+
2513
+ it('should only allow to return .json() with contentful status codes', async () => {
2514
+ const route = app.get('/', async (c) => c.json({}))
2515
+ type Actual = ExtractSchema<typeof route>['/']['$get']['status']
2516
+ expectTypeOf<Actual>().toEqualTypeOf<ContentfulStatusCode>()
2517
+ })
2518
+
2519
+ it('should only allow to return .body(null) with all status codes', async () => {
2520
+ const route = app.get('/', async (c) => c.body(null))
2521
+ type Actual = ExtractSchema<typeof route>['/']['$get']['status']
2522
+ expectTypeOf<Actual>().toEqualTypeOf<StatusCode>()
2523
+ })
2524
+
2525
+ it('should only allow to return .text() with contentful status codes', async () => {
2526
+ const route = app.get('/', async (c) => c.text('whatever'))
2527
+ type Actual = ExtractSchema<typeof route>['/']['$get']['status']
2528
+ expectTypeOf<Actual>().toEqualTypeOf<ContentfulStatusCode>()
2529
+ })
2530
+
2531
+ it('should throw type error when .json({}) is used with contentless status codes', async () => {
2532
+ // @ts-expect-error 204 is not contentful status code
2533
+ app.get('/', async (c) => c.json({}, 204))
2534
+ app.get('/', async (c) =>
2535
+ c.json(
2536
+ {},
2537
+ // @ts-expect-error 204 is not contentful status code
2538
+ {
2539
+ status: 204,
2540
+ }
2541
+ )
2542
+ )
2543
+ })
2544
+
2545
+ it('should throw type error when .body(content) is used with contentless status codes', async () => {
2546
+ // @ts-expect-error 204 is not contentful status code
2547
+ app.get('/', async (c) => c.body('content', 204))
2548
+ // @ts-expect-error 204 is not contentful status code
2549
+ app.get('/', async (c) => c.body('content', { status: 204 }))
2550
+ })
2551
+
2552
+ it('should throw type error when .text(content) is used with contentless status codes', async () => {
2553
+ // @ts-expect-error 204 is not contentful status code
2554
+ app.get('/', async (c) => c.text('content', 204))
2555
+ // @ts-expect-error 204 is not contentful status code
2556
+ app.get('/', async (c) => c.text('content', { status: 204 }))
2557
+ })
2558
+
2559
+ it('should throw type error when .html(content) is used with contentless status codes', async () => {
2560
+ // @ts-expect-error 204 is not contentful status code
2561
+ app.get('/', async (c) => c.html('<h1>title</h1>', 204))
2562
+ // @ts-expect-error 204 is not contentful status code
2563
+ app.get('/', async (c) => c.html('<h1>title</h1>', { status: 204 }))
2564
+ })
2565
+
2566
+ it('.body() not override other responses in acryl client', async () => {
2567
+ const router = app.get('/', async (c) => {
2568
+ if (c.req.header('Content-Type') === 'application/json') {
2569
+ return c.text('Hello', 200)
2570
+ }
2571
+
2572
+ if (c.req.header('Content-Type') === 'application/x-www-form-urlencoded') {
2573
+ return c.body('Hello', 201)
2574
+ }
2575
+
2576
+ return c.body(null, 204)
2577
+ })
2578
+
2579
+ type Actual = ExtractSchema<typeof router>['/']['$get']['status']
2580
+ expectTypeOf<Actual>().toEqualTypeOf<204 | 201 | 200>()
2581
+ })
2582
+ })
2583
+
2584
+ describe('RPC supports Middleware responses', () => {
2585
+ describe('Merge responses from multiple handlers, path pattern', () => {
2586
+ test('merge responses from 1 middleware', async () => {
2587
+ const middleware = createMiddleware(async (c) => c.json({ '400': true }, 400))
2588
+
2589
+ const app = new App().get('/test', middleware, (c) => c.json({ '200': true }, 200))
2590
+ const client = hc<typeof app>('http://localhost', { fetch: app.request })
2591
+ const res = await client.test.$get()
2592
+
2593
+ if (res.status === 200) {
2594
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
2595
+ }
2596
+ if (res.status === 400) {
2597
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
2598
+ }
2599
+ })
2600
+
2601
+ test('merge responses from 2 middlewares', async () => {
2602
+ const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
2603
+ const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
2604
+
2605
+ const app = new App().get('/test', middleware1, middleware2, (c) =>
2606
+ c.json({ '200': true }, 200)
2607
+ )
2608
+ const client = hc<typeof app>('http://localhost', { fetch: app.request })
2609
+ const res = await client.test.$get()
2610
+
2611
+ if (res.status === 200) {
2612
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
2613
+ }
2614
+ if (res.status === 400) {
2615
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
2616
+ }
2617
+ if (res.status === 401) {
2618
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
2619
+ }
2620
+ })
2621
+
2622
+ test('merge responses from 3 middlewares', async () => {
2623
+ const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
2624
+ const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
2625
+ const middleware3 = createMiddleware(async (c) => c.json({ '403': true }, 403))
2626
+
2627
+ const app = new App().get('/test', middleware1, middleware2, middleware3, (c) =>
2628
+ c.json({ '200': true }, 200)
2629
+ )
2630
+ const client = hc<typeof app>('http://localhost', { fetch: app.request })
2631
+ const res = await client.test.$get()
2632
+
2633
+ if (res.status === 200) {
2634
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
2635
+ }
2636
+ if (res.status === 400) {
2637
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
2638
+ }
2639
+ if (res.status === 401) {
2640
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
2641
+ }
2642
+ if (res.status === 403) {
2643
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 403: true }>()
2644
+ }
2645
+ })
2646
+
2647
+ test('merge responses from 4 middlewares', async () => {
2648
+ const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
2649
+ const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
2650
+ const middleware3 = createMiddleware(async (c) => c.json({ '403': true }, 403))
2651
+ const middleware4 = createMiddleware(async (c) => c.json({ '404': true }, 404))
2652
+
2653
+ const app = new App().get('/test', middleware1, middleware2, middleware3, middleware4, (c) =>
2654
+ c.json({ '200': true }, 200)
2655
+ )
2656
+ const client = hc<typeof app>('http://localhost', { fetch: app.request })
2657
+ const res = await client.test.$get()
2658
+
2659
+ if (res.status === 200) {
2660
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
2661
+ }
2662
+ if (res.status === 400) {
2663
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
2664
+ }
2665
+ if (res.status === 401) {
2666
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
2667
+ }
2668
+ if (res.status === 403) {
2669
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 403: true }>()
2670
+ }
2671
+ if (res.status === 404) {
2672
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 404: true }>()
2673
+ }
2674
+ })
2675
+
2676
+ test('merge responses from 5 middlewares', async () => {
2677
+ const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
2678
+ const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
2679
+ const middleware3 = createMiddleware(async (c) => c.json({ '403': true }, 403))
2680
+ const middleware4 = createMiddleware(async (c) => c.json({ '404': true }, 404))
2681
+ const middleware5 = createMiddleware(async (c) => c.json({ '300': true }, 300))
2682
+
2683
+ const app = new App().get(
2684
+ '/test',
2685
+ middleware1,
2686
+ middleware2,
2687
+ middleware3,
2688
+ middleware4,
2689
+ middleware5,
2690
+ (c) => c.json({ '200': true }, 200)
2691
+ )
2692
+ const client = hc<typeof app>('http://localhost', { fetch: app.request })
2693
+ const res = await client.test.$get()
2694
+
2695
+ if (res.status === 200) {
2696
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
2697
+ }
2698
+ if (res.status === 400) {
2699
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
2700
+ }
2701
+ if (res.status === 401) {
2702
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
2703
+ }
2704
+ if (res.status === 403) {
2705
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 403: true }>()
2706
+ }
2707
+ if (res.status === 300) {
2708
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 300: true }>()
2709
+ }
2710
+ })
2711
+
2712
+ test('merge responses from 6 middlewares', async () => {
2713
+ const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
2714
+ const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
2715
+ const middleware3 = createMiddleware(async (c) => c.json({ '403': true }, 403))
2716
+ const middleware4 = createMiddleware(async (c) => c.json({ '404': true }, 404))
2717
+ const middleware5 = createMiddleware(async (c) => c.json({ '300': true }, 300))
2718
+ const middleware6 = createMiddleware(async (c) => c.json({ '201': true }, 201))
2719
+
2720
+ const app = new App().get(
2721
+ '/test',
2722
+ middleware1,
2723
+ middleware2,
2724
+ middleware3,
2725
+ middleware4,
2726
+ middleware5,
2727
+ middleware6,
2728
+ (c) => c.json({ '200': true }, 200)
2729
+ )
2730
+ const client = hc<typeof app>('http://localhost', { fetch: app.request })
2731
+ const res = await client.test.$get()
2732
+
2733
+ if (res.status === 200) {
2734
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
2735
+ }
2736
+ if (res.status === 400) {
2737
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
2738
+ }
2739
+ if (res.status === 401) {
2740
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
2741
+ }
2742
+ if (res.status === 403) {
2743
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 403: true }>()
2744
+ }
2745
+ if (res.status === 404) {
2746
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 404: true }>()
2747
+ }
2748
+ if (res.status === 300) {
2749
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 300: true }>()
2750
+ }
2751
+ if (res.status === 201) {
2752
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 201: true }>()
2753
+ }
2754
+ })
2755
+
2756
+ test('merge responses from 7 middlewares', async () => {
2757
+ const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
2758
+ const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
2759
+ const middleware3 = createMiddleware(async (c) => c.json({ '403': true }, 403))
2760
+ const middleware4 = createMiddleware(async (c) => c.json({ '404': true }, 404))
2761
+ const middleware5 = createMiddleware(async (c) => c.json({ '300': true }, 300))
2762
+ const middleware6 = createMiddleware(async (c) => c.json({ '201': true }, 201))
2763
+ const middleware7 = createMiddleware(async (c) => c.json({ '202': true }, 202))
2764
+
2765
+ const app = new App().get(
2766
+ '/test',
2767
+ middleware1,
2768
+ middleware2,
2769
+ middleware3,
2770
+ middleware4,
2771
+ middleware5,
2772
+ middleware6,
2773
+ middleware7,
2774
+ (c) => c.json({ '200': true }, 200)
2775
+ )
2776
+ const client = hc<typeof app>('http://localhost', { fetch: app.request })
2777
+ const res = await client.test.$get()
2778
+
2779
+ if (res.status === 200) {
2780
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
2781
+ }
2782
+ if (res.status === 400) {
2783
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
2784
+ }
2785
+ if (res.status === 401) {
2786
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
2787
+ }
2788
+ if (res.status === 403) {
2789
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 403: true }>()
2790
+ }
2791
+ if (res.status === 404) {
2792
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 404: true }>()
2793
+ }
2794
+ if (res.status === 300) {
2795
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 300: true }>()
2796
+ }
2797
+ if (res.status === 201) {
2798
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 201: true }>()
2799
+ }
2800
+ if (res.status === 202) {
2801
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 202: true }>()
2802
+ }
2803
+ })
2804
+
2805
+ test('merge responses from 8 middlewares', async () => {
2806
+ const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
2807
+ const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
2808
+ const middleware3 = createMiddleware(async (c) => c.json({ '403': true }, 403))
2809
+ const middleware4 = createMiddleware(async (c) => c.json({ '404': true }, 404))
2810
+ const middleware5 = createMiddleware(async (c) => c.json({ '300': true }, 300))
2811
+ const middleware6 = createMiddleware(async (c) => c.json({ '201': true }, 201))
2812
+ const middleware7 = createMiddleware(async (c) => c.json({ '202': true }, 202))
2813
+ const middleware8 = createMiddleware(async (c) => c.json({ '203': true }, 203))
2814
+
2815
+ const app = new App().get(
2816
+ '/test',
2817
+ middleware1,
2818
+ middleware2,
2819
+ middleware3,
2820
+ middleware4,
2821
+ middleware5,
2822
+ middleware6,
2823
+ middleware7,
2824
+ middleware8,
2825
+ (c) => c.json({ '200': true }, 200)
2826
+ )
2827
+ const client = hc<typeof app>('http://localhost', { fetch: app.request })
2828
+ const res = await client.test.$get()
2829
+
2830
+ if (res.status === 200) {
2831
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
2832
+ }
2833
+ if (res.status === 400) {
2834
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
2835
+ }
2836
+ if (res.status === 401) {
2837
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
2838
+ }
2839
+ if (res.status === 403) {
2840
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 403: true }>()
2841
+ }
2842
+ if (res.status === 404) {
2843
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 404: true }>()
2844
+ }
2845
+ if (res.status === 300) {
2846
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 300: true }>()
2847
+ }
2848
+ if (res.status === 201) {
2849
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 201: true }>()
2850
+ }
2851
+ if (res.status === 202) {
2852
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 202: true }>()
2853
+ }
2854
+ if (res.status === 203) {
2855
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 203: true }>()
2856
+ }
2857
+ })
2858
+
2859
+ test('merge responses from 9 middlewares', async () => {
2860
+ const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
2861
+ const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
2862
+ const middleware3 = createMiddleware(async (c) => c.json({ '403': true }, 403))
2863
+ const middleware4 = createMiddleware(async (c) => c.json({ '404': true }, 404))
2864
+ const middleware5 = createMiddleware(async (c) => c.json({ '300': true }, 300))
2865
+ const middleware6 = createMiddleware(async (c) => c.json({ '201': true }, 201))
2866
+ const middleware7 = createMiddleware(async (c) => c.json({ '202': true }, 202))
2867
+ const middleware8 = createMiddleware(async (c) => c.json({ '203': true }, 203))
2868
+ const middleware9 = createMiddleware(async (c) => c.json({ '301': true }, 301))
2869
+
2870
+ const app = new App().get(
2871
+ '/test',
2872
+ middleware1,
2873
+ middleware2,
2874
+ middleware3,
2875
+ middleware4,
2876
+ middleware5,
2877
+ middleware6,
2878
+ middleware7,
2879
+ middleware8,
2880
+ middleware9,
2881
+ (c) => c.json({ '200': true }, 200)
2882
+ )
2883
+ const client = hc<typeof app>('http://localhost', { fetch: app.request })
2884
+ const res = await client.test.$get()
2885
+
2886
+ if (res.status === 200) {
2887
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
2888
+ }
2889
+ if (res.status === 400) {
2890
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
2891
+ }
2892
+ if (res.status === 401) {
2893
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
2894
+ }
2895
+ if (res.status === 403) {
2896
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 403: true }>()
2897
+ }
2898
+ if (res.status === 404) {
2899
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 404: true }>()
2900
+ }
2901
+ if (res.status === 300) {
2902
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 300: true }>()
2903
+ }
2904
+ if (res.status === 201) {
2905
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 201: true }>()
2906
+ }
2907
+ if (res.status === 202) {
2908
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 202: true }>()
2909
+ }
2910
+ if (res.status === 203) {
2911
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 203: true }>()
2912
+ }
2913
+ if (res.status === 301) {
2914
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 301: true }>()
2915
+ }
2916
+ })
2917
+ })
2918
+
2919
+ describe('Middleware returning union type (undefined | TypedResponse)', () => {
2920
+ test('middleware that conditionally returns response should merge types', async () => {
2921
+ const middleware = createMiddleware(async (c, next) => {
2922
+ if (Math.random() > 0.5) {
2923
+ return c.json({ cause: 'Unauthorized' }, 401)
2924
+ }
2925
+ await next()
2926
+ })
2927
+
2928
+ const app = new App().get('/test', middleware, (c) => c.json({ message: 'Hello' }, 200))
2929
+ const client = hc<typeof app>('http://localhost', {
2930
+ fetch: app.request,
2931
+ })
2932
+ const res = await client.test.$get()
2933
+
2934
+ if (res.status === 200) {
2935
+ expectTypeOf(await res.json()).toEqualTypeOf<{ message: string }>()
2936
+ }
2937
+ if (res.status === 401) {
2938
+ expectTypeOf(await res.json()).toEqualTypeOf<{ cause: string }>()
2939
+ }
2940
+ })
2941
+ })
2942
+
2943
+ describe('Merge responses from multiple handlers, no path pattern', () => {
2944
+ test('merge responses from 1 middleware', async () => {
2945
+ const middleware = createMiddleware(async (c) => c.json({ '400': true }, 400))
2946
+
2947
+ const app = new App().get(middleware, (c) => c.json({ '200': true }, 200))
2948
+ const client = hc<typeof app>('http://localhost', { fetch: app.request })
2949
+ const res = await client.index.$get()
2950
+
2951
+ if (res.status === 200) {
2952
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
2953
+ }
2954
+ if (res.status === 400) {
2955
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
2956
+ }
2957
+ })
2958
+
2959
+ test('merge responses from 2 middlewares', async () => {
2960
+ const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
2961
+ const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
2962
+
2963
+ const app = new App().get(middleware1, middleware2, (c) => c.json({ '200': true }, 200))
2964
+ const client = hc<typeof app>('http://localhost', { fetch: app.request })
2965
+ const res = await client.index.$get()
2966
+
2967
+ if (res.status === 200) {
2968
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
2969
+ }
2970
+ if (res.status === 400) {
2971
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
2972
+ }
2973
+ if (res.status === 401) {
2974
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
2975
+ }
2976
+ })
2977
+
2978
+ test('merge responses from 3 middlewares', async () => {
2979
+ const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
2980
+ const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
2981
+ const middleware3 = createMiddleware(async (c) => c.json({ '403': true }, 403))
2982
+
2983
+ const app = new App().get(middleware1, middleware2, middleware3, (c) =>
2984
+ c.json({ '200': true }, 200)
2985
+ )
2986
+ const client = hc<typeof app>('http://localhost', { fetch: app.request })
2987
+ const res = await client.index.$get()
2988
+
2989
+ if (res.status === 200) {
2990
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
2991
+ }
2992
+ if (res.status === 400) {
2993
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
2994
+ }
2995
+ if (res.status === 401) {
2996
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
2997
+ }
2998
+ if (res.status === 403) {
2999
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 403: true }>()
3000
+ }
3001
+ })
3002
+
3003
+ test('merge responses from 4 middlewares', async () => {
3004
+ const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
3005
+ const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
3006
+ const middleware3 = createMiddleware(async (c) => c.json({ '403': true }, 403))
3007
+ const middleware4 = createMiddleware(async (c) => c.json({ '404': true }, 404))
3008
+
3009
+ const app = new App().get(middleware1, middleware2, middleware3, middleware4, (c) =>
3010
+ c.json({ '200': true }, 200)
3011
+ )
3012
+ const client = hc<typeof app>('http://localhost', { fetch: app.request })
3013
+ const res = await client.index.$get()
3014
+
3015
+ if (res.status === 200) {
3016
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
3017
+ }
3018
+ if (res.status === 400) {
3019
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
3020
+ }
3021
+ if (res.status === 401) {
3022
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
3023
+ }
3024
+ if (res.status === 403) {
3025
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 403: true }>()
3026
+ }
3027
+ if (res.status === 404) {
3028
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 404: true }>()
3029
+ }
3030
+ })
3031
+
3032
+ test('merge responses from 5 middlewares', async () => {
3033
+ const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
3034
+ const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
3035
+ const middleware3 = createMiddleware(async (c) => c.json({ '403': true }, 403))
3036
+ const middleware4 = createMiddleware(async (c) => c.json({ '404': true }, 404))
3037
+ const middleware5 = createMiddleware(async (c) => c.json({ '300': true }, 300))
3038
+
3039
+ const app = new App().get(
3040
+ middleware1,
3041
+ middleware2,
3042
+ middleware3,
3043
+ middleware4,
3044
+ middleware5,
3045
+ (c) => c.json({ '200': true }, 200)
3046
+ )
3047
+ const client = hc<typeof app>('http://localhost', { fetch: app.request })
3048
+ const res = await client.index.$get()
3049
+
3050
+ if (res.status === 200) {
3051
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
3052
+ }
3053
+ if (res.status === 400) {
3054
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
3055
+ }
3056
+ if (res.status === 401) {
3057
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
3058
+ }
3059
+ if (res.status === 403) {
3060
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 403: true }>()
3061
+ }
3062
+ if (res.status === 300) {
3063
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 300: true }>()
3064
+ }
3065
+ })
3066
+
3067
+ test('merge responses from 6 middlewares', async () => {
3068
+ const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
3069
+ const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
3070
+ const middleware3 = createMiddleware(async (c) => c.json({ '403': true }, 403))
3071
+ const middleware4 = createMiddleware(async (c) => c.json({ '404': true }, 404))
3072
+ const middleware5 = createMiddleware(async (c) => c.json({ '300': true }, 300))
3073
+ const middleware6 = createMiddleware(async (c) => c.json({ '201': true }, 201))
3074
+
3075
+ const app = new App().get(
3076
+ middleware1,
3077
+ middleware2,
3078
+ middleware3,
3079
+ middleware4,
3080
+ middleware5,
3081
+ middleware6,
3082
+ (c) => c.json({ '200': true }, 200)
3083
+ )
3084
+ const client = hc<typeof app>('http://localhost', { fetch: app.request })
3085
+ const res = await client.index.$get()
3086
+
3087
+ if (res.status === 200) {
3088
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
3089
+ }
3090
+ if (res.status === 400) {
3091
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
3092
+ }
3093
+ if (res.status === 401) {
3094
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
3095
+ }
3096
+ if (res.status === 403) {
3097
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 403: true }>()
3098
+ }
3099
+ if (res.status === 404) {
3100
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 404: true }>()
3101
+ }
3102
+ if (res.status === 300) {
3103
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 300: true }>()
3104
+ }
3105
+ if (res.status === 201) {
3106
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 201: true }>()
3107
+ }
3108
+ })
3109
+
3110
+ test('merge responses from 7 middlewares', async () => {
3111
+ const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
3112
+ const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
3113
+ const middleware3 = createMiddleware(async (c) => c.json({ '403': true }, 403))
3114
+ const middleware4 = createMiddleware(async (c) => c.json({ '404': true }, 404))
3115
+ const middleware5 = createMiddleware(async (c) => c.json({ '300': true }, 300))
3116
+ const middleware6 = createMiddleware(async (c) => c.json({ '201': true }, 201))
3117
+ const middleware7 = createMiddleware(async (c) => c.json({ '202': true }, 202))
3118
+
3119
+ const app = new App().get(
3120
+ middleware1,
3121
+ middleware2,
3122
+ middleware3,
3123
+ middleware4,
3124
+ middleware5,
3125
+ middleware6,
3126
+ middleware7,
3127
+ (c) => c.json({ '200': true }, 200)
3128
+ )
3129
+ const client = hc<typeof app>('http://localhost', { fetch: app.request })
3130
+ const res = await client.index.$get()
3131
+
3132
+ if (res.status === 200) {
3133
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
3134
+ }
3135
+ if (res.status === 400) {
3136
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
3137
+ }
3138
+ if (res.status === 401) {
3139
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
3140
+ }
3141
+ if (res.status === 403) {
3142
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 403: true }>()
3143
+ }
3144
+ if (res.status === 404) {
3145
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 404: true }>()
3146
+ }
3147
+ if (res.status === 300) {
3148
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 300: true }>()
3149
+ }
3150
+ if (res.status === 201) {
3151
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 201: true }>()
3152
+ }
3153
+ if (res.status === 202) {
3154
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 202: true }>()
3155
+ }
3156
+ })
3157
+
3158
+ test('merge responses from 8 middlewares', async () => {
3159
+ const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
3160
+ const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
3161
+ const middleware3 = createMiddleware(async (c) => c.json({ '403': true }, 403))
3162
+ const middleware4 = createMiddleware(async (c) => c.json({ '404': true }, 404))
3163
+ const middleware5 = createMiddleware(async (c) => c.json({ '300': true }, 300))
3164
+ const middleware6 = createMiddleware(async (c) => c.json({ '201': true }, 201))
3165
+ const middleware7 = createMiddleware(async (c) => c.json({ '202': true }, 202))
3166
+ const middleware8 = createMiddleware(async (c) => c.json({ '203': true }, 203))
3167
+
3168
+ const app = new App().get(
3169
+ middleware1,
3170
+ middleware2,
3171
+ middleware3,
3172
+ middleware4,
3173
+ middleware5,
3174
+ middleware6,
3175
+ middleware7,
3176
+ middleware8,
3177
+ (c) => c.json({ '200': true }, 200)
3178
+ )
3179
+ const client = hc<typeof app>('http://localhost', { fetch: app.request })
3180
+ const res = await client.index.$get()
3181
+
3182
+ if (res.status === 200) {
3183
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
3184
+ }
3185
+ if (res.status === 400) {
3186
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
3187
+ }
3188
+ if (res.status === 401) {
3189
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
3190
+ }
3191
+ if (res.status === 403) {
3192
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 403: true }>()
3193
+ }
3194
+ if (res.status === 404) {
3195
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 404: true }>()
3196
+ }
3197
+ if (res.status === 300) {
3198
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 300: true }>()
3199
+ }
3200
+ if (res.status === 201) {
3201
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 201: true }>()
3202
+ }
3203
+ if (res.status === 202) {
3204
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 202: true }>()
3205
+ }
3206
+ if (res.status === 203) {
3207
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 203: true }>()
3208
+ }
3209
+ })
3210
+
3211
+ test('merge responses from 9 middlewares', async () => {
3212
+ const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
3213
+ const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
3214
+ const middleware3 = createMiddleware(async (c) => c.json({ '403': true }, 403))
3215
+ const middleware4 = createMiddleware(async (c) => c.json({ '404': true }, 404))
3216
+ const middleware5 = createMiddleware(async (c) => c.json({ '300': true }, 300))
3217
+ const middleware6 = createMiddleware(async (c) => c.json({ '201': true }, 201))
3218
+ const middleware7 = createMiddleware(async (c) => c.json({ '202': true }, 202))
3219
+ const middleware8 = createMiddleware(async (c) => c.json({ '203': true }, 203))
3220
+ const middleware9 = createMiddleware(async (c) => c.json({ '301': true }, 301))
3221
+
3222
+ const app = new App().get(
3223
+ middleware1,
3224
+ middleware2,
3225
+ middleware3,
3226
+ middleware4,
3227
+ middleware5,
3228
+ middleware6,
3229
+ middleware7,
3230
+ middleware8,
3231
+ middleware9,
3232
+ (c) => c.json({ '200': true }, 200)
3233
+ )
3234
+ const client = hc<typeof app>('http://localhost', { fetch: app.request })
3235
+ const res = await client.index.$get()
3236
+
3237
+ if (res.status === 200) {
3238
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
3239
+ }
3240
+ if (res.status === 400) {
3241
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
3242
+ }
3243
+ if (res.status === 401) {
3244
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
3245
+ }
3246
+ if (res.status === 403) {
3247
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 403: true }>()
3248
+ }
3249
+ if (res.status === 404) {
3250
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 404: true }>()
3251
+ }
3252
+ if (res.status === 300) {
3253
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 300: true }>()
3254
+ }
3255
+ if (res.status === 201) {
3256
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 201: true }>()
3257
+ }
3258
+ if (res.status === 202) {
3259
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 202: true }>()
3260
+ }
3261
+ if (res.status === 203) {
3262
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 203: true }>()
3263
+ }
3264
+ if (res.status === 301) {
3265
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 301: true }>()
3266
+ }
3267
+ })
3268
+ })
3269
+
3270
+ describe('Infers types with multiple middlewares but none returning response', () => {
3271
+ const middleware = createMiddleware(async () => {})
3272
+ const handler = (c: Context) => c.json({ ok: true }, 200)
3273
+ type Expected = {
3274
+ '/': {
3275
+ $get: {
3276
+ input: {}
3277
+ output: {
3278
+ ok: true
3279
+ }
3280
+ outputFormat: 'json'
3281
+ status: 200
3282
+ }
3283
+ }
3284
+ }
3285
+
3286
+ test('infer the correct response type with 1 middleware and 1 handler', () => {
3287
+ const routes = new App().get('/', middleware, handler)
3288
+ type Actual = ExtractSchema<typeof routes>
3289
+ type verify = Expect<Equal<Expected, Actual>>
3290
+ })
3291
+
3292
+ test('infer the correct response type with 1 middleware and 2 handler', () => {
3293
+ const routes = new App().get('/', middleware, handler, handler)
3294
+ type Actual = ExtractSchema<typeof routes>
3295
+ type verify = Expect<Equal<Expected, Actual>>
3296
+ })
3297
+
3298
+ test('infer the correct response type with 2 middleware and 1 handler', () => {
3299
+ const routes = new App().get('/', middleware, middleware, handler)
3300
+ type Actual = ExtractSchema<typeof routes>
3301
+ type verify = Expect<Equal<Expected, Actual>>
3302
+ })
3303
+
3304
+ test('infer the correct response type with 3 middleware and 1 handler', () => {
3305
+ const routes = new App().get('/', middleware, middleware, middleware, handler)
3306
+ type Actual = ExtractSchema<typeof routes>
3307
+ type verify = Expect<Equal<Expected, Actual>>
3308
+ })
3309
+
3310
+ test('infer the correct response type with 4 middleware and 1 handler', () => {
3311
+ const routes = new App().get('/', middleware, middleware, middleware, middleware, handler)
3312
+ type Actual = ExtractSchema<typeof routes>
3313
+ type verify = Expect<Equal<Expected, Actual>>
3314
+ })
3315
+
3316
+ test('infer the correct response type with 5 middleware and 1 handler', () => {
3317
+ const routes = new App().get(
3318
+ '/',
3319
+ middleware,
3320
+ middleware,
3321
+ middleware,
3322
+ middleware,
3323
+ middleware,
3324
+ handler
3325
+ )
3326
+ type Actual = ExtractSchema<typeof routes>
3327
+ type verify = Expect<Equal<Expected, Actual>>
3328
+ })
3329
+
3330
+ test('infer the correct response type with 6 middleware and 1 handler', () => {
3331
+ const routes = new App().get(
3332
+ '/',
3333
+ middleware,
3334
+ middleware,
3335
+ middleware,
3336
+ middleware,
3337
+ middleware,
3338
+ middleware,
3339
+ handler
3340
+ )
3341
+ type Actual = ExtractSchema<typeof routes>
3342
+ type verify = Expect<Equal<Expected, Actual>>
3343
+ })
3344
+
3345
+ test('infer the correct response type with 7 middleware and 1 handler', () => {
3346
+ const routes = new App().get(
3347
+ '/',
3348
+ middleware,
3349
+ middleware,
3350
+ middleware,
3351
+ middleware,
3352
+ middleware,
3353
+ middleware,
3354
+ middleware,
3355
+ handler
3356
+ )
3357
+ type Actual = ExtractSchema<typeof routes>
3358
+ type verify = Expect<Equal<Expected, Actual>>
3359
+ })
3360
+
3361
+ test('infer the correct response type with 8 middleware and 1 handler', () => {
3362
+ const routes = new App().get(
3363
+ '/',
3364
+ middleware,
3365
+ middleware,
3366
+ middleware,
3367
+ middleware,
3368
+ middleware,
3369
+ middleware,
3370
+ middleware,
3371
+ middleware,
3372
+ handler
3373
+ )
3374
+ type Actual = ExtractSchema<typeof routes>
3375
+ type verify = Expect<Equal<Expected, Actual>>
3376
+ })
3377
+
3378
+ test('infer the correct response type with 9 middleware and 1 handler', () => {
3379
+ const routes = new App().get(
3380
+ '/',
3381
+ middleware,
3382
+ middleware,
3383
+ middleware,
3384
+ middleware,
3385
+ middleware,
3386
+ middleware,
3387
+ middleware,
3388
+ middleware,
3389
+ middleware,
3390
+ handler
3391
+ )
3392
+ type Actual = ExtractSchema<typeof routes>
3393
+ type verify = Expect<Equal<Expected, Actual>>
3394
+ })
3395
+ })
3396
+
3397
+ describe('Infers types with multiple middlewares but only some returning response', () => {
3398
+ const handler = (c: Context) => c.json({ ok: true }, 200)
3399
+
3400
+ test('infer the correct response type with 2 middleware, only one returning response', () => {
3401
+ const middleware1 = async (c: Context) => {
3402
+ if (Math.random() > 0.5) {
3403
+ return c.json(
3404
+ {
3405
+ q: true,
3406
+ },
3407
+ 200
3408
+ )
3409
+ }
3410
+ }
3411
+ const middleware2 = async () => {}
3412
+
3413
+ const routes = new App().get('/', middleware1, middleware2, handler)
3414
+ type Actual = ExtractSchema<typeof routes>
3415
+ type Expected = {
3416
+ '/': {
3417
+ $get:
3418
+ | {
3419
+ input: {}
3420
+ output: {
3421
+ ok: true
3422
+ }
3423
+ outputFormat: 'json'
3424
+ status: 200
3425
+ }
3426
+ | {
3427
+ input: {}
3428
+ output: {
3429
+ q: true
3430
+ }
3431
+ outputFormat: 'json'
3432
+ status: 200
3433
+ }
3434
+ }
3435
+ }
3436
+ type verify = Expect<Equal<Expected, Actual>>
3437
+ })
3438
+
3439
+ test('infer the correct response type with 3 middleware, 2 returning response', () => {
3440
+ const middleware1 = async (c: Context) => {
3441
+ if (Math.random() > 0.5) {
3442
+ return c.json(
3443
+ {
3444
+ q1: true,
3445
+ },
3446
+ 200
3447
+ )
3448
+ }
3449
+ }
3450
+ const middleware2 = async (c: Context) => {
3451
+ if (Math.random() > 0.5) {
3452
+ return c.json(
3453
+ {
3454
+ q2: true,
3455
+ },
3456
+ 200
3457
+ )
3458
+ }
3459
+ }
3460
+ const middleware3 = async () => {}
3461
+
3462
+ const routes = new App().get('/', middleware1, middleware2, middleware3, handler)
3463
+ type Actual = ExtractSchema<typeof routes>
3464
+ type Expected = {
3465
+ '/': {
3466
+ $get:
3467
+ | {
3468
+ input: {}
3469
+ output: {
3470
+ ok: true
3471
+ }
3472
+ outputFormat: 'json'
3473
+ status: 200
3474
+ }
3475
+ | {
3476
+ input: {}
3477
+ output: {
3478
+ q1: true
3479
+ }
3480
+ outputFormat: 'json'
3481
+ status: 200
3482
+ }
3483
+ | {
3484
+ input: {}
3485
+ output: {
3486
+ q2: true
3487
+ }
3488
+ outputFormat: 'json'
3489
+ status: 200
3490
+ }
3491
+ }
3492
+ }
3493
+ type verify = Expect<Equal<Expected, Actual>>
3494
+ })
3495
+ })
3496
+
3497
+ describe('Merge responses from app.on handlers, single method', () => {
3498
+ test('merge responses from 1 middleware', async () => {
3499
+ const middleware = createMiddleware(async (c) => c.json({ '400': true }, 400))
3500
+
3501
+ const app = new App().on('GET', '/test', middleware, (c) => c.json({ '200': true }, 200))
3502
+ const client = hc<typeof app>('http://localhost', { fetch: app.request })
3503
+ const res = await client.test.$get()
3504
+
3505
+ if (res.status === 200) {
3506
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
3507
+ }
3508
+ if (res.status === 400) {
3509
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
3510
+ }
3511
+ })
3512
+
3513
+ test('merge responses from 2 middlewares', async () => {
3514
+ const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
3515
+ const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
3516
+
3517
+ const app = new App().on('GET', '/test', middleware1, middleware2, (c) =>
3518
+ c.json({ '200': true }, 200)
3519
+ )
3520
+ const client = hc<typeof app>('http://localhost', { fetch: app.request })
3521
+ const res = await client.test.$get()
3522
+
3523
+ if (res.status === 200) {
3524
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
3525
+ }
3526
+ if (res.status === 400) {
3527
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
3528
+ }
3529
+ if (res.status === 401) {
3530
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
3531
+ }
3532
+ })
3533
+
3534
+ test('merge responses from 5 middlewares', async () => {
3535
+ const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
3536
+ const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
3537
+ const middleware3 = createMiddleware(async (c) => c.json({ '403': true }, 403))
3538
+ const middleware4 = createMiddleware(async (c) => c.json({ '404': true }, 404))
3539
+ const middleware5 = createMiddleware(async (c) => c.json({ '300': true }, 300))
3540
+
3541
+ const app = new App().on(
3542
+ 'GET',
3543
+ '/test',
3544
+ middleware1,
3545
+ middleware2,
3546
+ middleware3,
3547
+ middleware4,
3548
+ middleware5,
3549
+ (c) => c.json({ '200': true }, 200)
3550
+ )
3551
+ const client = hc<typeof app>('http://localhost', { fetch: app.request })
3552
+ const res = await client.test.$get()
3553
+
3554
+ if (res.status === 200) {
3555
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
3556
+ }
3557
+ if (res.status === 400) {
3558
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
3559
+ }
3560
+ if (res.status === 401) {
3561
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
3562
+ }
3563
+ if (res.status === 403) {
3564
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 403: true }>()
3565
+ }
3566
+ if (res.status === 404) {
3567
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 404: true }>()
3568
+ }
3569
+ if (res.status === 300) {
3570
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 300: true }>()
3571
+ }
3572
+ })
3573
+
3574
+ test('merge responses from 9 middlewares', async () => {
3575
+ const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
3576
+ const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
3577
+ const middleware3 = createMiddleware(async (c) => c.json({ '403': true }, 403))
3578
+ const middleware4 = createMiddleware(async (c) => c.json({ '404': true }, 404))
3579
+ const middleware5 = createMiddleware(async (c) => c.json({ '300': true }, 300))
3580
+ const middleware6 = createMiddleware(async (c) => c.json({ '201': true }, 201))
3581
+ const middleware7 = createMiddleware(async (c) => c.json({ '202': true }, 202))
3582
+ const middleware8 = createMiddleware(async (c) => c.json({ '203': true }, 203))
3583
+ const middleware9 = createMiddleware(async (c) => c.json({ '301': true }, 301))
3584
+
3585
+ const app = new App().on(
3586
+ 'GET',
3587
+ '/test',
3588
+ middleware1,
3589
+ middleware2,
3590
+ middleware3,
3591
+ middleware4,
3592
+ middleware5,
3593
+ middleware6,
3594
+ middleware7,
3595
+ middleware8,
3596
+ middleware9,
3597
+ (c) => c.json({ '200': true }, 200)
3598
+ )
3599
+ const client = hc<typeof app>('http://localhost', { fetch: app.request })
3600
+ const res = await client.test.$get()
3601
+
3602
+ if (res.status === 200) {
3603
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
3604
+ }
3605
+ if (res.status === 400) {
3606
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
3607
+ }
3608
+ if (res.status === 401) {
3609
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
3610
+ }
3611
+ if (res.status === 403) {
3612
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 403: true }>()
3613
+ }
3614
+ if (res.status === 404) {
3615
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 404: true }>()
3616
+ }
3617
+ if (res.status === 300) {
3618
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 300: true }>()
3619
+ }
3620
+ if (res.status === 201) {
3621
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 201: true }>()
3622
+ }
3623
+ if (res.status === 202) {
3624
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 202: true }>()
3625
+ }
3626
+ if (res.status === 203) {
3627
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 203: true }>()
3628
+ }
3629
+ if (res.status === 301) {
3630
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 301: true }>()
3631
+ }
3632
+ })
3633
+ })
3634
+
3635
+ describe('Merge responses from app.on handlers, method[]', () => {
3636
+ test('merge responses from 1 middleware', async () => {
3637
+ const middleware = createMiddleware(async (c) => c.json({ '400': true }, 400))
3638
+
3639
+ const app = new App().on(['GET'], '/test', middleware, (c) => c.json({ '200': true }, 200))
3640
+ const client = hc<typeof app>('http://localhost', { fetch: app.request })
3641
+ const res = await client.test.$get()
3642
+
3643
+ if (res.status === 200) {
3644
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
3645
+ }
3646
+ if (res.status === 400) {
3647
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
3648
+ }
3649
+ })
3650
+
3651
+ test('merge responses from 2 middlewares', async () => {
3652
+ const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
3653
+ const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
3654
+
3655
+ const app = new App().on(['GET'], '/test', middleware1, middleware2, (c) =>
3656
+ c.json({ '200': true }, 200)
3657
+ )
3658
+ const client = hc<typeof app>('http://localhost', { fetch: app.request })
3659
+ const res = await client.test.$get()
3660
+
3661
+ if (res.status === 200) {
3662
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
3663
+ }
3664
+ if (res.status === 400) {
3665
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
3666
+ }
3667
+ if (res.status === 401) {
3668
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
3669
+ }
3670
+ })
3671
+
3672
+ test('merge responses from 5 middlewares', async () => {
3673
+ const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
3674
+ const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
3675
+ const middleware3 = createMiddleware(async (c) => c.json({ '403': true }, 403))
3676
+ const middleware4 = createMiddleware(async (c) => c.json({ '404': true }, 404))
3677
+ const middleware5 = createMiddleware(async (c) => c.json({ '300': true }, 300))
3678
+
3679
+ const app = new App().on(
3680
+ ['GET'],
3681
+ '/test',
3682
+ middleware1,
3683
+ middleware2,
3684
+ middleware3,
3685
+ middleware4,
3686
+ middleware5,
3687
+ (c) => c.json({ '200': true }, 200)
3688
+ )
3689
+ const client = hc<typeof app>('http://localhost', { fetch: app.request })
3690
+ const res = await client.test.$get()
3691
+
3692
+ if (res.status === 200) {
3693
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
3694
+ }
3695
+ if (res.status === 400) {
3696
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
3697
+ }
3698
+ if (res.status === 401) {
3699
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
3700
+ }
3701
+ if (res.status === 403) {
3702
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 403: true }>()
3703
+ }
3704
+ if (res.status === 404) {
3705
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 404: true }>()
3706
+ }
3707
+ if (res.status === 300) {
3708
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 300: true }>()
3709
+ }
3710
+ })
3711
+
3712
+ test('merge responses from 9 middlewares', async () => {
3713
+ const middleware1 = createMiddleware(async (c) => c.json({ '400': true }, 400))
3714
+ const middleware2 = createMiddleware(async (c) => c.json({ '401': true }, 401))
3715
+ const middleware3 = createMiddleware(async (c) => c.json({ '403': true }, 403))
3716
+ const middleware4 = createMiddleware(async (c) => c.json({ '404': true }, 404))
3717
+ const middleware5 = createMiddleware(async (c) => c.json({ '300': true }, 300))
3718
+ const middleware6 = createMiddleware(async (c) => c.json({ '201': true }, 201))
3719
+ const middleware7 = createMiddleware(async (c) => c.json({ '202': true }, 202))
3720
+ const middleware8 = createMiddleware(async (c) => c.json({ '203': true }, 203))
3721
+ const middleware9 = createMiddleware(async (c) => c.json({ '301': true }, 301))
3722
+
3723
+ const app = new App().on(
3724
+ ['GET'],
3725
+ '/test',
3726
+ middleware1,
3727
+ middleware2,
3728
+ middleware3,
3729
+ middleware4,
3730
+ middleware5,
3731
+ middleware6,
3732
+ middleware7,
3733
+ middleware8,
3734
+ middleware9,
3735
+ (c) => c.json({ '200': true }, 200)
3736
+ )
3737
+ const client = hc<typeof app>('http://localhost', { fetch: app.request })
3738
+ const res = await client.test.$get()
3739
+
3740
+ if (res.status === 200) {
3741
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 200: true }>()
3742
+ }
3743
+ if (res.status === 400) {
3744
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 400: true }>()
3745
+ }
3746
+ if (res.status === 401) {
3747
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 401: true }>()
3748
+ }
3749
+ if (res.status === 403) {
3750
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 403: true }>()
3751
+ }
3752
+ if (res.status === 404) {
3753
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 404: true }>()
3754
+ }
3755
+ if (res.status === 300) {
3756
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 300: true }>()
3757
+ }
3758
+ if (res.status === 201) {
3759
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 201: true }>()
3760
+ }
3761
+ if (res.status === 202) {
3762
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 202: true }>()
3763
+ }
3764
+ if (res.status === 203) {
3765
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 203: true }>()
3766
+ }
3767
+ if (res.status === 301) {
3768
+ expectTypeOf(await res.json()).toEqualTypeOf<{ 301: true }>()
3769
+ }
3770
+ })
3771
+ })
3772
+ })
3773
+
3774
+ describe('Handlers returning Promise<void>', () => {
3775
+ it('should not be added to schema when calling await next() with another route', async () => {
3776
+ const app = new App()
3777
+ .get('/', async (c, next) => {
3778
+ await next()
3779
+ })
3780
+ .get('/foo', async (c) => c.text('foo'))
3781
+
3782
+ const client = testClient(app)
3783
+
3784
+ // @ts-expect-error '/' route returns Promise<void>, so it should not be in schema
3785
+ const res = await client.index.$get()
3786
+ })
3787
+
3788
+ it('should not be added to schema when only Promise<void> handler exists', async () => {
3789
+ const app = new App().get('/', async (c, next) => {
3790
+ await next()
3791
+ })
3792
+
3793
+ const client = testClient(app)
3794
+
3795
+ // @ts-expect-error '/' route returns Promise<void>, so it should not be in schema
3796
+ const res = await client.index.$get()
3797
+ })
3798
+
3799
+ it('should set path from .use() for subsequent handlers without path', () => {
3800
+ const app = new App()
3801
+ .use('/:id', async (c, next) => {
3802
+ await next()
3803
+ })
3804
+ .post((c) => {
3805
+ c.req.param('id')
3806
+ return c.text('after')
3807
+ })
3808
+
3809
+ type Actual = ExtractSchema<typeof app>
3810
+ type Expected = {
3811
+ '/:id': {
3812
+ $post: {
3813
+ input: {
3814
+ param: {
3815
+ id: string
3816
+ }
3817
+ }
3818
+ output: 'after'
3819
+ outputFormat: 'text'
3820
+ status: ContentfulStatusCode
3821
+ }
3822
+ }
3823
+ }
3824
+ type verify = Expect<Equal<Expected, Actual>>
3825
+ })
3826
+
3827
+ it('should preserve existing routes when .use() sets a new path', () => {
3828
+ const app = new App()
3829
+ .get((c) => c.text('before'))
3830
+ .use('/:id', async (c, next) => {
3831
+ await next()
3832
+ })
3833
+ .post((c) => {
3834
+ c.req.param('id')
3835
+ return c.text('after')
3836
+ })
3837
+
3838
+ type Actual = ExtractSchema<typeof app>
3839
+ type Expected = {
3840
+ '/': {
3841
+ $get: {
3842
+ input: {}
3843
+ output: 'before'
3844
+ outputFormat: 'text'
3845
+ status: ContentfulStatusCode
3846
+ }
3847
+ }
3848
+ } & {
3849
+ '/:id': {
3850
+ $post: {
3851
+ input: {
3852
+ param: {
3853
+ id: string
3854
+ }
3855
+ }
3856
+ output: 'after'
3857
+ outputFormat: 'text'
3858
+ status: ContentfulStatusCode
3859
+ }
3860
+ }
3861
+ }
3862
+ type verify = Expect<Equal<Expected, Actual>>
3863
+ })
3864
+
3865
+ it('should work with .get() that returns Promise<void> instead of .use()', () => {
3866
+ const app = new App()
3867
+ .get((c) => c.text('before'))
3868
+ .get('/:id', async (c, next) => {
3869
+ await next()
3870
+ })
3871
+ .post((c) => {
3872
+ c.req.param('id')
3873
+ return c.text('after')
3874
+ })
3875
+
3876
+ type Actual = ExtractSchema<typeof app>
3877
+ type Expected = {
3878
+ '/': {
3879
+ $get: {
3880
+ input: {}
3881
+ output: 'before'
3882
+ outputFormat: 'text'
3883
+ status: ContentfulStatusCode
3884
+ }
3885
+ }
3886
+ } & {
3887
+ '/:id': {
3888
+ $post: {
3889
+ input: {
3890
+ param: {
3891
+ id: string
3892
+ }
3893
+ }
3894
+ output: 'after'
3895
+ outputFormat: 'text'
3896
+ status: ContentfulStatusCode
3897
+ }
3898
+ }
3899
+ }
3900
+ type verify = Expect<Equal<Expected, Actual>>
3901
+ })
3902
+
3903
+ it('should work with .get() that returns Promise<void> instead of .use() - multiple middleware', () => {
3904
+ const app = new App()
3905
+ .get((c) => c.text('before'))
3906
+ .get(
3907
+ '/:id',
3908
+ async (c, next) => {
3909
+ await next()
3910
+ },
3911
+ async (c, next) => {
3912
+ await next()
3913
+ }
3914
+ )
3915
+ .post((c) => {
3916
+ c.req.param('id')
3917
+ return c.text('after')
3918
+ })
3919
+
3920
+ type Actual = ExtractSchema<typeof app>
3921
+ type Expected = {
3922
+ '/': {
3923
+ $get: {
3924
+ input: {}
3925
+ output: 'before'
3926
+ outputFormat: 'text'
3927
+ status: ContentfulStatusCode
3928
+ }
3929
+ }
3930
+ } & {
3931
+ '/:id': {
3932
+ $post: {
3933
+ input: {
3934
+ param: {
3935
+ id: string
3936
+ }
3937
+ }
3938
+ output: 'after'
3939
+ outputFormat: 'text'
3940
+ status: ContentfulStatusCode
3941
+ }
3942
+ }
3943
+ }
3944
+ type verify = Expect<Equal<Expected, Actual>>
3945
+ })
3946
+
3947
+ it('should use the last registered path for a pathless handler with multiple middlewares', () => {
3948
+ const app = new App()
3949
+ .get('/foo', async (_c, next) => {
3950
+ await next()
3951
+ })
3952
+ .get('/bar', async (_c, next) => {
3953
+ await next()
3954
+ })
3955
+ .get((c) => c.text('after'))
3956
+
3957
+ type Actual = ExtractSchema<typeof app>
3958
+ type Expected = {
3959
+ '/bar': {
3960
+ $get: {
3961
+ input: {}
3962
+ output: 'after'
3963
+ outputFormat: 'text'
3964
+ status: ContentfulStatusCode
3965
+ }
3966
+ }
3967
+ }
3968
+ type verify = Expect<Equal<Expected, Actual>>
3969
+ })
3970
+
3971
+ it('should use the last registered path for a pathless handler with mixed handlers and middlewares', () => {
3972
+ const app = new App()
3973
+ .get('/void', async (_c, next) => {
3974
+ await next()
3975
+ })
3976
+ .get('/handler', (c) => c.text('handler'))
3977
+ .get((c) => c.text('after'))
3978
+
3979
+ type Actual = ExtractSchema<typeof app>
3980
+ type Expected = {
3981
+ '/handler': {
3982
+ $get: {
3983
+ input: {}
3984
+ output: 'handler'
3985
+ outputFormat: 'text'
3986
+ status: ContentfulStatusCode
3987
+ }
3988
+ }
3989
+ } & {
3990
+ '/handler': {
3991
+ $get: {
3992
+ input: {}
3993
+ output: 'after'
3994
+ outputFormat: 'text'
3995
+ status: ContentfulStatusCode
3996
+ }
3997
+ }
3998
+ }
3999
+ type verify = Expect<Equal<Expected, Actual>>
4000
+ })
4001
+
4002
+ it('should set path as `*` when .use() is called without path argument', () => {
4003
+ const app = new App()
4004
+ .get('/foo', async (_c, next) => {
4005
+ await next()
4006
+ })
4007
+ .use(async (c, next) => {
4008
+ await next()
4009
+ })
4010
+ .post((c) => {
4011
+ return c.text('after')
4012
+ })
4013
+
4014
+ type Actual = ExtractSchema<typeof app>
4015
+ type Expected = {
4016
+ '*': {
4017
+ $post: {
4018
+ input: {}
4019
+ output: 'after'
4020
+ outputFormat: 'text'
4021
+ status: ContentfulStatusCode
4022
+ }
4023
+ }
4024
+ }
4025
+ type verify = Expect<Equal<Expected, Actual>>
4026
+ })
4027
+ })
4028
+
4029
+ // Regression tests for #4388: routes before .use() with explicit paths should not be dropped
4030
+ describe('Routes before .use() with explicit paths (#4388)', () => {
4031
+ it('should preserve explicit-path .get() before .use() with path', () => {
4032
+ const app = new App()
4033
+ .get('/', (c) => c.text('Hello from /'))
4034
+ .use('/noop', async (c, next) => {
4035
+ await next()
4036
+ })
4037
+
4038
+ type Actual = ExtractSchema<typeof app>
4039
+ type Expected = {
4040
+ '/': {
4041
+ $get: {
4042
+ input: {}
4043
+ output: 'Hello from /'
4044
+ outputFormat: 'text'
4045
+ status: ContentfulStatusCode
4046
+ }
4047
+ }
4048
+ }
4049
+ type verify = Expect<Equal<Expected, Actual>>
4050
+ })
4051
+
4052
+ it('should preserve .get() route and infer .post() under .use() path', () => {
4053
+ const app = new App()
4054
+ .get('/', (c) => c.text('Hello from /'))
4055
+ .use('/:slug', async (c, next) => {
4056
+ await next()
4057
+ })
4058
+ .post((c) => c.text('posted'))
4059
+
4060
+ type Actual = ExtractSchema<typeof app>
4061
+ type Expected = {
4062
+ '/': {
4063
+ $get: {
4064
+ input: {}
4065
+ output: 'Hello from /'
4066
+ outputFormat: 'text'
4067
+ status: ContentfulStatusCode
4068
+ }
4069
+ }
4070
+ } & {
4071
+ '/:slug': {
4072
+ $post: {
4073
+ input: {
4074
+ param: {
4075
+ slug: string
4076
+ }
4077
+ }
4078
+ output: 'posted'
4079
+ outputFormat: 'text'
4080
+ status: ContentfulStatusCode
4081
+ }
4082
+ }
4083
+ }
4084
+ type verify = Expect<Equal<Expected, Actual>>
4085
+ })
4086
+
4087
+ it('should preserve routes through .route() wrapping', () => {
4088
+ const inner = new App()
4089
+ .get('/', (c) => c.text('index'))
4090
+ .use('/:slug', async (c, next) => {
4091
+ await next()
4092
+ })
4093
+ .post((c) => c.text('posted'))
4094
+
4095
+ const app = new App().route('/api', inner)
4096
+
4097
+ const client = hc<typeof app>('http://localhost')
4098
+ // '/api' $get should exist (from inner .get('/'))
4099
+ expectTypeOf(client.api.$get).toBeFunction()
4100
+ // '/api/:slug' $post should exist (from inner .post() after .use('/:slug'))
4101
+ expectTypeOf(client.api[':slug'].$post).toBeFunction()
4102
+ })
4103
+
4104
+ it('should preserve multiple explicit-path routes before .use()', () => {
4105
+ const app = new App()
4106
+ .get('/', (c) => c.text('home'))
4107
+ .get('/about', (c) => c.text('about'))
4108
+ .use('/mw', async (c, next) => {
4109
+ await next()
4110
+ })
4111
+
4112
+ type Actual = ExtractSchema<typeof app>
4113
+ type Expected = {
4114
+ '/': {
4115
+ $get: {
4116
+ input: {}
4117
+ output: 'home'
4118
+ outputFormat: 'text'
4119
+ status: ContentfulStatusCode
4120
+ }
4121
+ }
4122
+ } & {
4123
+ '/about': {
4124
+ $get: {
4125
+ input: {}
4126
+ output: 'about'
4127
+ outputFormat: 'text'
4128
+ status: ContentfulStatusCode
4129
+ }
4130
+ }
4131
+ }
4132
+ type verify = Expect<Equal<Expected, Actual>>
4133
+ })
4134
+ })