acrylblends 0.2.0 → 0.4.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 (335) hide show
  1. package/dist/index.d.ts +22 -16
  2. package/dist/index.js +65 -20
  3. package/dist/index.js.map +1 -1
  4. package/package.json +1 -1
  5. package/plugins/acryl-inspect/LICENSE +21 -0
  6. package/plugins/acryl-inspect/plugin.json +12 -0
  7. package/plugins/acryl-inspect/src/index.ts +3 -0
  8. package/plugins/acryl-inspect/src/report.ts +126 -0
  9. package/plugins/acryl-inspect/src/service.ts +68 -0
  10. package/plugins/acryl-inspect/tests/report.spec.ts +63 -0
  11. package/plugins/acryl-inspect/tests/service.spec.ts +121 -0
  12. package/plugins/acryl-inspect/upstream.json +18 -0
  13. package/plugins/acryl-node-server/LICENSE +47 -0
  14. package/plugins/acryl-node-server/plugin.json +13 -0
  15. package/plugins/acryl-node-server/src/adapter/conninfo.ts +23 -0
  16. package/plugins/acryl-node-server/src/adapter/early-hints.ts +55 -0
  17. package/plugins/acryl-node-server/src/adapter/error.ts +11 -0
  18. package/plugins/acryl-node-server/src/adapter/headers.ts +249 -0
  19. package/plugins/acryl-node-server/src/adapter/index.ts +6 -0
  20. package/plugins/acryl-node-server/src/adapter/listener.ts +540 -0
  21. package/plugins/acryl-node-server/src/adapter/request.ts +797 -0
  22. package/plugins/acryl-node-server/src/adapter/response.ts +186 -0
  23. package/plugins/acryl-node-server/src/adapter/serve-static.ts +238 -0
  24. package/plugins/acryl-node-server/src/adapter/server.ts +36 -0
  25. package/plugins/acryl-node-server/src/adapter/types.ts +82 -0
  26. package/plugins/acryl-node-server/src/adapter/url.ts +42 -0
  27. package/plugins/acryl-node-server/src/adapter/utils/response/constants.ts +1 -0
  28. package/plugins/acryl-node-server/src/adapter/utils/response.ts +4 -0
  29. package/plugins/acryl-node-server/src/adapter/utils/stream.ts +88 -0
  30. package/plugins/acryl-node-server/src/adapter/utils.ts +96 -0
  31. package/plugins/acryl-node-server/src/adapter/websocket-types.ts +45 -0
  32. package/plugins/acryl-node-server/src/adapter/websocket.ts +375 -0
  33. package/plugins/acryl-node-server/src/index.ts +107 -0
  34. package/plugins/acryl-node-server/tests/adapter/app.ts +76 -0
  35. package/plugins/acryl-node-server/tests/adapter/assets/.static/plain.txt +1 -0
  36. package/plugins/acryl-node-server/tests/adapter/assets/favicon.ico +0 -0
  37. package/plugins/acryl-node-server/tests/adapter/assets/secret.txt +1 -0
  38. package/plugins/acryl-node-server/tests/adapter/assets/static/acryl.html +1 -0
  39. package/plugins/acryl-node-server/tests/adapter/assets/static/admin/secret.txt +1 -0
  40. package/plugins/acryl-node-server/tests/adapter/assets/static/data.json +5 -0
  41. package/plugins/acryl-node-server/tests/adapter/assets/static/extensionless +1 -0
  42. package/plugins/acryl-node-server/tests/adapter/assets/static/foo..bar.txt +0 -0
  43. package/plugins/acryl-node-server/tests/adapter/assets/static/index.html +1 -0
  44. package/plugins/acryl-node-server/tests/adapter/assets/static/plain.txt +1 -0
  45. package/plugins/acryl-node-server/tests/adapter/assets/static-with-context-path/plain.txt +1 -0
  46. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.bin +1 -0
  47. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.bin.br +1 -0
  48. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt +1 -0
  49. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt.br +1 -0
  50. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt.zst +1 -0
  51. package/plugins/acryl-node-server/tests/adapter/conninfo.test.ts +34 -0
  52. package/plugins/acryl-node-server/tests/adapter/content-length.test.ts +248 -0
  53. package/plugins/acryl-node-server/tests/adapter/early-hints.test.ts +457 -0
  54. package/plugins/acryl-node-server/tests/adapter/fixtures/keys/agent1-cert.pem +23 -0
  55. package/plugins/acryl-node-server/tests/adapter/fixtures/keys/agent1-key.pem +27 -0
  56. package/plugins/acryl-node-server/tests/adapter/headers.test.ts +252 -0
  57. package/plugins/acryl-node-server/tests/adapter/helpers/request.ts +190 -0
  58. package/plugins/acryl-node-server/tests/adapter/listener.test.ts +727 -0
  59. package/plugins/acryl-node-server/tests/adapter/request-client-disconnect.test.ts +189 -0
  60. package/plugins/acryl-node-server/tests/adapter/request.test.ts +1230 -0
  61. package/plugins/acryl-node-server/tests/adapter/response.test.ts +285 -0
  62. package/plugins/acryl-node-server/tests/adapter/serve-static.test.ts +849 -0
  63. package/plugins/acryl-node-server/tests/adapter/server-socket.test.ts +790 -0
  64. package/plugins/acryl-node-server/tests/adapter/server.test.ts +1302 -0
  65. package/plugins/acryl-node-server/tests/adapter/setup.ts +16 -0
  66. package/plugins/acryl-node-server/tests/adapter/url.test.ts +76 -0
  67. package/plugins/acryl-node-server/tests/adapter/utils/response.test.ts +23 -0
  68. package/plugins/acryl-node-server/tests/adapter/utils/stream.test.ts +75 -0
  69. package/plugins/acryl-node-server/tests/adapter/utils.test.ts +235 -0
  70. package/plugins/acryl-node-server/tests/adapter/websocket.test.ts +260 -0
  71. package/plugins/acryl-node-server/tests/server.spec.ts +141 -0
  72. package/plugins/acryl-node-server/upstream.json +18 -0
  73. package/plugins/acryl-router/LICENSE +47 -0
  74. package/plugins/acryl-router/plugin.json +13 -0
  75. package/plugins/acryl-router/src/client.ts +2 -0
  76. package/plugins/acryl-router/src/connection.ts +42 -0
  77. package/plugins/acryl-router/src/core/app-base.ts +544 -0
  78. package/plugins/acryl-router/src/core/app.test.ts +3833 -0
  79. package/plugins/acryl-router/src/core/app.ts +34 -0
  80. package/plugins/acryl-router/src/core/client/client.test.ts +2313 -0
  81. package/plugins/acryl-router/src/core/client/client.ts +249 -0
  82. package/plugins/acryl-router/src/core/client/fetch-result-please.ts +95 -0
  83. package/plugins/acryl-router/src/core/client/index.ts +17 -0
  84. package/plugins/acryl-router/src/core/client/types.test.ts +149 -0
  85. package/plugins/acryl-router/src/core/client/types.ts +393 -0
  86. package/plugins/acryl-router/src/core/client/utils.test.ts +405 -0
  87. package/plugins/acryl-router/src/core/client/utils.ts +117 -0
  88. package/plugins/acryl-router/src/core/compose.test.ts +751 -0
  89. package/plugins/acryl-router/src/core/compose.ts +73 -0
  90. package/plugins/acryl-router/src/core/context.test.ts +629 -0
  91. package/plugins/acryl-router/src/core/context.ts +781 -0
  92. package/plugins/acryl-router/src/core/helper/accepts/accepts.test.ts +344 -0
  93. package/plugins/acryl-router/src/core/helper/accepts/accepts.ts +96 -0
  94. package/plugins/acryl-router/src/core/helper/accepts/index.ts +6 -0
  95. package/plugins/acryl-router/src/core/helper/adapter/index.test.ts +55 -0
  96. package/plugins/acryl-router/src/core/helper/adapter/index.ts +90 -0
  97. package/plugins/acryl-router/src/core/helper/conninfo/index.ts +6 -0
  98. package/plugins/acryl-router/src/core/helper/conninfo/types.ts +45 -0
  99. package/plugins/acryl-router/src/core/helper/cookie/index.test.ts +510 -0
  100. package/plugins/acryl-router/src/core/helper/cookie/index.ts +146 -0
  101. package/plugins/acryl-router/src/core/helper/factory/index.test.ts +434 -0
  102. package/plugins/acryl-router/src/core/helper/factory/index.ts +375 -0
  103. package/plugins/acryl-router/src/core/helper/html/index.test.ts +78 -0
  104. package/plugins/acryl-router/src/core/helper/html/index.ts +57 -0
  105. package/plugins/acryl-router/src/core/helper/route/index.test.ts +259 -0
  106. package/plugins/acryl-router/src/core/helper/route/index.ts +144 -0
  107. package/plugins/acryl-router/src/core/helper/streaming/index.ts +9 -0
  108. package/plugins/acryl-router/src/core/helper/streaming/sse.ts +101 -0
  109. package/plugins/acryl-router/src/core/helper/streaming/stream.test.ts +128 -0
  110. package/plugins/acryl-router/src/core/helper/streaming/stream.ts +45 -0
  111. package/plugins/acryl-router/src/core/helper/streaming/text.test.ts +34 -0
  112. package/plugins/acryl-router/src/core/helper/streaming/text.ts +15 -0
  113. package/plugins/acryl-router/src/core/helper/streaming/utils.ts +11 -0
  114. package/plugins/acryl-router/src/core/helper/testing/index.test.ts +42 -0
  115. package/plugins/acryl-router/src/core/helper/testing/index.ts +28 -0
  116. package/plugins/acryl-router/src/core/helper/websocket/index.test.ts +124 -0
  117. package/plugins/acryl-router/src/core/helper/websocket/index.ts +140 -0
  118. package/plugins/acryl-router/src/core/http-exception.test.ts +45 -0
  119. package/plugins/acryl-router/src/core/http-exception.ts +77 -0
  120. package/plugins/acryl-router/src/core/index.ts +53 -0
  121. package/plugins/acryl-router/src/core/middleware/basic-auth/index.test.ts +434 -0
  122. package/plugins/acryl-router/src/core/middleware/basic-auth/index.ts +152 -0
  123. package/plugins/acryl-router/src/core/middleware/bearer-auth/index.test.ts +1018 -0
  124. package/plugins/acryl-router/src/core/middleware/bearer-auth/index.ts +220 -0
  125. package/plugins/acryl-router/src/core/middleware/body-limit/index.test.ts +273 -0
  126. package/plugins/acryl-router/src/core/middleware/body-limit/index.ts +110 -0
  127. package/plugins/acryl-router/src/core/middleware/combine/index.test.ts +355 -0
  128. package/plugins/acryl-router/src/core/middleware/combine/index.ts +171 -0
  129. package/plugins/acryl-router/src/core/middleware/compress/index.test.ts +535 -0
  130. package/plugins/acryl-router/src/core/middleware/compress/index.ts +142 -0
  131. package/plugins/acryl-router/src/core/middleware/context-storage/index.test.ts +43 -0
  132. package/plugins/acryl-router/src/core/middleware/context-storage/index.ts +58 -0
  133. package/plugins/acryl-router/src/core/middleware/cors/index.test.ts +532 -0
  134. package/plugins/acryl-router/src/core/middleware/cors/index.ts +163 -0
  135. package/plugins/acryl-router/src/core/middleware/csrf/index.test.ts +534 -0
  136. package/plugins/acryl-router/src/core/middleware/csrf/index.ts +150 -0
  137. package/plugins/acryl-router/src/core/middleware/etag/digest.test.ts +21 -0
  138. package/plugins/acryl-router/src/core/middleware/etag/digest.ts +95 -0
  139. package/plugins/acryl-router/src/core/middleware/etag/index.test.ts +503 -0
  140. package/plugins/acryl-router/src/core/middleware/etag/index.ts +135 -0
  141. package/plugins/acryl-router/src/core/middleware/ip-restriction/index.test.ts +217 -0
  142. package/plugins/acryl-router/src/core/middleware/ip-restriction/index.ts +278 -0
  143. package/plugins/acryl-router/src/core/middleware/jwk/index.test.ts +1135 -0
  144. package/plugins/acryl-router/src/core/middleware/jwk/index.ts +1 -0
  145. package/plugins/acryl-router/src/core/middleware/jwk/jwk.ts +195 -0
  146. package/plugins/acryl-router/src/core/middleware/jwk/keys.test.json +48 -0
  147. package/plugins/acryl-router/src/core/middleware/jwt/index.test.ts +802 -0
  148. package/plugins/acryl-router/src/core/middleware/jwt/index.ts +9 -0
  149. package/plugins/acryl-router/src/core/middleware/jwt/jwt.ts +189 -0
  150. package/plugins/acryl-router/src/core/middleware/language/index.test.ts +552 -0
  151. package/plugins/acryl-router/src/core/middleware/language/index.ts +12 -0
  152. package/plugins/acryl-router/src/core/middleware/language/language.ts +314 -0
  153. package/plugins/acryl-router/src/core/middleware/logger/index.test.ts +204 -0
  154. package/plugins/acryl-router/src/core/middleware/logger/index.ts +95 -0
  155. package/plugins/acryl-router/src/core/middleware/method-not-allowed/index.test.ts +292 -0
  156. package/plugins/acryl-router/src/core/middleware/method-not-allowed/index.ts +141 -0
  157. package/plugins/acryl-router/src/core/middleware/method-override/index.test.ts +221 -0
  158. package/plugins/acryl-router/src/core/middleware/method-override/index.ts +147 -0
  159. package/plugins/acryl-router/src/core/middleware/powered-by/index.test.ts +37 -0
  160. package/plugins/acryl-router/src/core/middleware/powered-by/index.ts +35 -0
  161. package/plugins/acryl-router/src/core/middleware/pretty-json/index.test.ts +218 -0
  162. package/plugins/acryl-router/src/core/middleware/pretty-json/index.ts +67 -0
  163. package/plugins/acryl-router/src/core/middleware/request-id/index.test.ts +168 -0
  164. package/plugins/acryl-router/src/core/middleware/request-id/index.ts +7 -0
  165. package/plugins/acryl-router/src/core/middleware/request-id/request-id.ts +59 -0
  166. package/plugins/acryl-router/src/core/middleware/secure-headers/index.test.ts +671 -0
  167. package/plugins/acryl-router/src/core/middleware/secure-headers/index.ts +8 -0
  168. package/plugins/acryl-router/src/core/middleware/secure-headers/permissions-policy.ts +101 -0
  169. package/plugins/acryl-router/src/core/middleware/secure-headers/secure-headers.ts +340 -0
  170. package/plugins/acryl-router/src/core/middleware/serve-static/index.test.ts +450 -0
  171. package/plugins/acryl-router/src/core/middleware/serve-static/index.ts +135 -0
  172. package/plugins/acryl-router/src/core/middleware/serve-static/path.test.ts +50 -0
  173. package/plugins/acryl-router/src/core/middleware/serve-static/path.ts +25 -0
  174. package/plugins/acryl-router/src/core/middleware/timeout/index.test.ts +70 -0
  175. package/plugins/acryl-router/src/core/middleware/timeout/index.ts +58 -0
  176. package/plugins/acryl-router/src/core/middleware/timing/index.test.ts +310 -0
  177. package/plugins/acryl-router/src/core/middleware/timing/index.ts +8 -0
  178. package/plugins/acryl-router/src/core/middleware/timing/timing.ts +256 -0
  179. package/plugins/acryl-router/src/core/middleware/trailing-slash/index.test.ts +381 -0
  180. package/plugins/acryl-router/src/core/middleware/trailing-slash/index.ts +156 -0
  181. package/plugins/acryl-router/src/core/request/constants.ts +1 -0
  182. package/plugins/acryl-router/src/core/request.test.ts +751 -0
  183. package/plugins/acryl-router/src/core/request.ts +502 -0
  184. package/plugins/acryl-router/src/core/router/common.case.test.ts +1132 -0
  185. package/plugins/acryl-router/src/core/router/linear-router/index.ts +6 -0
  186. package/plugins/acryl-router/src/core/router/linear-router/router.test.ts +80 -0
  187. package/plugins/acryl-router/src/core/router/linear-router/router.ts +152 -0
  188. package/plugins/acryl-router/src/core/router/pattern-router/index.ts +6 -0
  189. package/plugins/acryl-router/src/core/router/pattern-router/router.test.ts +46 -0
  190. package/plugins/acryl-router/src/core/router/pattern-router/router.ts +54 -0
  191. package/plugins/acryl-router/src/core/router/reg-exp-router/index.ts +7 -0
  192. package/plugins/acryl-router/src/core/router/reg-exp-router/matcher.ts +33 -0
  193. package/plugins/acryl-router/src/core/router/reg-exp-router/node.ts +170 -0
  194. package/plugins/acryl-router/src/core/router/reg-exp-router/prepared-router.test.ts +154 -0
  195. package/plugins/acryl-router/src/core/router/reg-exp-router/prepared-router.ts +165 -0
  196. package/plugins/acryl-router/src/core/router/reg-exp-router/router.test.ts +265 -0
  197. package/plugins/acryl-router/src/core/router/reg-exp-router/router.ts +174 -0
  198. package/plugins/acryl-router/src/core/router/reg-exp-router/trie.ts +84 -0
  199. package/plugins/acryl-router/src/core/router/smart-router/index.ts +6 -0
  200. package/plugins/acryl-router/src/core/router/smart-router/router.test.ts +13 -0
  201. package/plugins/acryl-router/src/core/router/smart-router/router.ts +70 -0
  202. package/plugins/acryl-router/src/core/router/trie-router/index.ts +6 -0
  203. package/plugins/acryl-router/src/core/router/trie-router/node.test.ts +896 -0
  204. package/plugins/acryl-router/src/core/router/trie-router/node.ts +221 -0
  205. package/plugins/acryl-router/src/core/router/trie-router/router.test.ts +8 -0
  206. package/plugins/acryl-router/src/core/router/trie-router/router.ts +18 -0
  207. package/plugins/acryl-router/src/core/router/utils.ts +1 -0
  208. package/plugins/acryl-router/src/core/router.ts +103 -0
  209. package/plugins/acryl-router/src/core/types.test.ts +4134 -0
  210. package/plugins/acryl-router/src/core/types.ts +2782 -0
  211. package/plugins/acryl-router/src/core/utils/accept.test.ts +299 -0
  212. package/plugins/acryl-router/src/core/utils/accept.ts +268 -0
  213. package/plugins/acryl-router/src/core/utils/basic-auth.test.ts +56 -0
  214. package/plugins/acryl-router/src/core/utils/basic-auth.ts +26 -0
  215. package/plugins/acryl-router/src/core/utils/body.test.ts +496 -0
  216. package/plugins/acryl-router/src/core/utils/body.ts +263 -0
  217. package/plugins/acryl-router/src/core/utils/buffer.test.ts +147 -0
  218. package/plugins/acryl-router/src/core/utils/buffer.ts +117 -0
  219. package/plugins/acryl-router/src/core/utils/color.test.ts +23 -0
  220. package/plugins/acryl-router/src/core/utils/color.ts +56 -0
  221. package/plugins/acryl-router/src/core/utils/compress.ts +10 -0
  222. package/plugins/acryl-router/src/core/utils/concurrent.test.ts +69 -0
  223. package/plugins/acryl-router/src/core/utils/concurrent.ts +55 -0
  224. package/plugins/acryl-router/src/core/utils/constants.ts +4 -0
  225. package/plugins/acryl-router/src/core/utils/cookie.test.ts +460 -0
  226. package/plugins/acryl-router/src/core/utils/cookie.ts +281 -0
  227. package/plugins/acryl-router/src/core/utils/crypto.test.ts +43 -0
  228. package/plugins/acryl-router/src/core/utils/crypto.ts +58 -0
  229. package/plugins/acryl-router/src/core/utils/encode.test.ts +61 -0
  230. package/plugins/acryl-router/src/core/utils/encode.ts +34 -0
  231. package/plugins/acryl-router/src/core/utils/filepath.test.ts +78 -0
  232. package/plugins/acryl-router/src/core/utils/filepath.ts +60 -0
  233. package/plugins/acryl-router/src/core/utils/handler.ts +15 -0
  234. package/plugins/acryl-router/src/core/utils/headers.ts +355 -0
  235. package/plugins/acryl-router/src/core/utils/html.test.ts +16 -0
  236. package/plugins/acryl-router/src/core/utils/html.ts +182 -0
  237. package/plugins/acryl-router/src/core/utils/http-status.ts +72 -0
  238. package/plugins/acryl-router/src/core/utils/ipaddr.test.ts +149 -0
  239. package/plugins/acryl-router/src/core/utils/ipaddr.ts +374 -0
  240. package/plugins/acryl-router/src/core/utils/jwt/index.ts +7 -0
  241. package/plugins/acryl-router/src/core/utils/jwt/jwa.test.ts +86 -0
  242. package/plugins/acryl-router/src/core/utils/jwt/jwa.ts +38 -0
  243. package/plugins/acryl-router/src/core/utils/jwt/jws.ts +235 -0
  244. package/plugins/acryl-router/src/core/utils/jwt/jwt.test.ts +1788 -0
  245. package/plugins/acryl-router/src/core/utils/jwt/jwt.ts +292 -0
  246. package/plugins/acryl-router/src/core/utils/jwt/types.ts +157 -0
  247. package/plugins/acryl-router/src/core/utils/jwt/utf8.ts +7 -0
  248. package/plugins/acryl-router/src/core/utils/mime.test.ts +64 -0
  249. package/plugins/acryl-router/src/core/utils/mime.ts +94 -0
  250. package/plugins/acryl-router/src/core/utils/stream.test.ts +212 -0
  251. package/plugins/acryl-router/src/core/utils/stream.ts +107 -0
  252. package/plugins/acryl-router/src/core/utils/types.test.ts +295 -0
  253. package/plugins/acryl-router/src/core/utils/types.ts +123 -0
  254. package/plugins/acryl-router/src/core/utils/url.test.ts +384 -0
  255. package/plugins/acryl-router/src/core/utils/url.ts +337 -0
  256. package/plugins/acryl-router/src/core/validator/index.ts +8 -0
  257. package/plugins/acryl-router/src/core/validator/utils.test.ts +82 -0
  258. package/plugins/acryl-router/src/core/validator/utils.ts +69 -0
  259. package/plugins/acryl-router/src/core/validator/validator.test.ts +1488 -0
  260. package/plugins/acryl-router/src/core/validator/validator.ts +185 -0
  261. package/plugins/acryl-router/src/helpers/index.ts +13 -0
  262. package/plugins/acryl-router/src/helpers/schema-validator.ts +35 -0
  263. package/plugins/acryl-router/src/index.ts +6 -0
  264. package/plugins/acryl-router/src/middleware/index.ts +25 -0
  265. package/plugins/acryl-router/src/middleware/loopback-only.ts +88 -0
  266. package/plugins/acryl-router/src/service.ts +356 -0
  267. package/plugins/acryl-router/tests/docs-examples.spec.ts +152 -0
  268. package/plugins/acryl-router/tests/live-install.spec.ts +64 -0
  269. package/plugins/acryl-router/tests/loader.spec.ts +72 -0
  270. package/plugins/acryl-router/tests/service.spec.ts +409 -0
  271. package/plugins/acryl-router/tests/shadow-state.spec.ts +79 -0
  272. package/plugins/acryl-router/upstream.json +20 -0
  273. package/plugins/acryl-router-http/LICENSE +21 -0
  274. package/plugins/acryl-router-http/plugin.json +12 -0
  275. package/plugins/acryl-router-http/src/bridge.ts +54 -0
  276. package/plugins/acryl-router-http/src/index.ts +63 -0
  277. package/plugins/acryl-router-http/src/serve.ts +24 -0
  278. package/plugins/acryl-router-http/tests/adapter.spec.ts +174 -0
  279. package/plugins/acryl-router-http/tests/stream.spec.ts +133 -0
  280. package/plugins/acryl-router-http/tests/surfaces.spec.ts +101 -0
  281. package/plugins/acryl-router-http/upstream.json +22 -0
  282. package/plugins/acryl-state/LICENSE +21 -0
  283. package/plugins/acryl-state/NOTICE +30 -0
  284. package/plugins/acryl-state/plugin.json +12 -0
  285. package/plugins/acryl-state/src/cell.ts +67 -0
  286. package/plugins/acryl-state/src/index.ts +3 -0
  287. package/plugins/acryl-state/src/react.ts +11 -0
  288. package/plugins/acryl-state/src/service.ts +119 -0
  289. package/plugins/acryl-state/tests/cell.spec.ts +71 -0
  290. package/plugins/acryl-state/tests/react.spec.ts +39 -0
  291. package/plugins/acryl-state/tests/service.spec.ts +132 -0
  292. package/plugins/acryl-state/upstream.json +22 -0
  293. package/plugins/builder/script.ts +6 -10
  294. package/plugins/extensions/index.ts +20 -3
  295. package/plugins/extensions/resolve.ts +28 -3
  296. package/plugins/openai/prompt.ts +12 -6
  297. package/plugins/tasks/index.ts +14 -0
  298. package/plugins/tsconfig.json +14 -5
  299. package/templates/agent/blend.yaml +5 -7
  300. package/templates/agent/src/app.ts +8 -8
  301. package/templates/agent/src/routes.ts +39 -38
  302. package/templates/agent/template.json +3 -3
  303. package/templates/agent/test/builder.test.ts +22 -14
  304. package/templates/agent/test/model.test.ts +3 -3
  305. package/templates/agent/tsconfig.json +8 -5
  306. package/templates/{app → blank}/blend.yaml +3 -3
  307. package/templates/{app → blank}/src/app.ts +5 -5
  308. package/templates/blank/src/routes.ts +16 -0
  309. package/templates/blank/template.json +1 -0
  310. package/templates/{app → blank}/test/app.test.ts +1 -1
  311. package/templates/{app → blank}/tsconfig.json +2 -4
  312. package/templates/todo/README.md +27 -0
  313. package/templates/todo/blend.yaml +25 -0
  314. package/templates/todo/gitignore +2 -0
  315. package/templates/todo/package.json +23 -0
  316. package/templates/todo/src/app.ts +71 -0
  317. package/templates/todo/src/main.ts +19 -0
  318. package/templates/todo/src/page.ts +28 -0
  319. package/templates/todo/src/todo.ts +123 -0
  320. package/templates/todo/template.json +1 -0
  321. package/templates/todo/test/todo.test.ts +145 -0
  322. package/templates/todo/tsconfig.json +17 -0
  323. package/templates/todo/upstream.json +12 -0
  324. package/plugins/events/index.ts +0 -42
  325. package/plugins/events/plugin.json +0 -9
  326. package/plugins/http/index.ts +0 -64
  327. package/plugins/http/plugin.json +0 -10
  328. package/plugins/server/index.ts +0 -138
  329. package/plugins/server/plugin.json +0 -10
  330. package/templates/app/src/routes.ts +0 -14
  331. package/templates/app/template.json +0 -1
  332. /package/templates/{app → blank}/README.md +0 -0
  333. /package/templates/{app → blank}/gitignore +0 -0
  334. /package/templates/{app → blank}/package.json +0 -0
  335. /package/templates/{app → blank}/src/main.ts +0 -0
@@ -0,0 +1,292 @@
1
+ import { methodNotAllowed } from './index.ts'
2
+ import { App } from '../../app.ts'
3
+
4
+ describe('Method Not Allowed Middleware', () => {
5
+ it('infers the environment from the app', () => {
6
+ const app = new App<{ Variables: { requestId: string } }>()
7
+ const middleware = methodNotAllowed({
8
+ app,
9
+ onMethodNotAllowed: (c) => c.text(c.var.requestId, 405),
10
+ })
11
+
12
+ expect(middleware).toBeTypeOf('function')
13
+ })
14
+
15
+ it('returns 405 with every allowed method for an existing path', async () => {
16
+ const app = new App()
17
+ app.use(methodNotAllowed({ app }))
18
+ app.get('/resource', (c) => c.text('GET'))
19
+ app.post('/resource', (c) => c.text('POST'))
20
+ app.delete('/resource', (c) => c.text('DELETE'))
21
+
22
+ const res = await app.request('/resource', { method: 'PUT' })
23
+
24
+ expect(res.status).toBe(405)
25
+ expect(res.headers.get('Allow')).toBe('GET, HEAD, POST, DELETE')
26
+ expect(await res.text()).toBe('Method Not Allowed')
27
+
28
+ const resUsingCachedRouter = await app.request('/resource', { method: 'OPTIONS' })
29
+ expect(resUsingCachedRouter.status).toBe(405)
30
+ expect(resUsingCachedRouter.headers.get('Allow')).toBe('GET, HEAD, POST, DELETE')
31
+ })
32
+
33
+ it('does not affect an allowed method', async () => {
34
+ const app = new App()
35
+ app.use(methodNotAllowed({ app }))
36
+ app.get('/resource', (c) => c.text('GET'))
37
+
38
+ const res = await app.request('/resource')
39
+
40
+ expect(res.status).toBe(200)
41
+ expect(res.headers.has('Allow')).toBe(false)
42
+ expect(await res.text()).toBe('GET')
43
+ })
44
+
45
+ it('returns 404 when the path does not exist', async () => {
46
+ const app = new App()
47
+ app.use(methodNotAllowed({ app }))
48
+ app.get('/resource', (c) => c.text('GET'))
49
+
50
+ const res = await app.request('/missing', { method: 'POST' })
51
+
52
+ expect(res.status).toBe(404)
53
+ expect(res.headers.has('Allow')).toBe(false)
54
+ })
55
+
56
+ it('matches parameterized and overlapping routes without duplicate methods', async () => {
57
+ const app = new App()
58
+ app.use(methodNotAllowed({ app }))
59
+ app.get('/users/:id', (_c, next) => next())
60
+ app.get('/users/me', (c) => c.text('me'))
61
+ app.patch('/users/:id', (c) => c.text(c.req.param('id')))
62
+
63
+ const res = await app.request('/users/me', { method: 'POST' })
64
+
65
+ expect(res.status).toBe(405)
66
+ expect(res.headers.get('Allow')).toBe('GET, HEAD, PATCH')
67
+ })
68
+
69
+ it('preserves an intentional 404 from a handler registered for the request method', async () => {
70
+ const app = new App()
71
+ app.use(methodNotAllowed({ app }))
72
+ app.get('/users/:id', (c) => c.notFound())
73
+ app.post('/users/:id', (c) => c.text('Created'))
74
+
75
+ const res = await app.request('/users/123')
76
+
77
+ expect(res.status).toBe(404)
78
+ expect(res.headers.has('Allow')).toBe(false)
79
+ })
80
+
81
+ it('preserves an intentional 404 for HEAD when a GET route is registered', async () => {
82
+ const app = new App()
83
+ app.use(methodNotAllowed({ app }))
84
+ app.get('/users/:id', (c) => c.notFound())
85
+ app.post('/users/:id', (c) => c.text('Created'))
86
+
87
+ const res = await app.request('/users/123', { method: 'HEAD' })
88
+
89
+ expect(res.status).toBe(404)
90
+ expect(res.headers.has('Allow')).toBe(false)
91
+ })
92
+
93
+ it('preserves a 404 returned by the error handler', async () => {
94
+ const app = new App()
95
+ app.onError((_error, c) => c.text('Handled error', 404))
96
+ app.use(methodNotAllowed({ app }))
97
+ app.use('/resource', () => {
98
+ throw new Error('boom')
99
+ })
100
+ app.get('/resource', (c) => c.text('GET'))
101
+
102
+ const res = await app.request('/resource', { method: 'POST' })
103
+
104
+ expect(res.status).toBe(404)
105
+ expect(res.headers.has('Allow')).toBe(false)
106
+ expect(await res.text()).toBe('Handled error')
107
+ })
108
+
109
+ it('returns 405 for HEAD when the path does not support GET', async () => {
110
+ const app = new App()
111
+ app.use(methodNotAllowed({ app }))
112
+ app.post('/resource', (c) => c.text('POST'))
113
+
114
+ const res = await app.request('/resource', { method: 'HEAD' })
115
+
116
+ expect(res.status).toBe(405)
117
+ expect(res.headers.get('Allow')).toBe('POST')
118
+ expect(await res.text()).toBe('')
119
+ })
120
+
121
+ it('does not advertise explicit HEAD routes that App cannot dispatch', async () => {
122
+ const app = new App()
123
+ app.use(methodNotAllowed({ app }))
124
+ app.on('HEAD', '/resource', (c) => c.text('HEAD'))
125
+
126
+ const headRes = await app.request('/resource', { method: 'HEAD' })
127
+ expect(headRes.status).toBe(404)
128
+ expect(headRes.headers.has('Allow')).toBe(false)
129
+
130
+ const putRes = await app.request('/resource', { method: 'PUT' })
131
+ expect(putRes.status).toBe(404)
132
+ expect(putRes.headers.has('Allow')).toBe(false)
133
+ })
134
+
135
+ it('supports custom methods', async () => {
136
+ const app = new App()
137
+ app.use(methodNotAllowed({ app }))
138
+ app.on('PURGE', '/cache', (c) => c.text('Purged'))
139
+
140
+ const res = await app.request('/cache', { method: 'POST' })
141
+
142
+ expect(res.status).toBe(405)
143
+ expect(res.headers.get('Allow')).toBe('PURGE')
144
+ })
145
+
146
+ it('compares request methods case-sensitively', async () => {
147
+ const app = new App()
148
+ app.use(methodNotAllowed({ app }))
149
+ app.patch('/item', (c) => c.text('PATCH'))
150
+
151
+ const request = new Request('http://localhost/item', { method: 'PATCH' })
152
+ Object.defineProperty(request, 'method', { value: 'patch' })
153
+ const res = await app.request(request)
154
+
155
+ expect(res.status).toBe(405)
156
+ expect(res.headers.get('Allow')).toBe('PATCH')
157
+ })
158
+
159
+ it('works with routes mounted using app.route()', async () => {
160
+ const api = new App()
161
+ api.get('/resource', (c) => c.text('GET'))
162
+ api.post('/resource', (c) => c.text('POST'))
163
+
164
+ const app = new App()
165
+ app.use(methodNotAllowed({ app }))
166
+ app.route('/api', api)
167
+
168
+ const res = await app.request('/api/resource', { method: 'DELETE' })
169
+
170
+ expect(res.status).toBe(405)
171
+ expect(res.headers.get('Allow')).toBe('GET, HEAD, POST')
172
+ })
173
+
174
+ it('works when installed in an app mounted using app.route()', async () => {
175
+ const api = new App().basePath('/v1')
176
+ api.use(methodNotAllowed({ app: api }))
177
+ api.get('/resource', (c) => c.text('GET'))
178
+ api.post('/resource', (c) => c.text('POST'))
179
+
180
+ const app = new App()
181
+ app.route('/api', api)
182
+
183
+ const res = await app.request('/api/v1/resource', { method: 'DELETE' })
184
+
185
+ expect(res.status).toBe(405)
186
+ expect(res.headers.get('Allow')).toBe('GET, HEAD, POST')
187
+ })
188
+
189
+ it('ignores ALL routes when collecting allowed methods', async () => {
190
+ const app = new App()
191
+ app.use(methodNotAllowed({ app }))
192
+ app.all('/resource', (_c, next) => next())
193
+
194
+ const res = await app.request('/resource', { method: 'POST' })
195
+
196
+ expect(res.status).toBe(404)
197
+ expect(res.headers.has('Allow')).toBe(false)
198
+ })
199
+
200
+ it('does not invoke the error handler for a 405 response', async () => {
201
+ const app = new App()
202
+ const onError = vi.fn(() => new Response('error', { status: 500 }))
203
+ app.onError(onError)
204
+ app.use(methodNotAllowed({ app }))
205
+ app.get('/resource', (c) => c.text('GET'))
206
+
207
+ const res = await app.request('/resource', { method: 'POST' })
208
+
209
+ expect(res.status).toBe(405)
210
+ expect(onError).not.toHaveBeenCalled()
211
+ })
212
+
213
+ it('preserves downstream headers and lets outer middleware observe the final status', async () => {
214
+ const app = new App()
215
+ let observedStatus: number | undefined
216
+
217
+ app.use(async (c, next) => {
218
+ await next()
219
+ observedStatus = c.res.status
220
+ })
221
+ app.use(methodNotAllowed({ app }))
222
+ app.use(async (c, next) => {
223
+ await next()
224
+ c.header('X-Downstream', 'true')
225
+ })
226
+ app.get('/resource', (c) => c.text('GET'))
227
+
228
+ const res = await app.request('/resource', { method: 'POST' })
229
+
230
+ expect(res.status).toBe(405)
231
+ expect(res.headers.get('X-Downstream')).toBe('true')
232
+ expect(observedStatus).toBe(405)
233
+ })
234
+
235
+ it('replaces stale representation headers from the not-found response', async () => {
236
+ const app = new App()
237
+ app.use(methodNotAllowed({ app }))
238
+ app.get('/resource', (c) => c.text('GET'))
239
+ app.post('/resource', (c) => c.text('POST'))
240
+ app.notFound((c) =>
241
+ c.text('Missing', 404, {
242
+ Allow: 'BOGUS',
243
+ 'Content-Length': '7',
244
+ })
245
+ )
246
+
247
+ const res = await app.request('/resource', { method: 'PUT' })
248
+
249
+ expect(res.status).toBe(405)
250
+ expect(res.headers.get('Allow')).toBe('GET, HEAD, POST')
251
+ expect(res.headers.has('Content-Length')).toBe(false)
252
+ expect(await res.text()).toBe('Method Not Allowed')
253
+ })
254
+
255
+ it('supports a custom method-not-allowed response', async () => {
256
+ const app = new App()
257
+ app.use(
258
+ methodNotAllowed({
259
+ app,
260
+ onMethodNotAllowed: (c, allowedMethods) =>
261
+ c.json({ error: 'Method Not Allowed', allowedMethods }, 405, {
262
+ Allow: allowedMethods.join(', '),
263
+ }),
264
+ })
265
+ )
266
+ app.get('/resource', (c) => c.text('GET'))
267
+ app.post('/resource', (c) => c.text('POST'))
268
+
269
+ const res = await app.request('/resource', { method: 'PUT' })
270
+
271
+ expect(res.status).toBe(405)
272
+ expect(res.headers.get('Allow')).toBe('GET, HEAD, POST')
273
+ expect(res.headers.get('Content-Type')).toMatch(/^application\/json/)
274
+ expect(await res.json()).toEqual({
275
+ error: 'Method Not Allowed',
276
+ allowedMethods: ['GET', 'HEAD', 'POST'],
277
+ })
278
+ })
279
+
280
+ it('leaves a non-404 custom not-found response unchanged', async () => {
281
+ const app = new App()
282
+ app.use(methodNotAllowed({ app }))
283
+ app.get('/resource', (c) => c.text('GET'))
284
+ app.notFound((c) => c.text('Missing', 410))
285
+
286
+ const res = await app.request('/resource', { method: 'POST' })
287
+
288
+ expect(res.status).toBe(410)
289
+ expect(res.headers.has('Allow')).toBe(false)
290
+ expect(await res.text()).toBe('Missing')
291
+ })
292
+ })
@@ -0,0 +1,141 @@
1
+ /**
2
+ * @module
3
+ * Method Not Allowed Middleware for App.
4
+ */
5
+
6
+ import type { Context } from '../../context.ts'
7
+ import { basePath, matchedRoutes } from '../../helper/route/index.ts'
8
+ import type { App } from '../../app.ts'
9
+ import { METHOD_NAME_ALL } from '../../router.ts'
10
+ import { TrieRouter } from '../../router/trie-router/index.ts'
11
+ import type { Env, MiddlewareHandler } from '../../types.ts'
12
+
13
+ type MethodNotAllowedHandler<E extends Env> = (
14
+ c: Context<E>,
15
+ allowedMethods: string[]
16
+ ) => Response | Promise<Response>
17
+
18
+ type MethodNotAllowedOptions<E extends Env> = {
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ app: App<E, any, any>
21
+ onMethodNotAllowed?: MethodNotAllowedHandler<E>
22
+ }
23
+
24
+ /**
25
+ * Method Not Allowed Middleware for App.
26
+ *
27
+ * Returns a `405 Method Not Allowed` response with an `Allow` header when the request path
28
+ * matches a registered route but the request method is not supported.
29
+ *
30
+ * @param {MethodNotAllowedOptions} options - The options for the middleware.
31
+ * @param {App} options.app - The App instance used by the application.
32
+ * @param {MethodNotAllowedHandler} [options.onMethodNotAllowed] - Generates the response, including its `Allow` header.
33
+ * @returns {MiddlewareHandler} The middleware handler function.
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * const app = new App()
38
+ *
39
+ * app.use(methodNotAllowed({ app }))
40
+ *
41
+ * app.get('/hello', (c) => c.text('Hello!'))
42
+ * app.post('/hello', (c) => c.text('Posted!'))
43
+ *
44
+ * // PUT /hello -> 405 Method Not Allowed
45
+ * // Allow: GET, HEAD, POST
46
+ * ```
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * app.use(
51
+ * methodNotAllowed({
52
+ * app,
53
+ * onMethodNotAllowed: (c, methods) =>
54
+ * c.json({ error: 'Method Not Allowed' }, 405, { Allow: methods.join(', ') }),
55
+ * })
56
+ * )
57
+ * ```
58
+ */
59
+ export const methodNotAllowed = <E extends Env = Env>(
60
+ options: MethodNotAllowedOptions<E>
61
+ ): MiddlewareHandler<E> => {
62
+ let methodRouter: TrieRouter<string[]> | undefined
63
+
64
+ return async function methodNotAllowed(c, next) {
65
+ const routeIndex = c.req.routeIndex
66
+ await next()
67
+
68
+ if (c.res.status !== 404) {
69
+ return
70
+ }
71
+
72
+ if (c.error) {
73
+ return
74
+ }
75
+
76
+ if (!methodRouter) {
77
+ const methodsByPath = new Map<string, Set<string>>()
78
+
79
+ for (const route of options.app.routes) {
80
+ // App does not distinguish middleware registered with `app.use()` from handlers
81
+ // registered with `app.all()`, so `ALL` routes cannot contribute to the Allow header.
82
+ if (route.method === METHOD_NAME_ALL || route.method === 'HEAD') {
83
+ continue
84
+ }
85
+
86
+ const methods = methodsByPath.get(route.path) ?? new Set<string>()
87
+ methods.add(route.method)
88
+
89
+ // App dispatches HEAD requests to GET routes.
90
+ if (route.method === 'GET') {
91
+ methods.add('HEAD')
92
+ }
93
+
94
+ methodsByPath.set(route.path, methods)
95
+ }
96
+
97
+ methodRouter = new TrieRouter()
98
+ for (const [path, methods] of methodsByPath) {
99
+ methodRouter.add(METHOD_NAME_ALL, path, [...methods])
100
+ }
101
+ }
102
+
103
+ let requestPath = c.req.path
104
+ const routes = matchedRoutes(c)
105
+ const currentRoute = routes[routeIndex]
106
+ const sourceRoute = options.app.routes.find((route) => route.handler === methodNotAllowed)
107
+ if (currentRoute && sourceRoute) {
108
+ const currentBasePathParts = basePath(c, routeIndex).split('/').filter(Boolean)
109
+ const sourceBasePathLength = sourceRoute.basePath.split('/').filter(Boolean).length
110
+ const mountBasePathParts = currentBasePathParts.slice(
111
+ 0,
112
+ currentBasePathParts.length - sourceBasePathLength
113
+ )
114
+ if (mountBasePathParts.length > 0) {
115
+ const mountBasePath = `/${mountBasePathParts.join('/')}`
116
+ requestPath = c.req.path.slice(mountBasePath.length) || '/'
117
+ }
118
+ }
119
+
120
+ const allowedMethods = new Set<string>()
121
+ for (const [methods] of methodRouter.match(METHOD_NAME_ALL, requestPath)[0]) {
122
+ for (const method of methods) {
123
+ allowedMethods.add(method)
124
+ }
125
+ }
126
+
127
+ if (allowedMethods.size === 0 || allowedMethods.has(c.req.method)) {
128
+ return
129
+ }
130
+
131
+ // Prevent stale headers from being carried over from the 404 response.
132
+ c.res.headers.delete('Allow')
133
+ c.res.headers.delete('Content-Length')
134
+
135
+ const methods = [...allowedMethods]
136
+ const allow = methods.join(', ')
137
+ c.res = options.onMethodNotAllowed
138
+ ? await options.onMethodNotAllowed(c, methods)
139
+ : c.text('Method Not Allowed', 405, { Allow: allow })
140
+ }
141
+ }
@@ -0,0 +1,221 @@
1
+ import { App } from '../../app.ts'
2
+ import { methodOverride } from './index.ts'
3
+
4
+ describe('Method Override Middleware', () => {
5
+ describe('Form', () => {
6
+ const app = new App()
7
+ app.use('/posts/*', methodOverride({ app }))
8
+ app.use('/posts-custom/*', methodOverride({ app, form: 'custom-input-name' }))
9
+ app.on(['post', 'delete'], ['/posts', '/posts-custom'], async (c) => {
10
+ const form = await c.req.formData()
11
+ return c.json({
12
+ method: c.req.method,
13
+ message: form.get('message'),
14
+ contentType: c.req.header('content-type') ?? '',
15
+ })
16
+ })
17
+
18
+ describe('multipart/form-data', () => {
19
+ it('Should override POST to DELETE', async () => {
20
+ const form = new FormData()
21
+ form.append('message', 'Hello')
22
+ form.append('_method', 'DELETE')
23
+ const res = await app.request('/posts', {
24
+ body: form,
25
+ method: 'POST',
26
+ })
27
+ expect(res.status).toBe(200)
28
+ const data = await res.json()
29
+ expect(data.method).toBe('DELETE')
30
+ expect(data.message).toBe('Hello')
31
+ expect(data.contentType).toMatch(/^multipart\/form-data;/)
32
+ })
33
+
34
+ it('Should override POST to DELETE - with a custom form input name', async () => {
35
+ const form = new FormData()
36
+ form.append('message', 'Hello')
37
+ form.append('custom-input-name', 'DELETE')
38
+ const res = await app.request('/posts-custom', {
39
+ body: form,
40
+ method: 'POST',
41
+ })
42
+ expect(res.status).toBe(200)
43
+ const data = await res.json()
44
+ expect(data.method).toBe('DELETE')
45
+ expect(data.message).toBe('Hello')
46
+ expect(data.contentType).toMatch(/^multipart\/form-data;/)
47
+ })
48
+
49
+ it('Should override POST to PATCH - not found', async () => {
50
+ const form = new FormData()
51
+ form.append('message', 'Hello')
52
+ form.append('_method', 'PATCH')
53
+ const res = await app.request('/posts', {
54
+ body: form,
55
+ method: 'POST',
56
+ })
57
+ expect(res.status).toBe(404)
58
+ })
59
+ })
60
+
61
+ describe('application/x-www-form-urlencoded', () => {
62
+ it('Should override POST to DELETE', async () => {
63
+ const params = new URLSearchParams()
64
+ params.append('message', 'Hello')
65
+ params.append('_method', 'DELETE')
66
+ const res = await app.request('/posts', {
67
+ body: params,
68
+ headers: {
69
+ 'Content-Type': 'application/x-www-form-urlencoded',
70
+ },
71
+ method: 'POST',
72
+ })
73
+ expect(res.status).toBe(200)
74
+ const data = await res.json()
75
+ expect(data.method).toBe('DELETE')
76
+ expect(data.message).toBe('Hello')
77
+ expect(data.contentType).toBe('application/x-www-form-urlencoded')
78
+ })
79
+
80
+ it('Should override POST to DELETE - with a custom form input name', async () => {
81
+ const params = new URLSearchParams()
82
+ params.append('message', 'Hello')
83
+ params.append('custom-input-name', 'DELETE')
84
+ const res = await app.request('/posts-custom', {
85
+ body: params,
86
+ headers: {
87
+ 'Content-Type': 'application/x-www-form-urlencoded',
88
+ },
89
+ method: 'POST',
90
+ })
91
+ expect(res.status).toBe(200)
92
+ const data = await res.json()
93
+ expect(data.method).toBe('DELETE')
94
+ expect(data.message).toBe('Hello')
95
+ expect(data.contentType).toBe('application/x-www-form-urlencoded')
96
+ })
97
+
98
+ it('Should override POST to DELETE - with charset in Content-Type', async () => {
99
+ const params = new URLSearchParams()
100
+ params.append('message', 'Hello')
101
+ params.append('_method', 'DELETE')
102
+ const res = await app.request('/posts', {
103
+ body: params,
104
+ headers: {
105
+ 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
106
+ },
107
+ method: 'POST',
108
+ })
109
+ expect(res.status).toBe(200)
110
+ const data = await res.json()
111
+ expect(data.method).toBe('DELETE')
112
+ expect(data.message).toBe('Hello')
113
+ })
114
+
115
+ it('Should override POST to PATCH - not found', async () => {
116
+ const form = new FormData()
117
+ form.append('message', 'Hello')
118
+ form.append('_method', 'PATCH')
119
+ const res = await app.request('/posts', {
120
+ body: form,
121
+ method: 'POST',
122
+ })
123
+ expect(res.status).toBe(404)
124
+ })
125
+ })
126
+ })
127
+
128
+ describe('Header', () => {
129
+ const app = new App()
130
+ app.use('/posts/*', methodOverride({ app, header: 'X-METHOD-OVERRIDE' }))
131
+ app.on(['get', 'post', 'delete'], '/posts', async (c) => {
132
+ return c.json({
133
+ method: c.req.method,
134
+ headerValue: c.req.header('X-METHOD-OVERRIDE') ?? null,
135
+ })
136
+ })
137
+
138
+ it('Should override POST to DELETE', async () => {
139
+ const res = await app.request('/posts', {
140
+ method: 'POST',
141
+ headers: {
142
+ 'X-METHOD-OVERRIDE': 'DELETE',
143
+ },
144
+ })
145
+ expect(res.status).toBe(200)
146
+ expect(await res.json()).toEqual({
147
+ method: 'DELETE',
148
+ headerValue: null,
149
+ })
150
+ })
151
+
152
+ it('Should not override GET request', async () => {
153
+ const res = await app.request('/posts', {
154
+ method: 'GET',
155
+ headers: {
156
+ 'X-METHOD-OVERRIDE': 'DELETE',
157
+ },
158
+ })
159
+ expect(res.status).toBe(200)
160
+ expect(await res.json()).toEqual({
161
+ method: 'GET',
162
+ headerValue: 'DELETE', // It does not modify the headers.
163
+ })
164
+ })
165
+ })
166
+
167
+ describe('Query', () => {
168
+ const app = new App()
169
+ app.use('/posts/*', methodOverride({ app, query: '_method' }))
170
+ app.on(['get', 'post', 'delete'], '/posts', async (c) => {
171
+ return c.json({
172
+ method: c.req.method,
173
+ queryValue: c.req.query('_method') ?? null,
174
+ })
175
+ })
176
+
177
+ it('Should override POST to DELETE', async () => {
178
+ const res = await app.request('/posts?_method=delete', {
179
+ method: 'POST',
180
+ })
181
+ expect(res.status).toBe(200)
182
+ expect(await res.json()).toEqual({
183
+ method: 'DELETE',
184
+ queryValue: null,
185
+ })
186
+ })
187
+
188
+ it('Should override POST to DELETE when the request has a body', async () => {
189
+ const app = new App()
190
+ app.use('/posts/*', methodOverride({ app, query: '_method' }))
191
+ app.on(['post', 'delete'], '/posts', async (c) => {
192
+ return c.json({
193
+ method: c.req.method,
194
+ body: await c.req.text(),
195
+ queryValue: c.req.query('_method') ?? null,
196
+ })
197
+ })
198
+ const res = await app.request('/posts?_method=delete', {
199
+ method: 'POST',
200
+ body: new URLSearchParams({ message: 'Hello' }),
201
+ })
202
+ expect(res.status).toBe(200)
203
+ expect(await res.json()).toEqual({
204
+ method: 'DELETE',
205
+ body: 'message=Hello',
206
+ queryValue: null,
207
+ })
208
+ })
209
+
210
+ it('Should not override GET request', async () => {
211
+ const res = await app.request('/posts?_method=delete', {
212
+ method: 'GET',
213
+ })
214
+ expect(res.status).toBe(200)
215
+ expect(await res.json()).toEqual({
216
+ method: 'GET',
217
+ queryValue: 'delete', // It does not modify the queries.
218
+ })
219
+ })
220
+ })
221
+ })