acrylblends 0.3.0 → 0.4.1

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 (341) hide show
  1. package/dist/index.d.ts +6 -1
  2. package/dist/index.js +84 -5
  3. package/dist/index.js.map +1 -1
  4. package/docs/framework/LICENSE +23 -0
  5. package/docs/framework/README.md +63 -0
  6. package/docs/framework/UPSTREAM.md +3 -0
  7. package/docs/framework/api-summary.md +138 -0
  8. package/docs/framework/concepts.md +105 -0
  9. package/docs/framework/context.md +129 -0
  10. package/docs/framework/getting-started.md +146 -0
  11. package/docs/framework/helpers.md +118 -0
  12. package/docs/framework/middleware.md +154 -0
  13. package/docs/framework/migration-from-0-3.md +108 -0
  14. package/docs/framework/migration-from-dsh-webserver.md +103 -0
  15. package/docs/framework/node-server.md +75 -0
  16. package/docs/framework/plugin-lifecycle.md +111 -0
  17. package/docs/framework/request.md +106 -0
  18. package/docs/framework/routing.md +188 -0
  19. package/docs/framework/surfaces.md +71 -0
  20. package/docs/framework/upstream.json +9 -0
  21. package/docs/framework/validation.md +135 -0
  22. package/package.json +4 -2
  23. package/plugins/acryl-inspect/plugin.json +2 -1
  24. package/plugins/acryl-inspect/src/report.ts +21 -13
  25. package/plugins/acryl-inspect/src/service.ts +9 -7
  26. package/plugins/acryl-inspect/tests/report.spec.ts +13 -8
  27. package/plugins/acryl-inspect/tests/service.spec.ts +11 -10
  28. package/plugins/acryl-inspect/upstream.json +2 -2
  29. package/plugins/acryl-node-server/LICENSE +26 -0
  30. package/plugins/acryl-node-server/plugin.json +2 -1
  31. package/plugins/acryl-node-server/src/adapter/conninfo.ts +23 -0
  32. package/plugins/acryl-node-server/src/adapter/early-hints.ts +55 -0
  33. package/plugins/acryl-node-server/src/adapter/error.ts +11 -0
  34. package/plugins/acryl-node-server/src/adapter/headers.ts +249 -0
  35. package/plugins/acryl-node-server/src/adapter/index.ts +6 -0
  36. package/plugins/acryl-node-server/src/adapter/listener.ts +540 -0
  37. package/plugins/acryl-node-server/src/adapter/request.ts +797 -0
  38. package/plugins/acryl-node-server/src/adapter/response.ts +186 -0
  39. package/plugins/acryl-node-server/src/adapter/serve-static.ts +238 -0
  40. package/plugins/acryl-node-server/src/adapter/server.ts +36 -0
  41. package/plugins/acryl-node-server/src/adapter/types.ts +82 -0
  42. package/plugins/acryl-node-server/src/adapter/url.ts +42 -0
  43. package/plugins/acryl-node-server/src/adapter/utils/response/constants.ts +1 -0
  44. package/plugins/acryl-node-server/src/adapter/utils/response.ts +4 -0
  45. package/plugins/acryl-node-server/src/adapter/utils/stream.ts +88 -0
  46. package/plugins/acryl-node-server/src/adapter/utils.ts +96 -0
  47. package/plugins/acryl-node-server/src/adapter/websocket-types.ts +45 -0
  48. package/plugins/acryl-node-server/src/adapter/websocket.ts +375 -0
  49. package/plugins/acryl-node-server/src/index.ts +19 -6
  50. package/plugins/acryl-node-server/tests/adapter/app.ts +76 -0
  51. package/plugins/acryl-node-server/tests/adapter/assets/.static/plain.txt +1 -0
  52. package/plugins/acryl-node-server/tests/adapter/assets/favicon.ico +0 -0
  53. package/plugins/acryl-node-server/tests/adapter/assets/secret.txt +1 -0
  54. package/plugins/acryl-node-server/tests/adapter/assets/static/acryl.html +1 -0
  55. package/plugins/acryl-node-server/tests/adapter/assets/static/admin/secret.txt +1 -0
  56. package/plugins/acryl-node-server/tests/adapter/assets/static/data.json +5 -0
  57. package/plugins/acryl-node-server/tests/adapter/assets/static/extensionless +1 -0
  58. package/plugins/acryl-node-server/tests/adapter/assets/static/foo..bar.txt +0 -0
  59. package/plugins/acryl-node-server/tests/adapter/assets/static/index.html +1 -0
  60. package/plugins/acryl-node-server/tests/adapter/assets/static/plain.txt +1 -0
  61. package/plugins/acryl-node-server/tests/adapter/assets/static-with-context-path/plain.txt +1 -0
  62. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.bin +1 -0
  63. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.bin.br +1 -0
  64. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt +1 -0
  65. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt.br +1 -0
  66. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt.zst +1 -0
  67. package/plugins/acryl-node-server/tests/adapter/conninfo.test.ts +34 -0
  68. package/plugins/acryl-node-server/tests/adapter/content-length.test.ts +248 -0
  69. package/plugins/acryl-node-server/tests/adapter/early-hints.test.ts +457 -0
  70. package/plugins/acryl-node-server/tests/adapter/fixtures/keys/agent1-cert.pem +23 -0
  71. package/plugins/acryl-node-server/tests/adapter/fixtures/keys/agent1-key.pem +27 -0
  72. package/plugins/acryl-node-server/tests/adapter/headers.test.ts +252 -0
  73. package/plugins/acryl-node-server/tests/adapter/helpers/request.ts +190 -0
  74. package/plugins/acryl-node-server/tests/adapter/listener.test.ts +727 -0
  75. package/plugins/acryl-node-server/tests/adapter/request-client-disconnect.test.ts +189 -0
  76. package/plugins/acryl-node-server/tests/adapter/request.test.ts +1230 -0
  77. package/plugins/acryl-node-server/tests/adapter/response.test.ts +285 -0
  78. package/plugins/acryl-node-server/tests/adapter/serve-static.test.ts +849 -0
  79. package/plugins/acryl-node-server/tests/adapter/server-socket.test.ts +790 -0
  80. package/plugins/acryl-node-server/tests/adapter/server.test.ts +1302 -0
  81. package/plugins/acryl-node-server/tests/adapter/setup.ts +16 -0
  82. package/plugins/acryl-node-server/tests/adapter/url.test.ts +76 -0
  83. package/plugins/acryl-node-server/tests/adapter/utils/response.test.ts +23 -0
  84. package/plugins/acryl-node-server/tests/adapter/utils/stream.test.ts +75 -0
  85. package/plugins/acryl-node-server/tests/adapter/utils.test.ts +235 -0
  86. package/plugins/acryl-node-server/tests/adapter/websocket.test.ts +260 -0
  87. package/plugins/acryl-node-server/tests/server.spec.ts +44 -8
  88. package/plugins/acryl-node-server/upstream.json +3 -3
  89. package/plugins/acryl-router/LICENSE +26 -0
  90. package/plugins/acryl-router/plugin.json +2 -1
  91. package/plugins/acryl-router/src/client.ts +2 -0
  92. package/plugins/acryl-router/src/connection.ts +42 -0
  93. package/plugins/acryl-router/src/core/app-base.ts +544 -0
  94. package/plugins/acryl-router/src/core/app.test.ts +3833 -0
  95. package/plugins/acryl-router/src/core/app.ts +34 -0
  96. package/plugins/acryl-router/src/core/client/client.test.ts +2313 -0
  97. package/plugins/acryl-router/src/core/client/client.ts +249 -0
  98. package/plugins/acryl-router/src/core/client/fetch-result-please.ts +95 -0
  99. package/plugins/acryl-router/src/core/client/index.ts +17 -0
  100. package/plugins/acryl-router/src/core/client/types.test.ts +149 -0
  101. package/plugins/acryl-router/src/core/client/types.ts +393 -0
  102. package/plugins/acryl-router/src/core/client/utils.test.ts +405 -0
  103. package/plugins/acryl-router/src/core/client/utils.ts +117 -0
  104. package/plugins/acryl-router/src/core/compose.test.ts +751 -0
  105. package/plugins/acryl-router/src/core/compose.ts +73 -0
  106. package/plugins/acryl-router/src/core/context.test.ts +629 -0
  107. package/plugins/acryl-router/src/core/context.ts +781 -0
  108. package/plugins/acryl-router/src/core/helper/accepts/accepts.test.ts +344 -0
  109. package/plugins/acryl-router/src/core/helper/accepts/accepts.ts +96 -0
  110. package/plugins/acryl-router/src/core/helper/accepts/index.ts +6 -0
  111. package/plugins/acryl-router/src/core/helper/adapter/index.test.ts +55 -0
  112. package/plugins/acryl-router/src/core/helper/adapter/index.ts +90 -0
  113. package/plugins/acryl-router/src/core/helper/conninfo/index.ts +6 -0
  114. package/plugins/acryl-router/src/core/helper/conninfo/types.ts +45 -0
  115. package/plugins/acryl-router/src/core/helper/cookie/index.test.ts +510 -0
  116. package/plugins/acryl-router/src/core/helper/cookie/index.ts +146 -0
  117. package/plugins/acryl-router/src/core/helper/factory/index.test.ts +434 -0
  118. package/plugins/acryl-router/src/core/helper/factory/index.ts +375 -0
  119. package/plugins/acryl-router/src/core/helper/html/index.test.ts +78 -0
  120. package/plugins/acryl-router/src/core/helper/html/index.ts +57 -0
  121. package/plugins/acryl-router/src/core/helper/route/index.test.ts +259 -0
  122. package/plugins/acryl-router/src/core/helper/route/index.ts +144 -0
  123. package/plugins/acryl-router/src/core/helper/streaming/index.ts +9 -0
  124. package/plugins/acryl-router/src/core/helper/streaming/sse.ts +101 -0
  125. package/plugins/acryl-router/src/core/helper/streaming/stream.test.ts +128 -0
  126. package/plugins/acryl-router/src/core/helper/streaming/stream.ts +45 -0
  127. package/plugins/acryl-router/src/core/helper/streaming/text.test.ts +34 -0
  128. package/plugins/acryl-router/src/core/helper/streaming/text.ts +15 -0
  129. package/plugins/acryl-router/src/core/helper/streaming/utils.ts +11 -0
  130. package/plugins/acryl-router/src/core/helper/testing/index.test.ts +42 -0
  131. package/plugins/acryl-router/src/core/helper/testing/index.ts +28 -0
  132. package/plugins/acryl-router/src/core/helper/websocket/index.test.ts +124 -0
  133. package/plugins/acryl-router/src/core/helper/websocket/index.ts +140 -0
  134. package/plugins/acryl-router/src/core/http-exception.test.ts +45 -0
  135. package/plugins/acryl-router/src/core/http-exception.ts +77 -0
  136. package/plugins/acryl-router/src/core/index.ts +53 -0
  137. package/plugins/acryl-router/src/core/middleware/basic-auth/index.test.ts +434 -0
  138. package/plugins/acryl-router/src/core/middleware/basic-auth/index.ts +152 -0
  139. package/plugins/acryl-router/src/core/middleware/bearer-auth/index.test.ts +1018 -0
  140. package/plugins/acryl-router/src/core/middleware/bearer-auth/index.ts +220 -0
  141. package/plugins/acryl-router/src/core/middleware/body-limit/index.test.ts +273 -0
  142. package/plugins/acryl-router/src/core/middleware/body-limit/index.ts +110 -0
  143. package/plugins/acryl-router/src/core/middleware/combine/index.test.ts +355 -0
  144. package/plugins/acryl-router/src/core/middleware/combine/index.ts +171 -0
  145. package/plugins/acryl-router/src/core/middleware/compress/index.test.ts +535 -0
  146. package/plugins/acryl-router/src/core/middleware/compress/index.ts +142 -0
  147. package/plugins/acryl-router/src/core/middleware/context-storage/index.test.ts +43 -0
  148. package/plugins/acryl-router/src/core/middleware/context-storage/index.ts +58 -0
  149. package/plugins/acryl-router/src/core/middleware/cors/index.test.ts +532 -0
  150. package/plugins/acryl-router/src/core/middleware/cors/index.ts +163 -0
  151. package/plugins/acryl-router/src/core/middleware/csrf/index.test.ts +534 -0
  152. package/plugins/acryl-router/src/core/middleware/csrf/index.ts +150 -0
  153. package/plugins/acryl-router/src/core/middleware/etag/digest.test.ts +21 -0
  154. package/plugins/acryl-router/src/core/middleware/etag/digest.ts +95 -0
  155. package/plugins/acryl-router/src/core/middleware/etag/index.test.ts +503 -0
  156. package/plugins/acryl-router/src/core/middleware/etag/index.ts +135 -0
  157. package/plugins/acryl-router/src/core/middleware/ip-restriction/index.test.ts +217 -0
  158. package/plugins/acryl-router/src/core/middleware/ip-restriction/index.ts +278 -0
  159. package/plugins/acryl-router/src/core/middleware/jwk/index.test.ts +1135 -0
  160. package/plugins/acryl-router/src/core/middleware/jwk/index.ts +1 -0
  161. package/plugins/acryl-router/src/core/middleware/jwk/jwk.ts +195 -0
  162. package/plugins/acryl-router/src/core/middleware/jwk/keys.test.json +48 -0
  163. package/plugins/acryl-router/src/core/middleware/jwt/index.test.ts +802 -0
  164. package/plugins/acryl-router/src/core/middleware/jwt/index.ts +9 -0
  165. package/plugins/acryl-router/src/core/middleware/jwt/jwt.ts +189 -0
  166. package/plugins/acryl-router/src/core/middleware/language/index.test.ts +552 -0
  167. package/plugins/acryl-router/src/core/middleware/language/index.ts +12 -0
  168. package/plugins/acryl-router/src/core/middleware/language/language.ts +314 -0
  169. package/plugins/acryl-router/src/core/middleware/logger/index.test.ts +204 -0
  170. package/plugins/acryl-router/src/core/middleware/logger/index.ts +95 -0
  171. package/plugins/acryl-router/src/core/middleware/method-not-allowed/index.test.ts +292 -0
  172. package/plugins/acryl-router/src/core/middleware/method-not-allowed/index.ts +141 -0
  173. package/plugins/acryl-router/src/core/middleware/method-override/index.test.ts +221 -0
  174. package/plugins/acryl-router/src/core/middleware/method-override/index.ts +147 -0
  175. package/plugins/acryl-router/src/core/middleware/powered-by/index.test.ts +37 -0
  176. package/plugins/acryl-router/src/core/middleware/powered-by/index.ts +35 -0
  177. package/plugins/acryl-router/src/core/middleware/pretty-json/index.test.ts +218 -0
  178. package/plugins/acryl-router/src/core/middleware/pretty-json/index.ts +67 -0
  179. package/plugins/acryl-router/src/core/middleware/request-id/index.test.ts +168 -0
  180. package/plugins/acryl-router/src/core/middleware/request-id/index.ts +7 -0
  181. package/plugins/acryl-router/src/core/middleware/request-id/request-id.ts +59 -0
  182. package/plugins/acryl-router/src/core/middleware/secure-headers/index.test.ts +671 -0
  183. package/plugins/acryl-router/src/core/middleware/secure-headers/index.ts +8 -0
  184. package/plugins/acryl-router/src/core/middleware/secure-headers/permissions-policy.ts +101 -0
  185. package/plugins/acryl-router/src/core/middleware/secure-headers/secure-headers.ts +340 -0
  186. package/plugins/acryl-router/src/core/middleware/serve-static/index.test.ts +450 -0
  187. package/plugins/acryl-router/src/core/middleware/serve-static/index.ts +135 -0
  188. package/plugins/acryl-router/src/core/middleware/serve-static/path.test.ts +50 -0
  189. package/plugins/acryl-router/src/core/middleware/serve-static/path.ts +25 -0
  190. package/plugins/acryl-router/src/core/middleware/timeout/index.test.ts +70 -0
  191. package/plugins/acryl-router/src/core/middleware/timeout/index.ts +58 -0
  192. package/plugins/acryl-router/src/core/middleware/timing/index.test.ts +310 -0
  193. package/plugins/acryl-router/src/core/middleware/timing/index.ts +8 -0
  194. package/plugins/acryl-router/src/core/middleware/timing/timing.ts +256 -0
  195. package/plugins/acryl-router/src/core/middleware/trailing-slash/index.test.ts +381 -0
  196. package/plugins/acryl-router/src/core/middleware/trailing-slash/index.ts +156 -0
  197. package/plugins/acryl-router/src/core/request/constants.ts +1 -0
  198. package/plugins/acryl-router/src/core/request.test.ts +751 -0
  199. package/plugins/acryl-router/src/core/request.ts +502 -0
  200. package/plugins/acryl-router/src/core/router/common.case.test.ts +1132 -0
  201. package/plugins/acryl-router/src/core/router/linear-router/index.ts +6 -0
  202. package/plugins/acryl-router/src/core/router/linear-router/router.test.ts +80 -0
  203. package/plugins/acryl-router/src/core/router/linear-router/router.ts +152 -0
  204. package/plugins/acryl-router/src/core/router/pattern-router/index.ts +6 -0
  205. package/plugins/acryl-router/src/core/router/pattern-router/router.test.ts +46 -0
  206. package/plugins/acryl-router/src/core/router/pattern-router/router.ts +54 -0
  207. package/plugins/acryl-router/src/core/router/reg-exp-router/index.ts +7 -0
  208. package/plugins/acryl-router/src/core/router/reg-exp-router/matcher.ts +33 -0
  209. package/plugins/acryl-router/src/core/router/reg-exp-router/node.ts +170 -0
  210. package/plugins/acryl-router/src/core/router/reg-exp-router/prepared-router.test.ts +154 -0
  211. package/plugins/acryl-router/src/core/router/reg-exp-router/prepared-router.ts +165 -0
  212. package/plugins/acryl-router/src/core/router/reg-exp-router/router.test.ts +265 -0
  213. package/plugins/acryl-router/src/core/router/reg-exp-router/router.ts +174 -0
  214. package/plugins/acryl-router/src/core/router/reg-exp-router/trie.ts +84 -0
  215. package/plugins/acryl-router/src/core/router/smart-router/index.ts +6 -0
  216. package/plugins/acryl-router/src/core/router/smart-router/router.test.ts +13 -0
  217. package/plugins/acryl-router/src/core/router/smart-router/router.ts +70 -0
  218. package/plugins/acryl-router/src/core/router/trie-router/index.ts +6 -0
  219. package/plugins/acryl-router/src/core/router/trie-router/node.test.ts +896 -0
  220. package/plugins/acryl-router/src/core/router/trie-router/node.ts +221 -0
  221. package/plugins/acryl-router/src/core/router/trie-router/router.test.ts +8 -0
  222. package/plugins/acryl-router/src/core/router/trie-router/router.ts +18 -0
  223. package/plugins/acryl-router/src/core/router/utils.ts +1 -0
  224. package/plugins/acryl-router/src/core/router.ts +103 -0
  225. package/plugins/acryl-router/src/core/types.test.ts +4134 -0
  226. package/plugins/acryl-router/src/core/types.ts +2782 -0
  227. package/plugins/acryl-router/src/core/utils/accept.test.ts +299 -0
  228. package/plugins/acryl-router/src/core/utils/accept.ts +268 -0
  229. package/plugins/acryl-router/src/core/utils/basic-auth.test.ts +56 -0
  230. package/plugins/acryl-router/src/core/utils/basic-auth.ts +26 -0
  231. package/plugins/acryl-router/src/core/utils/body.test.ts +496 -0
  232. package/plugins/acryl-router/src/core/utils/body.ts +263 -0
  233. package/plugins/acryl-router/src/core/utils/buffer.test.ts +147 -0
  234. package/plugins/acryl-router/src/core/utils/buffer.ts +117 -0
  235. package/plugins/acryl-router/src/core/utils/color.test.ts +23 -0
  236. package/plugins/acryl-router/src/core/utils/color.ts +56 -0
  237. package/plugins/acryl-router/src/core/utils/compress.ts +10 -0
  238. package/plugins/acryl-router/src/core/utils/concurrent.test.ts +69 -0
  239. package/plugins/acryl-router/src/core/utils/concurrent.ts +55 -0
  240. package/plugins/acryl-router/src/core/utils/constants.ts +4 -0
  241. package/plugins/acryl-router/src/core/utils/cookie.test.ts +460 -0
  242. package/plugins/acryl-router/src/core/utils/cookie.ts +281 -0
  243. package/plugins/acryl-router/src/core/utils/crypto.test.ts +43 -0
  244. package/plugins/acryl-router/src/core/utils/crypto.ts +58 -0
  245. package/plugins/acryl-router/src/core/utils/encode.test.ts +61 -0
  246. package/plugins/acryl-router/src/core/utils/encode.ts +34 -0
  247. package/plugins/acryl-router/src/core/utils/filepath.test.ts +78 -0
  248. package/plugins/acryl-router/src/core/utils/filepath.ts +60 -0
  249. package/plugins/acryl-router/src/core/utils/handler.ts +15 -0
  250. package/plugins/acryl-router/src/core/utils/headers.ts +355 -0
  251. package/plugins/acryl-router/src/core/utils/html.test.ts +16 -0
  252. package/plugins/acryl-router/src/core/utils/html.ts +182 -0
  253. package/plugins/acryl-router/src/core/utils/http-status.ts +72 -0
  254. package/plugins/acryl-router/src/core/utils/ipaddr.test.ts +149 -0
  255. package/plugins/acryl-router/src/core/utils/ipaddr.ts +374 -0
  256. package/plugins/acryl-router/src/core/utils/jwt/index.ts +7 -0
  257. package/plugins/acryl-router/src/core/utils/jwt/jwa.test.ts +86 -0
  258. package/plugins/acryl-router/src/core/utils/jwt/jwa.ts +38 -0
  259. package/plugins/acryl-router/src/core/utils/jwt/jws.ts +235 -0
  260. package/plugins/acryl-router/src/core/utils/jwt/jwt.test.ts +1788 -0
  261. package/plugins/acryl-router/src/core/utils/jwt/jwt.ts +292 -0
  262. package/plugins/acryl-router/src/core/utils/jwt/types.ts +157 -0
  263. package/plugins/acryl-router/src/core/utils/jwt/utf8.ts +7 -0
  264. package/plugins/acryl-router/src/core/utils/mime.test.ts +64 -0
  265. package/plugins/acryl-router/src/core/utils/mime.ts +94 -0
  266. package/plugins/acryl-router/src/core/utils/stream.test.ts +212 -0
  267. package/plugins/acryl-router/src/core/utils/stream.ts +107 -0
  268. package/plugins/acryl-router/src/core/utils/types.test.ts +295 -0
  269. package/plugins/acryl-router/src/core/utils/types.ts +123 -0
  270. package/plugins/acryl-router/src/core/utils/url.test.ts +384 -0
  271. package/plugins/acryl-router/src/core/utils/url.ts +337 -0
  272. package/plugins/acryl-router/src/core/validator/index.ts +8 -0
  273. package/plugins/acryl-router/src/core/validator/utils.test.ts +82 -0
  274. package/plugins/acryl-router/src/core/validator/utils.ts +69 -0
  275. package/plugins/acryl-router/src/core/validator/validator.test.ts +1488 -0
  276. package/plugins/acryl-router/src/core/validator/validator.ts +185 -0
  277. package/plugins/acryl-router/src/helpers/index.ts +13 -3
  278. package/plugins/acryl-router/src/helpers/schema-validator.ts +35 -0
  279. package/plugins/acryl-router/src/index.ts +6 -6
  280. package/plugins/acryl-router/src/middleware/index.ts +25 -6
  281. package/plugins/acryl-router/src/middleware/loopback-only.ts +53 -14
  282. package/plugins/acryl-router/src/service.ts +262 -64
  283. package/plugins/acryl-router/tests/docs-examples.spec.ts +152 -0
  284. package/plugins/acryl-router/tests/live-install.spec.ts +2 -2
  285. package/plugins/acryl-router/tests/loader.spec.ts +3 -2
  286. package/plugins/acryl-router/tests/loopback-only.spec.ts +80 -0
  287. package/plugins/acryl-router/tests/service.spec.ts +281 -66
  288. package/plugins/acryl-router/tests/shadow-state.spec.ts +79 -0
  289. package/plugins/acryl-router/upstream.json +2 -2
  290. package/plugins/acryl-router-http/plugin.json +2 -1
  291. package/plugins/acryl-router-http/src/bridge.ts +3 -1
  292. package/plugins/acryl-router-http/tests/adapter.spec.ts +16 -12
  293. package/plugins/acryl-router-http/tests/surfaces.spec.ts +12 -10
  294. package/plugins/acryl-router-http/upstream.json +3 -3
  295. package/plugins/acryl-state/NOTICE +30 -0
  296. package/plugins/acryl-state/plugin.json +1 -1
  297. package/plugins/acryl-state/src/cell.ts +1 -1
  298. package/plugins/acryl-state/src/service.ts +33 -15
  299. package/plugins/acryl-state/upstream.json +6 -4
  300. package/plugins/builder/script.ts +4 -7
  301. package/plugins/extensions/index.ts +6 -5
  302. package/plugins/extensions/resolve.ts +28 -3
  303. package/plugins/openai/prompt.ts +9 -7
  304. package/plugins/tsconfig.json +5 -4
  305. package/shared/AGENTS.core.md +54 -0
  306. package/templates/agent/AGENTS.md +6 -0
  307. package/templates/agent/README.md +2 -2
  308. package/templates/agent/blend.yaml +1 -1
  309. package/templates/agent/src/routes.ts +37 -27
  310. package/templates/agent/test/model.test.ts +2 -1
  311. package/templates/agent/tsconfig.json +8 -5
  312. package/templates/blank/AGENTS.md +5 -0
  313. package/templates/blank/README.md +1 -1
  314. package/templates/blank/src/routes.ts +6 -4
  315. package/templates/blank/tsconfig.json +2 -4
  316. package/templates/todo/AGENTS.md +5 -0
  317. package/templates/todo/src/page.ts +1 -1
  318. package/templates/todo/src/todo.ts +58 -29
  319. package/templates/todo/tsconfig.json +2 -4
  320. package/templates/todo/upstream.json +2 -2
  321. package/plugins/acryl-router/src/compose.ts +0 -21
  322. package/plugins/acryl-router/src/context.ts +0 -59
  323. package/plugins/acryl-router/src/helpers/read-body.ts +0 -59
  324. package/plugins/acryl-router/src/helpers/sse.ts +0 -83
  325. package/plugins/acryl-router/src/helpers/validate.ts +0 -44
  326. package/plugins/acryl-router/src/http-error.ts +0 -21
  327. package/plugins/acryl-router/src/middleware/body-limit.ts +0 -25
  328. package/plugins/acryl-router/src/middleware/cors.ts +0 -46
  329. package/plugins/acryl-router/src/middleware/logger.ts +0 -21
  330. package/plugins/acryl-router/src/middleware/request-id.ts +0 -24
  331. package/plugins/acryl-router/src/middleware/secure-headers.ts +0 -23
  332. package/plugins/acryl-router/src/pattern.ts +0 -89
  333. package/plugins/acryl-router/src/registrar.ts +0 -90
  334. package/plugins/acryl-router/src/table.ts +0 -156
  335. package/plugins/acryl-router/src/types.ts +0 -73
  336. package/plugins/acryl-router/src/util.ts +0 -23
  337. package/plugins/acryl-router/tests/api.spec.ts +0 -223
  338. package/plugins/acryl-router/tests/conformance.ts +0 -170
  339. package/plugins/acryl-router/tests/helpers.spec.ts +0 -172
  340. package/plugins/acryl-router/tests/middleware.spec.ts +0 -183
  341. package/plugins/acryl-router/tests/table.spec.ts +0 -47
@@ -0,0 +1,70 @@
1
+ import type { HTTPExceptionFunction } from './index.ts'
2
+ import { timeout } from './index.ts'
3
+ import type { Context } from '../../context.ts'
4
+ import { App } from '../../app.ts'
5
+ import { HTTPException } from '../../http-exception.ts'
6
+
7
+ describe('Timeout API', () => {
8
+ const app = new App()
9
+
10
+ app.use('/slow-endpoint', timeout(1000))
11
+ app.use(
12
+ '/slow-endpoint/custom',
13
+ timeout(
14
+ 1100,
15
+ () => new HTTPException(408, { message: 'Request timeout. Please try again later.' })
16
+ )
17
+ )
18
+ const exception500: HTTPExceptionFunction = (context: Context) =>
19
+ new HTTPException(500, { message: `Internal Server Error at ${context.req.path}` })
20
+ app.use('/slow-endpoint/error', timeout(1200, exception500))
21
+ app.use('/normal-endpoint', timeout(1000))
22
+
23
+ app.get('/slow-endpoint', async (c) => {
24
+ await new Promise((resolve) => setTimeout(resolve, 1100))
25
+ return c.text('This should not show up')
26
+ })
27
+
28
+ app.get('/slow-endpoint/custom', async (c) => {
29
+ await new Promise((resolve) => setTimeout(resolve, 1200))
30
+ return c.text('This should not show up')
31
+ })
32
+
33
+ app.get('/slow-endpoint/error', async (c) => {
34
+ await new Promise((resolve) => setTimeout(resolve, 1300))
35
+ return c.text('This should not show up')
36
+ })
37
+
38
+ app.get('/normal-endpoint', async (c) => {
39
+ await new Promise((resolve) => setTimeout(resolve, 900))
40
+ return c.text('This should not show up')
41
+ })
42
+
43
+ it('Should trigger default timeout exception', async () => {
44
+ const res = await app.request('http://localhost/slow-endpoint')
45
+ expect(res).not.toBeNull()
46
+ expect(res.status).toBe(504)
47
+ expect(await res.text()).toContain('Gateway Timeout')
48
+ })
49
+
50
+ it('Should apply custom exception with function', async () => {
51
+ const res = await app.request('http://localhost/slow-endpoint/custom')
52
+ expect(res).not.toBeNull()
53
+ expect(res.status).toBe(408)
54
+ expect(await res.text()).toContain('Request timeout. Please try again later.')
55
+ })
56
+
57
+ it('Error timeout with custom status code and message', async () => {
58
+ const res = await app.request('http://localhost/slow-endpoint/error')
59
+ expect(res).not.toBeNull()
60
+ expect(res.status).toBe(500)
61
+ expect(await res.text()).toContain('Internal Server Error at /slow-endpoint/error')
62
+ })
63
+
64
+ it('No Timeout should pass', async () => {
65
+ const res = await app.request('http://localhost/normal-endpoint')
66
+ expect(res).not.toBeNull()
67
+ expect(res.status).toBe(200)
68
+ expect(await res.text()).toContain('This should not show up')
69
+ })
70
+ })
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @module
3
+ * Timeout Middleware for App.
4
+ */
5
+
6
+ import type { Context } from '../../context.ts'
7
+ import { HTTPException } from '../../http-exception.ts'
8
+ import type { MiddlewareHandler } from '../../types.ts'
9
+
10
+ export type HTTPExceptionFunction = (context: Context) => HTTPException
11
+
12
+ const defaultTimeoutException = new HTTPException(504, {
13
+ message: 'Gateway Timeout',
14
+ })
15
+
16
+ /**
17
+ * Timeout Middleware for App.
18
+ *
19
+ * @param {number} duration - The timeout duration in milliseconds.
20
+ * @param {HTTPExceptionFunction | HTTPException} [exception=defaultTimeoutException] - The exception to throw when the timeout occurs. Can be a function that returns an HTTPException or an HTTPException object.
21
+ * @returns {MiddlewareHandler} The middleware handler function.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * const app = new App()
26
+ *
27
+ * app.use(
28
+ * '/long-request',
29
+ * timeout(5000) // Set timeout to 5 seconds
30
+ * )
31
+ *
32
+ * app.get('/long-request', async (c) => {
33
+ * await someLongRunningFunction()
34
+ * return c.text('Completed within time limit')
35
+ * })
36
+ * ```
37
+ */
38
+ export const timeout = (
39
+ duration: number,
40
+ exception: HTTPExceptionFunction | HTTPException = defaultTimeoutException
41
+ ): MiddlewareHandler => {
42
+ return async function timeout(context, next) {
43
+ let timer: number | undefined
44
+ const timeoutPromise = new Promise<void>((_, reject) => {
45
+ timer = setTimeout(() => {
46
+ reject(typeof exception === 'function' ? exception(context) : exception)
47
+ }, duration) as unknown as number
48
+ })
49
+
50
+ try {
51
+ await Promise.race([next(), timeoutPromise])
52
+ } finally {
53
+ if (timer !== undefined) {
54
+ clearTimeout(timer)
55
+ }
56
+ }
57
+ }
58
+ }
@@ -0,0 +1,310 @@
1
+ import { endTime, setMetric, startTime, timing, wrapTime } from './index.ts'
2
+ import { App } from '../../app.ts'
3
+
4
+ describe('Server-Timing API', () => {
5
+ const app = new App()
6
+
7
+ const totalDescription = 'my total DescRipTion!'
8
+ const name = 'sleep'
9
+ const region = 'region'
10
+ const regionDesc = 'europe-west3'
11
+
12
+ app.use(
13
+ '*',
14
+ timing({
15
+ totalDescription,
16
+ })
17
+ )
18
+ app.get('/', (c) => c.text('/'))
19
+ app.get('/api', async (c) => {
20
+ startTime(c, name)
21
+ await new Promise((r) => setTimeout(r, 30))
22
+ endTime(c, name)
23
+
24
+ return c.text('api!')
25
+ })
26
+ app.get('/api-wrap', async (c) => {
27
+ await wrapTime(c, name, new Promise((r) => setTimeout(r, 30)))
28
+
29
+ return c.text('api!')
30
+ })
31
+ app.get('/api-wrap-throw', async (c) => {
32
+ try {
33
+ await wrapTime(c, name, new Promise((_, r) => setTimeout(r, 30)))
34
+ } catch (e) {
35
+ return c.text(`error :( ${e}`, 500)
36
+ }
37
+
38
+ return c.text('api!')
39
+ })
40
+ app.get('/cache', async (c) => {
41
+ setMetric(c, region, regionDesc)
42
+
43
+ return c.text('cache!')
44
+ })
45
+
46
+ const sub = new App()
47
+
48
+ sub.use(timing())
49
+ sub.get('/', (c) => c.text('sub'))
50
+ app.route('/sub', sub)
51
+
52
+ it('Should contain total duration', async () => {
53
+ const res = await app.request('http://localhost/')
54
+ expect(res).not.toBeNull()
55
+ expect(res.headers.has('server-timing')).toBeTruthy()
56
+ expect(res.headers.get('server-timing')?.includes('total;dur=')).toBeTruthy()
57
+ expect(res.headers.get('server-timing')?.includes(totalDescription)).toBeTruthy()
58
+ })
59
+
60
+ it('Should contain value metrics', async () => {
61
+ const res = await app.request('http://localhost/api')
62
+ expect(res).not.toBeNull()
63
+ expect(res.headers.has('server-timing')).toBeTruthy()
64
+ expect(res.headers.get('server-timing')?.includes(`${name};dur=`)).toBeTruthy()
65
+ expect(res.headers.get('server-timing')?.includes(name)).toBeTruthy()
66
+ })
67
+
68
+ it('Should contain value metrics, wrapped', async () => {
69
+ const res = await app.request('http://localhost/api-wrap')
70
+ expect(res).not.toBeNull()
71
+ expect(res.headers.has('server-timing')).toBeTruthy()
72
+ expect(res.headers.get('server-timing')?.includes(`${name};dur=`)).toBeTruthy()
73
+ expect(res.headers.get('server-timing')?.includes(name)).toBeTruthy()
74
+ })
75
+
76
+ it('Should contain value metrics, wrapped throw', async () => {
77
+ const res = await app.request('http://localhost/api-wrap-throw')
78
+ expect(res).not.toBeNull()
79
+ expect(res.headers.has('server-timing')).toBeTruthy()
80
+ expect(res.status).toBeGreaterThanOrEqual(500)
81
+ expect(res.headers.get('server-timing')?.includes(`${name};dur=`)).toBeTruthy()
82
+ expect(res.headers.get('server-timing')?.includes(name)).toBeTruthy()
83
+ })
84
+
85
+ it('Should contain value-less metrics', async () => {
86
+ const res = await app.request('http://localhost/cache')
87
+ expect(res).not.toBeNull()
88
+ expect(res.headers.has('server-timing')).toBeTruthy()
89
+ expect(
90
+ res.headers.get('server-timing')?.includes(`${region};desc="${regionDesc}"`)
91
+ ).toBeTruthy()
92
+ expect(res.headers.get('server-timing')?.includes(region)).toBeTruthy()
93
+ expect(res.headers.get('server-timing')?.includes(regionDesc)).toBeTruthy()
94
+ })
95
+
96
+ it('Should not be enabled if the main app has the timing middleware', async () => {
97
+ const consoleWarnSpy = vi.spyOn(console, 'warn')
98
+ const res = await app.request('/sub')
99
+ expect(res.status).toBe(200)
100
+ expect(res.headers.has('server-timing')).toBeTruthy()
101
+ expect(res.headers.get('server-timing')?.includes(totalDescription)).toBeTruthy()
102
+ expect(consoleWarnSpy).not.toHaveBeenCalled()
103
+ consoleWarnSpy.mockRestore()
104
+ })
105
+
106
+ describe('Should handle timing options', async () => {
107
+ it('Should handle autoEnd option', async () => {
108
+ const autoEndApp = new App()
109
+
110
+ autoEndApp.use('*', timing())
111
+ autoEndApp.get('/', (c) => {
112
+ startTime(c, 'test')
113
+ return c.text('/')
114
+ })
115
+
116
+ const res = await autoEndApp.request('/')
117
+
118
+ expect(res.status).toBe(200)
119
+ expect(res.headers.get('server-timing')).toContain('test;dur=')
120
+
121
+ const disabledAutoEndApp = new App()
122
+
123
+ disabledAutoEndApp.use('*', timing({ autoEnd: false }))
124
+ disabledAutoEndApp.get('/', (c) => {
125
+ startTime(c, 'test')
126
+ return c.text('/')
127
+ })
128
+
129
+ const disabledRes = await disabledAutoEndApp.request('/')
130
+
131
+ expect(disabledRes.status).toBe(200)
132
+ expect(disabledRes.headers.get('server-timing')).not.toContain('test;dur=')
133
+ })
134
+
135
+ it('Should use enabled function return value', async () => {
136
+ const enabledApp = new App()
137
+ const enabled = vi.fn(() => false)
138
+
139
+ enabledApp.use('*', timing({ enabled }))
140
+ enabledApp.get('/', (c) => c.text('/'))
141
+
142
+ const res = await enabledApp.request('/')
143
+
144
+ expect(res.status).toBe(200)
145
+ expect(enabled).toHaveBeenCalled()
146
+ expect(res.headers.has('server-timing')).toBeFalsy()
147
+ })
148
+
149
+ it('Should handle total false and value-less metrics without description', async () => {
150
+ const metricApp = new App()
151
+
152
+ metricApp.use('*', timing({ total: false }))
153
+ metricApp.get('/', (c) => {
154
+ setMetric(c, 'test')
155
+ return c.text('/')
156
+ })
157
+
158
+ const res = await metricApp.request('/')
159
+
160
+ expect(res.status).toBe(200)
161
+ expect(res.headers.get('server-timing')).toBe('test')
162
+ })
163
+ })
164
+
165
+ describe('Should handle crossOrigin setting', async () => {
166
+ it('Should do nothing when crossOrigin is falsy', async () => {
167
+ const crossOriginApp = new App()
168
+
169
+ crossOriginApp.use(
170
+ '*',
171
+ timing({
172
+ crossOrigin: false,
173
+ })
174
+ )
175
+
176
+ crossOriginApp.get('/', (c) => c.text('/'))
177
+
178
+ const res = await crossOriginApp.request('http://localhost/')
179
+
180
+ expect(res).not.toBeNull()
181
+ expect(res.headers.has('server-timing')).toBeTruthy()
182
+ expect(res.headers.has('timing-allow-origin')).toBeFalsy()
183
+ })
184
+
185
+ it('Should set Timing-Allow-Origin to * when crossOrigin is true', async () => {
186
+ const crossOriginApp = new App()
187
+
188
+ crossOriginApp.use(
189
+ '*',
190
+ timing({
191
+ crossOrigin: true,
192
+ })
193
+ )
194
+
195
+ crossOriginApp.get('/', (c) => c.text('/'))
196
+
197
+ const res = await crossOriginApp.request('http://localhost/')
198
+
199
+ expect(res).not.toBeNull()
200
+ expect(res.headers.has('server-timing')).toBeTruthy()
201
+ expect(res.headers.has('timing-allow-origin')).toBeTruthy()
202
+ expect(res.headers.get('timing-allow-origin')).toBe('*')
203
+ })
204
+
205
+ it('Should set Timing-Allow-Origin to the value of crossOrigin when it is a string', async () => {
206
+ const crossOriginApp = new App()
207
+
208
+ crossOriginApp.use(
209
+ '*',
210
+ timing({
211
+ crossOrigin: 'https://example.com',
212
+ })
213
+ )
214
+
215
+ crossOriginApp.get('/', (c) => c.text('/'))
216
+
217
+ const res = await crossOriginApp.request('http://localhost/')
218
+
219
+ expect(res).not.toBeNull()
220
+ expect(res.headers.has('server-timing')).toBeTruthy()
221
+ expect(res.headers.has('timing-allow-origin')).toBeTruthy()
222
+ expect(res.headers.get('timing-allow-origin')).toBe('https://example.com')
223
+ })
224
+
225
+ it('Should set Timing-Allow-Origin to the return value of crossOrigin when it is a function', async () => {
226
+ const crossOriginApp = new App()
227
+
228
+ crossOriginApp.use(
229
+ '*',
230
+ timing({
231
+ crossOrigin: (c) => c.req.header('origin') ?? '*',
232
+ })
233
+ )
234
+
235
+ crossOriginApp.get('/', (c) => c.text('/'))
236
+
237
+ const res = await crossOriginApp.request('http://localhost/', {
238
+ headers: {
239
+ origin: 'https://example.com',
240
+ },
241
+ })
242
+
243
+ expect(res).not.toBeNull()
244
+ expect(res.headers.has('server-timing')).toBeTruthy()
245
+ expect(res.headers.has('timing-allow-origin')).toBeTruthy()
246
+ expect(res.headers.get('timing-allow-origin')).toBe('https://example.com')
247
+ })
248
+
249
+ it("Should use Date.now as backup if performance API isn't available", async () => {
250
+ const originalPerformance = globalThis.performance
251
+ // @ts-expect-error disable performance API for testing
252
+ delete globalThis.performance
253
+ const DateNowSpy = vi.spyOn(Date, 'now').mockReturnValue(123456789)
254
+
255
+ const app = new App().use(timing()).get('/', (c) => {
256
+ startTime(c, 'test')
257
+ endTime(c, 'test')
258
+ return c.text('hello')
259
+ })
260
+
261
+ const res = await app.request('/')
262
+ expect(res.status).toBe(200)
263
+
264
+ expect(DateNowSpy).toHaveBeenCalled()
265
+
266
+ DateNowSpy.mockRestore()
267
+ globalThis.performance = originalPerformance
268
+ })
269
+ })
270
+
271
+ describe('Configuration validation', async () => {
272
+ it('Should silently warn when no timing metrics are available', async () => {
273
+ const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
274
+
275
+ const app = new App().get('/', (c) => {
276
+ setMetric(c, 'test', 123)
277
+ startTime(c, 'test')
278
+ endTime(c, 'test')
279
+ return c.text('hello')
280
+ })
281
+
282
+ const res = await app.request('/')
283
+ expect(res.status).toBe(200)
284
+
285
+ expect(consoleWarnSpy).toHaveBeenNthCalledWith(
286
+ 2,
287
+ 'Metrics not initialized! Please add the `timing()` middleware to this route!'
288
+ )
289
+
290
+ consoleWarnSpy.mockRestore()
291
+ })
292
+
293
+ it('Should silently warn when trying to end a non-existent timer', async () => {
294
+ const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
295
+
296
+ const app = new App().use(timing()).get('/', (c) => {
297
+ setMetric(c, 'region', 'europe-west3')
298
+ endTime(c, 'nonExistentTimer')
299
+ return c.text('hello')
300
+ })
301
+
302
+ const res = await app.request('/')
303
+ expect(res.status).toBe(200)
304
+
305
+ expect(consoleWarnSpy).toHaveBeenCalledWith('Timer "nonExistentTimer" does not exist!')
306
+
307
+ consoleWarnSpy.mockRestore()
308
+ })
309
+ })
310
+ })
@@ -0,0 +1,8 @@
1
+ import type { TimingVariables } from './timing.ts'
2
+
3
+ export type { TimingVariables }
4
+ export { timing, setMetric, startTime, endTime, wrapTime } from './timing.ts'
5
+
6
+ declare module '../../context.ts' {
7
+ interface ContextVariableMap extends TimingVariables {}
8
+ }
@@ -0,0 +1,256 @@
1
+ /**
2
+ * @module
3
+ * Server-Timing Middleware for App.
4
+ */
5
+
6
+ import type { Context } from '../../context.ts'
7
+ import type { MiddlewareHandler } from '../../types.ts'
8
+ import '../../context.ts'
9
+
10
+ export type TimingVariables = {
11
+ metric?: {
12
+ headers: string[]
13
+ timers: Map<string, Timer>
14
+ }
15
+ }
16
+
17
+ interface Timer {
18
+ description?: string
19
+ start: number
20
+ }
21
+
22
+ interface TimingOptions {
23
+ total?: boolean
24
+ enabled?: boolean | ((c: Context) => boolean)
25
+ totalDescription?: string
26
+ autoEnd?: boolean
27
+ crossOrigin?: boolean | string | ((c: Context) => boolean | string)
28
+ }
29
+
30
+ const getTime = (): number => {
31
+ try {
32
+ return performance.now()
33
+ } catch {}
34
+ return Date.now()
35
+ }
36
+
37
+ /**
38
+ * Server-Timing Middleware for App.
39
+ *
40
+ *
41
+ * @param {TimingOptions} [config] - The options for the timing middleware.
42
+ * @param {boolean} [config.total=true] - Show the total response time.
43
+ * @param {boolean | ((c: Context) => boolean)} [config.enabled=true] - Whether timings should be added to the headers or not.
44
+ * @param {string} [config.totalDescription=Total Response Time] - Description for the total response time.
45
+ * @param {boolean} [config.autoEnd=true] - If `startTime()` should end automatically at the end of the request.
46
+ * @param {boolean | string | ((c: Context) => boolean | string)} [config.crossOrigin=false] - The origin this timings header should be readable.
47
+ * @returns {MiddlewareHandler} The middleware handler function.
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * const app = new App()
52
+ *
53
+ * // add the middleware to your router
54
+ * app.use(timing());
55
+ *
56
+ * app.get('/', async (c) => {
57
+ * // add custom metrics
58
+ * setMetric(c, 'region', 'europe-west3')
59
+ *
60
+ * // add custom metrics with timing, must be in milliseconds
61
+ * setMetric(c, 'custom', 23.8, 'My custom Metric')
62
+ *
63
+ * // start a new timer
64
+ * startTime(c, 'db');
65
+ *
66
+ * const data = await db.findMany(...);
67
+ *
68
+ * // end the timer
69
+ * endTime(c, 'db');
70
+ *
71
+ * return c.json({ response: data });
72
+ * });
73
+ * ```
74
+ */
75
+ export const timing = (config?: TimingOptions): MiddlewareHandler => {
76
+ const options: TimingOptions = {
77
+ total: true,
78
+ enabled: true,
79
+ totalDescription: 'Total Response Time',
80
+ autoEnd: true,
81
+ crossOrigin: false,
82
+ ...config,
83
+ }
84
+ return async function timing(c, next) {
85
+ const headers: string[] = []
86
+ const timers = new Map<string, Timer>()
87
+
88
+ if (c.get('metric')) {
89
+ return await next()
90
+ }
91
+
92
+ c.set('metric', { headers, timers })
93
+
94
+ if (options.total) {
95
+ startTime(c, 'total', options.totalDescription)
96
+ }
97
+ await next()
98
+
99
+ if (options.total) {
100
+ endTime(c, 'total')
101
+ }
102
+
103
+ if (options.autoEnd) {
104
+ timers.forEach((_, key) => {
105
+ endTime(c, key)
106
+ })
107
+ }
108
+
109
+ const enabled = typeof options.enabled === 'function' ? options.enabled(c) : options.enabled
110
+
111
+ if (enabled) {
112
+ c.res.headers.append('Server-Timing', headers.join(','))
113
+
114
+ const crossOrigin =
115
+ typeof options.crossOrigin === 'function' ? options.crossOrigin(c) : options.crossOrigin
116
+
117
+ if (crossOrigin) {
118
+ c.res.headers.append(
119
+ 'Timing-Allow-Origin',
120
+ typeof crossOrigin === 'string' ? crossOrigin : '*'
121
+ )
122
+ }
123
+ }
124
+ }
125
+ }
126
+
127
+ interface SetMetric {
128
+ (c: Context, name: string, value: number, description?: string, precision?: number): void
129
+
130
+ (c: Context, name: string, description?: string): void
131
+ }
132
+
133
+ /**
134
+ * Set a metric for the timing middleware.
135
+ *
136
+ * @param {Context} c - The context of the request.
137
+ * @param {string} name - The name of the metric.
138
+ * @param {number | string} [valueDescription] - The value or description of the metric.
139
+ * @param {string} [description] - The description of the metric.
140
+ * @param {number} [precision] - The precision of the metric value.
141
+ *
142
+ * @example
143
+ * ```ts
144
+ * setMetric(c, 'region', 'europe-west3')
145
+ * setMetric(c, 'custom', 23.8, 'My custom Metric')
146
+ * ```
147
+ */
148
+ export const setMetric: SetMetric = (
149
+ c: Context,
150
+ name: string,
151
+ valueDescription: number | string | undefined,
152
+ description?: string,
153
+ precision?: number
154
+ ) => {
155
+ const metrics = c.get('metric')
156
+ if (!metrics) {
157
+ console.warn('Metrics not initialized! Please add the `timing()` middleware to this route!')
158
+ return
159
+ }
160
+ if (typeof valueDescription === 'number') {
161
+ const dur = valueDescription.toFixed(precision || 1)
162
+
163
+ const metric = description ? `${name};dur=${dur};desc="${description}"` : `${name};dur=${dur}`
164
+
165
+ metrics.headers.push(metric)
166
+ } else {
167
+ // Value-less metric
168
+ const metric = valueDescription ? `${name};desc="${valueDescription}"` : `${name}`
169
+
170
+ metrics.headers.push(metric)
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Start a timer for the timing middleware.
176
+ *
177
+ * @param {Context} c - The context of the request.
178
+ * @param {string} name - The name of the timer.
179
+ * @param {string} [description] - The description of the timer.
180
+ *
181
+ * @example
182
+ * ```ts
183
+ * startTime(c, 'db')
184
+ * ```
185
+ */
186
+ export const startTime = (c: Context, name: string, description?: string) => {
187
+ const metrics = c.get('metric')
188
+ if (!metrics) {
189
+ console.warn('Metrics not initialized! Please add the `timing()` middleware to this route!')
190
+ return
191
+ }
192
+ metrics.timers.set(name, { description, start: getTime() })
193
+ }
194
+
195
+ /**
196
+ * End a timer for the timing middleware.
197
+ *
198
+ * @param {Context} c - The context of the request.
199
+ * @param {string} name - The name of the timer.
200
+ * @param {number} [precision] - The precision of the timer value.
201
+ *
202
+ * @example
203
+ * ```ts
204
+ * endTime(c, 'db')
205
+ * ```
206
+ */
207
+ export const endTime = (c: Context, name: string, precision?: number) => {
208
+ const metrics = c.get('metric')
209
+ if (!metrics) {
210
+ console.warn('Metrics not initialized! Please add the `timing()` middleware to this route!')
211
+ return
212
+ }
213
+ const timer = metrics.timers.get(name)
214
+ if (!timer) {
215
+ console.warn(`Timer "${name}" does not exist!`)
216
+ return
217
+ }
218
+ const { description, start } = timer
219
+
220
+ const duration = getTime() - start
221
+
222
+ setMetric(c, name, duration, description, precision)
223
+ metrics.timers.delete(name)
224
+ }
225
+
226
+ /**
227
+ * Wrap a Promise to capture its duration.
228
+ * @param {Context} c - The context of the request.
229
+ * @param {string} name - The name of the timer.
230
+ * @param {Promise<T>} callable - The Promise to time.
231
+ * @param {string} [description] - The description of the timer.
232
+ * @param {number} [precision] - The precision of the timer value.
233
+ *
234
+ * @example
235
+ * ```ts
236
+ * // Instead of this:
237
+ * const data = await db.findMany(...);
238
+ *
239
+ * // do this:
240
+ * const data = await wrapTime(c, 'query', db.findMany(...));
241
+ * ```
242
+ * */
243
+ export async function wrapTime<T>(
244
+ c: Context,
245
+ name: string,
246
+ callable: Promise<T>,
247
+ description?: string,
248
+ precision?: number
249
+ ): Promise<T> {
250
+ startTime(c, name, description)
251
+ try {
252
+ return await callable
253
+ } finally {
254
+ endTime(c, name, precision)
255
+ }
256
+ }