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,292 @@
1
+ /**
2
+ * @module
3
+ * JSON Web Token (JWT)
4
+ * https://datatracker.ietf.org/doc/html/rfc7519
5
+ */
6
+
7
+ import { decodeBase64Url, encodeBase64Url } from '../encode.ts'
8
+ import { AlgorithmTypes } from './jwa.ts'
9
+ import type { AsymmetricAlgorithm, SignatureAlgorithm, SymmetricAlgorithm } from './jwa.ts'
10
+ import { signing, verifying } from './jws.ts'
11
+ import type { AppJsonWebKey, SignatureKey } from './jws.ts'
12
+ import {
13
+ JwtAlgorithmMismatch,
14
+ JwtAlgorithmNotAllowed,
15
+ JwtAlgorithmRequired,
16
+ JwtHeaderInvalid,
17
+ JwtHeaderRequiresKid,
18
+ JwtPayloadRequiresAud,
19
+ JwtSymmetricAlgorithmNotAllowed,
20
+ JwtTokenAudience,
21
+ JwtTokenExpired,
22
+ JwtTokenInvalid,
23
+ JwtTokenIssuedAt,
24
+ JwtTokenIssuer,
25
+ JwtTokenNotBefore,
26
+ JwtTokenSignatureMismatched,
27
+ } from './types.ts'
28
+ import type { JWTPayload } from './types.ts'
29
+ import { utf8Decoder, utf8Encoder } from './utf8.ts'
30
+
31
+ const encodeJwtPart = (part: unknown): string =>
32
+ encodeBase64Url(utf8Encoder.encode(JSON.stringify(part)).buffer).replace(/=/g, '')
33
+ const encodeSignaturePart = (buf: ArrayBufferLike): string => encodeBase64Url(buf).replace(/=/g, '')
34
+
35
+ const decodeJwtPart = (part: string): TokenHeader | JWTPayload | undefined =>
36
+ JSON.parse(utf8Decoder.decode(decodeBase64Url(part)))
37
+
38
+ export interface TokenHeader {
39
+ alg: SignatureAlgorithm
40
+ typ?: 'JWT'
41
+ kid?: string
42
+ }
43
+
44
+ export function isTokenHeader(obj: unknown): obj is TokenHeader {
45
+ if (typeof obj === 'object' && obj !== null) {
46
+ const objWithAlg = obj as { [key: string]: unknown }
47
+ return (
48
+ 'alg' in objWithAlg &&
49
+ Object.values(AlgorithmTypes).includes(objWithAlg.alg as AlgorithmTypes) &&
50
+ (!('typ' in objWithAlg) || objWithAlg.typ === 'JWT')
51
+ )
52
+ }
53
+ return false
54
+ }
55
+
56
+ export const sign = async (
57
+ payload: JWTPayload,
58
+ privateKey: SignatureKey,
59
+ alg: SignatureAlgorithm = 'HS256'
60
+ ): Promise<string> => {
61
+ const encodedPayload = encodeJwtPart(payload)
62
+ let encodedHeader
63
+ if (typeof privateKey === 'object' && 'alg' in privateKey) {
64
+ alg = privateKey.alg as SignatureAlgorithm
65
+ encodedHeader = encodeJwtPart({ alg, typ: 'JWT', kid: privateKey.kid })
66
+ } else {
67
+ encodedHeader = encodeJwtPart({ alg, typ: 'JWT' })
68
+ }
69
+
70
+ const partialToken = `${encodedHeader}.${encodedPayload}`
71
+
72
+ const signaturePart = await signing(privateKey, alg, utf8Encoder.encode(partialToken))
73
+ const signature = encodeSignaturePart(signaturePart)
74
+
75
+ return `${partialToken}.${signature}`
76
+ }
77
+
78
+ export type VerifyOptions = {
79
+ /** The expected issuer used for verifying the token */
80
+ iss?: string | RegExp
81
+ /** Verify the `nbf` claim (default: `true`) */
82
+ nbf?: boolean
83
+ /** Verify the `exp` claim (default: `true`) */
84
+ exp?: boolean
85
+ /** Verify the `iat` claim (default: `true`) */
86
+ iat?: boolean
87
+ /** Acceptable audience(s) for the token */
88
+ aud?: string | string[] | RegExp
89
+ }
90
+
91
+ export type VerifyOptionsWithAlg = {
92
+ /** The algorithm used for decoding the token */
93
+ alg: SignatureAlgorithm
94
+ } & VerifyOptions
95
+
96
+ export const verify = async (
97
+ token: string,
98
+ publicKey: SignatureKey,
99
+ algOrOptions: SignatureAlgorithm | VerifyOptionsWithAlg
100
+ ): Promise<JWTPayload> => {
101
+ if (!algOrOptions) {
102
+ throw new JwtAlgorithmRequired()
103
+ }
104
+
105
+ const {
106
+ alg,
107
+ iss,
108
+ nbf = true,
109
+ exp = true,
110
+ iat = true,
111
+ aud,
112
+ } = typeof algOrOptions === 'string' ? { alg: algOrOptions } : algOrOptions
113
+
114
+ if (!alg) {
115
+ throw new JwtAlgorithmRequired()
116
+ }
117
+
118
+ const tokenParts = token.split('.')
119
+ if (tokenParts.length !== 3) {
120
+ throw new JwtTokenInvalid(token)
121
+ }
122
+
123
+ const { header, payload } = decode(token)
124
+ if (!isTokenHeader(header)) {
125
+ throw new JwtHeaderInvalid(header)
126
+ }
127
+ if (header.alg !== alg) {
128
+ throw new JwtAlgorithmMismatch(alg, header.alg)
129
+ }
130
+ const now = Math.floor(Date.now() / 1000)
131
+ if (nbf && payload.nbf !== undefined) {
132
+ if (typeof payload.nbf !== 'number' || !Number.isFinite(payload.nbf) || payload.nbf > now) {
133
+ throw new JwtTokenNotBefore(token)
134
+ }
135
+ }
136
+ if (exp && payload.exp !== undefined) {
137
+ if (typeof payload.exp !== 'number' || !Number.isFinite(payload.exp) || payload.exp <= now) {
138
+ throw new JwtTokenExpired(token)
139
+ }
140
+ }
141
+ if (iat && payload.iat !== undefined) {
142
+ if (typeof payload.iat !== 'number' || !Number.isFinite(payload.iat) || now < payload.iat) {
143
+ throw new JwtTokenIssuedAt(now, payload.iat)
144
+ }
145
+ }
146
+ if (iss) {
147
+ if (!payload.iss) {
148
+ throw new JwtTokenIssuer(iss, null)
149
+ }
150
+ if (typeof iss === 'string' && payload.iss !== iss) {
151
+ throw new JwtTokenIssuer(iss, payload.iss)
152
+ }
153
+ if (iss instanceof RegExp && !iss.test(payload.iss)) {
154
+ throw new JwtTokenIssuer(iss, payload.iss)
155
+ }
156
+ }
157
+
158
+ if (aud) {
159
+ if (!payload.aud) {
160
+ throw new JwtPayloadRequiresAud(payload)
161
+ }
162
+
163
+ const audiences = Array.isArray(payload.aud) ? payload.aud : [payload.aud]
164
+ const matched = audiences.some((payloadAud): boolean =>
165
+ aud instanceof RegExp
166
+ ? aud.test(payloadAud)
167
+ : typeof aud === 'string'
168
+ ? payloadAud === aud
169
+ : Array.isArray(aud) && aud.includes(payloadAud)
170
+ )
171
+ if (!matched) {
172
+ throw new JwtTokenAudience(aud, payload.aud)
173
+ }
174
+ }
175
+
176
+ const headerPayload = token.substring(0, token.lastIndexOf('.'))
177
+ let signature: Uint8Array<ArrayBuffer>
178
+ try {
179
+ signature = decodeBase64Url(tokenParts[2])
180
+ } catch {
181
+ throw new JwtTokenInvalid(token)
182
+ }
183
+ const verified = await verifying(publicKey, alg, signature, utf8Encoder.encode(headerPayload))
184
+ if (!verified) {
185
+ throw new JwtTokenSignatureMismatched(token)
186
+ }
187
+
188
+ return payload
189
+ }
190
+
191
+ // Symmetric algorithms that are not allowed for JWK verification
192
+ const symmetricAlgorithms: SymmetricAlgorithm[] = [
193
+ AlgorithmTypes.HS256,
194
+ AlgorithmTypes.HS384,
195
+ AlgorithmTypes.HS512,
196
+ ]
197
+
198
+ export const verifyWithJwks = async (
199
+ token: string,
200
+ options: {
201
+ keys?: AppJsonWebKey[]
202
+ jwks_uri?: string
203
+ verification?: VerifyOptions
204
+ allowedAlgorithms: readonly AsymmetricAlgorithm[]
205
+ },
206
+ init?: RequestInit
207
+ ): Promise<JWTPayload> => {
208
+ const verifyOpts = options.verification || {}
209
+
210
+ const header = decodeHeader(token)
211
+
212
+ if (!isTokenHeader(header)) {
213
+ throw new JwtHeaderInvalid(header)
214
+ }
215
+ if (!header.kid) {
216
+ throw new JwtHeaderRequiresKid(header)
217
+ }
218
+
219
+ // Reject symmetric algorithms (HS256, HS384, HS512) to prevent algorithm confusion attacks
220
+ if (symmetricAlgorithms.includes(header.alg as SymmetricAlgorithm)) {
221
+ throw new JwtSymmetricAlgorithmNotAllowed(header.alg)
222
+ }
223
+
224
+ // Validate against allowed algorithms
225
+ if (!options.allowedAlgorithms.includes(header.alg as AsymmetricAlgorithm)) {
226
+ throw new JwtAlgorithmNotAllowed(header.alg, options.allowedAlgorithms)
227
+ }
228
+
229
+ let verifyKeys = options.keys ? [...options.keys] : undefined
230
+
231
+ if (options.jwks_uri) {
232
+ const response = await fetch(options.jwks_uri, init)
233
+ if (!response.ok) {
234
+ throw new Error(`failed to fetch JWKS from ${options.jwks_uri}`)
235
+ }
236
+ const data = (await response.json()) as { keys?: JsonWebKey[] }
237
+ if (!data.keys) {
238
+ throw new Error('invalid JWKS response. "keys" field is missing')
239
+ }
240
+ if (!Array.isArray(data.keys)) {
241
+ throw new Error('invalid JWKS response. "keys" field is not an array')
242
+ }
243
+ verifyKeys ??= []
244
+ verifyKeys.push(...(data.keys as AppJsonWebKey[]))
245
+ } else if (!verifyKeys) {
246
+ throw new Error('verifyWithJwks requires options for either "keys" or "jwks_uri" or both')
247
+ }
248
+
249
+ const matchingKey = verifyKeys.find((key) => key.kid === header.kid)
250
+ if (!matchingKey) {
251
+ throw new JwtTokenInvalid(token)
252
+ }
253
+
254
+ // Verify that JWK's alg matches JWT header's alg when JWK has alg field
255
+ if (matchingKey.alg && matchingKey.alg !== header.alg) {
256
+ throw new JwtAlgorithmMismatch(matchingKey.alg, header.alg)
257
+ }
258
+
259
+ return await verify(token, matchingKey, {
260
+ alg: header.alg,
261
+ ...verifyOpts,
262
+ })
263
+ }
264
+
265
+ export const decode = (token: string): { header: TokenHeader; payload: JWTPayload } => {
266
+ const parts = token.split('.')
267
+ if (parts.length !== 3) {
268
+ throw new JwtTokenInvalid(token)
269
+ }
270
+ try {
271
+ const header = decodeJwtPart(parts[0]) as TokenHeader
272
+ const payload = decodeJwtPart(parts[1]) as JWTPayload
273
+ return {
274
+ header,
275
+ payload,
276
+ }
277
+ } catch {
278
+ throw new JwtTokenInvalid(token)
279
+ }
280
+ }
281
+
282
+ export const decodeHeader = (token: string): TokenHeader => {
283
+ const parts = token.split('.')
284
+ if (parts.length !== 3) {
285
+ throw new JwtTokenInvalid(token)
286
+ }
287
+ try {
288
+ return decodeJwtPart(parts[0]) as TokenHeader
289
+ } catch {
290
+ throw new JwtTokenInvalid(token)
291
+ }
292
+ }
@@ -0,0 +1,157 @@
1
+ /**
2
+ * @module
3
+ * Type definitions for JWT utilities.
4
+ */
5
+
6
+ export class JwtAlgorithmNotImplemented extends Error {
7
+ constructor(alg: string) {
8
+ super(`${alg} is not an implemented algorithm`)
9
+ this.name = 'JwtAlgorithmNotImplemented'
10
+ }
11
+ }
12
+
13
+ export class JwtAlgorithmRequired extends Error {
14
+ constructor() {
15
+ super('JWT verification requires "alg" option to be specified')
16
+ this.name = 'JwtAlgorithmRequired'
17
+ }
18
+ }
19
+
20
+ export class JwtAlgorithmMismatch extends Error {
21
+ constructor(expected: string, actual: string) {
22
+ super(`JWT algorithm mismatch: expected "${expected}", got "${actual}"`)
23
+ this.name = 'JwtAlgorithmMismatch'
24
+ }
25
+ }
26
+
27
+ export class JwtTokenInvalid extends Error {
28
+ constructor(token: string) {
29
+ super(`invalid JWT token: ${token}`)
30
+ this.name = 'JwtTokenInvalid'
31
+ }
32
+ }
33
+
34
+ export class JwtTokenNotBefore extends Error {
35
+ constructor(token: string) {
36
+ super(`token (${token}) is being used before it's valid`)
37
+ this.name = 'JwtTokenNotBefore'
38
+ }
39
+ }
40
+
41
+ export class JwtTokenExpired extends Error {
42
+ constructor(token: string) {
43
+ super(`token (${token}) expired`)
44
+ this.name = 'JwtTokenExpired'
45
+ }
46
+ }
47
+
48
+ export class JwtTokenIssuedAt extends Error {
49
+ constructor(currentTimestamp: number, iat: number) {
50
+ super(
51
+ `Invalid "iat" claim, must be a valid number lower than "${currentTimestamp}" (iat: "${iat}")`
52
+ )
53
+ this.name = 'JwtTokenIssuedAt'
54
+ }
55
+ }
56
+
57
+ export class JwtTokenIssuer extends Error {
58
+ constructor(expected: string | RegExp, iss: string | null) {
59
+ super(`expected issuer "${expected}", got ${iss ? `"${iss}"` : 'none'} `)
60
+ this.name = 'JwtTokenIssuer'
61
+ }
62
+ }
63
+
64
+ export class JwtHeaderInvalid extends Error {
65
+ constructor(header: object) {
66
+ super(`jwt header is invalid: ${JSON.stringify(header)}`)
67
+ this.name = 'JwtHeaderInvalid'
68
+ }
69
+ }
70
+
71
+ export class JwtHeaderRequiresKid extends Error {
72
+ constructor(header: object) {
73
+ super(`required "kid" in jwt header: ${JSON.stringify(header)}`)
74
+ this.name = 'JwtHeaderRequiresKid'
75
+ }
76
+ }
77
+
78
+ export class JwtSymmetricAlgorithmNotAllowed extends Error {
79
+ constructor(alg: string) {
80
+ super(`symmetric algorithm "${alg}" is not allowed for JWK verification`)
81
+ this.name = 'JwtSymmetricAlgorithmNotAllowed'
82
+ }
83
+ }
84
+
85
+ export class JwtAlgorithmNotAllowed extends Error {
86
+ constructor(alg: string, allowedAlgorithms: readonly string[]) {
87
+ super(`algorithm "${alg}" is not in the allowed list: [${allowedAlgorithms.join(', ')}]`)
88
+ this.name = 'JwtAlgorithmNotAllowed'
89
+ }
90
+ }
91
+
92
+ export class JwtTokenSignatureMismatched extends Error {
93
+ constructor(token: string) {
94
+ super(`token(${token}) signature mismatched`)
95
+ this.name = 'JwtTokenSignatureMismatched'
96
+ }
97
+ }
98
+
99
+ export class JwtPayloadRequiresAud extends Error {
100
+ constructor(payload: object) {
101
+ super(`required "aud" in jwt payload: ${JSON.stringify(payload)}`)
102
+ this.name = 'JwtPayloadRequiresAud'
103
+ }
104
+ }
105
+
106
+ export class JwtTokenAudience extends Error {
107
+ constructor(expected: string | string[] | RegExp, aud: string | string[]) {
108
+ super(
109
+ `expected audience "${
110
+ Array.isArray(expected) ? expected.join(', ') : expected
111
+ }", got "${aud}"`
112
+ )
113
+ this.name = 'JwtTokenAudience'
114
+ }
115
+ }
116
+
117
+ export const CryptoKeyUsage = {
118
+ Encrypt: 'encrypt',
119
+ Decrypt: 'decrypt',
120
+ Sign: 'sign',
121
+ Verify: 'verify',
122
+ DeriveKey: 'deriveKey',
123
+ DeriveBits: 'deriveBits',
124
+ WrapKey: 'wrapKey',
125
+ UnwrapKey: 'unwrapKey',
126
+ } as const
127
+ export type CryptoKeyUsage = (typeof CryptoKeyUsage)[keyof typeof CryptoKeyUsage]
128
+
129
+ /**
130
+ * JWT Payload
131
+ */
132
+ export type JWTPayload = {
133
+ [key: string]: unknown
134
+ /**
135
+ * The token is checked to ensure it has not expired.
136
+ */
137
+ exp?: number
138
+ /**
139
+ * The token is checked to ensure it is not being used before a specified time.
140
+ */
141
+ nbf?: number
142
+ /**
143
+ * The token is checked to ensure it is not issued in the future.
144
+ */
145
+ iat?: number
146
+ /**
147
+ * The token is checked to ensure it has been issued by a trusted issuer.
148
+ */
149
+ iss?: string
150
+
151
+ /**
152
+ * The token is checked to ensure it is intended for a specific audience.
153
+ */
154
+ aud?: string | string[]
155
+ }
156
+
157
+ export type { AppJsonWebKey } from './jws.ts'
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @module
3
+ * Functions for encoding/decoding UTF8.
4
+ */
5
+
6
+ export const utf8Encoder: TextEncoder = new TextEncoder()
7
+ export const utf8Decoder: TextDecoder = new TextDecoder()
@@ -0,0 +1,64 @@
1
+ import { getExtension, getMimeType } from './mime.ts'
2
+
3
+ const mime = {
4
+ m3u8: 'application/vnd.apple.mpegurl',
5
+ ts: 'video/mp2t',
6
+ }
7
+
8
+ describe('mime', () => {
9
+ it('getMimeType', () => {
10
+ expect(getMimeType('hello.txt')).toBe('text/plain; charset=utf-8')
11
+ expect(getMimeType('hello.html')).toBe('text/html; charset=utf-8')
12
+ expect(getMimeType('hello.json')).toBe('application/json')
13
+ expect(getMimeType('favicon.ico')).toBe('image/x-icon')
14
+ expect(getMimeType('good.morning.hello.gif')).toBe('image/gif')
15
+ expect(getMimeType('site.webmanifest')).toBe('application/manifest+json')
16
+ expect(getMimeType('goodmorninghellogif')).toBeUndefined()
17
+ expect(getMimeType('indexjs.abcd')).toBeUndefined()
18
+ expect(getMimeType('IMAGE.JPG')).toBe('image/jpeg')
19
+ })
20
+
21
+ it('getMimeType returns charset parameter for text and XML-based formats', () => {
22
+ expect(getMimeType('icon.svg')).toBe('image/svg+xml; charset=utf-8')
23
+ expect(getMimeType('page.xhtml')).toBe('application/xhtml+xml; charset=utf-8')
24
+ expect(getMimeType('feed.xml')).toBe('application/xml; charset=utf-8')
25
+ expect(getMimeType('events.ics')).toBe('text/calendar; charset=utf-8')
26
+ expect(getMimeType('script.mjs')).toBe('text/javascript; charset=utf-8')
27
+ expect(getMimeType('style.css')).toBe('text/css; charset=utf-8')
28
+ expect(getMimeType('data.csv')).toBe('text/csv; charset=utf-8')
29
+ })
30
+
31
+ it('getMimeType with custom mime', () => {
32
+ expect(getMimeType('morning-routine.m3u8', mime)).toBe('application/vnd.apple.mpegurl')
33
+ expect(getMimeType('morning-routine1.ts', mime)).toBe('video/mp2t')
34
+ expect(getMimeType('readme.txt', mime)).toBeUndefined()
35
+ })
36
+
37
+ it('getExtension', () => {
38
+ expect(getExtension('audio/aac')).toBe('aac')
39
+ expect(getExtension('video/x-msvideo')).toBe('avi')
40
+ expect(getExtension('image/avif')).toBe('avif')
41
+ expect(getExtension('text/css')).toBe('css')
42
+ expect(getExtension('text/html')).toBe('htm')
43
+ expect(getExtension('image/jpeg')).toBe('jpeg')
44
+ expect(getExtension('text/javascript')).toBe('js')
45
+ expect(getExtension('application/json')).toBe('json')
46
+ expect(getExtension('audio/mpeg')).toBe('mp3')
47
+ expect(getExtension('video/mp4')).toBe('mp4')
48
+ expect(getExtension('application/pdf')).toBe('pdf')
49
+ expect(getExtension('image/png')).toBe('png')
50
+ expect(getExtension('application/zip')).toBe('zip')
51
+ expect(getExtension('non/existent')).toBeUndefined()
52
+ })
53
+
54
+ it('getExtension matches MIME types regardless of charset/parameters', () => {
55
+ expect(getExtension('text/html; charset=utf-8')).toBe('htm')
56
+ expect(getExtension('text/plain; charset=utf-8')).toBe('txt')
57
+ expect(getExtension('image/svg+xml; charset=utf-8')).toBe('svg')
58
+ expect(getExtension('application/xml; charset=utf-8')).toBe('xml')
59
+ expect(getExtension('application/xhtml+xml; charset=utf-8')).toBe('xhtml')
60
+ expect(getExtension('text/css; charset=utf-16')).toBe('css')
61
+ expect(getExtension('image/svg+xml')).toBe('svg')
62
+ expect(getExtension('application/xml')).toBe('xml')
63
+ })
64
+ })
@@ -0,0 +1,94 @@
1
+ /**
2
+ * @module
3
+ * MIME utility.
4
+ */
5
+
6
+ export const getMimeType = (
7
+ filename: string,
8
+ mimes: Record<string, string> = baseMimes
9
+ ): string | undefined => {
10
+ const regexp = /\.([a-zA-Z0-9]+?)$/
11
+ const match = filename.match(regexp)
12
+ if (!match) {
13
+ return
14
+ }
15
+ return mimes[match[1].toLowerCase()]
16
+ }
17
+
18
+ export const getExtension = (mimeType: string): string | undefined => {
19
+ const baseType = mimeType.split(';', 1)[0].trim()
20
+ for (const ext in baseMimes) {
21
+ const stored = baseMimes[ext]
22
+ if (stored === mimeType || stored.split(';', 1)[0].trim() === baseType) {
23
+ return ext
24
+ }
25
+ }
26
+ }
27
+
28
+ export { baseMimes as mimes }
29
+
30
+ /**
31
+ * Union types for BaseMime
32
+ */
33
+ export type BaseMime = (typeof _baseMimes)[keyof typeof _baseMimes]
34
+
35
+ const _baseMimes = {
36
+ aac: 'audio/aac',
37
+ avi: 'video/x-msvideo',
38
+ avif: 'image/avif',
39
+ av1: 'video/av1',
40
+ bin: 'application/octet-stream',
41
+ bmp: 'image/bmp',
42
+ css: 'text/css; charset=utf-8',
43
+ csv: 'text/csv; charset=utf-8',
44
+ eot: 'application/vnd.ms-fontobject',
45
+ epub: 'application/epub+zip',
46
+ gif: 'image/gif',
47
+ gz: 'application/gzip',
48
+ htm: 'text/html; charset=utf-8',
49
+ html: 'text/html; charset=utf-8',
50
+ ico: 'image/x-icon',
51
+ ics: 'text/calendar; charset=utf-8',
52
+ jpeg: 'image/jpeg',
53
+ jpg: 'image/jpeg',
54
+ js: 'text/javascript; charset=utf-8',
55
+ json: 'application/json',
56
+ jsonld: 'application/ld+json',
57
+ map: 'application/json',
58
+ mid: 'audio/x-midi',
59
+ midi: 'audio/x-midi',
60
+ mjs: 'text/javascript; charset=utf-8',
61
+ mp3: 'audio/mpeg',
62
+ mp4: 'video/mp4',
63
+ mpeg: 'video/mpeg',
64
+ oga: 'audio/ogg',
65
+ ogv: 'video/ogg',
66
+ ogx: 'application/ogg',
67
+ opus: 'audio/opus',
68
+ otf: 'font/otf',
69
+ pdf: 'application/pdf',
70
+ png: 'image/png',
71
+ rtf: 'application/rtf',
72
+ svg: 'image/svg+xml; charset=utf-8',
73
+ tif: 'image/tiff',
74
+ tiff: 'image/tiff',
75
+ ts: 'video/mp2t',
76
+ ttf: 'font/ttf',
77
+ txt: 'text/plain; charset=utf-8',
78
+ wasm: 'application/wasm',
79
+ webm: 'video/webm',
80
+ weba: 'audio/webm',
81
+ webmanifest: 'application/manifest+json',
82
+ webp: 'image/webp',
83
+ woff: 'font/woff',
84
+ woff2: 'font/woff2',
85
+ xhtml: 'application/xhtml+xml; charset=utf-8',
86
+ xml: 'application/xml; charset=utf-8',
87
+ zip: 'application/zip',
88
+ '3gp': 'video/3gpp',
89
+ '3g2': 'video/3gpp2',
90
+ gltf: 'model/gltf+json',
91
+ glb: 'model/gltf-binary',
92
+ } as const
93
+
94
+ const baseMimes: Record<string, BaseMime> = _baseMimes