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,220 @@
1
+ /**
2
+ * @module
3
+ * Bearer Auth Middleware for App.
4
+ */
5
+
6
+ import type { Context } from '../../context.ts'
7
+ import { HTTPException } from '../../http-exception.ts'
8
+ import type { Env, MiddlewareHandler } from '../../types.ts'
9
+ import { timingSafeEqual } from '../../utils/buffer.ts'
10
+ import type { ContentfulStatusCode } from '../../utils/http-status.ts'
11
+
12
+ const TOKEN_STRINGS = '[A-Za-z0-9._~+/-]+=*'
13
+ const PREFIX = 'Bearer'
14
+ const HEADER = 'Authorization'
15
+
16
+ type MessageFunction = (c: Context) => string | object | Promise<string | object>
17
+ type CustomizedErrorResponseOptions = {
18
+ wwwAuthenticateHeader?: string | object | MessageFunction
19
+ message?: string | object | MessageFunction
20
+ }
21
+
22
+ type BearerAuthOptions<E extends Env = Env> =
23
+ | {
24
+ token: string | string[]
25
+ realm?: string
26
+ prefix?: string
27
+ headerName?: string
28
+ hashFunction?: Function
29
+ /**
30
+ * @deprecated Use noAuthenticationHeader.message instead
31
+ */
32
+ noAuthenticationHeaderMessage?: string | object | MessageFunction
33
+ noAuthenticationHeader?: CustomizedErrorResponseOptions
34
+ /**
35
+ * @deprecated Use invalidAuthenticationHeader.message instead
36
+ */
37
+ invalidAuthenticationHeaderMessage?: string | object | MessageFunction
38
+ invalidAuthenticationHeader?: CustomizedErrorResponseOptions
39
+ /**
40
+ * @deprecated Use invalidToken.message instead
41
+ */
42
+ invalidTokenMessage?: string | object | MessageFunction
43
+ invalidToken?: CustomizedErrorResponseOptions
44
+ }
45
+ | {
46
+ realm?: string
47
+ prefix?: string
48
+ headerName?: string
49
+ verifyToken: (token: string, c: Context<E>) => boolean | Promise<boolean>
50
+ hashFunction?: Function
51
+ /**
52
+ * @deprecated Use noAuthenticationHeader.message instead
53
+ */
54
+ noAuthenticationHeaderMessage?: string | object | MessageFunction
55
+ noAuthenticationHeader?: CustomizedErrorResponseOptions
56
+ /**
57
+ * @deprecated Use invalidAuthenticationHeader.message instead
58
+ */
59
+ invalidAuthenticationHeaderMessage?: string | object | MessageFunction
60
+ invalidAuthenticationHeader?: CustomizedErrorResponseOptions
61
+ /**
62
+ * @deprecated Use invalidToken.message instead
63
+ */
64
+ invalidTokenMessage?: string | object | MessageFunction
65
+ invalidToken?: CustomizedErrorResponseOptions
66
+ }
67
+
68
+ /**
69
+ * Bearer Auth Middleware for App.
70
+ *
71
+ *
72
+ * @template E - The environment type.
73
+ * @param {BearerAuthOptions} options - The options for the bearer authentication middleware.
74
+ * @param {string | string[]} [options.token] - The string or array of strings to validate the incoming bearer token against.
75
+ * @param {Function} [options.verifyToken] - The function to verify the token.
76
+ * @param {string} [options.realm=""] - The domain name of the realm, as part of the returned WWW-Authenticate challenge header.
77
+ * @param {string} [options.prefix="Bearer"] - The prefix (or known as `schema`) for the Authorization header value. If set to the empty string, no prefix is expected.
78
+ * @param {string} [options.headerName=Authorization] - The header name.
79
+ * @param {Function} [options.hashFunction] - A function to handle hashing for safe comparison of authentication tokens.
80
+ * @param {string | object | MessageFunction} [options.noAuthenticationHeader.message="Unauthorized"] - The no authentication header message.
81
+ * @param {string | object | MessageFunction} [options.noAuthenticationHeader.wwwAuthenticateHeader="Bearer realm=\"\""] - The response header value for the WWW-Authenticate header when no authentication header is provided.
82
+ * @param {string | object | MessageFunction} [options.invalidAuthenticationHeader.message="Bad Request"] - The invalid authentication header message.
83
+ * @param {string | object | MessageFunction} [options.invalidAuthenticationHeader.wwwAuthenticateHeader="Bearer error=\"invalid_request\""] - The response header value for the WWW-Authenticate header when authentication header is invalid.
84
+ * @param {string | object | MessageFunction} [options.invalidToken.message="Unauthorized"] - The invalid token message.
85
+ * @param {string | object | MessageFunction} [options.invalidToken.wwwAuthenticateHeader="Bearer error=\"invalid_token\""] - The response header value for the WWW-Authenticate header when token is invalid.
86
+ * @returns {MiddlewareHandler<E>} The middleware handler function.
87
+ * @throws {Error} If neither "token" nor "verifyToken" options are provided.
88
+ * @throws {HTTPException} If authentication fails, with 401 status code for missing or invalid token, or 400 status code for invalid request.
89
+ *
90
+ * @example
91
+ * ```ts
92
+ * const app = new App()
93
+ *
94
+ * const token = process.env.API_TOKEN // never a literal in source
95
+ *
96
+ * app.use('/api/*', bearerAuth({ token }))
97
+ *
98
+ * app.get('/api/page', (c) => {
99
+ * return c.json({ message: 'You are authorized' })
100
+ * })
101
+ * ```
102
+ */
103
+ export const bearerAuth = <E extends Env = Env>(
104
+ options: BearerAuthOptions<E>
105
+ ): MiddlewareHandler<E> => {
106
+ if (!('token' in options || 'verifyToken' in options)) {
107
+ throw new Error('bearer auth middleware requires options for "token" or "verifyToken"')
108
+ }
109
+ if (!options.realm) {
110
+ options.realm = ''
111
+ }
112
+ if (options.prefix === undefined) {
113
+ options.prefix = PREFIX
114
+ }
115
+
116
+ const realm = options.realm?.replace(/"/g, '\\"')
117
+ const prefix = options.prefix
118
+ const tokenRegexp = new RegExp(`^${TOKEN_STRINGS}$`)
119
+ const wwwAuthenticatePrefix = prefix === '' ? '' : `${prefix} `
120
+
121
+ const throwHTTPException = async (
122
+ c: Context,
123
+ status: ContentfulStatusCode,
124
+ wwwAuthenticateHeader: string | object | MessageFunction,
125
+ messageOption: string | object | MessageFunction
126
+ ): Promise<Response> => {
127
+ const wwwAuthenticateHeaderValue: string | object =
128
+ typeof wwwAuthenticateHeader === 'function'
129
+ ? await wwwAuthenticateHeader(c)
130
+ : wwwAuthenticateHeader
131
+
132
+ const headers = {
133
+ 'WWW-Authenticate':
134
+ typeof wwwAuthenticateHeaderValue === 'string'
135
+ ? wwwAuthenticateHeaderValue
136
+ : `${wwwAuthenticatePrefix}${Object.entries(wwwAuthenticateHeaderValue)
137
+ .map(([key, value]) => `${key}="${value}"`)
138
+ .join(',')}`,
139
+ }
140
+ const responseMessage =
141
+ typeof messageOption === 'function' ? await messageOption(c) : messageOption
142
+ const res =
143
+ typeof responseMessage === 'string'
144
+ ? new Response(responseMessage, { status, headers })
145
+ : new Response(JSON.stringify(responseMessage), {
146
+ status,
147
+ headers: {
148
+ ...headers,
149
+ 'content-type': 'application/json',
150
+ },
151
+ })
152
+ throw new HTTPException(status, { res })
153
+ }
154
+
155
+ return async function bearerAuth(c, next) {
156
+ const headerToken = c.req.header(options.headerName || HEADER)
157
+ if (!headerToken) {
158
+ // No Authorization header
159
+ await throwHTTPException(
160
+ c,
161
+ 401,
162
+ options.noAuthenticationHeader?.wwwAuthenticateHeader ||
163
+ `${wwwAuthenticatePrefix}realm="${realm}"`,
164
+ options.noAuthenticationHeader?.message ||
165
+ options.noAuthenticationHeaderMessage ||
166
+ 'Unauthorized'
167
+ )
168
+ } else {
169
+ let tokenValue: string | undefined
170
+
171
+ if (prefix === '') {
172
+ tokenValue = headerToken
173
+ } else {
174
+ const headerLower = headerToken.toLowerCase()
175
+ const prefixLower = prefix.toLowerCase()
176
+ if (headerLower.startsWith(prefixLower) && headerToken[prefix.length] === ' ') {
177
+ tokenValue = headerToken.slice(prefix.length).trimStart()
178
+ }
179
+ }
180
+
181
+ if (!tokenValue || !tokenRegexp.test(tokenValue)) {
182
+ // Invalid Request
183
+ await throwHTTPException(
184
+ c,
185
+ 400,
186
+ options.invalidAuthenticationHeader?.wwwAuthenticateHeader ||
187
+ `${wwwAuthenticatePrefix}error="invalid_request"`,
188
+ options.invalidAuthenticationHeader?.message ||
189
+ options.invalidAuthenticationHeaderMessage ||
190
+ 'Bad Request'
191
+ )
192
+ } else {
193
+ let equal = false
194
+ if ('verifyToken' in options) {
195
+ equal = await options.verifyToken(tokenValue, c)
196
+ } else if (typeof options.token === 'string') {
197
+ equal = await timingSafeEqual(options.token, tokenValue, options.hashFunction)
198
+ } else if (Array.isArray(options.token) && options.token.length > 0) {
199
+ for (const token of options.token) {
200
+ if (await timingSafeEqual(token, tokenValue, options.hashFunction)) {
201
+ equal = true
202
+ break
203
+ }
204
+ }
205
+ }
206
+ if (!equal) {
207
+ // Invalid Token
208
+ await throwHTTPException(
209
+ c,
210
+ 401,
211
+ options.invalidToken?.wwwAuthenticateHeader ||
212
+ `${wwwAuthenticatePrefix}error="invalid_token"`,
213
+ options.invalidToken?.message || options.invalidTokenMessage || 'Unauthorized'
214
+ )
215
+ }
216
+ }
217
+ }
218
+ await next()
219
+ }
220
+ }
@@ -0,0 +1,273 @@
1
+ import { bodyLimit } from './index.ts'
2
+ import { App } from '../../app.ts'
3
+
4
+ const buildRequestInit = (init: RequestInit = {}): RequestInit & { duplex: 'half' } => {
5
+ const headers: Record<string, string> = {
6
+ 'Content-Type': 'text/plain',
7
+ }
8
+ if (typeof init.body === 'string') {
9
+ headers['Content-Length'] = init.body.length.toString()
10
+ }
11
+ return {
12
+ method: 'POST',
13
+ headers,
14
+ body: null,
15
+ ...init,
16
+ duplex: 'half',
17
+ }
18
+ }
19
+
20
+ describe('Body Limit Middleware', () => {
21
+ let app: App
22
+
23
+ const exampleText = 'data is so hot' // 14byte
24
+ const exampleText2 = 'data is so hot and cute' // 23byte
25
+
26
+ beforeEach(() => {
27
+ app = new App()
28
+ app.use('*', bodyLimit({ maxSize: 14 }))
29
+ app.get('/', (c) => c.text('index'))
30
+ app.post('/body-limit-15byte', async (c) => {
31
+ return c.text(await c.req.raw.text())
32
+ })
33
+ })
34
+
35
+ describe('GET request', () => {
36
+ it('should return 200 response', async () => {
37
+ const res = await app.request('/')
38
+ expect(res).not.toBeNull()
39
+ expect(res.status).toBe(200)
40
+ expect(await res.text()).toBe('index')
41
+ })
42
+ })
43
+
44
+ describe('POST request', () => {
45
+ describe('string body', () => {
46
+ it('should return 200 response', async () => {
47
+ const res = await app.request('/body-limit-15byte', buildRequestInit({ body: exampleText }))
48
+
49
+ expect(res).not.toBeNull()
50
+ expect(res.status).toBe(200)
51
+ expect(await res.text()).toBe(exampleText)
52
+ })
53
+
54
+ it('should return 413 response', async () => {
55
+ const res = await app.request(
56
+ '/body-limit-15byte',
57
+ buildRequestInit({ body: exampleText2 })
58
+ )
59
+
60
+ expect(res).not.toBeNull()
61
+ expect(res.status).toBe(413)
62
+ expect(await res.text()).toBe('Payload Too Large')
63
+ })
64
+ })
65
+
66
+ describe('ReadableStream body', () => {
67
+ it('should return 200 response', async () => {
68
+ const contents = ['a', 'b', 'c']
69
+ const stream = new ReadableStream({
70
+ start(controller) {
71
+ while (contents.length) {
72
+ controller.enqueue(new TextEncoder().encode(contents.shift() as string))
73
+ }
74
+ controller.close()
75
+ },
76
+ })
77
+ const res = await app.request('/body-limit-15byte', buildRequestInit({ body: stream }))
78
+
79
+ expect(res).not.toBeNull()
80
+ expect(res.status).toBe(200)
81
+ expect(await res.text()).toBe('abc')
82
+ })
83
+
84
+ it('should return 413 response', async () => {
85
+ const readSpy = vi.fn().mockImplementation(() => {
86
+ return {
87
+ done: false,
88
+ value: new TextEncoder().encode(exampleText),
89
+ }
90
+ })
91
+ const stream = new ReadableStream()
92
+ vi.spyOn(stream, 'getReader').mockReturnValue({
93
+ read: readSpy,
94
+ } as unknown as ReadableStreamDefaultReader)
95
+ const res = await app.request('/body-limit-15byte', buildRequestInit({ body: stream }))
96
+
97
+ expect(res).not.toBeNull()
98
+ expect(res.status).toBe(413)
99
+ expect(readSpy).toHaveBeenCalledTimes(2)
100
+ expect(await res.text()).toBe('Payload Too Large')
101
+ })
102
+ })
103
+ })
104
+
105
+ describe('custom error handler', () => {
106
+ beforeEach(() => {
107
+ app = new App()
108
+ app.post(
109
+ '/text-limit-15byte-custom',
110
+ bodyLimit({
111
+ maxSize: 15,
112
+ onError: (c) => {
113
+ return c.text('no', 413)
114
+ },
115
+ }),
116
+ (c) => {
117
+ return c.text('yes')
118
+ }
119
+ )
120
+ })
121
+
122
+ it('should return the custom error handler', async () => {
123
+ const res = await app.request(
124
+ '/text-limit-15byte-custom',
125
+ buildRequestInit({ body: exampleText2 })
126
+ )
127
+
128
+ expect(res).not.toBeNull()
129
+ expect(res.status).toBe(413)
130
+ expect(await res.text()).toBe('no')
131
+ })
132
+ })
133
+
134
+ describe('Transfer-Encoding and Content-Length headers', () => {
135
+ beforeEach(() => {
136
+ app = new App()
137
+ app.use('*', bodyLimit({ maxSize: 10 }))
138
+ app.post('/test', async (c) => {
139
+ return c.text(await c.req.text())
140
+ })
141
+ })
142
+
143
+ it('should prioritize Transfer-Encoding over Content-Length', async () => {
144
+ // Create a chunked body that exceeds the limit
145
+ const largeContent = 'this is a large content that exceeds 10 bytes'
146
+ const chunks = [largeContent.slice(0, 20), largeContent.slice(20)]
147
+
148
+ const stream = new ReadableStream({
149
+ start(controller) {
150
+ chunks.forEach((chunk) => {
151
+ controller.enqueue(new TextEncoder().encode(chunk))
152
+ })
153
+ controller.close()
154
+ },
155
+ })
156
+
157
+ const res = await app.request('/test', {
158
+ method: 'POST',
159
+ headers: {
160
+ 'Content-Length': '5', // Small content-length (bypass attempt)
161
+ 'Transfer-Encoding': 'chunked', // But chunked encoding with large content
162
+ },
163
+ body: stream,
164
+ duplex: 'half',
165
+ } as RequestInit)
166
+
167
+ // Should reject based on actual chunked content size, not Content-Length
168
+ expect(res.status).toBe(413)
169
+ })
170
+
171
+ it('should handle only Content-Length header correctly', async () => {
172
+ const smallContent = 'small'
173
+ const res = await app.request('/test', buildRequestInit({ body: smallContent }))
174
+
175
+ expect(res.status).toBe(200)
176
+ expect(await res.text()).toBe(smallContent)
177
+ })
178
+
179
+ it('should handle only Transfer-Encoding header correctly', async () => {
180
+ const content = 'test'
181
+ const stream = new ReadableStream({
182
+ start(controller) {
183
+ controller.enqueue(new TextEncoder().encode(content))
184
+ controller.close()
185
+ },
186
+ })
187
+
188
+ const res = await app.request('/test', {
189
+ method: 'POST',
190
+ headers: {
191
+ 'Transfer-Encoding': 'chunked',
192
+ },
193
+ body: stream,
194
+ duplex: 'half',
195
+ } as RequestInit)
196
+
197
+ expect(res.status).toBe(200)
198
+ expect(await res.text()).toBe(content)
199
+ })
200
+ })
201
+
202
+ describe('chunked body bypass scenarios', () => {
203
+ const handler = vi.fn()
204
+
205
+ const buildOversizedRequestInit = (): RequestInit & { duplex: 'half' } => ({
206
+ method: 'POST',
207
+ headers: { 'Transfer-Encoding': 'chunked' },
208
+ body: new ReadableStream({
209
+ start(controller) {
210
+ controller.enqueue(new TextEncoder().encode('1234'))
211
+ controller.enqueue(new TextEncoder().encode('5678901234567890'))
212
+ controller.close()
213
+ },
214
+ }),
215
+ duplex: 'half',
216
+ })
217
+
218
+ beforeEach(() => {
219
+ handler.mockReset()
220
+ app = new App()
221
+ app.use('*', bodyLimit({ maxSize: 8 }))
222
+ app.post('/no-read', (c) => {
223
+ handler()
224
+ return c.text('NO-READ-OK')
225
+ })
226
+ app.post('/partial-read', async (c) => {
227
+ handler()
228
+ const reader = c.req.raw.body?.getReader()
229
+ const chunk = reader ? await reader.read() : { value: undefined }
230
+ return c.text(new TextDecoder().decode(chunk.value))
231
+ })
232
+ app.post('/swallow-error', async (c) => {
233
+ handler()
234
+ const body = c.req.raw.body
235
+ if (!body) {
236
+ return c.text('no body')
237
+ }
238
+ const reader = body.getReader()
239
+ let seen = ''
240
+ try {
241
+ for (;;) {
242
+ const { done, value } = await reader.read()
243
+ if (done) {
244
+ break
245
+ }
246
+ seen += new TextDecoder().decode(value)
247
+ }
248
+ } catch {
249
+ // intentionally swallow read errors
250
+ }
251
+ return c.text(`processed:${seen}`)
252
+ })
253
+ })
254
+
255
+ it('should reject when handler does not read the body', async () => {
256
+ const res = await app.request('/no-read', buildOversizedRequestInit())
257
+ expect(res.status).toBe(413)
258
+ expect(handler).not.toHaveBeenCalled()
259
+ })
260
+
261
+ it('should reject when handler reads only the first chunk', async () => {
262
+ const res = await app.request('/partial-read', buildOversizedRequestInit())
263
+ expect(res.status).toBe(413)
264
+ expect(handler).not.toHaveBeenCalled()
265
+ })
266
+
267
+ it('should reject when handler swallows body read errors', async () => {
268
+ const res = await app.request('/swallow-error', buildOversizedRequestInit())
269
+ expect(res.status).toBe(413)
270
+ expect(handler).not.toHaveBeenCalled()
271
+ })
272
+ })
273
+ })
@@ -0,0 +1,110 @@
1
+ /**
2
+ * @module
3
+ * Body Limit Middleware for App.
4
+ */
5
+
6
+ import type { Context } from '../../context.ts'
7
+ import { HTTPException } from '../../http-exception.ts'
8
+ import type { MiddlewareHandler } from '../../types.ts'
9
+
10
+ const ERROR_MESSAGE = 'Payload Too Large'
11
+
12
+ type OnError = (c: Context) => Response | Promise<Response>
13
+ type BodyLimitOptions = {
14
+ maxSize: number
15
+ onError?: OnError
16
+ }
17
+
18
+ /**
19
+ * Body Limit Middleware for App.
20
+ *
21
+ *
22
+ * @param {BodyLimitOptions} options - The options for the body limit middleware.
23
+ * @param {number} options.maxSize - The maximum body size allowed.
24
+ * @param {OnError} [options.onError] - The error handler to be invoked if the specified body size is exceeded.
25
+ * @returns {MiddlewareHandler} The middleware handler function.
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * const app = new App()
30
+ *
31
+ * app.post(
32
+ * '/upload',
33
+ * bodyLimit({
34
+ * maxSize: 50 * 1024, // 50kb
35
+ * onError: (c) => {
36
+ * return c.text('overflow :(', 413)
37
+ * },
38
+ * }),
39
+ * async (c) => {
40
+ * const body = await c.req.parseBody()
41
+ * if (body['file'] instanceof File) {
42
+ * console.log(`Got file sized: ${body['file'].size}`)
43
+ * }
44
+ * return c.text('pass :)')
45
+ * }
46
+ * )
47
+ * ```
48
+ */
49
+ export const bodyLimit = (options: BodyLimitOptions): MiddlewareHandler => {
50
+ const onError: OnError =
51
+ options.onError ||
52
+ (() => {
53
+ const res = new Response(ERROR_MESSAGE, {
54
+ status: 413,
55
+ })
56
+ throw new HTTPException(413, { res })
57
+ })
58
+ const maxSize = options.maxSize
59
+
60
+ return async function bodyLimit(c, next) {
61
+ if (!c.req.raw.body) {
62
+ // maybe GET or HEAD request
63
+ return next()
64
+ }
65
+
66
+ const hasTransferEncoding = c.req.raw.headers.has('transfer-encoding')
67
+ const hasContentLength = c.req.raw.headers.has('content-length')
68
+
69
+ if (hasContentLength && !hasTransferEncoding) {
70
+ // Only Content-Length present - we can trust it
71
+ const contentLength = parseInt(c.req.raw.headers.get('content-length') || '0', 10)
72
+ return contentLength > maxSize ? onError(c) : next()
73
+ }
74
+
75
+ // Transfer-Encoding present (chunked) or no length headers.
76
+ // Per RFC 7230, when both are present Transfer-Encoding takes precedence
77
+ // and Content-Length is ignored. Read the body up-front so the size check
78
+ // is final before the handler runs, regardless of how (or whether) the
79
+ // handler reads the body.
80
+ let size = 0
81
+ const chunks: Uint8Array[] = []
82
+ const rawReader = c.req.raw.body.getReader()
83
+ for (;;) {
84
+ const { done, value } = await rawReader.read()
85
+ if (done) {
86
+ break
87
+ }
88
+ size += value.length
89
+ if (size > maxSize) {
90
+ return onError(c)
91
+ }
92
+ chunks.push(value)
93
+ }
94
+
95
+ const requestInit: RequestInit & { duplex: 'half' } = {
96
+ body: new ReadableStream({
97
+ start(controller) {
98
+ for (const chunk of chunks) {
99
+ controller.enqueue(chunk)
100
+ }
101
+ controller.close()
102
+ },
103
+ }),
104
+ duplex: 'half',
105
+ }
106
+ c.req.raw = new Request(c.req.raw, requestInit as RequestInit)
107
+
108
+ return next()
109
+ }
110
+ }