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,314 @@
1
+ /**
2
+ * @module
3
+ * Language module for App.
4
+ */
5
+ import type { Context } from '../../context.ts'
6
+ import { setCookie, getCookie } from '../../helper/cookie/index.ts'
7
+ import type { MiddlewareHandler } from '../../types.ts'
8
+ import { parseAccept } from '../../utils/accept.ts'
9
+
10
+ export type DetectorType = 'path' | 'querystring' | 'cookie' | 'header'
11
+ export type CacheType = 'cookie'
12
+
13
+ export interface DetectorOptions {
14
+ /** Order of language detection strategies */
15
+ order: DetectorType[]
16
+ /** Query parameter name for language */
17
+ lookupQueryString: string
18
+ /** Cookie name for language */
19
+ lookupCookie: string
20
+ /** Index in URL path where language code appears */
21
+ lookupFromPathIndex: number
22
+ /** Header key for language detection */
23
+ lookupFromHeaderKey: string
24
+ /** Caching strategies */
25
+ caches: CacheType[] | false
26
+ /** Cookie configuration options */
27
+ cookieOptions?: {
28
+ domain?: string
29
+ path?: string
30
+ sameSite?: 'Strict' | 'Lax' | 'None'
31
+ secure?: boolean
32
+ maxAge?: number
33
+ httpOnly?: boolean
34
+ }
35
+ /** Whether to ignore case in language codes */
36
+ ignoreCase: boolean
37
+ /** Default language if none detected */
38
+ fallbackLanguage: string
39
+ /** List of supported language codes */
40
+ supportedLanguages: string[]
41
+ /** Optional function to transform detected language codes */
42
+ convertDetectedLanguage?: (lang: string) => string
43
+ /** Enable debug logging */
44
+ debug?: boolean
45
+ }
46
+
47
+ export interface LanguageVariables {
48
+ language: string
49
+ }
50
+
51
+ export const DEFAULT_OPTIONS: DetectorOptions = {
52
+ order: ['querystring', 'cookie', 'header'],
53
+ lookupQueryString: 'lang',
54
+ lookupCookie: 'language',
55
+ lookupFromHeaderKey: 'accept-language',
56
+ lookupFromPathIndex: 0,
57
+ caches: ['cookie'],
58
+ ignoreCase: true,
59
+ fallbackLanguage: 'en',
60
+ supportedLanguages: ['en'],
61
+ cookieOptions: {
62
+ sameSite: 'Strict',
63
+ secure: true,
64
+ maxAge: 365 * 24 * 60 * 60,
65
+ httpOnly: true,
66
+ },
67
+ debug: false,
68
+ }
69
+ /**
70
+ * Parse Accept-Language header values with quality scores
71
+ * @param header Accept-Language header string
72
+ * @returns Array of parsed languages with quality scores
73
+ */
74
+ export function parseAcceptLanguage(header: string): Array<{ lang: string; q: number }> {
75
+ return parseAccept(header).map(({ type, q }) => ({ lang: type, q }))
76
+ }
77
+
78
+ /**
79
+ * Validate and normalize language codes
80
+ * @param lang Language code to normalize
81
+ * @param options Detector options
82
+ * @returns Normalized language code or undefined
83
+ */
84
+ export const normalizeLanguage = (
85
+ lang: string | null | undefined,
86
+ options: DetectorOptions
87
+ ): string | undefined => {
88
+ if (!lang) {
89
+ return undefined
90
+ }
91
+
92
+ try {
93
+ let normalizedLang = lang.trim()
94
+ if (options.convertDetectedLanguage) {
95
+ normalizedLang = options.convertDetectedLanguage(normalizedLang)
96
+ }
97
+
98
+ const compLang = options.ignoreCase ? normalizedLang.toLowerCase() : normalizedLang
99
+ const compSupported = options.supportedLanguages.map((l) =>
100
+ options.ignoreCase ? l.toLowerCase() : l
101
+ )
102
+
103
+ // Exact match
104
+ const exactIndex = compSupported.indexOf(compLang)
105
+ if (exactIndex !== -1) {
106
+ return options.supportedLanguages[exactIndex]
107
+ }
108
+
109
+ // Progressive truncation (RFC 4647 Lookup)
110
+ let longestMatchIndex = -1
111
+ let longestMatchLength = -1
112
+ for (let i = 0; i < compSupported.length; i++) {
113
+ const candidate = compSupported[i]
114
+ if (
115
+ candidate.length < compLang.length &&
116
+ candidate.length > longestMatchLength &&
117
+ compLang.startsWith(candidate) &&
118
+ compLang[candidate.length] === '-'
119
+ ) {
120
+ longestMatchIndex = i
121
+ longestMatchLength = candidate.length
122
+ }
123
+ }
124
+ if (longestMatchIndex !== -1) {
125
+ return options.supportedLanguages[longestMatchIndex]
126
+ }
127
+
128
+ return undefined
129
+ } catch {
130
+ return undefined
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Detects language from query parameter
136
+ */
137
+ export const detectFromQuery = (c: Context, options: DetectorOptions): string | undefined => {
138
+ const query = c.req.query(options.lookupQueryString)
139
+ return normalizeLanguage(query, options)
140
+ }
141
+
142
+ /**
143
+ * Detects language from cookie
144
+ */
145
+ export const detectFromCookie = (c: Context, options: DetectorOptions): string | undefined => {
146
+ const cookie = getCookie(c, options.lookupCookie)
147
+ return normalizeLanguage(cookie, options)
148
+ }
149
+
150
+ /**
151
+ * Detects language from Accept-Language header
152
+ */
153
+ export function detectFromHeader(c: Context, options: DetectorOptions): string | undefined {
154
+ try {
155
+ const acceptLanguage = c.req.header(options.lookupFromHeaderKey)
156
+ if (!acceptLanguage) {
157
+ return undefined
158
+ }
159
+
160
+ const languages = parseAcceptLanguage(acceptLanguage)
161
+ for (const { lang, q } of languages) {
162
+ // A quality value of 0 means the language is explicitly rejected
163
+ // (RFC 9110 §12.5.4), so it must not be detected.
164
+ if (q === 0) {
165
+ continue
166
+ }
167
+ const normalizedLang = normalizeLanguage(lang, options)
168
+ if (normalizedLang) {
169
+ return normalizedLang
170
+ }
171
+ }
172
+ return undefined
173
+ } catch {
174
+ return undefined
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Detects language from URL path
180
+ */
181
+ export function detectFromPath(c: Context, options: DetectorOptions): string | undefined {
182
+ const url = new URL(c.req.url)
183
+ const pathSegments = url.pathname.split('/').filter(Boolean)
184
+ const langSegment = pathSegments[options.lookupFromPathIndex]
185
+ return normalizeLanguage(langSegment, options)
186
+ }
187
+
188
+ /**
189
+ * Collection of all language detection strategies
190
+ */
191
+ export const detectors = {
192
+ querystring: detectFromQuery,
193
+ cookie: detectFromCookie,
194
+ header: detectFromHeader,
195
+ path: detectFromPath,
196
+ } as const
197
+
198
+ /** Type for detector functions */
199
+ export type DetectorFunction = (c: Context, options: DetectorOptions) => string | undefined
200
+
201
+ /** Type-safe detector map */
202
+ export type Detectors = Record<keyof typeof detectors, DetectorFunction>
203
+
204
+ /**
205
+ * Validate detector options
206
+ * @param options Detector options to validate
207
+ * @throws Error if options are invalid
208
+ */
209
+ export function validateOptions(options: DetectorOptions): void {
210
+ if (!options.supportedLanguages.includes(options.fallbackLanguage)) {
211
+ throw new Error('Fallback language must be included in supported languages')
212
+ }
213
+
214
+ if (options.lookupFromPathIndex < 0) {
215
+ throw new Error('Path index must be non-negative')
216
+ }
217
+
218
+ if (!options.order.every((detector) => Object.keys(detectors).includes(detector))) {
219
+ throw new Error('Invalid detector type in order array')
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Cache detected language
225
+ */
226
+ function cacheLanguage(c: Context, language: string, options: DetectorOptions): void {
227
+ if (!Array.isArray(options.caches) || !options.caches.includes('cookie')) {
228
+ return
229
+ }
230
+
231
+ try {
232
+ setCookie(c, options.lookupCookie, language, options.cookieOptions)
233
+ } catch (error) {
234
+ if (options.debug) {
235
+ console.error('Failed to cache language:', error)
236
+ }
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Detect language from request
242
+ */
243
+ const detectLanguage = (c: Context, options: DetectorOptions): string => {
244
+ let detectedLang: string | undefined
245
+
246
+ for (const detectorName of options.order) {
247
+ const detector = detectors[detectorName]
248
+
249
+ try {
250
+ detectedLang = detector(c, options)
251
+ if (detectedLang) {
252
+ if (options.debug) {
253
+ console.log(`Language detected from ${detectorName}: ${detectedLang}`)
254
+ }
255
+ break
256
+ }
257
+ } catch (error) {
258
+ if (options.debug) {
259
+ console.error(`Error in ${detectorName} detector:`, error)
260
+ }
261
+ continue
262
+ }
263
+ }
264
+
265
+ const finalLang = detectedLang || options.fallbackLanguage
266
+
267
+ if (detectedLang && options.caches) {
268
+ cacheLanguage(c, finalLang, options)
269
+ }
270
+
271
+ return finalLang
272
+ }
273
+
274
+ /**
275
+ * Language detector middleware factory
276
+ * @param userOptions Configuration options for the language detector
277
+ * @returns App middleware function
278
+ *
279
+ * @example
280
+ * ```ts
281
+ * type Variables = LanguageVariables
282
+ * const app = new App<{ Variables: Variables }>()
283
+ *
284
+ * app.use(
285
+ * languageDetector({
286
+ * supportedLanguages: ['en', 'ja'], // Must include fallback
287
+ * fallbackLanguage: 'en', // Required
288
+ * })
289
+ * )
290
+ *
291
+ * app.get('/', (c) => {
292
+ * const lang = c.get('language')
293
+ * return c.text(`Current language: ${lang}`)
294
+ * })
295
+ * ```
296
+ */
297
+ export const languageDetector = (userOptions: Partial<DetectorOptions>): MiddlewareHandler => {
298
+ const options: DetectorOptions = {
299
+ ...DEFAULT_OPTIONS,
300
+ ...userOptions,
301
+ cookieOptions: {
302
+ ...DEFAULT_OPTIONS.cookieOptions,
303
+ ...userOptions.cookieOptions,
304
+ },
305
+ }
306
+
307
+ validateOptions(options)
308
+
309
+ return async function languageDetector(ctx, next) {
310
+ const lang = detectLanguage(ctx, options)
311
+ ctx.set('language', lang)
312
+ await next()
313
+ }
314
+ }
@@ -0,0 +1,204 @@
1
+ import { logger } from './index.ts'
2
+ import { App } from '../../app.ts'
3
+
4
+ describe('Logger by Middleware', () => {
5
+ let app: App
6
+ let log: string
7
+
8
+ beforeEach(() => {
9
+ function sleep(time: number) {
10
+ return new Promise((resolve) => setTimeout(resolve, time))
11
+ }
12
+
13
+ app = new App()
14
+
15
+ const logFn = (str: string) => {
16
+ log = str
17
+ }
18
+
19
+ const shortRandomString = 'acryl'
20
+ const longRandomString = 'acryl'.repeat(1000)
21
+
22
+ app.use('*', logger(logFn))
23
+ app.get('/short', (c) => c.text(shortRandomString))
24
+ app.get('/long', (c) => c.text(longRandomString))
25
+ app.get('/seconds', async (c) => {
26
+ await sleep(1000)
27
+
28
+ return c.text(longRandomString)
29
+ })
30
+ app.get('/empty', (c) => c.text(''))
31
+ app.get('/redirect', (c) => {
32
+ return c.redirect('/empty', 301)
33
+ })
34
+ app.get('/server-error', (c) => {
35
+ const res = new Response('', { status: 511 })
36
+ if (c.req.query('status')) {
37
+ // test status code not yet supported by runtime `Response` object
38
+ Object.defineProperty(res, 'status', { value: parseInt(c.req.query('status') as string) })
39
+ }
40
+ return res
41
+ })
42
+ })
43
+
44
+ it('Log status 200 with empty body', async () => {
45
+ const res = await app.request('http://localhost/empty')
46
+ expect(res).not.toBeNull()
47
+ expect(res.status).toBe(200)
48
+ expect(log.startsWith('--> GET /empty \x1b[32m200\x1b[0m')).toBe(true)
49
+ expect(log).toMatch(/m?s$/)
50
+ })
51
+
52
+ it('Log status 200 with small body', async () => {
53
+ const res = await app.request('http://localhost/short')
54
+ expect(res).not.toBeNull()
55
+ expect(res.status).toBe(200)
56
+ expect(log.startsWith('--> GET /short \x1b[32m200\x1b[0m')).toBe(true)
57
+ expect(log).toMatch(/m?s$/)
58
+ })
59
+
60
+ it('Log status 200 with small body and query param', async () => {
61
+ const res = await app.request('http://localhost/short?foo=bar')
62
+ expect(res).not.toBeNull()
63
+ expect(res.status).toBe(200)
64
+ expect(log.startsWith('--> GET /short?foo=bar \x1b[32m200\x1b[0m')).toBe(true)
65
+ expect(log).toMatch(/m?s$/)
66
+ })
67
+
68
+ it('Log status 200 with big body', async () => {
69
+ const res = await app.request('http://localhost/long')
70
+ expect(res).not.toBeNull()
71
+ expect(res.status).toBe(200)
72
+ expect(log.startsWith('--> GET /long \x1b[32m200\x1b[0m')).toBe(true)
73
+ expect(log).toMatch(/m?s$/)
74
+ })
75
+
76
+ it('Time in seconds', async () => {
77
+ const res = await app.request('http://localhost/seconds')
78
+ expect(res).not.toBeNull()
79
+ expect(res.status).toBe(200)
80
+ expect(log.startsWith('--> GET /seconds \x1b[32m200\x1b[0m')).toBe(true)
81
+ expect(log).toMatch(/1s/)
82
+ })
83
+
84
+ it('Log status 301 with empty body', async () => {
85
+ const res = await app.request('http://localhost/redirect')
86
+ expect(res).not.toBeNull()
87
+ expect(res.status).toBe(301)
88
+ expect(log.startsWith('--> GET /redirect \x1b[36m301\x1b[0m')).toBe(true)
89
+ expect(log).toMatch(/m?s$/)
90
+ })
91
+
92
+ it('Log status 404', async () => {
93
+ const msg = 'Default 404 Not Found'
94
+ app.all('*', (c) => {
95
+ return c.text(msg, 404)
96
+ })
97
+ const res = await app.request('http://localhost/notfound')
98
+ expect(res).not.toBeNull()
99
+ expect(res.status).toBe(404)
100
+ expect(log.startsWith('--> GET /notfound \x1b[33m404\x1b[0m')).toBe(true)
101
+ expect(log).toMatch(/m?s$/)
102
+ })
103
+
104
+ it('Log status 511 with empty body', async () => {
105
+ const res = await app.request('http://localhost/server-error')
106
+ expect(res).not.toBeNull()
107
+ expect(res.status).toBe(511)
108
+ expect(log.startsWith('--> GET /server-error \x1b[31m511\x1b[0m')).toBe(true)
109
+ expect(log).toMatch(/m?s$/)
110
+ })
111
+
112
+ it('Log status 100', async () => {
113
+ const res = await app.request('http://localhost/server-error?status=100')
114
+ expect(res).not.toBeNull()
115
+ expect(res.status).toBe(100)
116
+ expect(log.startsWith('--> GET /server-error?status=100 100')).toBe(true)
117
+ expect(log).toMatch(/m?s$/)
118
+ })
119
+
120
+ it('Log status 700', async () => {
121
+ const res = await app.request('http://localhost/server-error?status=700')
122
+ expect(res).not.toBeNull()
123
+ expect(res.status).toBe(700)
124
+ expect(log.startsWith('--> GET /server-error?status=700 700')).toBe(true)
125
+ expect(log).toMatch(/m?s$/)
126
+ })
127
+ })
128
+
129
+ describe('Logger by Middleware in NO_COLOR', () => {
130
+ let app: App
131
+ let log: string
132
+
133
+ beforeEach(() => {
134
+ vi.stubEnv('NO_COLOR', '1')
135
+ function sleep(time: number) {
136
+ return new Promise((resolve) => setTimeout(resolve, time))
137
+ }
138
+
139
+ app = new App()
140
+
141
+ const logFn = (str: string) => {
142
+ log = str
143
+ }
144
+
145
+ const shortRandomString = 'acryl'
146
+ const longRandomString = 'acryl'.repeat(1000)
147
+
148
+ app.use('*', logger(logFn))
149
+ app.get('/short', (c) => c.text(shortRandomString))
150
+ app.get('/long', (c) => c.text(longRandomString))
151
+ app.get('/seconds', async (c) => {
152
+ await sleep(1000)
153
+
154
+ return c.text(longRandomString)
155
+ })
156
+ app.get('/empty', (c) => c.text(''))
157
+ })
158
+ afterAll(() => {
159
+ vi.unstubAllEnvs()
160
+ })
161
+ it('Log status 200 with empty body', async () => {
162
+ const res = await app.request('http://localhost/empty')
163
+ expect(res).not.toBeNull()
164
+ expect(res.status).toBe(200)
165
+ expect(log.startsWith('--> GET /empty 200')).toBe(true)
166
+ expect(log).toMatch(/m?s$/)
167
+ })
168
+
169
+ it('Log status 200 with small body', async () => {
170
+ const res = await app.request('http://localhost/short')
171
+ expect(res).not.toBeNull()
172
+ expect(res.status).toBe(200)
173
+ expect(log.startsWith('--> GET /short 200')).toBe(true)
174
+ expect(log).toMatch(/m?s$/)
175
+ })
176
+
177
+ it('Log status 200 with big body', async () => {
178
+ const res = await app.request('http://localhost/long')
179
+ expect(res).not.toBeNull()
180
+ expect(res.status).toBe(200)
181
+ expect(log.startsWith('--> GET /long 200')).toBe(true)
182
+ expect(log).toMatch(/m?s$/)
183
+ })
184
+
185
+ it('Time in seconds', async () => {
186
+ const res = await app.request('http://localhost/seconds')
187
+ expect(res).not.toBeNull()
188
+ expect(res.status).toBe(200)
189
+ expect(log.startsWith('--> GET /seconds 200')).toBe(true)
190
+ expect(log).toMatch(/1s/)
191
+ })
192
+
193
+ it('Log status 404', async () => {
194
+ const msg = 'Default 404 Not Found'
195
+ app.all('*', (c) => {
196
+ return c.text(msg, 404)
197
+ })
198
+ const res = await app.request('http://localhost/notfound')
199
+ expect(res).not.toBeNull()
200
+ expect(res.status).toBe(404)
201
+ expect(log.startsWith('--> GET /notfound 404')).toBe(true)
202
+ expect(log).toMatch(/m?s$/)
203
+ })
204
+ })
@@ -0,0 +1,95 @@
1
+ /**
2
+ * @module
3
+ * Logger Middleware for App.
4
+ */
5
+
6
+ import type { MiddlewareHandler } from '../../types.ts'
7
+ import { getColorEnabledAsync } from '../../utils/color.ts'
8
+
9
+ const LogPrefix = {
10
+ Outgoing: '-->',
11
+ Incoming: '<--',
12
+ Error: 'xxx',
13
+ } as const
14
+ type LogPrefix = (typeof LogPrefix)[keyof typeof LogPrefix]
15
+
16
+ const humanize = (times: string[]) => {
17
+ const [delimiter, separator] = [',', '.']
18
+
19
+ const orderTimes = times.map((v) => v.replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1' + delimiter))
20
+
21
+ return orderTimes.join(separator)
22
+ }
23
+
24
+ const time = (start: number) => {
25
+ const delta = Date.now() - start
26
+ return humanize([delta < 1000 ? delta + 'ms' : Math.round(delta / 1000) + 's'])
27
+ }
28
+
29
+ const colorStatus = async (status: number) => {
30
+ const colorEnabled = await getColorEnabledAsync()
31
+ if (colorEnabled) {
32
+ switch ((status / 100) | 0) {
33
+ case 5: // red = error
34
+ return `\x1b[31m${status}\x1b[0m`
35
+ case 4: // yellow = warning
36
+ return `\x1b[33m${status}\x1b[0m`
37
+ case 3: // cyan = redirect
38
+ return `\x1b[36m${status}\x1b[0m`
39
+ case 2: // green = success
40
+ return `\x1b[32m${status}\x1b[0m`
41
+ }
42
+ }
43
+ // Fallback to unsupported status code.
44
+ // E.g.) Bun and Deno supports new Response with 101, but Node.js does not.
45
+ // And those may evolve to accept more status.
46
+ return `${status}`
47
+ }
48
+
49
+ type PrintFunc = (str: string, ...rest: string[]) => void
50
+
51
+ async function log(
52
+ fn: PrintFunc,
53
+ prefix: string,
54
+ method: string,
55
+ path: string,
56
+ status: number = 0,
57
+ elapsed?: string
58
+ ) {
59
+ const out =
60
+ prefix === LogPrefix.Incoming
61
+ ? `${prefix} ${method} ${path}`
62
+ : `${prefix} ${method} ${path} ${await colorStatus(status)} ${elapsed}`
63
+ fn(out)
64
+ }
65
+
66
+ /**
67
+ * Logger Middleware for App.
68
+ *
69
+ *
70
+ * @param {PrintFunc} [fn=console.log] - Optional function for customized logging behavior.
71
+ * @returns {MiddlewareHandler} The middleware handler function.
72
+ *
73
+ * @example
74
+ * ```ts
75
+ * const app = new App()
76
+ *
77
+ * app.use(logger())
78
+ * app.get('/', (c) => c.text('Hello App!'))
79
+ * ```
80
+ */
81
+ export const logger = (fn: PrintFunc = console.log): MiddlewareHandler => {
82
+ return async function logger(c, next) {
83
+ const { method, url } = c.req
84
+
85
+ const path = url.slice(url.indexOf('/', 8))
86
+
87
+ await log(fn, LogPrefix.Incoming, method, path)
88
+
89
+ const start = Date.now()
90
+
91
+ await next()
92
+
93
+ await log(fn, LogPrefix.Outgoing, method, path, c.res.status, time(start))
94
+ }
95
+ }