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,68 @@
1
+ /**
2
+ * The ACRYL inspector (`ctx.inspect`): what every running plugin requires, provides and registers, read from the live
3
+ * registry. It owns no state and registers nothing of its own beyond an optional read-only pair of router routes, which are
4
+ * reachable in process unless some plugin mounts their prefix over HTTP.
5
+ */
6
+
7
+ import { Context, Service } from '@deepseek-ai/cordis'
8
+ import { loopbackOnly } from '../../acryl-router/src/middleware/index.ts'
9
+ import { App } from '../../acryl-router/src/index.ts'
10
+ import { explain, ownerOf, renderReports, reportFiber, type FiberView, type PluginReport } from './report.ts'
11
+
12
+ declare module '@deepseek-ai/cordis' {
13
+ interface Context {
14
+ inspect: Inspect
15
+ }
16
+ }
17
+
18
+ /** Stable Cordis plugin name, equal to the package name and the Loader row id. */
19
+ export const name = 'acryl-inspect'
20
+
21
+ export class Inspect extends Service {
22
+ constructor(ctx: Context) {
23
+ super(ctx, 'inspect')
24
+ }
25
+
26
+ /** A report for every live plugin instance, in registry order. */
27
+ plugins(): PluginReport[] {
28
+ const fibers = [...this.ctx.registry.values()].flatMap(runtime => [...runtime.fibers] as unknown as FiberView[])
29
+ // An unnamed inline callback is part of the plugin that wrote it: its routes and mounts are that plugin's.
30
+ const adopted = new Map<FiberView, FiberView[]>()
31
+ const owners: FiberView[] = []
32
+ for (const fiber of fibers) {
33
+ const owner = ownerOf(fiber)
34
+ if (owner === fiber) owners.push(fiber)
35
+ else adopted.set(owner, [...(adopted.get(owner) ?? []), fiber])
36
+ }
37
+ return owners.map(fiber => reportFiber(fiber, adopted.get(fiber) ?? []))
38
+ }
39
+
40
+ /** The text explanation of the plugin called `pluginName`, or `undefined` when none is registered. */
41
+ explain(pluginName: string): string | undefined {
42
+ const found = this.plugins().filter(report => report.name === pluginName)
43
+ return found.length === 0 ? undefined : found.map(explain).join('\n\n')
44
+ }
45
+
46
+ /** The whole system as text. */
47
+ render(): string {
48
+ return renderReports(this.plugins())
49
+ }
50
+ }
51
+
52
+ export function apply(ctx: Context): void {
53
+ const inspect = new Inspect(ctx)
54
+ // The same facts on the router, for any surface; present only where a router is. A request over a socket must come from this machine to this app;
55
+ // the terminal calls the router in process and is the host's own code.
56
+ ctx.inject(['router'], inner => {
57
+ const api = new App()
58
+ .use(loopbackOnly({ inProcess: 'allow' }))
59
+ .get('/plugins', c => c.json(inspect.plugins()))
60
+ .get('/plugins/:name', c => {
61
+ const text = inspect.explain(c.req.param('name'))
62
+ return text === undefined ? c.json({ error: 'no such plugin' }, 404) : c.text(text)
63
+ })
64
+ inner.router.route('/api/inspect', api)
65
+ // Over HTTP only where an adapter exists (Web and Desktop); the terminal needs none.
66
+ inner.inject(['routerHttp'], http => { http.routerHttp.mount('/api/inspect') })
67
+ })
68
+ }
@@ -0,0 +1,63 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { explain, renderReports, reportFiber, stateName, type FiberView } from '../src/report.ts'
3
+
4
+ function fiber(over: Partial<FiberView> & { effects?: FiberView['getEffects'] extends () => infer R ? R : never } = {}): FiberView {
5
+ const { effects = [], ...rest } = over
6
+ return { state: 2, inject: {}, runtime: { name: 'demo' }, getEffects: () => effects, ...rest }
7
+ }
8
+
9
+ describe('reportFiber', () => {
10
+ it('sorts effects into provides, mounts with their routes, and everything else, including nested ones', () => {
11
+ const report = reportFiber(fiber({
12
+ inject: { router: null, acrylSettings: null },
13
+ effects: [
14
+ { label: 'ctx.provide("things")', children: [] },
15
+ { label: 'acryl-router: route /api (ALL /api/*, GET /api/things/:id)', children: [] },
16
+ { label: 'acryl-router: route /api/things (POST /api/things)', children: [{ label: 'inner timer', children: [] }] },
17
+ { label: 'ctx.on("internal/status")', children: [] },
18
+ ],
19
+ }))
20
+ expect(report).toEqual({
21
+ name: 'demo',
22
+ state: 'ACTIVE',
23
+ requires: ['acrylSettings', 'router'],
24
+ provides: ['things'],
25
+ mounts: ['/api', '/api/things'],
26
+ routes: [{ method: 'ALL', path: '/api/*' }, { method: 'GET', path: '/api/things/:id' }, { method: 'POST', path: '/api/things' }],
27
+ other: ['inner timer', 'ctx.on("internal/status")'],
28
+ removedOnUnload: 5,
29
+ })
30
+ })
31
+
32
+ it('names the state and an anonymous plugin', () => {
33
+ expect(stateName(0)).toBe('PENDING')
34
+ expect(stateName(2)).toBe('ACTIVE')
35
+ expect(stateName(5)).toBe('UNLOADING')
36
+ expect(reportFiber(fiber({ runtime: null })).name).toBe('anonymous')
37
+ })
38
+ })
39
+
40
+ describe('explain', () => {
41
+ it('says what a plugin needs, adds and removes on unload', () => {
42
+ const text = explain(reportFiber(fiber({
43
+ inject: { router: null },
44
+ effects: [{ label: 'acryl-router: route / (GET /a)', children: [] }],
45
+ })))
46
+ expect(text).toContain('demo (ACTIVE)')
47
+ expect(text).toContain('needs: router')
48
+ expect(text).toContain('routes: GET /a')
49
+ expect(text).toContain('unloading: removes 1 registration and nothing else')
50
+ })
51
+
52
+ it('says a pending plugin is waiting for its services', () => {
53
+ const text = explain(reportFiber(fiber({ state: 0, inject: { router: null, webServer: null } })))
54
+ expect(text).toContain('demo (PENDING)')
55
+ expect(text).toContain('it starts when router, webServer are available')
56
+ })
57
+
58
+ it('renders several plugins as blocks', () => {
59
+ const a = reportFiber(fiber({ runtime: { name: 'a' } }))
60
+ const b = reportFiber(fiber({ runtime: { name: 'b' } }))
61
+ expect(renderReports([a, b]).split('\n\n')).toHaveLength(2)
62
+ })
63
+ })
@@ -0,0 +1,121 @@
1
+ /**
2
+ * The inspector against real Cordis plugins: what it reports is what Cordis holds, so the tests build a small system and
3
+ * compare the report with what the plugins actually did.
4
+ */
5
+
6
+ import { Context } from '@deepseek-ai/cordis'
7
+ import { App, apply as routerApply, name as routerName } from '../../acryl-router/src/index.ts'
8
+ import { afterEach, describe, expect, it } from 'vitest'
9
+ import { apply, name, type PluginReport } from '../src/index.ts'
10
+
11
+ const contexts: Context[] = []
12
+ const settle = (): Promise<void> => new Promise(resolve => setTimeout(resolve, 0))
13
+ afterEach(async () => {
14
+ while (contexts.length > 0) await contexts.pop()?.fiber.dispose()
15
+ })
16
+
17
+ const market = {
18
+ name: 'demo-market',
19
+ inject: ['router'],
20
+ apply(ctx: Context): void {
21
+ ctx.router.route('/api/demo', new App()
22
+ .use(async (_c, next) => { await next() })
23
+ .get('/state', c => c.json({ ok: true }))
24
+ .post('/sources', c => c.json({ ok: true }, 201)))
25
+ },
26
+ }
27
+
28
+ async function system(): Promise<Context> {
29
+ const ctx = new Context()
30
+ contexts.push(ctx)
31
+ ctx.plugin({ name: routerName, apply: routerApply }, { trailingSlash: 'strict' })
32
+ ctx.plugin({ name, apply })
33
+ await settle()
34
+ return ctx
35
+ }
36
+
37
+ const byName = (reports: readonly PluginReport[], pluginName: string): PluginReport => {
38
+ const found = reports.find(report => report.name === pluginName)
39
+ if (found === undefined) throw new Error(`no report for ${pluginName}`)
40
+ return found
41
+ }
42
+
43
+ describe('acryl-inspect', () => {
44
+ it('counts what an inline ctx.inject callback registers as the owning plugin\'s', async () => {
45
+ const ctx = await system()
46
+ ctx.plugin({
47
+ name: 'demo-inline',
48
+ apply(inner: Context): void {
49
+ inner.inject(['router'], deeper => { deeper.router.route('/', new App().get('/api/inline', c => c.json({ ok: true }))) })
50
+ },
51
+ })
52
+ await settle()
53
+ const reports = ctx.inspect.plugins()
54
+ expect(byName(reports, 'demo-inline').routes).toEqual([{ method: 'GET', path: '/api/inline' }])
55
+ expect(reports.every(report => report.name !== 'anonymous')).toBe(true)
56
+ })
57
+
58
+ it('reports what a plugin requires, provides and registers, from the running system', async () => {
59
+ const ctx = await system()
60
+ ctx.plugin(market)
61
+ await settle()
62
+ const reports = ctx.inspect.plugins()
63
+
64
+ expect(byName(reports, 'acryl-router')).toMatchObject({ state: 'ACTIVE', requires: [], provides: ['router'] })
65
+ expect(byName(reports, 'acryl-inspect')).toMatchObject({ state: 'ACTIVE', provides: ['inspect'] })
66
+ const demo = byName(reports, 'demo-market')
67
+ expect(demo).toMatchObject({
68
+ state: 'ACTIVE',
69
+ requires: ['router'],
70
+ provides: [],
71
+ mounts: ['/api/demo'],
72
+ routes: [{ method: 'ALL', path: '/api/demo/*' }, { method: 'GET', path: '/api/demo/state' }, { method: 'POST', path: '/api/demo/sources' }],
73
+ removedOnUnload: 1,
74
+ })
75
+ })
76
+
77
+ it('shows a plugin whose service is missing as PENDING and says what it waits for', async () => {
78
+ const ctx = await system()
79
+ ctx.plugin({ name: 'needs-a-thing', inject: ['neverProvided'], apply() { /* never runs */ } })
80
+ await settle()
81
+ expect(byName(ctx.inspect.plugins(), 'needs-a-thing')).toMatchObject({ state: 'PENDING', requires: ['neverProvided'] })
82
+ expect(ctx.inspect.explain('needs-a-thing')).toContain('it starts when neverProvided is available')
83
+ })
84
+
85
+ it('stops listing a plugin that unloaded, and the router really lost what the report said would go', async () => {
86
+ const ctx = await system()
87
+ const baseline = ctx.router.size
88
+ const fiber = ctx.plugin(market)
89
+ await settle()
90
+ const promised = byName(ctx.inspect.plugins(), 'demo-market').removedOnUnload
91
+ expect(ctx.router.size - baseline).toBe(promised)
92
+
93
+ await fiber.dispose()
94
+ await settle()
95
+ expect(ctx.inspect.plugins().some(report => report.name === 'demo-market' && report.state === 'ACTIVE')).toBe(false)
96
+ expect(ctx.router.size).toBe(baseline)
97
+ })
98
+
99
+ it('serves the same facts on the router, in process', async () => {
100
+ const ctx = await system()
101
+ ctx.plugin(market)
102
+ await settle()
103
+ const list = await ctx.router.fetch(new Request('http://acryl.test/api/inspect/plugins'))
104
+ expect(list.status).toBe(200)
105
+ const reports = await list.json() as PluginReport[]
106
+ expect(byName(reports, 'demo-market').routes).toHaveLength(3)
107
+
108
+ const one = await ctx.router.fetch(new Request('http://acryl.test/api/inspect/plugins/demo-market'))
109
+ expect(await one.text()).toContain('removes 1 registration and nothing else')
110
+ expect((await ctx.router.fetch(new Request('http://acryl.test/api/inspect/plugins/nope'))).status).toBe(404)
111
+ })
112
+
113
+ it('needs no router: without one it still answers in code and registers no routes', async () => {
114
+ const ctx = new Context()
115
+ contexts.push(ctx)
116
+ ctx.plugin({ name, apply })
117
+ await settle()
118
+ expect(byName(ctx.inspect.plugins(), 'acryl-inspect').routes).toEqual([])
119
+ expect(ctx.inspect.render()).toContain('acryl-inspect (ACTIVE)')
120
+ })
121
+ })
@@ -0,0 +1,18 @@
1
+ {
2
+ "source": {
3
+ "repo": "acryldev/acryl",
4
+ "path": "plugins/acryl-inspect",
5
+ "commit": "df79b188398b6d4f8cceda35a72b88300a10a9ce"
6
+ },
7
+ "vendored": [
8
+ "src",
9
+ "tests",
10
+ "LICENSE"
11
+ ],
12
+ "rewrittenSpecifiers": 3,
13
+ "digest": "sha256:e9887df0d2b5274d4fc5de4c730975528a84fd1d270994ee85a2789bec101db7",
14
+ "testsNotRun": {},
15
+ "notice": [
16
+ "LICENSE"
17
+ ]
18
+ }
@@ -0,0 +1,47 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 WebBoxes s.r.o.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ ---
24
+
25
+ The files under src/adapter and tests/adapter are derived from @hono/node-server v2.1.5 (https://github.com/honojs/node-server), used under this license:
26
+
27
+ MIT License
28
+
29
+ Copyright (c) 2022 - present, Yusuke Wada and Hono contributors
30
+
31
+ Permission is hereby granted, free of charge, to any person obtaining a copy
32
+ of this software and associated documentation files (the "Software"), to deal
33
+ in the Software without restriction, including without limitation the rights
34
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
35
+ copies of the Software, and to permit persons to whom the Software is
36
+ furnished to do so, subject to the following conditions:
37
+
38
+ The above copyright notice and this permission notice shall be included in all
39
+ copies or substantial portions of the Software.
40
+
41
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
42
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
43
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
44
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
45
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
46
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
47
+ SOFTWARE.
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "acryl-node-server",
3
+ "description": "The standalone HTTP server as a Cordis plugin: one node:http listener that sends every request to the router. Port, loopback host and next-free-port scan; ends open streams and frees the port when unloaded. Vendored from ACRYL.",
4
+ "requires": ["acryl-router", "acryl-router-http"],
5
+ "inject": ["router"],
6
+ "provides": ["nodeServer"],
7
+ "dependencies": {
8
+ "@deepseek-ai/cordis": "4.0.4",
9
+ "@deepseek-ai/schemastery": "3.18.4"
10
+ },
11
+ "mount": "root.plugin(router); root.plugin(nodeServer, { port: 3000, hostname: '127.0.0.1', scan: 20 }); ctx.nodeServer.url",
12
+ "omit": ["**/*.test.*", "**/*.case.ts"]
13
+ }
@@ -0,0 +1,23 @@
1
+ import type { GetConnInfo } from '../../../acryl-router/src/helpers/index.ts'
2
+ import type { HttpBindings } from './types.ts'
3
+
4
+ /**
5
+ * ConnInfo Helper for Node.js
6
+ * @param c Context
7
+ * @returns ConnInfo
8
+ */
9
+ export const getConnInfo: GetConnInfo = (c) => {
10
+ const bindings = (c.env.server ? c.env.server : c.env) as HttpBindings
11
+
12
+ const address = bindings.incoming.socket.remoteAddress
13
+ const port = bindings.incoming.socket.remotePort
14
+ const family = bindings.incoming.socket.remoteFamily
15
+
16
+ return {
17
+ remote: {
18
+ address,
19
+ port,
20
+ addressType: family === 'IPv4' ? 'IPv4' : family === 'IPv6' ? 'IPv6' : void 0,
21
+ },
22
+ }
23
+ }
@@ -0,0 +1,55 @@
1
+ import type { Context, Env, MiddlewareHandler } from '../../../acryl-router/src/index.ts'
2
+ import type { HttpBindings } from './types.ts'
3
+
4
+ export type EarlyHintsOptions<E extends Env = Env> = {
5
+ link: string | string[] | ((c: Context<E>) => string | string[] | undefined)
6
+ }
7
+
8
+ /**
9
+ * Early Hints middleware for Node.js
10
+ * Automatically sends a 103 Early Hints informational response with the specified Link header(s).
11
+ *
12
+ * @param options EarlyHintsOptions
13
+ * @returns MiddlewareHandler
14
+ */
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
+ export const earlyHints = <E extends Env = any>(
17
+ options: EarlyHintsOptions<E>
18
+ ): MiddlewareHandler<E> => {
19
+ let warned = false
20
+
21
+ return async (c, next) => {
22
+ const mode = c.req.header('Sec-Fetch-Mode')
23
+ const dest = c.req.header('Sec-Fetch-Dest')
24
+
25
+ if ((mode && mode !== 'navigate') || (dest && dest !== 'document')) {
26
+ return next()
27
+ }
28
+
29
+ const env = c.env || {}
30
+ const bindings = ('server' in env ? env.server : env) as HttpBindings
31
+ const outgoing = bindings?.outgoing
32
+
33
+ // Capability check: outgoing.writeEarlyHints exists and is a function.
34
+ // This guard exists for non-Node runtimes and non-HTTP bindings.
35
+ if (typeof outgoing?.writeEarlyHints !== 'function') {
36
+ if (!warned) {
37
+ console.warn(
38
+ 'Early Hints Middleware is not supported because writeEarlyHints is not defined.'
39
+ )
40
+ warned = true
41
+ }
42
+ return await next()
43
+ }
44
+
45
+ if (!outgoing.headersSent) {
46
+ const link = typeof options.link === 'function' ? options.link(c) : options.link
47
+
48
+ if (link !== undefined && (Array.isArray(link) ? link.length > 0 : Boolean(link))) {
49
+ outgoing.writeEarlyHints({ link })
50
+ }
51
+ }
52
+
53
+ await next()
54
+ }
55
+ }
@@ -0,0 +1,11 @@
1
+ export class RequestError extends Error {
2
+ constructor(
3
+ message: string,
4
+ options?: {
5
+ cause?: unknown
6
+ }
7
+ ) {
8
+ super(message, options)
9
+ this.name = 'RequestError'
10
+ }
11
+ }
@@ -0,0 +1,249 @@
1
+ import type { IncomingMessage } from 'node:http'
2
+ import { Http2ServerRequest } from 'node:http2'
3
+
4
+ type IncomingHeadersSource = Pick<IncomingMessage | Http2ServerRequest, 'rawHeaders'> & {
5
+ headers?: IncomingMessage['headers']
6
+ }
7
+
8
+ // Node's HTTP/1 parser already joins ordinary repeated headers with the same
9
+ // separators as WHATWG Headers, so its parsed object is a safe fast path for
10
+ // those names. It discards repeats of this fixed set by default, however, and
11
+ // HTTP/2 has different collapsing rules; resolve those cases from rawHeaders.
12
+ // https://nodejs.org/api/http.html#messageheaders
13
+ // https://github.com/nodejs/node/blob/v26.7.0/lib/_http_incoming.js
14
+ // https://www.rfc-editor.org/rfc/rfc9110.html#section-5.2
15
+ const nonJoinedHeaders = new Set([
16
+ 'age',
17
+ 'authorization',
18
+ 'content-length',
19
+ 'content-type',
20
+ 'etag',
21
+ 'expires',
22
+ 'from',
23
+ 'host',
24
+ 'if-modified-since',
25
+ 'if-unmodified-since',
26
+ 'last-modified',
27
+ 'location',
28
+ 'max-forwards',
29
+ 'proxy-authorization',
30
+ 'referer',
31
+ 'retry-after',
32
+ 'server',
33
+ 'user-agent',
34
+ ])
35
+
36
+ // Converted from RFC 9110's `field-name = token` and `token`/`tchar` ABNF.
37
+ // https://www.rfc-editor.org/rfc/rfc9110.html#section-5.1
38
+ // https://www.rfc-editor.org/rfc/rfc9110.html#section-5.6.2
39
+ const validHeaderName = /^[!#$%&'*+\-.^_`|~\dA-Za-z]+$/
40
+
41
+ const isHttpWhitespace = (code: number): boolean =>
42
+ code === 0x09 || code === 0x0a || code === 0x0d || code === 0x20
43
+
44
+ const normalizeHeaderValue = (value: string): string => {
45
+ if (
46
+ !isHttpWhitespace(value.charCodeAt(0)) &&
47
+ !isHttpWhitespace(value.charCodeAt(value.length - 1))
48
+ ) {
49
+ return value
50
+ }
51
+ let start = 0
52
+ let end = value.length
53
+ while (start < end && isHttpWhitespace(value.charCodeAt(start))) {
54
+ start++
55
+ }
56
+ while (end > start && isHttpWhitespace(value.charCodeAt(end - 1))) {
57
+ end--
58
+ }
59
+ return value.slice(start, end)
60
+ }
61
+
62
+ const forbiddenHeaderValue = /[\0\r\n]/
63
+
64
+ export const GlobalHeaders = globalThis.Headers
65
+ export type GlobalHeaders = InstanceType<typeof GlobalHeaders>
66
+
67
+ const materializeHeaders = (
68
+ rawHeaders: string[],
69
+ HeadersCtor: typeof GlobalHeaders = GlobalHeaders
70
+ ): GlobalHeaders => {
71
+ const headers = new HeadersCtor()
72
+ for (let i = 0; i < rawHeaders.length; i += 2) {
73
+ const name = rawHeaders[i]
74
+ if (!name.startsWith(':')) {
75
+ headers.append(name, rawHeaders[i + 1])
76
+ }
77
+ }
78
+ return headers
79
+ }
80
+
81
+ export class RequestHeaders {
82
+ #incoming: IncomingHeadersSource
83
+ #rawHeaders?: string[]
84
+ #headers?: GlobalHeaders
85
+ #invalidValue?: boolean
86
+
87
+ constructor(incoming: IncomingHeadersSource) {
88
+ this.#incoming = incoming
89
+ if (incoming instanceof Http2ServerRequest) {
90
+ this.#rawHeaders = incoming.rawHeaders.slice()
91
+ }
92
+ }
93
+
94
+ get #lazyRawHeaders(): string[] {
95
+ return (this.#rawHeaders ??= this.#incoming.rawHeaders.slice())
96
+ }
97
+
98
+ get #native(): GlobalHeaders {
99
+ if (!this.#headers) {
100
+ this.#headers = materializeHeaders(this.#lazyRawHeaders)
101
+ this.#rawHeaders = undefined
102
+ }
103
+ return this.#headers
104
+ }
105
+
106
+ #normalizedName(name: string): string | undefined {
107
+ if (typeof name !== 'string') {
108
+ return
109
+ }
110
+ if (!validHeaderName.test(name)) {
111
+ throw new TypeError(`Invalid header name: ${name}`)
112
+ }
113
+ return name.toLowerCase()
114
+ }
115
+
116
+ // The HTTP/1 fast path trusts Node's parser-produced headers object. Mutating
117
+ // it through the incoming binding is outside this optimization's contract;
118
+ // detecting such changes would require scanning or copying every header.
119
+ #lookupHttp1(lowerName: string): string | null | undefined {
120
+ const headers =
121
+ this.#incoming instanceof Http2ServerRequest ? undefined : this.#incoming.headers
122
+ if (
123
+ !headers ||
124
+ nonJoinedHeaders.has(lowerName) ||
125
+ lowerName === 'set-cookie' ||
126
+ lowerName === '__proto__'
127
+ ) {
128
+ return
129
+ }
130
+
131
+ if (!Object.hasOwn(headers, lowerName)) {
132
+ return null
133
+ }
134
+ const rawValue = headers[lowerName]
135
+ if (typeof rawValue === 'string') {
136
+ const value = normalizeHeaderValue(rawValue)
137
+ return forbiddenHeaderValue.test(value) ? undefined : value
138
+ }
139
+ return
140
+ }
141
+
142
+ #lookup(rawHeaders: string[], lowerName: string): string | null | undefined {
143
+ const separator = lowerName === 'cookie' ? '; ' : ', '
144
+ let value: string | null = null
145
+ for (let i = 0; i < rawHeaders.length; i += 2) {
146
+ const rawName = rawHeaders[i]
147
+ if (rawName.length === lowerName.length && rawName.toLowerCase() === lowerName) {
148
+ const rawValue = normalizeHeaderValue(rawHeaders[i + 1])
149
+ if (forbiddenHeaderValue.test(rawValue)) {
150
+ this.#invalidValue = true
151
+ return
152
+ }
153
+ value = value === null ? rawValue : value + separator + rawValue
154
+ }
155
+ }
156
+
157
+ return value
158
+ }
159
+
160
+ append(name: string, value: string): void {
161
+ this.#native.append(name, value)
162
+ }
163
+
164
+ delete(name: string): void {
165
+ this.#native.delete(name)
166
+ }
167
+
168
+ get(name: string): string | null {
169
+ const lowerName = this.#normalizedName(name)
170
+ if (lowerName && !this.#headers && !this.#invalidValue) {
171
+ const http1Value = this.#lookupHttp1(lowerName)
172
+ if (http1Value !== undefined) {
173
+ return http1Value
174
+ }
175
+ const value = this.#lookup(this.#lazyRawHeaders, lowerName)
176
+ if (value !== undefined) {
177
+ return value
178
+ }
179
+ }
180
+ return this.#native.get(name)
181
+ }
182
+
183
+ has(name: string): boolean {
184
+ const lowerName = this.#normalizedName(name)
185
+ if (lowerName && !this.#headers && !this.#invalidValue) {
186
+ const http1Value = this.#lookupHttp1(lowerName)
187
+ if (http1Value !== undefined) {
188
+ return http1Value !== null
189
+ }
190
+ const value = this.#lookup(this.#lazyRawHeaders, lowerName)
191
+ if (value !== undefined) {
192
+ return value !== null
193
+ }
194
+ }
195
+ return this.#native.has(name)
196
+ }
197
+
198
+ set(name: string, value: string): void {
199
+ this.#native.set(name, value)
200
+ }
201
+
202
+ getSetCookie(): string[] {
203
+ return this.#native.getSetCookie()
204
+ }
205
+
206
+ keys(): HeadersIterator<string> {
207
+ return this.#native.keys()
208
+ }
209
+
210
+ values(): HeadersIterator<string> {
211
+ return this.#native.values()
212
+ }
213
+
214
+ entries(): HeadersIterator<[string, string]> {
215
+ return this.#native.entries()
216
+ }
217
+
218
+ forEach(
219
+ callback: (value: string, key: string, parent: GlobalHeaders) => void,
220
+ thisArg?: unknown
221
+ ): void {
222
+ this.#native.forEach((value, key) => {
223
+ callback.call(thisArg, value, key, this as unknown as GlobalHeaders)
224
+ })
225
+ }
226
+
227
+ [Symbol.iterator](): HeadersIterator<[string, string]> {
228
+ return this.entries()
229
+ }
230
+ }
231
+
232
+ Object.defineProperty(RequestHeaders.prototype, Symbol.for('nodejs.util.inspect.custom'), {
233
+ value: function (this: RequestHeaders, depth: number, options: object, inspectFn: Function) {
234
+ const props = Object.fromEntries(this)
235
+ return `Headers (lightweight) ${inspectFn(props, { ...options, depth: depth == null ? null : depth - 1 })}`
236
+ },
237
+ })
238
+
239
+ // Keep request headers compatible with the captured Headers constructor so
240
+ // `request.headers instanceof Headers` remains true without Symbol.hasInstance
241
+ // or replacing the global constructor.
242
+ Object.setPrototypeOf(RequestHeaders.prototype, GlobalHeaders.prototype)
243
+
244
+ // Preserve the previous live-global behavior when a consumer installs a
245
+ // Headers polyfill after this module has initialized.
246
+ export const newHeadersFromIncoming = (incoming: IncomingHeadersSource): GlobalHeaders =>
247
+ globalThis.Headers === GlobalHeaders
248
+ ? (new RequestHeaders(incoming) as unknown as GlobalHeaders)
249
+ : materializeHeaders(incoming.rawHeaders, globalThis.Headers)
@@ -0,0 +1,6 @@
1
+ export { serve, createAdaptorServer } from './server.ts'
2
+ export { upgradeWebSocket } from './websocket.ts'
3
+ export { getRequestListener } from './listener.ts'
4
+ export { RequestError } from './request.ts'
5
+ export type { HttpBindings, Http2Bindings, ServerType } from './types.ts'
6
+ export type { WebSocketData, WebSocketLike, WebSocketServerLike } from './websocket-types.ts'