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,671 @@
1
+ import { NONCE, secureHeaders } from './index.ts'
2
+ import type { ContentSecurityPolicyOptionHandler } from './index.ts'
3
+ import { App } from '../../app.ts'
4
+ import { poweredBy } from '../powered-by/index.ts'
5
+
6
+ declare module '../../context.ts' {
7
+ interface ContextVariableMap {
8
+ ['test-scriptSrc-nonce']?: string
9
+ ['test-styleSrc-nonce']?: string
10
+ }
11
+ }
12
+
13
+ describe('Secure Headers Middleware', () => {
14
+ it('default middleware', async () => {
15
+ const app = new App()
16
+ app.use('*', secureHeaders())
17
+ app.get('/test', async (ctx) => {
18
+ return ctx.text('test')
19
+ })
20
+
21
+ const res = await app.request('/test')
22
+ expect(res).not.toBeNull()
23
+ expect(res.status).toBe(200)
24
+ expect(res.headers.get('X-Frame-Options')).toEqual('SAMEORIGIN')
25
+ expect(res.headers.get('Strict-Transport-Security')).toEqual(
26
+ 'max-age=15552000; includeSubDomains'
27
+ )
28
+ expect(res.headers.get('X-Download-Options')).toEqual('noopen')
29
+ expect(res.headers.get('X-XSS-Protection')).toEqual('0')
30
+ expect(res.headers.get('X-Powered-By')).toBeNull()
31
+ expect(res.headers.get('X-DNS-Prefetch-Control')).toEqual('off')
32
+ expect(res.headers.get('X-Content-Type-Options')).toEqual('nosniff')
33
+ expect(res.headers.get('Referrer-Policy')).toEqual('no-referrer')
34
+ expect(res.headers.get('X-Permitted-Cross-Domain-Policies')).toEqual('none')
35
+ expect(res.headers.get('Cross-Origin-Resource-Policy')).toEqual('same-origin')
36
+ expect(res.headers.get('Cross-Origin-Opener-Policy')).toEqual('same-origin')
37
+ expect(res.headers.get('Origin-Agent-Cluster')).toEqual('?1')
38
+ expect(res.headers.get('Permissions-Policy')).toBeNull()
39
+ expect(res.headers.get('Content-Security-Policy')).toBeFalsy()
40
+ expect(res.headers.get('Content-Security-Policy-ReportOnly')).toBeFalsy()
41
+ })
42
+
43
+ it('all headers enabled', async () => {
44
+ const app = new App()
45
+ app.use(
46
+ '*',
47
+ secureHeaders({
48
+ contentSecurityPolicy: {
49
+ defaultSrc: ["'self'"],
50
+ },
51
+ contentSecurityPolicyReportOnly: {
52
+ defaultSrc: ["'self'"],
53
+ },
54
+ crossOriginEmbedderPolicy: true,
55
+ permissionsPolicy: {
56
+ camera: [],
57
+ },
58
+ })
59
+ )
60
+ app.get('/test', async (ctx) => {
61
+ return ctx.text('test')
62
+ })
63
+
64
+ const res = await app.request('/test')
65
+ expect(res).not.toBeNull()
66
+ expect(res.status).toBe(200)
67
+ expect(res.headers.get('X-Frame-Options')).toEqual('SAMEORIGIN')
68
+ expect(res.headers.get('Strict-Transport-Security')).toEqual(
69
+ 'max-age=15552000; includeSubDomains'
70
+ )
71
+ expect(res.headers.get('X-Download-Options')).toEqual('noopen')
72
+ expect(res.headers.get('X-XSS-Protection')).toEqual('0')
73
+ expect(res.headers.get('X-Powered-By')).toBeNull()
74
+ expect(res.headers.get('X-DNS-Prefetch-Control')).toEqual('off')
75
+ expect(res.headers.get('X-Content-Type-Options')).toEqual('nosniff')
76
+ expect(res.headers.get('Referrer-Policy')).toEqual('no-referrer')
77
+ expect(res.headers.get('X-Permitted-Cross-Domain-Policies')).toEqual('none')
78
+ expect(res.headers.get('Cross-Origin-Resource-Policy')).toEqual('same-origin')
79
+ expect(res.headers.get('Cross-Origin-Opener-Policy')).toEqual('same-origin')
80
+ expect(res.headers.get('Origin-Agent-Cluster')).toEqual('?1')
81
+ expect(res.headers.get('Cross-Origin-Embedder-Policy')).toEqual('require-corp')
82
+ expect(res.headers.get('Permissions-Policy')).toEqual('camera=()')
83
+ expect(res.headers.get('Content-Security-Policy')).toEqual("default-src 'self'")
84
+ expect(res.headers.get('Content-Security-Policy-Report-Only')).toEqual("default-src 'self'")
85
+ })
86
+
87
+ it('specific headers disabled', async () => {
88
+ const app = new App()
89
+ app.use('*', secureHeaders({ xFrameOptions: false, xXssProtection: false }))
90
+ app.get('/test', async (ctx) => {
91
+ return ctx.text('test')
92
+ })
93
+
94
+ const res = await app.request('/test')
95
+ expect(res).not.toBeNull()
96
+ expect(res.status).toBe(200)
97
+ expect(res.headers.get('X-Frame-Options')).toBeNull()
98
+ expect(res.headers.get('Strict-Transport-Security')).toEqual(
99
+ 'max-age=15552000; includeSubDomains'
100
+ )
101
+ expect(res.headers.get('X-Download-Options')).toEqual('noopen')
102
+ expect(res.headers.get('X-XSS-Protection')).toBeNull()
103
+ expect(res.headers.get('X-Powered-By')).toBeNull()
104
+ expect(res.headers.get('X-DNS-Prefetch-Control')).toEqual('off')
105
+ expect(res.headers.get('X-Content-Type-Options')).toEqual('nosniff')
106
+ expect(res.headers.get('Referrer-Policy')).toEqual('no-referrer')
107
+ expect(res.headers.get('X-Permitted-Cross-Domain-Policies')).toEqual('none')
108
+ expect(res.headers.get('Cross-Origin-Resource-Policy')).toEqual('same-origin')
109
+ expect(res.headers.get('Cross-Origin-Opener-Policy')).toEqual('same-origin')
110
+ expect(res.headers.get('Permissions-Policy')).toBeNull()
111
+ expect(res.headers.get('Origin-Agent-Cluster')).toEqual('?1')
112
+ })
113
+
114
+ it('should remove x-powered-by header', async () => {
115
+ const appBefore = new App()
116
+ appBefore.use('*', secureHeaders())
117
+ appBefore.use('*', poweredBy())
118
+
119
+ const resBefore = await appBefore.request('/')
120
+ expect(resBefore.headers.get('x-powered-by')).toBeFalsy()
121
+
122
+ const appAfter = new App()
123
+ appAfter.use('*', poweredBy())
124
+ appAfter.use('*', secureHeaders())
125
+
126
+ const resAfter = await appAfter.request('/')
127
+ expect(resAfter.headers.get('x-powered-by')).toBe('ACRYL')
128
+ })
129
+
130
+ it('should override Strict-Transport-Security header after middleware', async () => {
131
+ const app = new App()
132
+ app.use('/test1', secureHeaders())
133
+
134
+ app.all('*', async (c) => {
135
+ c.res.headers.set('Strict-Transport-Security', 'App')
136
+ return c.text('header updated')
137
+ })
138
+
139
+ const res1 = await app.request('/test1')
140
+ expect(res1.headers.get('Strict-Transport-Security')).toEqual(
141
+ 'max-age=15552000; includeSubDomains'
142
+ )
143
+
144
+ const res2 = await app.request('/test2')
145
+ expect(res2.headers.get('Strict-Transport-Security')).toEqual('App')
146
+ })
147
+
148
+ it('should use custom value when overridden', async () => {
149
+ const app = new App()
150
+ app.use(
151
+ '/test',
152
+ secureHeaders({
153
+ strictTransportSecurity: 'max-age=31536000; includeSubDomains; preload;',
154
+ xFrameOptions: 'DENY',
155
+ xXssProtection: '1',
156
+ })
157
+ )
158
+
159
+ const res = await app.request('/test')
160
+ expect(res.headers.get('Strict-Transport-Security')).toEqual(
161
+ 'max-age=31536000; includeSubDomains; preload;'
162
+ )
163
+ expect(res.headers.get('X-FRAME-OPTIONS')).toEqual('DENY')
164
+ expect(res.headers.get('X-XSS-Protection')).toEqual('1')
165
+ })
166
+
167
+ it('should set Permission-Policy header correctly', async () => {
168
+ const app = new App()
169
+ app.use(
170
+ '/test',
171
+ secureHeaders({
172
+ permissionsPolicy: {
173
+ fullscreen: ['self'],
174
+ bluetooth: ['none'],
175
+ payment: ['self', 'example.com'],
176
+ syncXhr: [],
177
+ camera: false,
178
+ microphone: true,
179
+ geolocation: ['*'],
180
+ usb: ['self', 'https://a.example.com', 'https://b.example.com'],
181
+ accelerometer: ['https://*.example.com'],
182
+ gyroscope: ['src'],
183
+ magnetometer: ['https://a.example.com', 'https://b.example.com'],
184
+ mediasession: ['self'],
185
+ deferredFetch: ['none'],
186
+ },
187
+ })
188
+ )
189
+
190
+ const res = await app.request('/test')
191
+ expect(res.headers.get('Permissions-Policy')).toEqual(
192
+ 'fullscreen=(self), bluetooth=(), payment=(self "example.com"), sync-xhr=(), camera=(), microphone=*, ' +
193
+ 'geolocation=*, usb=(self "https://a.example.com" "https://b.example.com"), ' +
194
+ 'accelerometer=("https://*.example.com"), gyroscope=(src), ' +
195
+ 'magnetometer=("https://a.example.com" "https://b.example.com"), ' +
196
+ 'mediasession=(self), deferred-fetch=()'
197
+ )
198
+ })
199
+
200
+ it('Remove X-Powered-By', async () => {
201
+ const app = new App()
202
+
203
+ app.get('/test', secureHeaders(), poweredBy(), async (c) => {
204
+ return c.text('App is hot')
205
+ })
206
+
207
+ app.get(
208
+ '/test2',
209
+ secureHeaders({
210
+ removePoweredBy: false,
211
+ }),
212
+ poweredBy(),
213
+ async (c) => {
214
+ return c.text('App is hot')
215
+ }
216
+ )
217
+
218
+ const res = await app.request('/test')
219
+ const poweredby = res.headers.get('X-Powered-By')
220
+ expect(poweredby).toEqual(null)
221
+ expect(await res.text()).toEqual('App is hot')
222
+
223
+ const res2 = await app.request('/test2')
224
+ const poweredby2 = res2.headers.get('X-Powered-By')
225
+ expect(poweredby2).toEqual('ACRYL')
226
+ expect(await res2.text()).toEqual('App is hot')
227
+ })
228
+
229
+ describe.each([
230
+ { cspSettingName: 'contentSecurityPolicy', cspHeaderName: 'Content-Security-Policy' },
231
+ {
232
+ cspSettingName: 'contentSecurityPolicyReportOnly',
233
+ cspHeaderName: 'Content-Security-Policy-Report-Only',
234
+ },
235
+ ])('CSP Setting ($cspSettingName)', ({ cspSettingName, cspHeaderName }) => {
236
+ it('CSP Setting', async () => {
237
+ const app = new App()
238
+ app.use(
239
+ '/test',
240
+ secureHeaders({
241
+ [cspSettingName]: {
242
+ defaultSrc: ["'self'"],
243
+ baseUri: ["'self'"],
244
+ fontSrc: ["'self'", 'https:', 'data:'],
245
+ frameAncestors: ["'self'"],
246
+ imgSrc: ["'self'", 'data:'],
247
+ objectSrc: ["'none'"],
248
+ scriptSrc: ["'self'"],
249
+ scriptSrcAttr: ["'none'"],
250
+ styleSrc: ["'self'", 'https:', "'unsafe-inline'"],
251
+ requireTrustedTypesFor: ["'script'"],
252
+ trustedTypes: ["'none'"],
253
+ },
254
+ })
255
+ )
256
+
257
+ app.all('*', async (c) => {
258
+ c.res.headers.set('Strict-Transport-Security', 'App')
259
+ return c.text('header updated')
260
+ })
261
+
262
+ const res = await app.request('/test')
263
+ expect(res.headers.get(cspHeaderName)).toEqual(
264
+ "default-src 'self'; base-uri 'self'; font-src 'self' https: data:; frame-ancestors 'self'; img-src 'self' data:; object-src 'none'; script-src 'self'; script-src-attr 'none'; style-src 'self' https: 'unsafe-inline'; require-trusted-types-for 'script'; trusted-types 'none'"
265
+ )
266
+ })
267
+
268
+ it('CSP Setting one only', async () => {
269
+ const app = new App()
270
+ app.use(
271
+ '/test',
272
+ secureHeaders({
273
+ [cspSettingName]: {
274
+ defaultSrc: ["'self'"],
275
+ },
276
+ })
277
+ )
278
+
279
+ app.all('*', async (c) => {
280
+ return c.text('header updated')
281
+ })
282
+
283
+ const res = await app.request('/test')
284
+ expect(res.headers.get(cspHeaderName)).toEqual("default-src 'self'")
285
+ })
286
+
287
+ it('No CSP Setting', async () => {
288
+ const app = new App()
289
+ app.use('/test', secureHeaders({ [cspSettingName]: {} }))
290
+
291
+ app.all('*', async (c) => {
292
+ return c.text('header updated')
293
+ })
294
+
295
+ const res = await app.request('/test')
296
+ expect(res.headers.get(cspHeaderName)).toEqual('')
297
+ })
298
+
299
+ it('CSP with reportTo', async () => {
300
+ const app = new App()
301
+ app.use(
302
+ '/test1',
303
+ secureHeaders({
304
+ reportingEndpoints: [
305
+ {
306
+ name: 'endpoint-1',
307
+ url: 'https://example.com/reports',
308
+ },
309
+ ],
310
+ [cspSettingName]: {
311
+ defaultSrc: ["'self'"],
312
+ reportTo: 'endpoint-1',
313
+ },
314
+ })
315
+ )
316
+
317
+ app.use(
318
+ '/test2',
319
+ secureHeaders({
320
+ reportTo: [
321
+ {
322
+ group: 'endpoint-1',
323
+ max_age: 10886400,
324
+ endpoints: [{ url: 'https://example.com/reports' }],
325
+ },
326
+ ],
327
+ [cspSettingName]: {
328
+ defaultSrc: ["'self'"],
329
+ reportTo: 'endpoint-1',
330
+ },
331
+ })
332
+ )
333
+
334
+ app.use(
335
+ '/test3',
336
+ secureHeaders({
337
+ reportTo: [
338
+ {
339
+ group: 'g1',
340
+ max_age: 10886400,
341
+ endpoints: [
342
+ { url: 'https://a.example.com/reports' },
343
+ { url: 'https://b.example.com/reports' },
344
+ ],
345
+ },
346
+ {
347
+ group: 'g2',
348
+ max_age: 10886400,
349
+ endpoints: [
350
+ { url: 'https://c.example.com/reports' },
351
+ { url: 'https://d.example.com/reports' },
352
+ ],
353
+ },
354
+ ],
355
+ [cspSettingName]: {
356
+ defaultSrc: ["'self'"],
357
+ reportTo: 'g2',
358
+ },
359
+ })
360
+ )
361
+
362
+ app.use(
363
+ '/test4',
364
+ secureHeaders({
365
+ reportingEndpoints: [
366
+ {
367
+ name: 'e1',
368
+ url: 'https://a.example.com/reports',
369
+ },
370
+ {
371
+ name: 'e2',
372
+ url: 'https://b.example.com/reports',
373
+ },
374
+ ],
375
+ [cspSettingName]: {
376
+ defaultSrc: ["'self'"],
377
+ reportTo: 'e1',
378
+ },
379
+ })
380
+ )
381
+
382
+ app.all('*', async (c) => {
383
+ return c.text('header updated')
384
+ })
385
+
386
+ const res1 = await app.request('/test1')
387
+ expect(res1.headers.get('Reporting-Endpoints')).toEqual(
388
+ 'endpoint-1="https://example.com/reports"'
389
+ )
390
+ expect(res1.headers.get(cspHeaderName)).toEqual("default-src 'self'; report-to endpoint-1")
391
+
392
+ const res2 = await app.request('/test2')
393
+ expect(res2.headers.get('Report-To')).toEqual(
394
+ '{"group":"endpoint-1","max_age":10886400,"endpoints":[{"url":"https://example.com/reports"}]}'
395
+ )
396
+ expect(res2.headers.get(cspHeaderName)).toEqual("default-src 'self'; report-to endpoint-1")
397
+
398
+ const res3 = await app.request('/test3')
399
+ expect(res3.headers.get('Report-To')).toEqual(
400
+ '{"group":"g1","max_age":10886400,"endpoints":[{"url":"https://a.example.com/reports"},{"url":"https://b.example.com/reports"}]}, {"group":"g2","max_age":10886400,"endpoints":[{"url":"https://c.example.com/reports"},{"url":"https://d.example.com/reports"}]}'
401
+ )
402
+ expect(res3.headers.get(cspHeaderName)).toEqual("default-src 'self'; report-to g2")
403
+
404
+ const res4 = await app.request('/test4')
405
+ expect(res4.headers.get('Reporting-Endpoints')).toEqual(
406
+ 'e1="https://a.example.com/reports", e2="https://b.example.com/reports"'
407
+ )
408
+ expect(res4.headers.get(cspHeaderName)).toEqual("default-src 'self'; report-to e1")
409
+ })
410
+
411
+ it('CSP nonce for script-src', async () => {
412
+ const app = new App()
413
+ app.use(
414
+ '/test',
415
+ secureHeaders({
416
+ [cspSettingName]: {
417
+ scriptSrc: ["'self'", NONCE],
418
+ },
419
+ })
420
+ )
421
+
422
+ app.all('*', async (c) => {
423
+ return c.text(`nonce: ${c.get('secureHeadersNonce')}`)
424
+ })
425
+
426
+ const res = await app.request('/test')
427
+ const csp = res.headers.get(cspHeaderName)
428
+ const nonce = csp?.match(/script-src 'self' 'nonce-([a-zA-Z0-9+/]+=*)'/)?.[1] || ''
429
+ expect(csp).toMatch(`script-src 'self' 'nonce-${nonce}'`)
430
+ expect(await res.text()).toEqual(`nonce: ${nonce}`)
431
+ })
432
+
433
+ it('CSP nonce for script-src and style-src', async () => {
434
+ const app = new App()
435
+ app.use(
436
+ '/test',
437
+ secureHeaders({
438
+ [cspSettingName]: {
439
+ scriptSrc: ["'self'", NONCE],
440
+ styleSrc: ["'self'", NONCE],
441
+ },
442
+ })
443
+ )
444
+
445
+ app.all('*', async (c) => {
446
+ return c.text(`nonce: ${c.get('secureHeadersNonce')}`)
447
+ })
448
+
449
+ const res = await app.request('/test')
450
+ const csp = res.headers.get(cspHeaderName)
451
+ const nonce = csp?.match(/script-src 'self' 'nonce-([a-zA-Z0-9+/]+=*)'/)?.[1] || ''
452
+ expect(csp).toMatch(`script-src 'self' 'nonce-${nonce}'`)
453
+ expect(csp).toMatch(`style-src 'self' 'nonce-${nonce}'`)
454
+ expect(await res.text()).toEqual(`nonce: ${nonce}`)
455
+ })
456
+
457
+ it('CSP nonce by app own function', async () => {
458
+ const app = new App()
459
+ const setNonce: ContentSecurityPolicyOptionHandler = (ctx, directive) => {
460
+ ctx.set(`test-${directive}-nonce`, directive)
461
+ return `'nonce-${directive}'`
462
+ }
463
+ app.use(
464
+ '/test',
465
+ secureHeaders({
466
+ [cspSettingName]: {
467
+ scriptSrc: ["'self'", setNonce],
468
+ styleSrc: ["'self'", setNonce],
469
+ },
470
+ })
471
+ )
472
+
473
+ app.all('*', async (c) => {
474
+ return c.text(
475
+ `script: ${c.get('test-scriptSrc-nonce')}, style: ${c.get('test-styleSrc-nonce')}`
476
+ )
477
+ })
478
+
479
+ const res = await app.request('/test')
480
+ const csp = res.headers.get(cspHeaderName)
481
+ expect(csp).toMatch("script-src 'self' 'nonce-scriptSrc'")
482
+ expect(csp).toMatch("style-src 'self' 'nonce-styleSrc'")
483
+ expect(await res.text()).toEqual('script: scriptSrc, style: styleSrc')
484
+ })
485
+ })
486
+
487
+ describe('CSP with combined modes', () => {
488
+ it('keeps the enforced policy when report-only uses a nonce', async () => {
489
+ const app = new App()
490
+ app.use(
491
+ '/test',
492
+ secureHeaders({
493
+ contentSecurityPolicy: {
494
+ defaultSrc: ["'self'"],
495
+ },
496
+ contentSecurityPolicyReportOnly: {
497
+ scriptSrc: ["'self'", NONCE],
498
+ },
499
+ })
500
+ )
501
+ app.all('*', (c) => c.text('test'))
502
+
503
+ const res = await app.request('/test')
504
+
505
+ expect(res.status).toBe(200)
506
+ expect(res.headers.get('Content-Security-Policy')).toBe("default-src 'self'")
507
+ expect(res.headers.get('Content-Security-Policy-Report-Only')).toMatch(
508
+ /^script-src 'self' 'nonce-[a-zA-Z0-9+/]+=*'$/
509
+ )
510
+ })
511
+
512
+ it('keeps the report-only policy when the enforced policy uses a nonce', async () => {
513
+ const app = new App()
514
+ app.use(
515
+ '/test',
516
+ secureHeaders({
517
+ contentSecurityPolicy: {
518
+ scriptSrc: ["'self'", NONCE],
519
+ },
520
+ contentSecurityPolicyReportOnly: {
521
+ defaultSrc: ["'self'"],
522
+ },
523
+ })
524
+ )
525
+ app.all('*', (c) => c.text('test'))
526
+
527
+ const res = await app.request('/test')
528
+
529
+ expect(res.status).toBe(200)
530
+ expect(res.headers.get('Content-Security-Policy')).toMatch(
531
+ /^script-src 'self' 'nonce-[a-zA-Z0-9+/]+=*'$/
532
+ )
533
+ expect(res.headers.get('Content-Security-Policy-Report-Only')).toBe("default-src 'self'")
534
+ })
535
+
536
+ it('supports nonces in both policies', async () => {
537
+ const app = new App()
538
+ app.use(
539
+ '/test',
540
+ secureHeaders({
541
+ contentSecurityPolicy: {
542
+ scriptSrc: ["'self'", NONCE],
543
+ },
544
+ contentSecurityPolicyReportOnly: {
545
+ styleSrc: ["'self'", NONCE],
546
+ },
547
+ })
548
+ )
549
+ app.all('*', (c) => c.text('test'))
550
+
551
+ const res = await app.request('/test')
552
+ const csp = res.headers.get('Content-Security-Policy')
553
+ const reportOnly = res.headers.get('Content-Security-Policy-Report-Only')
554
+ const nonce = csp?.match(/'nonce-([^']+)'/)?.[1]
555
+
556
+ expect(res.status).toBe(200)
557
+ expect(nonce).toBeTruthy()
558
+ expect(reportOnly).toContain(`'nonce-${nonce}'`)
559
+ })
560
+ })
561
+
562
+ // OUR NEW REPORT-URI TESTS
563
+ describe('CSP report-uri directive', () => {
564
+ it('should set report-uri with single endpoint', async () => {
565
+ const app = new App()
566
+ app.use(
567
+ '/test',
568
+ secureHeaders({
569
+ contentSecurityPolicy: {
570
+ defaultSrc: ["'self'"],
571
+ reportUri: '/csp-report',
572
+ },
573
+ })
574
+ )
575
+
576
+ const res = await app.request('/test')
577
+ expect(res.headers.get('Content-Security-Policy')).toEqual(
578
+ "default-src 'self'; report-uri /csp-report"
579
+ )
580
+ })
581
+
582
+ it('should set report-uri with multiple endpoints', async () => {
583
+ const app = new App()
584
+ app.use(
585
+ '/test',
586
+ secureHeaders({
587
+ contentSecurityPolicy: {
588
+ defaultSrc: ["'self'"],
589
+ reportUri: ['/endpoint1', '/endpoint2'],
590
+ },
591
+ })
592
+ )
593
+
594
+ const res = await app.request('/test')
595
+ expect(res.headers.get('Content-Security-Policy')).toEqual(
596
+ "default-src 'self'; report-uri /endpoint1 /endpoint2"
597
+ )
598
+ })
599
+
600
+ it('should work with report-to and report-uri together', async () => {
601
+ const app = new App()
602
+ app.use(
603
+ '/test',
604
+ secureHeaders({
605
+ contentSecurityPolicy: {
606
+ defaultSrc: ["'self'"],
607
+ reportTo: 'endpoint-1',
608
+ reportUri: '/legacy-report',
609
+ },
610
+ })
611
+ )
612
+
613
+ const res = await app.request('/test')
614
+ expect(res.headers.get('Content-Security-Policy')).toEqual(
615
+ "default-src 'self'; report-to endpoint-1; report-uri /legacy-report"
616
+ )
617
+ })
618
+
619
+ it('should work with Content-Security-Policy-Report-Only', async () => {
620
+ const app = new App()
621
+ app.use(
622
+ '/test',
623
+ secureHeaders({
624
+ contentSecurityPolicyReportOnly: {
625
+ defaultSrc: ["'self'"],
626
+ reportUri: '/csp-report-only',
627
+ },
628
+ })
629
+ )
630
+
631
+ const res = await app.request('/test')
632
+ expect(res.headers.get('Content-Security-Policy-Report-Only')).toEqual(
633
+ "default-src 'self'; report-uri /csp-report-only"
634
+ )
635
+ })
636
+
637
+ // ADDED MISSING TEST CASE for Report-Only:
638
+
639
+ it('should not output report-uri when reportUri is not set', async () => {
640
+ const app = new App()
641
+ app.use(
642
+ '/test',
643
+ secureHeaders({
644
+ contentSecurityPolicy: {
645
+ defaultSrc: ["'self'"],
646
+ },
647
+ })
648
+ )
649
+
650
+ const res = await app.request('/test')
651
+ const header = res.headers.get('Content-Security-Policy') || ''
652
+ expect(header.includes('report-uri')).toBe(false)
653
+ })
654
+
655
+ it('should not output report-uri in Report-Only when reportUri is not set', async () => {
656
+ const app = new App()
657
+ app.use(
658
+ '/test',
659
+ secureHeaders({
660
+ contentSecurityPolicyReportOnly: {
661
+ defaultSrc: ["'self'"],
662
+ },
663
+ })
664
+ )
665
+
666
+ const res = await app.request('/test')
667
+ const header = res.headers.get('Content-Security-Policy-Report-Only') || ''
668
+ expect(header.includes('report-uri')).toBe(false)
669
+ })
670
+ })
671
+ })
@@ -0,0 +1,8 @@
1
+ export type { ContentSecurityPolicyOptionHandler } from './secure-headers.ts'
2
+ export { NONCE, secureHeaders } from './secure-headers.ts'
3
+ import type { SecureHeadersVariables } from './secure-headers.ts'
4
+ export type { SecureHeadersVariables }
5
+
6
+ declare module '../../context.ts' {
7
+ interface ContextVariableMap extends SecureHeadersVariables {}
8
+ }