acrylblends 0.3.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 (315) hide show
  1. package/dist/index.d.ts +6 -1
  2. package/dist/index.js +28 -3
  3. package/dist/index.js.map +1 -1
  4. package/package.json +1 -1
  5. package/plugins/acryl-inspect/plugin.json +2 -1
  6. package/plugins/acryl-inspect/src/report.ts +16 -13
  7. package/plugins/acryl-inspect/src/service.ts +9 -7
  8. package/plugins/acryl-inspect/tests/report.spec.ts +7 -8
  9. package/plugins/acryl-inspect/tests/service.spec.ts +11 -10
  10. package/plugins/acryl-inspect/upstream.json +2 -2
  11. package/plugins/acryl-node-server/LICENSE +26 -0
  12. package/plugins/acryl-node-server/plugin.json +2 -1
  13. package/plugins/acryl-node-server/src/adapter/conninfo.ts +23 -0
  14. package/plugins/acryl-node-server/src/adapter/early-hints.ts +55 -0
  15. package/plugins/acryl-node-server/src/adapter/error.ts +11 -0
  16. package/plugins/acryl-node-server/src/adapter/headers.ts +249 -0
  17. package/plugins/acryl-node-server/src/adapter/index.ts +6 -0
  18. package/plugins/acryl-node-server/src/adapter/listener.ts +540 -0
  19. package/plugins/acryl-node-server/src/adapter/request.ts +797 -0
  20. package/plugins/acryl-node-server/src/adapter/response.ts +186 -0
  21. package/plugins/acryl-node-server/src/adapter/serve-static.ts +238 -0
  22. package/plugins/acryl-node-server/src/adapter/server.ts +36 -0
  23. package/plugins/acryl-node-server/src/adapter/types.ts +82 -0
  24. package/plugins/acryl-node-server/src/adapter/url.ts +42 -0
  25. package/plugins/acryl-node-server/src/adapter/utils/response/constants.ts +1 -0
  26. package/plugins/acryl-node-server/src/adapter/utils/response.ts +4 -0
  27. package/plugins/acryl-node-server/src/adapter/utils/stream.ts +88 -0
  28. package/plugins/acryl-node-server/src/adapter/utils.ts +96 -0
  29. package/plugins/acryl-node-server/src/adapter/websocket-types.ts +45 -0
  30. package/plugins/acryl-node-server/src/adapter/websocket.ts +375 -0
  31. package/plugins/acryl-node-server/src/index.ts +19 -6
  32. package/plugins/acryl-node-server/tests/adapter/app.ts +76 -0
  33. package/plugins/acryl-node-server/tests/adapter/assets/.static/plain.txt +1 -0
  34. package/plugins/acryl-node-server/tests/adapter/assets/favicon.ico +0 -0
  35. package/plugins/acryl-node-server/tests/adapter/assets/secret.txt +1 -0
  36. package/plugins/acryl-node-server/tests/adapter/assets/static/acryl.html +1 -0
  37. package/plugins/acryl-node-server/tests/adapter/assets/static/admin/secret.txt +1 -0
  38. package/plugins/acryl-node-server/tests/adapter/assets/static/data.json +5 -0
  39. package/plugins/acryl-node-server/tests/adapter/assets/static/extensionless +1 -0
  40. package/plugins/acryl-node-server/tests/adapter/assets/static/foo..bar.txt +0 -0
  41. package/plugins/acryl-node-server/tests/adapter/assets/static/index.html +1 -0
  42. package/plugins/acryl-node-server/tests/adapter/assets/static/plain.txt +1 -0
  43. package/plugins/acryl-node-server/tests/adapter/assets/static-with-context-path/plain.txt +1 -0
  44. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.bin +1 -0
  45. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.bin.br +1 -0
  46. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt +1 -0
  47. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt.br +1 -0
  48. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt.zst +1 -0
  49. package/plugins/acryl-node-server/tests/adapter/conninfo.test.ts +34 -0
  50. package/plugins/acryl-node-server/tests/adapter/content-length.test.ts +248 -0
  51. package/plugins/acryl-node-server/tests/adapter/early-hints.test.ts +457 -0
  52. package/plugins/acryl-node-server/tests/adapter/fixtures/keys/agent1-cert.pem +23 -0
  53. package/plugins/acryl-node-server/tests/adapter/fixtures/keys/agent1-key.pem +27 -0
  54. package/plugins/acryl-node-server/tests/adapter/headers.test.ts +252 -0
  55. package/plugins/acryl-node-server/tests/adapter/helpers/request.ts +190 -0
  56. package/plugins/acryl-node-server/tests/adapter/listener.test.ts +727 -0
  57. package/plugins/acryl-node-server/tests/adapter/request-client-disconnect.test.ts +189 -0
  58. package/plugins/acryl-node-server/tests/adapter/request.test.ts +1230 -0
  59. package/plugins/acryl-node-server/tests/adapter/response.test.ts +285 -0
  60. package/plugins/acryl-node-server/tests/adapter/serve-static.test.ts +849 -0
  61. package/plugins/acryl-node-server/tests/adapter/server-socket.test.ts +790 -0
  62. package/plugins/acryl-node-server/tests/adapter/server.test.ts +1302 -0
  63. package/plugins/acryl-node-server/tests/adapter/setup.ts +16 -0
  64. package/plugins/acryl-node-server/tests/adapter/url.test.ts +76 -0
  65. package/plugins/acryl-node-server/tests/adapter/utils/response.test.ts +23 -0
  66. package/plugins/acryl-node-server/tests/adapter/utils/stream.test.ts +75 -0
  67. package/plugins/acryl-node-server/tests/adapter/utils.test.ts +235 -0
  68. package/plugins/acryl-node-server/tests/adapter/websocket.test.ts +260 -0
  69. package/plugins/acryl-node-server/tests/server.spec.ts +44 -8
  70. package/plugins/acryl-node-server/upstream.json +3 -3
  71. package/plugins/acryl-router/LICENSE +26 -0
  72. package/plugins/acryl-router/plugin.json +2 -1
  73. package/plugins/acryl-router/src/client.ts +2 -0
  74. package/plugins/acryl-router/src/connection.ts +42 -0
  75. package/plugins/acryl-router/src/core/app-base.ts +544 -0
  76. package/plugins/acryl-router/src/core/app.test.ts +3833 -0
  77. package/plugins/acryl-router/src/core/app.ts +34 -0
  78. package/plugins/acryl-router/src/core/client/client.test.ts +2313 -0
  79. package/plugins/acryl-router/src/core/client/client.ts +249 -0
  80. package/plugins/acryl-router/src/core/client/fetch-result-please.ts +95 -0
  81. package/plugins/acryl-router/src/core/client/index.ts +17 -0
  82. package/plugins/acryl-router/src/core/client/types.test.ts +149 -0
  83. package/plugins/acryl-router/src/core/client/types.ts +393 -0
  84. package/plugins/acryl-router/src/core/client/utils.test.ts +405 -0
  85. package/plugins/acryl-router/src/core/client/utils.ts +117 -0
  86. package/plugins/acryl-router/src/core/compose.test.ts +751 -0
  87. package/plugins/acryl-router/src/core/compose.ts +73 -0
  88. package/plugins/acryl-router/src/core/context.test.ts +629 -0
  89. package/plugins/acryl-router/src/core/context.ts +781 -0
  90. package/plugins/acryl-router/src/core/helper/accepts/accepts.test.ts +344 -0
  91. package/plugins/acryl-router/src/core/helper/accepts/accepts.ts +96 -0
  92. package/plugins/acryl-router/src/core/helper/accepts/index.ts +6 -0
  93. package/plugins/acryl-router/src/core/helper/adapter/index.test.ts +55 -0
  94. package/plugins/acryl-router/src/core/helper/adapter/index.ts +90 -0
  95. package/plugins/acryl-router/src/core/helper/conninfo/index.ts +6 -0
  96. package/plugins/acryl-router/src/core/helper/conninfo/types.ts +45 -0
  97. package/plugins/acryl-router/src/core/helper/cookie/index.test.ts +510 -0
  98. package/plugins/acryl-router/src/core/helper/cookie/index.ts +146 -0
  99. package/plugins/acryl-router/src/core/helper/factory/index.test.ts +434 -0
  100. package/plugins/acryl-router/src/core/helper/factory/index.ts +375 -0
  101. package/plugins/acryl-router/src/core/helper/html/index.test.ts +78 -0
  102. package/plugins/acryl-router/src/core/helper/html/index.ts +57 -0
  103. package/plugins/acryl-router/src/core/helper/route/index.test.ts +259 -0
  104. package/plugins/acryl-router/src/core/helper/route/index.ts +144 -0
  105. package/plugins/acryl-router/src/core/helper/streaming/index.ts +9 -0
  106. package/plugins/acryl-router/src/core/helper/streaming/sse.ts +101 -0
  107. package/plugins/acryl-router/src/core/helper/streaming/stream.test.ts +128 -0
  108. package/plugins/acryl-router/src/core/helper/streaming/stream.ts +45 -0
  109. package/plugins/acryl-router/src/core/helper/streaming/text.test.ts +34 -0
  110. package/plugins/acryl-router/src/core/helper/streaming/text.ts +15 -0
  111. package/plugins/acryl-router/src/core/helper/streaming/utils.ts +11 -0
  112. package/plugins/acryl-router/src/core/helper/testing/index.test.ts +42 -0
  113. package/plugins/acryl-router/src/core/helper/testing/index.ts +28 -0
  114. package/plugins/acryl-router/src/core/helper/websocket/index.test.ts +124 -0
  115. package/plugins/acryl-router/src/core/helper/websocket/index.ts +140 -0
  116. package/plugins/acryl-router/src/core/http-exception.test.ts +45 -0
  117. package/plugins/acryl-router/src/core/http-exception.ts +77 -0
  118. package/plugins/acryl-router/src/core/index.ts +53 -0
  119. package/plugins/acryl-router/src/core/middleware/basic-auth/index.test.ts +434 -0
  120. package/plugins/acryl-router/src/core/middleware/basic-auth/index.ts +152 -0
  121. package/plugins/acryl-router/src/core/middleware/bearer-auth/index.test.ts +1018 -0
  122. package/plugins/acryl-router/src/core/middleware/bearer-auth/index.ts +220 -0
  123. package/plugins/acryl-router/src/core/middleware/body-limit/index.test.ts +273 -0
  124. package/plugins/acryl-router/src/core/middleware/body-limit/index.ts +110 -0
  125. package/plugins/acryl-router/src/core/middleware/combine/index.test.ts +355 -0
  126. package/plugins/acryl-router/src/core/middleware/combine/index.ts +171 -0
  127. package/plugins/acryl-router/src/core/middleware/compress/index.test.ts +535 -0
  128. package/plugins/acryl-router/src/core/middleware/compress/index.ts +142 -0
  129. package/plugins/acryl-router/src/core/middleware/context-storage/index.test.ts +43 -0
  130. package/plugins/acryl-router/src/core/middleware/context-storage/index.ts +58 -0
  131. package/plugins/acryl-router/src/core/middleware/cors/index.test.ts +532 -0
  132. package/plugins/acryl-router/src/core/middleware/cors/index.ts +163 -0
  133. package/plugins/acryl-router/src/core/middleware/csrf/index.test.ts +534 -0
  134. package/plugins/acryl-router/src/core/middleware/csrf/index.ts +150 -0
  135. package/plugins/acryl-router/src/core/middleware/etag/digest.test.ts +21 -0
  136. package/plugins/acryl-router/src/core/middleware/etag/digest.ts +95 -0
  137. package/plugins/acryl-router/src/core/middleware/etag/index.test.ts +503 -0
  138. package/plugins/acryl-router/src/core/middleware/etag/index.ts +135 -0
  139. package/plugins/acryl-router/src/core/middleware/ip-restriction/index.test.ts +217 -0
  140. package/plugins/acryl-router/src/core/middleware/ip-restriction/index.ts +278 -0
  141. package/plugins/acryl-router/src/core/middleware/jwk/index.test.ts +1135 -0
  142. package/plugins/acryl-router/src/core/middleware/jwk/index.ts +1 -0
  143. package/plugins/acryl-router/src/core/middleware/jwk/jwk.ts +195 -0
  144. package/plugins/acryl-router/src/core/middleware/jwk/keys.test.json +48 -0
  145. package/plugins/acryl-router/src/core/middleware/jwt/index.test.ts +802 -0
  146. package/plugins/acryl-router/src/core/middleware/jwt/index.ts +9 -0
  147. package/plugins/acryl-router/src/core/middleware/jwt/jwt.ts +189 -0
  148. package/plugins/acryl-router/src/core/middleware/language/index.test.ts +552 -0
  149. package/plugins/acryl-router/src/core/middleware/language/index.ts +12 -0
  150. package/plugins/acryl-router/src/core/middleware/language/language.ts +314 -0
  151. package/plugins/acryl-router/src/core/middleware/logger/index.test.ts +204 -0
  152. package/plugins/acryl-router/src/core/middleware/logger/index.ts +95 -0
  153. package/plugins/acryl-router/src/core/middleware/method-not-allowed/index.test.ts +292 -0
  154. package/plugins/acryl-router/src/core/middleware/method-not-allowed/index.ts +141 -0
  155. package/plugins/acryl-router/src/core/middleware/method-override/index.test.ts +221 -0
  156. package/plugins/acryl-router/src/core/middleware/method-override/index.ts +147 -0
  157. package/plugins/acryl-router/src/core/middleware/powered-by/index.test.ts +37 -0
  158. package/plugins/acryl-router/src/core/middleware/powered-by/index.ts +35 -0
  159. package/plugins/acryl-router/src/core/middleware/pretty-json/index.test.ts +218 -0
  160. package/plugins/acryl-router/src/core/middleware/pretty-json/index.ts +67 -0
  161. package/plugins/acryl-router/src/core/middleware/request-id/index.test.ts +168 -0
  162. package/plugins/acryl-router/src/core/middleware/request-id/index.ts +7 -0
  163. package/plugins/acryl-router/src/core/middleware/request-id/request-id.ts +59 -0
  164. package/plugins/acryl-router/src/core/middleware/secure-headers/index.test.ts +671 -0
  165. package/plugins/acryl-router/src/core/middleware/secure-headers/index.ts +8 -0
  166. package/plugins/acryl-router/src/core/middleware/secure-headers/permissions-policy.ts +101 -0
  167. package/plugins/acryl-router/src/core/middleware/secure-headers/secure-headers.ts +340 -0
  168. package/plugins/acryl-router/src/core/middleware/serve-static/index.test.ts +450 -0
  169. package/plugins/acryl-router/src/core/middleware/serve-static/index.ts +135 -0
  170. package/plugins/acryl-router/src/core/middleware/serve-static/path.test.ts +50 -0
  171. package/plugins/acryl-router/src/core/middleware/serve-static/path.ts +25 -0
  172. package/plugins/acryl-router/src/core/middleware/timeout/index.test.ts +70 -0
  173. package/plugins/acryl-router/src/core/middleware/timeout/index.ts +58 -0
  174. package/plugins/acryl-router/src/core/middleware/timing/index.test.ts +310 -0
  175. package/plugins/acryl-router/src/core/middleware/timing/index.ts +8 -0
  176. package/plugins/acryl-router/src/core/middleware/timing/timing.ts +256 -0
  177. package/plugins/acryl-router/src/core/middleware/trailing-slash/index.test.ts +381 -0
  178. package/plugins/acryl-router/src/core/middleware/trailing-slash/index.ts +156 -0
  179. package/plugins/acryl-router/src/core/request/constants.ts +1 -0
  180. package/plugins/acryl-router/src/core/request.test.ts +751 -0
  181. package/plugins/acryl-router/src/core/request.ts +502 -0
  182. package/plugins/acryl-router/src/core/router/common.case.test.ts +1132 -0
  183. package/plugins/acryl-router/src/core/router/linear-router/index.ts +6 -0
  184. package/plugins/acryl-router/src/core/router/linear-router/router.test.ts +80 -0
  185. package/plugins/acryl-router/src/core/router/linear-router/router.ts +152 -0
  186. package/plugins/acryl-router/src/core/router/pattern-router/index.ts +6 -0
  187. package/plugins/acryl-router/src/core/router/pattern-router/router.test.ts +46 -0
  188. package/plugins/acryl-router/src/core/router/pattern-router/router.ts +54 -0
  189. package/plugins/acryl-router/src/core/router/reg-exp-router/index.ts +7 -0
  190. package/plugins/acryl-router/src/core/router/reg-exp-router/matcher.ts +33 -0
  191. package/plugins/acryl-router/src/core/router/reg-exp-router/node.ts +170 -0
  192. package/plugins/acryl-router/src/core/router/reg-exp-router/prepared-router.test.ts +154 -0
  193. package/plugins/acryl-router/src/core/router/reg-exp-router/prepared-router.ts +165 -0
  194. package/plugins/acryl-router/src/core/router/reg-exp-router/router.test.ts +265 -0
  195. package/plugins/acryl-router/src/core/router/reg-exp-router/router.ts +174 -0
  196. package/plugins/acryl-router/src/core/router/reg-exp-router/trie.ts +84 -0
  197. package/plugins/acryl-router/src/core/router/smart-router/index.ts +6 -0
  198. package/plugins/acryl-router/src/core/router/smart-router/router.test.ts +13 -0
  199. package/plugins/acryl-router/src/core/router/smart-router/router.ts +70 -0
  200. package/plugins/acryl-router/src/core/router/trie-router/index.ts +6 -0
  201. package/plugins/acryl-router/src/core/router/trie-router/node.test.ts +896 -0
  202. package/plugins/acryl-router/src/core/router/trie-router/node.ts +221 -0
  203. package/plugins/acryl-router/src/core/router/trie-router/router.test.ts +8 -0
  204. package/plugins/acryl-router/src/core/router/trie-router/router.ts +18 -0
  205. package/plugins/acryl-router/src/core/router/utils.ts +1 -0
  206. package/plugins/acryl-router/src/core/router.ts +103 -0
  207. package/plugins/acryl-router/src/core/types.test.ts +4134 -0
  208. package/plugins/acryl-router/src/core/types.ts +2782 -0
  209. package/plugins/acryl-router/src/core/utils/accept.test.ts +299 -0
  210. package/plugins/acryl-router/src/core/utils/accept.ts +268 -0
  211. package/plugins/acryl-router/src/core/utils/basic-auth.test.ts +56 -0
  212. package/plugins/acryl-router/src/core/utils/basic-auth.ts +26 -0
  213. package/plugins/acryl-router/src/core/utils/body.test.ts +496 -0
  214. package/plugins/acryl-router/src/core/utils/body.ts +263 -0
  215. package/plugins/acryl-router/src/core/utils/buffer.test.ts +147 -0
  216. package/plugins/acryl-router/src/core/utils/buffer.ts +117 -0
  217. package/plugins/acryl-router/src/core/utils/color.test.ts +23 -0
  218. package/plugins/acryl-router/src/core/utils/color.ts +56 -0
  219. package/plugins/acryl-router/src/core/utils/compress.ts +10 -0
  220. package/plugins/acryl-router/src/core/utils/concurrent.test.ts +69 -0
  221. package/plugins/acryl-router/src/core/utils/concurrent.ts +55 -0
  222. package/plugins/acryl-router/src/core/utils/constants.ts +4 -0
  223. package/plugins/acryl-router/src/core/utils/cookie.test.ts +460 -0
  224. package/plugins/acryl-router/src/core/utils/cookie.ts +281 -0
  225. package/plugins/acryl-router/src/core/utils/crypto.test.ts +43 -0
  226. package/plugins/acryl-router/src/core/utils/crypto.ts +58 -0
  227. package/plugins/acryl-router/src/core/utils/encode.test.ts +61 -0
  228. package/plugins/acryl-router/src/core/utils/encode.ts +34 -0
  229. package/plugins/acryl-router/src/core/utils/filepath.test.ts +78 -0
  230. package/plugins/acryl-router/src/core/utils/filepath.ts +60 -0
  231. package/plugins/acryl-router/src/core/utils/handler.ts +15 -0
  232. package/plugins/acryl-router/src/core/utils/headers.ts +355 -0
  233. package/plugins/acryl-router/src/core/utils/html.test.ts +16 -0
  234. package/plugins/acryl-router/src/core/utils/html.ts +182 -0
  235. package/plugins/acryl-router/src/core/utils/http-status.ts +72 -0
  236. package/plugins/acryl-router/src/core/utils/ipaddr.test.ts +149 -0
  237. package/plugins/acryl-router/src/core/utils/ipaddr.ts +374 -0
  238. package/plugins/acryl-router/src/core/utils/jwt/index.ts +7 -0
  239. package/plugins/acryl-router/src/core/utils/jwt/jwa.test.ts +86 -0
  240. package/plugins/acryl-router/src/core/utils/jwt/jwa.ts +38 -0
  241. package/plugins/acryl-router/src/core/utils/jwt/jws.ts +235 -0
  242. package/plugins/acryl-router/src/core/utils/jwt/jwt.test.ts +1788 -0
  243. package/plugins/acryl-router/src/core/utils/jwt/jwt.ts +292 -0
  244. package/plugins/acryl-router/src/core/utils/jwt/types.ts +157 -0
  245. package/plugins/acryl-router/src/core/utils/jwt/utf8.ts +7 -0
  246. package/plugins/acryl-router/src/core/utils/mime.test.ts +64 -0
  247. package/plugins/acryl-router/src/core/utils/mime.ts +94 -0
  248. package/plugins/acryl-router/src/core/utils/stream.test.ts +212 -0
  249. package/plugins/acryl-router/src/core/utils/stream.ts +107 -0
  250. package/plugins/acryl-router/src/core/utils/types.test.ts +295 -0
  251. package/plugins/acryl-router/src/core/utils/types.ts +123 -0
  252. package/plugins/acryl-router/src/core/utils/url.test.ts +384 -0
  253. package/plugins/acryl-router/src/core/utils/url.ts +337 -0
  254. package/plugins/acryl-router/src/core/validator/index.ts +8 -0
  255. package/plugins/acryl-router/src/core/validator/utils.test.ts +82 -0
  256. package/plugins/acryl-router/src/core/validator/utils.ts +69 -0
  257. package/plugins/acryl-router/src/core/validator/validator.test.ts +1488 -0
  258. package/plugins/acryl-router/src/core/validator/validator.ts +185 -0
  259. package/plugins/acryl-router/src/helpers/index.ts +13 -3
  260. package/plugins/acryl-router/src/helpers/schema-validator.ts +35 -0
  261. package/plugins/acryl-router/src/index.ts +6 -6
  262. package/plugins/acryl-router/src/middleware/index.ts +25 -6
  263. package/plugins/acryl-router/src/middleware/loopback-only.ts +19 -13
  264. package/plugins/acryl-router/src/service.ts +256 -64
  265. package/plugins/acryl-router/tests/docs-examples.spec.ts +152 -0
  266. package/plugins/acryl-router/tests/live-install.spec.ts +2 -2
  267. package/plugins/acryl-router/tests/loader.spec.ts +3 -2
  268. package/plugins/acryl-router/tests/service.spec.ts +264 -66
  269. package/plugins/acryl-router/tests/shadow-state.spec.ts +79 -0
  270. package/plugins/acryl-router/upstream.json +2 -2
  271. package/plugins/acryl-router-http/plugin.json +2 -1
  272. package/plugins/acryl-router-http/src/bridge.ts +3 -1
  273. package/plugins/acryl-router-http/tests/adapter.spec.ts +16 -12
  274. package/plugins/acryl-router-http/tests/surfaces.spec.ts +12 -10
  275. package/plugins/acryl-router-http/upstream.json +3 -3
  276. package/plugins/acryl-state/NOTICE +30 -0
  277. package/plugins/acryl-state/plugin.json +1 -1
  278. package/plugins/acryl-state/src/cell.ts +1 -1
  279. package/plugins/acryl-state/src/service.ts +33 -15
  280. package/plugins/acryl-state/upstream.json +6 -4
  281. package/plugins/builder/script.ts +4 -7
  282. package/plugins/extensions/index.ts +2 -2
  283. package/plugins/extensions/resolve.ts +28 -3
  284. package/plugins/openai/prompt.ts +9 -7
  285. package/plugins/tsconfig.json +5 -4
  286. package/templates/agent/src/routes.ts +37 -27
  287. package/templates/agent/test/model.test.ts +2 -1
  288. package/templates/agent/tsconfig.json +8 -5
  289. package/templates/blank/src/routes.ts +6 -4
  290. package/templates/blank/tsconfig.json +2 -4
  291. package/templates/todo/src/page.ts +1 -1
  292. package/templates/todo/src/todo.ts +56 -29
  293. package/templates/todo/tsconfig.json +2 -4
  294. package/templates/todo/upstream.json +2 -2
  295. package/plugins/acryl-router/src/compose.ts +0 -21
  296. package/plugins/acryl-router/src/context.ts +0 -59
  297. package/plugins/acryl-router/src/helpers/read-body.ts +0 -59
  298. package/plugins/acryl-router/src/helpers/sse.ts +0 -83
  299. package/plugins/acryl-router/src/helpers/validate.ts +0 -44
  300. package/plugins/acryl-router/src/http-error.ts +0 -21
  301. package/plugins/acryl-router/src/middleware/body-limit.ts +0 -25
  302. package/plugins/acryl-router/src/middleware/cors.ts +0 -46
  303. package/plugins/acryl-router/src/middleware/logger.ts +0 -21
  304. package/plugins/acryl-router/src/middleware/request-id.ts +0 -24
  305. package/plugins/acryl-router/src/middleware/secure-headers.ts +0 -23
  306. package/plugins/acryl-router/src/pattern.ts +0 -89
  307. package/plugins/acryl-router/src/registrar.ts +0 -90
  308. package/plugins/acryl-router/src/table.ts +0 -156
  309. package/plugins/acryl-router/src/types.ts +0 -73
  310. package/plugins/acryl-router/src/util.ts +0 -23
  311. package/plugins/acryl-router/tests/api.spec.ts +0 -223
  312. package/plugins/acryl-router/tests/conformance.ts +0 -170
  313. package/plugins/acryl-router/tests/helpers.spec.ts +0 -172
  314. package/plugins/acryl-router/tests/middleware.spec.ts +0 -183
  315. package/plugins/acryl-router/tests/table.spec.ts +0 -47
@@ -0,0 +1,263 @@
1
+ /**
2
+ * @module
3
+ * Body utility.
4
+ */
5
+
6
+ import type { AppRequest } from '../request.ts'
7
+ import { bufferToFormData } from './buffer.ts'
8
+
9
+ const MAX_NESTING_DEPTH = 32
10
+ const MAX_NESTED_OBJECTS = 10_000
11
+
12
+ type BodyDataValueDot = { [x: string]: string | File | BodyDataValueDot }
13
+ type BodyDataValueDotAll = {
14
+ [x: string]: string | File | (string | File)[] | BodyDataValueDotAll
15
+ }
16
+ type SimplifyBodyData<T> = {
17
+ [K in keyof T]: string | File | (string | File)[] | BodyDataValueDotAll extends T[K]
18
+ ? string | File | (string | File)[] | BodyDataValueDotAll
19
+ : string | File | BodyDataValueDot extends T[K]
20
+ ? string | File | BodyDataValueDot
21
+ : string | File | (string | File)[] extends T[K]
22
+ ? string | File | (string | File)[]
23
+ : string | File
24
+ } & {}
25
+
26
+ type BodyDataValueComponent<T> =
27
+ | string
28
+ | File
29
+ | (T extends { all: false }
30
+ ? never // explicitly set to false
31
+ : T extends { all: true } | { all: boolean }
32
+ ? (string | File)[] // use all option
33
+ : never) // without options
34
+ type BodyDataValueObject<T> = { [key: string]: BodyDataValueComponent<T> | BodyDataValueObject<T> }
35
+ type BodyDataValue<T> =
36
+ | BodyDataValueComponent<T>
37
+ | (T extends { dot: false }
38
+ ? never // explicitly set to false
39
+ : T extends { dot: true } | { dot: boolean }
40
+ ? BodyDataValueObject<T> // use dot option
41
+ : never) // without options
42
+ export type BodyData<T extends Partial<ParseBodyOptions> = {}> = SimplifyBodyData<
43
+ Record<string, BodyDataValue<T>>
44
+ >
45
+
46
+ export type ParseBodyOptions = {
47
+ /**
48
+ * Determines whether all fields with multiple values should be parsed as arrays.
49
+ * @default false
50
+ * @example
51
+ * const data = new FormData()
52
+ * data.append('file', 'aaa')
53
+ * data.append('file', 'bbb')
54
+ * data.append('message', 'hello')
55
+ *
56
+ * If all is false:
57
+ * parseBody should return { file: 'bbb', message: 'hello' }
58
+ *
59
+ * If all is true:
60
+ * parseBody should return { file: ['aaa', 'bbb'], message: 'hello' }
61
+ */
62
+ all: boolean
63
+ /**
64
+ * Determines whether all fields with dot notation should be parsed as nested objects.
65
+ * @default false
66
+ * @example
67
+ * const data = new FormData()
68
+ * data.append('obj.key1', 'value1')
69
+ * data.append('obj.key2', 'value2')
70
+ *
71
+ * If dot is false:
72
+ * parseBody should return { 'obj.key1': 'value1', 'obj.key2': 'value2' }
73
+ *
74
+ * If dot is true:
75
+ * parseBody should return { obj: { key1: 'value1', key2: 'value2' } }
76
+ */
77
+ dot: boolean
78
+ }
79
+
80
+ const isRawRequest = (request: AppRequest | Request): request is Request => 'headers' in request // 'headers' method exists only on Request, not on AppRequest.
81
+
82
+ /**
83
+ * Parses the body of a request based on the provided options.
84
+ *
85
+ * @template T - The type of the parsed body data.
86
+ * @param {AppRequest | Request} request - The request object to parse.
87
+ * @param {Partial<ParseBodyOptions>} [options] - Options for parsing the body.
88
+ * @returns {Promise<T>} The parsed body data.
89
+ */
90
+ interface ParseBody {
91
+ <Options extends Partial<ParseBodyOptions>, T extends BodyData<Options>>(
92
+ request: AppRequest | Request,
93
+ options?: Options
94
+ ): Promise<T>
95
+ <T extends BodyData>(
96
+ request: AppRequest | Request,
97
+ options?: Partial<ParseBodyOptions>
98
+ ): Promise<T>
99
+ }
100
+ export const parseBody: ParseBody = async (
101
+ request: AppRequest | Request,
102
+ options = Object.create(null)
103
+ ) => {
104
+ const { all = false, dot = false } = options
105
+
106
+ const headers = isRawRequest(request) ? request.headers : request.raw.headers
107
+ const contentType = headers.get('Content-Type')
108
+
109
+ const mediaType = contentType?.split(';')[0].trim().toLowerCase()
110
+
111
+ if (mediaType === 'multipart/form-data' || mediaType === 'application/x-www-form-urlencoded') {
112
+ return parseFormData(request, { all, dot })
113
+ }
114
+
115
+ return {}
116
+ }
117
+
118
+ /**
119
+ * Parses form data from a request.
120
+ *
121
+ * @template T - The type of the parsed body data.
122
+ * @param {AppRequest | Request} request - The request object containing form data.
123
+ * @param {ParseBodyOptions} options - Options for parsing the form data.
124
+ * @returns {Promise<T>} The parsed body data.
125
+ */
126
+ async function parseFormData<T extends BodyData>(
127
+ request: AppRequest | Request,
128
+ options: ParseBodyOptions
129
+ ): Promise<T> {
130
+ if (!isRawRequest(request) && request.bodyCache.formData) {
131
+ return convertFormDataToBodyData<T>(
132
+ await (request.bodyCache.formData as FormData | Promise<FormData>),
133
+ options
134
+ )
135
+ }
136
+ const headers = isRawRequest(request) ? request.headers : request.raw.headers
137
+ const arrayBuffer = await (request as Request).arrayBuffer()
138
+ const formDataPromise = bufferToFormData(arrayBuffer, headers.get('Content-Type') || '')
139
+ if (!isRawRequest(request)) {
140
+ // Cache so that a later `c.req.formData()` reuses the already-consumed body
141
+ request.bodyCache.formData = formDataPromise as unknown as FormData
142
+ }
143
+ const formData = await formDataPromise
144
+
145
+ if (formData) {
146
+ return convertFormDataToBodyData<T>(formData, options)
147
+ }
148
+
149
+ return {} as T
150
+ }
151
+
152
+ /**
153
+ * Converts form data to body data based on the provided options.
154
+ *
155
+ * @template T - The type of the parsed body data.
156
+ * @param {FormData} formData - The form data to convert.
157
+ * @param {ParseBodyOptions} options - Options for parsing the form data.
158
+ * @returns {T} The converted body data.
159
+ */
160
+ function convertFormDataToBodyData<T extends BodyData = BodyData>(
161
+ formData: FormData,
162
+ options: ParseBodyOptions
163
+ ): T {
164
+ const form: BodyData = Object.create(null)
165
+ const nestingState = { count: 0 }
166
+
167
+ formData.forEach((value, key) => {
168
+ const shouldParseAllValues = options.all || key.endsWith('[]')
169
+
170
+ if (!shouldParseAllValues) {
171
+ form[key] = value
172
+ } else {
173
+ handleParsingAllValues(form, key, value)
174
+ }
175
+ })
176
+
177
+ if (options.dot) {
178
+ Object.entries(form).forEach(([key, value]) => {
179
+ const shouldParseDotValues = key.includes('.')
180
+
181
+ if (shouldParseDotValues) {
182
+ handleParsingNestedValues(form, key, value, nestingState)
183
+ delete form[key]
184
+ }
185
+ })
186
+ }
187
+
188
+ return form as T
189
+ }
190
+
191
+ /**
192
+ * Handles parsing all values for a given key, supporting multiple values as arrays.
193
+ *
194
+ * @param {BodyData} form - The form data object.
195
+ * @param {string} key - The key to parse.
196
+ * @param {FormDataEntryValue} value - The value to assign.
197
+ */
198
+ const handleParsingAllValues = (
199
+ form: BodyData<{ all: true }>,
200
+ key: string,
201
+ value: FormDataEntryValue
202
+ ): void => {
203
+ if (form[key] !== undefined) {
204
+ if (Array.isArray(form[key])) {
205
+ ;(form[key] as (string | File)[]).push(value)
206
+ } else {
207
+ form[key] = [form[key] as string | File, value]
208
+ }
209
+ } else {
210
+ if (!key.endsWith('[]')) {
211
+ form[key] = value
212
+ } else {
213
+ form[key] = [value]
214
+ }
215
+ }
216
+ }
217
+
218
+ /**
219
+ * Handles parsing nested values using dot notation keys.
220
+ *
221
+ * @param {BodyData} form - The form data object.
222
+ * @param {string} key - The dot notation key.
223
+ * @param {BodyDataValue} value - The value to assign.
224
+ */
225
+ const handleParsingNestedValues = (
226
+ form: BodyData,
227
+ key: string,
228
+ value: BodyDataValue<Partial<ParseBodyOptions>>,
229
+ state: { count: number }
230
+ ): void => {
231
+ if (/(?:^|\.)__proto__\./.test(key)) {
232
+ return
233
+ }
234
+
235
+ let nestedForm = form
236
+ const keys = key.split('.', MAX_NESTING_DEPTH + 2)
237
+ if (keys.length > MAX_NESTING_DEPTH + 1) {
238
+ throwNestingLimitExceeded()
239
+ }
240
+
241
+ keys.forEach((key, index) => {
242
+ if (index === keys.length - 1) {
243
+ nestedForm[key] = value
244
+ } else {
245
+ if (
246
+ !nestedForm[key] ||
247
+ typeof nestedForm[key] !== 'object' ||
248
+ Array.isArray(nestedForm[key]) ||
249
+ nestedForm[key] instanceof File
250
+ ) {
251
+ if (state.count++ >= MAX_NESTED_OBJECTS) {
252
+ throwNestingLimitExceeded()
253
+ }
254
+ nestedForm[key] = Object.create(null)
255
+ }
256
+ nestedForm = nestedForm[key] as unknown as BodyData
257
+ }
258
+ })
259
+ }
260
+
261
+ const throwNestingLimitExceeded = (): never => {
262
+ throw new Error('Nesting limit exceeded')
263
+ }
@@ -0,0 +1,147 @@
1
+ import { createHash } from 'node:crypto'
2
+ import { bufferToFormData, bufferToString, equal, timingSafeEqual } from './buffer.ts'
3
+
4
+ describe('equal', () => {
5
+ it('should return true for identical ArrayBuffers', () => {
6
+ const buffer1 = new ArrayBuffer(1)
7
+ const buffer2 = buffer1
8
+ expect(equal(buffer1, buffer2)).toBe(true)
9
+ })
10
+
11
+ it('should return false for ArrayBuffers of different lengths', () => {
12
+ const buffer1 = new ArrayBuffer(1)
13
+ const buffer2 = new ArrayBuffer(2)
14
+ expect(equal(buffer1, buffer2)).toBe(false)
15
+ })
16
+
17
+ it('should return false for ArrayBuffers with different content', () => {
18
+ const buffer1 = new Uint8Array([1, 2, 3, 4]).buffer
19
+ const buffer2 = new Uint8Array([2, 2, 3, 4]).buffer
20
+ expect(equal(buffer1, buffer2)).toBe(false)
21
+ })
22
+
23
+ it('should return true for ArrayBuffers with identical content', () => {
24
+ const buffer1 = new Uint8Array([1, 2, 3, 4]).buffer
25
+ const buffer2 = new Uint8Array([1, 2, 3, 4]).buffer
26
+ expect(equal(buffer1, buffer2)).toBe(true)
27
+ })
28
+ })
29
+
30
+ describe('buffer', () => {
31
+ it('positive', async () => {
32
+ expect(
33
+ await timingSafeEqual(
34
+ '127e6fbfe24a750e72930c220a8e138275656b8e5d8f48a98c3c92df2caba935',
35
+ '127e6fbfe24a750e72930c220a8e138275656b8e5d8f48a98c3c92df2caba935'
36
+ )
37
+ ).toBe(true)
38
+ expect(await timingSafeEqual('a', 'a')).toBe(true)
39
+ expect(await timingSafeEqual('', '')).toBe(true)
40
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
41
+ // @ts-ignore
42
+ expect(await timingSafeEqual(undefined, undefined)).toBe(true)
43
+ })
44
+
45
+ it('negative', async () => {
46
+ expect(await timingSafeEqual('a', 'b')).toBe(false)
47
+ expect(
48
+ await timingSafeEqual('a', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')
49
+ ).toBe(false)
50
+ expect(
51
+ await timingSafeEqual('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'a')
52
+ ).toBe(false)
53
+ expect(await timingSafeEqual('alpha', 'beta')).toBe(false)
54
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
55
+ // @ts-ignore
56
+ expect(await timingSafeEqual(false, undefined)).toBe(false)
57
+
58
+ expect(
59
+ await timingSafeEqual(
60
+ // well known md5 hash collision
61
+ // https://marc-stevens.nl/research/md5-1block-collision/
62
+ 'TEXTCOLLBYfGiJUETHQ4hAcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak',
63
+ 'TEXTCOLLBYfGiJUETHQ4hEcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak',
64
+ (input) => createHash('md5').update(input).digest('hex')
65
+ )
66
+ ).toBe(false)
67
+ })
68
+
69
+ it.skip('comparing variables except string are deprecated', async () => {
70
+ expect(await timingSafeEqual(true, true)).toBe(true)
71
+ expect(await timingSafeEqual(false, false)).toBe(true)
72
+ expect(
73
+ await timingSafeEqual(true, true, (d: boolean) =>
74
+ createHash('sha256').update(d.toString()).digest('hex')
75
+ )
76
+ )
77
+ expect(await timingSafeEqual(false, true)).toBe(false)
78
+ expect(
79
+ await timingSafeEqual(
80
+ () => {},
81
+ () => {}
82
+ )
83
+ ).toBe(false)
84
+ expect(await timingSafeEqual({}, {})).toBe(false)
85
+ expect(await timingSafeEqual({ a: 1 }, { a: 1 })).toBe(false)
86
+ expect(await timingSafeEqual({ a: 1 }, { a: 2 })).toBe(false)
87
+ expect(await timingSafeEqual([1, 2], [1, 2])).toBe(false)
88
+ expect(await timingSafeEqual([1, 2], [1, 2, 3])).toBe(false)
89
+ expect(await timingSafeEqual('a', 'b', () => undefined)).toBe(false)
90
+ })
91
+ })
92
+
93
+ describe('bufferToString', () => {
94
+ it('Should return あいうえお', () => {
95
+ const bytes = [227, 129, 130, 227, 129, 132, 227, 129, 134, 227, 129, 136, 227, 129, 138]
96
+ const buffer = Uint8Array.from(bytes).buffer
97
+ expect(bufferToString(buffer)).toBe('あいうえお')
98
+ })
99
+ it('should return the passed arguments as is ', () => {
100
+ const notBuffer = 'あいうえお' as unknown as ArrayBuffer
101
+ expect(bufferToString(notBuffer)).toBe('あいうえお')
102
+ })
103
+ })
104
+
105
+ describe('bufferToFormData', () => {
106
+ it('Should parse multipart/form-data from ArrayBuffer', async () => {
107
+ const encoder = new TextEncoder()
108
+ const testData =
109
+ '--sampleboundary\r\nContent-Disposition: form-data; name="test"\r\n\r\nHello\r\n--sampleboundary--'
110
+ const arrayBuffer = encoder.encode(testData).buffer
111
+
112
+ const result = await bufferToFormData(
113
+ arrayBuffer,
114
+ 'multipart/form-data; boundary=sampleboundary'
115
+ )
116
+
117
+ expect(result.get('test')).toBe('Hello')
118
+ })
119
+
120
+ it('Should parse mixed-case multipart/form-data media type while keeping the boundary', async () => {
121
+ const encoder = new TextEncoder()
122
+ const testData =
123
+ '--sampleBoundary\r\nContent-Disposition: form-data; name="test"\r\n\r\nHello\r\n--sampleBoundary--'
124
+ const arrayBuffer = encoder.encode(testData).buffer
125
+
126
+ const result = await bufferToFormData(
127
+ arrayBuffer,
128
+ 'Multipart/Form-Data; boundary=sampleBoundary'
129
+ )
130
+
131
+ expect(result.get('test')).toBe('Hello')
132
+ })
133
+
134
+ it('Should parse application/x-www-form-urlencoded from ArrayBuffer', async () => {
135
+ const encoder = new TextEncoder()
136
+ const searchParams = new URLSearchParams()
137
+ searchParams.append('id', '123')
138
+ searchParams.append('title', 'Good title')
139
+ const testData = searchParams.toString()
140
+ const arrayBuffer = encoder.encode(testData).buffer
141
+
142
+ const result = await bufferToFormData(arrayBuffer, 'application/x-www-form-urlencoded')
143
+
144
+ expect(result.get('id')).toBe('123')
145
+ expect(result.get('title')).toBe('Good title')
146
+ })
147
+ })
@@ -0,0 +1,117 @@
1
+ /**
2
+ * @module
3
+ * Buffer utility.
4
+ */
5
+
6
+ import { sha256 } from './crypto.ts'
7
+
8
+ export const equal = (a: ArrayBuffer, b: ArrayBuffer): boolean => {
9
+ if (a === b) {
10
+ return true
11
+ }
12
+ if (a.byteLength !== b.byteLength) {
13
+ return false
14
+ }
15
+
16
+ const va = new DataView(a)
17
+ const vb = new DataView(b)
18
+
19
+ let i = va.byteLength
20
+ while (i--) {
21
+ if (va.getUint8(i) !== vb.getUint8(i)) {
22
+ return false
23
+ }
24
+ }
25
+
26
+ return true
27
+ }
28
+
29
+ const constantTimeEqualString = (a: string, b: string): boolean => {
30
+ const aLen = a.length
31
+ const bLen = b.length
32
+ const maxLen = Math.max(aLen, bLen)
33
+ let out = aLen ^ bLen
34
+ for (let i = 0; i < maxLen; i++) {
35
+ const aChar = i < aLen ? a.charCodeAt(i) : 0
36
+ const bChar = i < bLen ? b.charCodeAt(i) : 0
37
+ out |= aChar ^ bChar
38
+ }
39
+ return out === 0
40
+ }
41
+
42
+ type StringHashFunction = (input: string) => string | null | Promise<string | null>
43
+
44
+ const timingSafeEqualString = async (
45
+ a: string,
46
+ b: string,
47
+ hashFunction?: StringHashFunction
48
+ ): Promise<boolean> => {
49
+ if (!hashFunction) {
50
+ hashFunction = sha256
51
+ }
52
+
53
+ const [sa, sb] = await Promise.all([hashFunction(a), hashFunction(b)])
54
+
55
+ if (sa == null || sb == null || typeof sa !== 'string' || typeof sb !== 'string') {
56
+ return false
57
+ }
58
+
59
+ const hashEqual = constantTimeEqualString(sa, sb)
60
+ const originalEqual = constantTimeEqualString(a, b)
61
+
62
+ return hashEqual && originalEqual
63
+ }
64
+
65
+ type TimingSafeEqual = {
66
+ (a: string, b: string, hashFunction?: StringHashFunction): Promise<boolean>
67
+ /**
68
+ * @deprecated object and boolean signatures that take boolean as first and second arguments, and functions with signatures that take non-string arguments have been deprecated
69
+ */
70
+ (
71
+ a: string | object | boolean,
72
+ b: string | object | boolean,
73
+ hashFunction?: Function
74
+ ): Promise<boolean>
75
+ }
76
+ export const timingSafeEqual: TimingSafeEqual = async (
77
+ a,
78
+ b,
79
+ hashFunction?: Function
80
+ ): Promise<boolean> => {
81
+ if (typeof a === 'string' && typeof b === 'string') {
82
+ return timingSafeEqualString(a, b, hashFunction as StringHashFunction)
83
+ }
84
+
85
+ if (!hashFunction) {
86
+ hashFunction = sha256
87
+ }
88
+
89
+ const [sa, sb] = await Promise.all([hashFunction(a), hashFunction(b)])
90
+
91
+ if (!sa || !sb || typeof sa !== 'string' || typeof sb !== 'string') {
92
+ return false
93
+ }
94
+
95
+ return timingSafeEqualString(sa, sb)
96
+ }
97
+
98
+ export const bufferToString = (buffer: ArrayBuffer): string => {
99
+ if (buffer instanceof ArrayBuffer) {
100
+ const enc = new TextDecoder('utf-8')
101
+ return enc.decode(buffer)
102
+ }
103
+ return buffer
104
+ }
105
+
106
+ export const bufferToFormData = (
107
+ arrayBuffer: ArrayBuffer,
108
+ contentType: string
109
+ ): Promise<FormData> => {
110
+ const response = new Response(arrayBuffer, {
111
+ headers: {
112
+ // Normalize the media type (case-insensitive) while keeping parameters like the boundary
113
+ 'Content-Type': contentType.replace(/^[^;]+/, (mediaType) => mediaType.toLowerCase()),
114
+ },
115
+ })
116
+ return response.formData()
117
+ }
@@ -0,0 +1,23 @@
1
+ import { getColorEnabled, getColorEnabledAsync } from './color.ts'
2
+
3
+ describe('getColorEnabled() / getColorEnabledAsync() - With colors enabled', () => {
4
+ it('should return true', async () => {
5
+ expect(getColorEnabled()).toBe(true)
6
+ expect(await getColorEnabledAsync()).toBe(true)
7
+ })
8
+ })
9
+
10
+ describe('getColorEnabled() / getColorEnabledAsync() - With NO_COLOR environment variable set', () => {
11
+ beforeAll(() => {
12
+ vi.stubEnv('NO_COLOR', '1')
13
+ })
14
+
15
+ afterAll(() => {
16
+ vi.unstubAllEnvs()
17
+ })
18
+
19
+ it('should return false', async () => {
20
+ expect(getColorEnabled()).toBe(false)
21
+ expect(await getColorEnabledAsync()).toBe(false)
22
+ })
23
+ })
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @module
3
+ * Color utility.
4
+ */
5
+
6
+ /**
7
+ * Get whether color change on terminal is enabled or disabled.
8
+ * If `NO_COLOR` environment variable is set, this function returns `false`.
9
+ * Unlike getColorEnabledAsync(), this cannot check Cloudflare environment variables.
10
+ * @see {@link https://no-color.org/}
11
+ *
12
+ * @returns {boolean}
13
+ */
14
+ export function getColorEnabled(): boolean {
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
+ const { process, Deno } = globalThis as any
17
+
18
+ const isNoColor =
19
+ typeof Deno?.noColor === 'boolean'
20
+ ? (Deno.noColor as boolean)
21
+ : process !== undefined
22
+ ? // eslint-disable-next-line no-unsafe-optional-chaining
23
+ 'NO_COLOR' in process?.env
24
+ : false
25
+
26
+ return !isNoColor
27
+ }
28
+
29
+ /**
30
+ * Get whether color change on terminal is enabled or disabled.
31
+ * If `NO_COLOR` environment variable is set, this function returns `false`.
32
+ * @see {@link https://no-color.org/}
33
+ *
34
+ * @returns {boolean}
35
+ */
36
+ export async function getColorEnabledAsync(): Promise<boolean> {
37
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
+ const { navigator } = globalThis as any
39
+ // Avoid analysis of cloudflare scheme by bundlers
40
+ const cfWorkers = 'cloudflare:workers'
41
+
42
+ const isNoColor =
43
+ navigator !== undefined && navigator.userAgent === 'Cloudflare-Workers'
44
+ ? await (async () => {
45
+ try {
46
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
47
+ // @ts-ignore
48
+ return 'NO_COLOR' in ((await import(cfWorkers)).env ?? {}) // {} is for backward compat
49
+ } catch {
50
+ return false
51
+ }
52
+ })()
53
+ : !getColorEnabled()
54
+
55
+ return !isNoColor
56
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @module
3
+ * Constants for compression.
4
+ */
5
+
6
+ /**
7
+ * Match for compressible content type.
8
+ */
9
+ export const COMPRESSIBLE_CONTENT_TYPE_REGEX =
10
+ /^\s*(?:text\/(?!event-stream(?:[;\s]|$))[^;\s]+|application\/(?:javascript|json|xml|xml-dtd|ecmascript|dart|msgpack|postscript|rtf|tar|toml|vnd\.dart|vnd\.ms-fontobject|vnd\.ms-opentype|vnd\.msgpack|wasm|x-httpd-php|x-javascript|x-msgpack|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|msgpack))(?:[;\s]|$)/i
@@ -0,0 +1,69 @@
1
+ import { createPool } from './concurrent.ts'
2
+
3
+ describe('concurrent execution', () => {
4
+ test.each`
5
+ concurrency | count
6
+ ${1} | ${10}
7
+ ${10} | ${10}
8
+ ${100} | ${10}
9
+ ${Infinity} | ${2000}
10
+ `('concurrency $concurrency, count $count', async ({ concurrency, count }) => {
11
+ const running = new Set()
12
+
13
+ const pool = createPool({ concurrency })
14
+ let resolve: (() => void) | undefined
15
+ const promise = new Promise<void>((r) => {
16
+ resolve = r
17
+ })
18
+ const fn = async (i: number) => {
19
+ if (running.size > concurrency) {
20
+ throw new Error('concurrency exceeded')
21
+ }
22
+
23
+ running.add(i)
24
+ await promise
25
+ running.delete(i)
26
+ return i
27
+ }
28
+
29
+ const jobs = new Array(count).fill(0).map((_, i) => () => fn(i))
30
+ const expectedResults = new Array(count).fill(0).map((_, i) => i)
31
+ const resultPromises = jobs.map((job) => pool.run(job))
32
+
33
+ expect(running.size).toBe(Math.min(concurrency, count))
34
+ resolve?.()
35
+ const results = await Promise.all(resultPromises)
36
+ expect(running.size).toBe(0)
37
+ expect(results).toEqual(expectedResults)
38
+ })
39
+
40
+ describe('with interval', () => {
41
+ test.each`
42
+ concurrency | interval
43
+ ${1} | ${10}
44
+ ${2} | ${10}
45
+ `('concurrency $concurrency, interval $interval', async ({ concurrency, interval }) => {
46
+ const workingTimeQueue: number[] = []
47
+ const pool = createPool({ concurrency, interval })
48
+ const fn = async (i: number) => {
49
+ const now = Date.now()
50
+ if (workingTimeQueue.length >= concurrency) {
51
+ const last = workingTimeQueue.shift()
52
+ // Not so accurate, -1 ms is acceptable
53
+ if (last && now - last < interval - 1) {
54
+ throw new Error('interval violated')
55
+ }
56
+ }
57
+ workingTimeQueue.push(now)
58
+ return i
59
+ }
60
+
61
+ const jobs = new Array(10).fill(0).map((_, i) => () => fn(i))
62
+ const expectedResults = new Array(10).fill(0).map((_, i) => i)
63
+ const resultPromises = jobs.map((job) => pool.run(job))
64
+
65
+ const results = await Promise.all(resultPromises)
66
+ expect(results).toEqual(expectedResults)
67
+ })
68
+ })
69
+ })