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,186 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ // Define lightweight pseudo Response class and replace global.Response with it.
3
+
4
+ import type { OutgoingHttpHeaders } from 'node:http'
5
+
6
+ export const defaultContentType = 'text/plain; charset=UTF-8'
7
+
8
+ const responseCache = Symbol('responseCache')
9
+ const getResponseCache = Symbol('getResponseCache')
10
+ export const cacheKey = Symbol('cache')
11
+
12
+ export type InternalCache = [
13
+ number,
14
+ string | ReadableStream | null,
15
+ Record<string, string> | [string, string][] | Headers | OutgoingHttpHeaders | undefined,
16
+ ]
17
+ interface LightResponse {
18
+ [responseCache]?: globalThis.Response
19
+ [cacheKey]?: InternalCache
20
+ }
21
+
22
+ export const GlobalResponse = global.Response
23
+ export class Response {
24
+ #body?: BodyInit | null
25
+ #init?: ResponseInit;
26
+
27
+ [getResponseCache](): globalThis.Response {
28
+ // If `cacheKey` has been populated with a live `Headers` instance, the
29
+ // user (or middleware) may have mutated it after construction. Use those
30
+ // headers so the GlobalResponse reflects the current state.
31
+ const cache = (this as LightResponse)[cacheKey]
32
+ const liveHeaders = cache && cache[2] instanceof Headers ? cache[2] : undefined
33
+ delete (this as LightResponse)[cacheKey]
34
+ return ((this as LightResponse)[responseCache] ||= new GlobalResponse(
35
+ this.#body,
36
+ liveHeaders
37
+ ? {
38
+ status: this.#init?.status,
39
+ statusText: this.#init?.statusText,
40
+ headers: liveHeaders,
41
+ }
42
+ : this.#init
43
+ ))
44
+ }
45
+
46
+ constructor(body?: BodyInit | null, init?: ResponseInit) {
47
+ let headers: HeadersInit | undefined
48
+ this.#body = body
49
+ if (init instanceof GlobalResponse) {
50
+ const cachedGlobalResponse = (init as any)[responseCache]
51
+ if (cachedGlobalResponse) {
52
+ this.#init = cachedGlobalResponse
53
+ // instantiate GlobalResponse cache and this object always returns value from global.Response
54
+ this[getResponseCache]()
55
+ return
56
+ }
57
+ this.#init = init instanceof Response ? init.#init : init
58
+ headers = new Headers(init.headers)
59
+ } else {
60
+ this.#init = init
61
+ }
62
+
63
+ if (
64
+ body == null ||
65
+ typeof body === 'string' ||
66
+ typeof (body as ReadableStream)?.getReader !== 'undefined' ||
67
+ body instanceof Blob ||
68
+ body instanceof Uint8Array
69
+ ) {
70
+ ;(this as any)[cacheKey] = [init?.status || 200, body ?? null, headers || init?.headers]
71
+ }
72
+ }
73
+
74
+ get headers(): Headers {
75
+ const cache = (this as LightResponse)[cacheKey] as InternalCache
76
+ if (cache) {
77
+ if (!(cache[2] instanceof Headers)) {
78
+ cache[2] = new Headers(
79
+ (cache[2] ||
80
+ (cache[1] === null ? undefined : { 'content-type': defaultContentType })) as HeadersInit
81
+ )
82
+ }
83
+ return cache[2]
84
+ }
85
+ return this[getResponseCache]().headers
86
+ }
87
+
88
+ get status() {
89
+ return (
90
+ ((this as LightResponse)[cacheKey] as InternalCache | undefined)?.[0] ??
91
+ this[getResponseCache]().status
92
+ )
93
+ }
94
+
95
+ get ok() {
96
+ const status = this.status
97
+ return status >= 200 && status < 300
98
+ }
99
+ }
100
+ ;['body', 'bodyUsed', 'redirected', 'statusText', 'trailers', 'type', 'url'].forEach((k) => {
101
+ Object.defineProperty(Response.prototype, k, {
102
+ get() {
103
+ return this[getResponseCache]()[k]
104
+ },
105
+ })
106
+ })
107
+ ;['arrayBuffer', 'blob', 'clone', 'formData', 'json', 'text'].forEach((k) => {
108
+ Object.defineProperty(Response.prototype, k, {
109
+ value: function () {
110
+ return this[getResponseCache]()[k]()
111
+ },
112
+ })
113
+ })
114
+
115
+ Object.defineProperty(Response.prototype, Symbol.for('nodejs.util.inspect.custom'), {
116
+ value: function (depth: number, options: object, inspectFn: Function) {
117
+ const props: Record<string, unknown> = {
118
+ status: this.status,
119
+ headers: this.headers,
120
+ ok: this.ok,
121
+ nativeResponse: (this as LightResponse)[responseCache],
122
+ }
123
+ return `Response (lightweight) ${inspectFn(props, { ...options, depth: depth == null ? null : depth - 1 })}`
124
+ },
125
+ })
126
+
127
+ Object.setPrototypeOf(Response, GlobalResponse)
128
+ Object.setPrototypeOf(Response.prototype, GlobalResponse.prototype)
129
+
130
+ // Fast path regex: matches http:// or https:// followed by RFC 3986 allowed chars.
131
+ // Character class covers unreserved + reserved chars plus `%` for percent-encoding.
132
+ // ! #-; = ?-[ ] _ a-z ~ A-Z (A-Z is within ?-[ range but listed for clarity)
133
+ const validRedirectUrl = /^https?:\/\/[!#-;=?-[\]_a-z~A-Z]+$/
134
+ const parseRedirectUrl = (url: string | URL): string => {
135
+ if (url instanceof URL) {
136
+ return url.href
137
+ }
138
+ if (validRedirectUrl.test(url)) {
139
+ return url
140
+ }
141
+ return new URL(url).href
142
+ }
143
+
144
+ const validRedirectStatuses = new Set([301, 302, 303, 307, 308])
145
+
146
+ // Override Response.json() and Response.redirect() to return a LightweightResponse
147
+ // so the listener fast-path (cacheKey check) is hit instead of falling through to ReadableStream reading.
148
+ Object.defineProperty(Response, 'redirect', {
149
+ value: function redirect(url: string | URL, status = 302): Response {
150
+ if (!validRedirectStatuses.has(status)) {
151
+ throw new RangeError('Invalid status code')
152
+ }
153
+ return new Response(null, {
154
+ status,
155
+ headers: { location: parseRedirectUrl(url) },
156
+ })
157
+ },
158
+ writable: true,
159
+ configurable: true,
160
+ })
161
+
162
+ Object.defineProperty(Response, 'json', {
163
+ value: function json(data?: unknown, init?: ResponseInit): Response {
164
+ const body = JSON.stringify(data)
165
+ if (body === undefined) {
166
+ throw new TypeError('The data is not JSON serializable')
167
+ }
168
+ const initHeaders = init?.headers
169
+ let headers: Record<string, string> | Headers
170
+ if (initHeaders) {
171
+ headers = new Headers(initHeaders)
172
+ if (!headers.has('content-type')) {
173
+ headers.set('content-type', 'application/json')
174
+ }
175
+ } else {
176
+ headers = { 'content-type': 'application/json' }
177
+ }
178
+ return new Response(body, {
179
+ status: init?.status ?? 200,
180
+ statusText: init?.statusText,
181
+ headers,
182
+ })
183
+ },
184
+ writable: true,
185
+ configurable: true,
186
+ })
@@ -0,0 +1,238 @@
1
+ import type { Context, Env, MiddlewareHandler } from '../../../acryl-router/src/index.ts'
2
+ import { getMimeType } from '../../../acryl-router/src/helpers/index.ts'
3
+ import type { Stats } from 'node:fs'
4
+ import { createReadStream, statSync, existsSync } from 'node:fs'
5
+ import { join } from 'node:path'
6
+ import { createStreamBody } from './utils/stream.ts'
7
+
8
+ export type ServeStaticOptions<E extends Env = Env> = {
9
+ /**
10
+ * Root path. Relative path is based on current working directory from which the app was started.
11
+ */
12
+ root?: string
13
+ path?: string
14
+ index?: string // default is 'index.html'
15
+ precompressed?: boolean
16
+ /** Allow percent signs in the routed request path. Defaults to false. */
17
+ allowPercentInPath?: boolean
18
+ rewriteRequestPath?: (path: string, c: Context<E>) => string
19
+ onFound?: (path: string, c: Context<E>) => void | Promise<void>
20
+ onNotFound?: (path: string, c: Context<E>) => void | Promise<void>
21
+ }
22
+
23
+ const COMPRESSIBLE_CONTENT_TYPE_REGEX =
24
+ /^\s*(?:text\/[^;\s]+|application\/(?:javascript|json|xml|xml-dtd|ecmascript|dart|postscript|rtf|tar|toml|vnd\.dart|vnd\.ms-fontobject|vnd\.ms-opentype|wasm|x-httpd-php|x-javascript|x-ns-proxy-autoconfig|x-sh|x-tar|x-virtualbox-hdd|x-virtualbox-ova|x-virtualbox-ovf|x-virtualbox-vbox|x-virtualbox-vdi|x-virtualbox-vhd|x-virtualbox-vmdk|x-www-form-urlencoded)|font\/(?:otf|ttf)|image\/(?:bmp|vnd\.adobe\.photoshop|vnd\.microsoft\.icon|vnd\.ms-dds|x-icon|x-ms-bmp)|message\/rfc822|model\/gltf-binary|x-shader\/x-fragment|x-shader\/x-vertex|[^;\s]+?\+(?:json|text|xml|yaml))(?:[;\s]|$)/i
25
+ const ENCODINGS = {
26
+ br: '.br',
27
+ zstd: '.zst',
28
+ gzip: '.gz',
29
+ } as const
30
+ const ENCODINGS_ORDERED_KEYS = Object.keys(ENCODINGS) as (keyof typeof ENCODINGS)[]
31
+
32
+ const getStats = (path: string) => {
33
+ let stats: Stats | undefined
34
+ try {
35
+ stats = statSync(path)
36
+ } catch {}
37
+ return stats
38
+ }
39
+
40
+ type ByteRangeSpec =
41
+ | { type: 'bounded'; start: number; end: number }
42
+ | { type: 'open-ended'; start: number }
43
+ | { type: 'suffix'; length: number }
44
+
45
+ type ByteRange = { start: number; end: number }
46
+
47
+ const BYTE_RANGE_PATTERN = /^(?:bytes=)?(?!-$)(\d*)-(\d*)$/
48
+
49
+ const parseByteRange = (range: string): ByteRangeSpec | undefined => {
50
+ const match = range.match(BYTE_RANGE_PATTERN)
51
+ if (!match) {
52
+ return undefined
53
+ }
54
+
55
+ const [, start, end] = match
56
+
57
+ if (start === '') {
58
+ return { type: 'suffix', length: Number(end) }
59
+ }
60
+
61
+ if (end === '') {
62
+ return { type: 'open-ended', start: Number(start) }
63
+ }
64
+
65
+ return { type: 'bounded', start: Number(start), end: Number(end) }
66
+ }
67
+
68
+ const resolveByteRange = (spec: ByteRangeSpec, size: number): ByteRange | undefined => {
69
+ if (size === 0) {
70
+ return undefined
71
+ }
72
+
73
+ if (spec.type === 'suffix') {
74
+ if (spec.length === 0) {
75
+ return undefined
76
+ }
77
+
78
+ return { start: Math.max(size - spec.length, 0), end: size - 1 }
79
+ }
80
+
81
+ const end = spec.type === 'bounded' ? Math.min(spec.end, size - 1) : size - 1
82
+ if (spec.start >= size || spec.start > end) {
83
+ return undefined
84
+ }
85
+
86
+ return { start: spec.start, end }
87
+ }
88
+
89
+ type Decoder = (str: string) => string
90
+
91
+ const tryDecode = (str: string, decoder: Decoder): string => {
92
+ try {
93
+ return decoder(str)
94
+ } catch {
95
+ // Decode only valid %xx sequences in chunks; keep undecodable parts as-is
96
+ return str.replace(/(?:%[0-9A-Fa-f]{2})+/g, (match) => {
97
+ try {
98
+ return decoder(match)
99
+ } catch {
100
+ return match
101
+ }
102
+ })
103
+ }
104
+ }
105
+
106
+ const tryDecodeURI = (str: string) => tryDecode(str, decodeURI)
107
+
108
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
109
+ export const serveStatic = <E extends Env = any>(
110
+ options: ServeStaticOptions<E> = { root: '' }
111
+ ): MiddlewareHandler<E> => {
112
+ const root = options.root || ''
113
+ const optionPath = options.path
114
+
115
+ if (root !== '' && !existsSync(root)) {
116
+ console.error(`serveStatic: root path '${root}' is not found, are you sure it's correct?`)
117
+ }
118
+
119
+ return async (c, next) => {
120
+ // Do nothing if Response is already set
121
+ if (c.finalized) {
122
+ return next()
123
+ }
124
+
125
+ if (c.req.method !== 'GET' && c.req.method !== 'HEAD') {
126
+ return next()
127
+ }
128
+
129
+ let filename: string
130
+
131
+ if (optionPath) {
132
+ filename = optionPath
133
+ } else {
134
+ try {
135
+ if (!options.allowPercentInPath && c.req.path.includes('%')) {
136
+ throw new Error()
137
+ }
138
+ filename = tryDecodeURI(c.req.path)
139
+ if (/(?:^|[\/\\])\.{1,2}(?:$|[\/\\])|[\/\\]{2,}|\\/.test(filename)) {
140
+ throw new Error()
141
+ }
142
+ } catch {
143
+ await options.onNotFound?.(c.req.path, c)
144
+ return next()
145
+ }
146
+ }
147
+
148
+ let path = join(
149
+ root,
150
+ !optionPath && options.rewriteRequestPath ? options.rewriteRequestPath(filename, c) : filename
151
+ )
152
+
153
+ let stats = getStats(path)
154
+
155
+ if (stats && stats.isDirectory()) {
156
+ const indexFile = options.index ?? 'index.html'
157
+ path = join(path, indexFile)
158
+ stats = getStats(path)
159
+ }
160
+
161
+ if (!stats) {
162
+ await options.onNotFound?.(path, c)
163
+ return next()
164
+ }
165
+
166
+ const mimeType = getMimeType(path)
167
+ c.header('Content-Type', mimeType || 'application/octet-stream')
168
+
169
+ if (
170
+ options.precompressed &&
171
+ (!mimeType ||
172
+ mimeType === 'application/octet-stream' ||
173
+ COMPRESSIBLE_CONTENT_TYPE_REGEX.test(mimeType))
174
+ ) {
175
+ const acceptEncodingSet = new Set(
176
+ c.req
177
+ .header('Accept-Encoding')
178
+ ?.split(',')
179
+ .map((encoding) => encoding.trim())
180
+ )
181
+
182
+ for (const encoding of ENCODINGS_ORDERED_KEYS) {
183
+ if (!acceptEncodingSet.has(encoding)) {
184
+ continue
185
+ }
186
+ const precompressedStats = getStats(path + ENCODINGS[encoding])
187
+ if (precompressedStats) {
188
+ c.header('Content-Encoding', encoding)
189
+ c.header('Vary', 'Accept-Encoding', { append: true })
190
+ stats = precompressedStats
191
+ path = path + ENCODINGS[encoding]
192
+ break
193
+ }
194
+ }
195
+ }
196
+
197
+ let result
198
+ const size = stats.size
199
+ const range = c.req.header('range') || ''
200
+ c.header('Last-Modified', stats.mtime.toUTCString())
201
+
202
+ if (c.req.method === 'HEAD') {
203
+ c.header('Content-Length', size.toString())
204
+ c.status(200)
205
+ result = c.body(null)
206
+ } else if (!range) {
207
+ c.header('Content-Length', size.toString())
208
+ result = c.body(createStreamBody(createReadStream(path)), 200)
209
+ } else {
210
+ c.header('Accept-Ranges', 'bytes')
211
+
212
+ // Preserve the existing behavior of serving the whole representation for
213
+ // a malformed range.
214
+ const rangeSpec: ByteRangeSpec = parseByteRange(range) ?? {
215
+ type: 'open-ended',
216
+ start: 0,
217
+ }
218
+ const resolvedRange = resolveByteRange(rangeSpec, size)
219
+
220
+ if (!resolvedRange) {
221
+ c.header('Content-Range', `bytes */${size}`)
222
+ result = c.body(null, 416)
223
+ } else {
224
+ const { start, end } = resolvedRange
225
+ const chunkSize = end - start + 1
226
+ const stream = createReadStream(path, { start, end })
227
+
228
+ c.header('Content-Length', chunkSize.toString())
229
+ c.header('Content-Range', `bytes ${start}-${end}/${size}`)
230
+
231
+ result = c.body(createStreamBody(stream), 206)
232
+ }
233
+ }
234
+
235
+ await options.onFound?.(path, c)
236
+ return result
237
+ }
238
+ }
@@ -0,0 +1,36 @@
1
+ import { createServer as createServerHTTP } from 'node:http'
2
+ import type { AddressInfo } from 'node:net'
3
+ import { getRequestListener } from './listener.ts'
4
+ import type { Options, ServerType } from './types.ts'
5
+ import { setupWebSocket } from './websocket.ts'
6
+
7
+ export const createAdaptorServer = (options: Options): ServerType => {
8
+ const fetchCallback = options.fetch
9
+ const requestListener = getRequestListener(fetchCallback, {
10
+ hostname: options.hostname,
11
+ overrideGlobalObjects: options.overrideGlobalObjects,
12
+ autoCleanupIncoming: options.autoCleanupIncoming,
13
+ })
14
+ // ts will complain about createServerHTTP and createServerHTTP2 not being callable, which works just fine
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
+ const createServer: any = options.createServer || createServerHTTP
17
+ const server: ServerType = createServer(options.serverOptions || {}, requestListener)
18
+ if (options.websocket && options.websocket.server) {
19
+ if (options.websocket.server.options.noServer !== true)
20
+ throw new Error('WebSocket server must be created with { noServer: true } option')
21
+ setupWebSocket({ server, fetchCallback, wss: options.websocket.server })
22
+ }
23
+ return server
24
+ }
25
+
26
+ export const serve = (
27
+ options: Options,
28
+ listeningListener?: (info: AddressInfo) => void
29
+ ): ServerType => {
30
+ const server = createAdaptorServer(options)
31
+ server.listen(options?.port ?? 3000, options.hostname, () => {
32
+ const serverInfo = server.address() as AddressInfo
33
+ listeningListener && listeningListener(serverInfo)
34
+ })
35
+ return server
36
+ }
@@ -0,0 +1,82 @@
1
+ import type {
2
+ createServer,
3
+ IncomingMessage,
4
+ Server,
5
+ ServerOptions as HttpServerOptions,
6
+ ServerResponse as HttpServerResponse,
7
+ } from 'node:http'
8
+ import type {
9
+ createSecureServer as createSecureHttp2Server,
10
+ createServer as createHttp2Server,
11
+ Http2ServerRequest,
12
+ Http2Server,
13
+ Http2ServerResponse,
14
+ Http2SecureServer,
15
+ SecureServerOptions as SecureHttp2ServerOptions,
16
+ ServerOptions as Http2ServerOptions,
17
+ } from 'node:http2'
18
+ import type {
19
+ createServer as createHttpsServer,
20
+ ServerOptions as HttpsServerOptions,
21
+ } from 'node:https'
22
+ import type { WebSocketServerLike } from './websocket-types.ts'
23
+
24
+ export type HttpBindings = {
25
+ incoming: IncomingMessage
26
+ outgoing: HttpServerResponse
27
+ }
28
+
29
+ export type Http2Bindings = {
30
+ incoming: Http2ServerRequest
31
+ outgoing: Http2ServerResponse
32
+ }
33
+
34
+ export type FetchCallback = (
35
+ request: Request,
36
+ env: HttpBindings | Http2Bindings
37
+ ) => Promise<unknown> | unknown
38
+
39
+ export type NextHandlerOption = {
40
+ fetch: FetchCallback
41
+ }
42
+
43
+ export type ServerType = Server | Http2Server | Http2SecureServer
44
+
45
+ type createHttpOptions = {
46
+ serverOptions?: HttpServerOptions
47
+ createServer?: typeof createServer
48
+ }
49
+
50
+ type createHttpsOptions = {
51
+ serverOptions?: HttpsServerOptions
52
+ createServer?: typeof createHttpsServer
53
+ }
54
+
55
+ type createHttp2Options = {
56
+ serverOptions?: Http2ServerOptions
57
+ createServer?: typeof createHttp2Server
58
+ }
59
+
60
+ type createSecureHttp2Options = {
61
+ serverOptions?: SecureHttp2ServerOptions
62
+ createServer?: typeof createSecureHttp2Server
63
+ }
64
+
65
+ export type ServerOptions =
66
+ | createHttpOptions
67
+ | createHttpsOptions
68
+ | createHttp2Options
69
+ | createSecureHttp2Options
70
+
71
+ export type Options = {
72
+ fetch: FetchCallback
73
+ overrideGlobalObjects?: boolean
74
+ autoCleanupIncoming?: boolean
75
+ port?: number
76
+ hostname?: string
77
+ websocket?: {
78
+ server: WebSocketServerLike
79
+ }
80
+ } & ServerOptions
81
+
82
+ export type CustomErrorHandler = (err: unknown) => void | Response | Promise<void | Response>
@@ -0,0 +1,42 @@
1
+ import { RequestError } from './error.ts'
2
+
3
+ // Fast-path character validation for request URLs.
4
+ // Matches: ! # $ & ' ( ) * + , - . / 0-9 : ; = ? @ A-Z [ ] _ a-z ~
5
+ // Rejects: control chars, space, " % < > \ ^ ` { | } DEL, non-ASCII → fallback to new URL()
6
+ const reValidRequestUrl = /^\/[!#$&-;=?-\[\]_a-z~]*$/
7
+
8
+ // Dot segments: /. or /.. followed by / ? # or end-of-string
9
+ const reDotSegment = /\/\.\.?(?:[/?#]|$)/
10
+
11
+ // Host validation: a-z 0-9 . - _ with optional port 1000-59999
12
+ const reValidHost = /^[a-z0-9._-]+(?::(?:[1-5]\d{3,4}|[6-9]\d{3}))?$/
13
+
14
+ export const buildUrl = (scheme: string, host: string, incomingUrl: string) => {
15
+ const url = `${scheme}://${host}${incomingUrl}`
16
+
17
+ if (!reValidHost.test(host)) {
18
+ const urlObj = new URL(url)
19
+
20
+ // if suspicious, check by host. host header sometimes contains port.
21
+ if (
22
+ urlObj.hostname.length !== host.length &&
23
+ urlObj.hostname !== (host.includes(':') ? host.replace(/:\d+$/, '') : host).toLowerCase()
24
+ ) {
25
+ throw new RequestError('Invalid host header')
26
+ }
27
+ return urlObj.href
28
+ } else if (incomingUrl.length === 0) {
29
+ return url + '/'
30
+ } else {
31
+ if (incomingUrl.charCodeAt(0) !== 0x2f) {
32
+ // '/'
33
+ throw new RequestError('Invalid URL')
34
+ }
35
+
36
+ if (!reValidRequestUrl.test(incomingUrl) || reDotSegment.test(incomingUrl)) {
37
+ return new URL(url).href
38
+ }
39
+
40
+ return url
41
+ }
42
+ }
@@ -0,0 +1 @@
1
+ export const X_ALREADY_SENT = 'x-acryl-already-sent'
@@ -0,0 +1,4 @@
1
+ import { X_ALREADY_SENT } from './response/constants.ts'
2
+ export const RESPONSE_ALREADY_SENT = new Response(null, {
3
+ headers: { [X_ALREADY_SENT]: 'true' },
4
+ })
@@ -0,0 +1,88 @@
1
+ import type { ReadStream } from 'node:fs'
2
+ import { versions } from 'node:process'
3
+ import { Readable } from 'node:stream'
4
+
5
+ // In Node.js versions that do not have the following PR applied, using Readable.toWeb may cause unexpected exceptions.
6
+ // https://github.com/nodejs/node/pull/54206
7
+ const pr54206Applied = () => {
8
+ const [major, minor] = versions.node.split('.').map((component) => parseInt(component))
9
+ return major >= 23 || (major === 22 && minor >= 7) || (major === 20 && minor >= 18)
10
+ }
11
+ const useReadableToWeb = pr54206Applied()
12
+
13
+ export const createStreamBody = (
14
+ stream: ReadStream,
15
+ useNativeReadableToWeb = useReadableToWeb
16
+ ): ReadableStream<Uint8Array> => {
17
+ if (useNativeReadableToWeb) {
18
+ return Readable.toWeb(stream) as ReadableStream<Uint8Array>
19
+ }
20
+
21
+ let controller: ReadableStreamDefaultController<Uint8Array> | undefined
22
+ let settled = false
23
+
24
+ const cleanup = () => {
25
+ stream.off('data', onData)
26
+ stream.off('error', onError)
27
+ stream.off('end', onTerminate)
28
+ stream.off('close', onTerminate)
29
+ }
30
+
31
+ const settle = (callback?: () => void) => {
32
+ if (settled) {
33
+ return
34
+ }
35
+ settled = true
36
+ cleanup()
37
+ callback?.()
38
+ }
39
+
40
+ const onData = (chunk: Buffer | string) => {
41
+ if (settled || !controller) {
42
+ return
43
+ }
44
+ // createReadStream is called without `encoding`, so chunks are always Buffer.
45
+ controller.enqueue(chunk as Buffer)
46
+ if ((controller.desiredSize ?? 0) <= 0) {
47
+ stream.pause()
48
+ }
49
+ }
50
+
51
+ const onError = (error: Error) => {
52
+ settle(() => {
53
+ controller?.error(error)
54
+ })
55
+ }
56
+
57
+ const onTerminate = () => {
58
+ settle(() => {
59
+ controller?.close()
60
+ })
61
+ }
62
+
63
+ return new ReadableStream<Uint8Array>({
64
+ start(streamController) {
65
+ controller = streamController
66
+ stream.on('data', onData)
67
+ stream.on('error', onError)
68
+ stream.on('end', onTerminate)
69
+ stream.on('close', onTerminate)
70
+ stream.pause()
71
+ },
72
+
73
+ pull() {
74
+ if (!settled) {
75
+ stream.resume()
76
+ }
77
+ },
78
+
79
+ cancel() {
80
+ settle()
81
+ // Suppress late `error` emitted between destroy() and the terminal `close`.
82
+ const ignoreError = () => {}
83
+ stream.on('error', ignoreError)
84
+ stream.once('close', () => stream.off('error', ignoreError))
85
+ stream.destroy()
86
+ },
87
+ })
88
+ }