acrylblends 0.6.3 → 0.7.0

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