acrylblends 0.4.0 → 0.5.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 (426) hide show
  1. package/LICENSE +73 -0
  2. package/dist/index.js +56 -2
  3. package/dist/index.js.map +1 -1
  4. package/docs/framework/LICENSE +23 -0
  5. package/docs/framework/README.md +63 -0
  6. package/docs/framework/UPSTREAM.md +3 -0
  7. package/docs/framework/agents/index.md +24 -0
  8. package/docs/framework/api/acryl.md +259 -0
  9. package/docs/framework/api/context.md +527 -0
  10. package/docs/framework/api/exception.md +80 -0
  11. package/docs/framework/api/index.md +12 -0
  12. package/docs/framework/api/presets.md +65 -0
  13. package/docs/framework/api/request.md +396 -0
  14. package/docs/framework/api/routing.md +303 -0
  15. package/docs/framework/api/summary.md +140 -0
  16. package/docs/framework/concepts/benchmarks.md +26 -0
  17. package/docs/framework/concepts/developer-experience.md +5 -0
  18. package/docs/framework/concepts/middleware.md +21 -0
  19. package/docs/framework/concepts/motivation.md +14 -0
  20. package/docs/framework/concepts/routers.md +63 -0
  21. package/docs/framework/concepts/stacks.md +235 -0
  22. package/docs/framework/concepts/web-standard.md +37 -0
  23. package/docs/framework/cordis/acryl-in-a-cordis-app.md +146 -0
  24. package/docs/framework/cordis/concepts.md +105 -0
  25. package/docs/framework/cordis/middleware.md +8 -0
  26. package/docs/framework/cordis/migration-from-0-3.md +111 -0
  27. package/docs/framework/cordis/migration-from-0-4.md +14 -0
  28. package/docs/framework/cordis/migration-from-dsh-webserver.md +103 -0
  29. package/docs/framework/cordis/mounting.md +85 -0
  30. package/docs/framework/cordis/node-server.md +75 -0
  31. package/docs/framework/cordis/plugin-lifecycle.md +111 -0
  32. package/docs/framework/cordis/surfaces.md +71 -0
  33. package/docs/framework/cordis/transport-and-cancellation.md +57 -0
  34. package/docs/framework/cordis/validation.md +135 -0
  35. package/docs/framework/getting-started/basic.md +196 -0
  36. package/docs/framework/getting-started/bun.md +221 -0
  37. package/docs/framework/getting-started/nodejs.md +344 -0
  38. package/docs/framework/getting-started/other-runtimes.md +23 -0
  39. package/docs/framework/guides/best-practices.md +231 -0
  40. package/docs/framework/guides/create-acrylblends.md +79 -0
  41. package/docs/framework/guides/examples.md +3 -0
  42. package/docs/framework/guides/faq.md +19 -0
  43. package/docs/framework/guides/helpers.md +33 -0
  44. package/docs/framework/guides/jsx-dom.md +299 -0
  45. package/docs/framework/guides/jsx.md +440 -0
  46. package/docs/framework/guides/middleware.md +253 -0
  47. package/docs/framework/guides/rpc.md +886 -0
  48. package/docs/framework/guides/testing.md +133 -0
  49. package/docs/framework/guides/validation.md +383 -0
  50. package/docs/framework/helpers/accepts.md +60 -0
  51. package/docs/framework/helpers/adapter.md +87 -0
  52. package/docs/framework/helpers/conninfo.md +112 -0
  53. package/docs/framework/helpers/cookie.md +232 -0
  54. package/docs/framework/helpers/css.md +285 -0
  55. package/docs/framework/helpers/dev.md +67 -0
  56. package/docs/framework/helpers/factory.md +180 -0
  57. package/docs/framework/helpers/html.md +123 -0
  58. package/docs/framework/helpers/jwt.md +184 -0
  59. package/docs/framework/helpers/proxy.md +118 -0
  60. package/docs/framework/helpers/route.md +143 -0
  61. package/docs/framework/helpers/ssg.md +393 -0
  62. package/docs/framework/helpers/streaming.md +119 -0
  63. package/docs/framework/helpers/testing.md +101 -0
  64. package/docs/framework/helpers/websocket.md +215 -0
  65. package/docs/framework/middleware/builtin/basic-auth.md +165 -0
  66. package/docs/framework/middleware/builtin/bearer-auth.md +138 -0
  67. package/docs/framework/middleware/builtin/body-limit.md +71 -0
  68. package/docs/framework/middleware/builtin/cache.md +122 -0
  69. package/docs/framework/middleware/builtin/combine.md +94 -0
  70. package/docs/framework/middleware/builtin/compress.md +59 -0
  71. package/docs/framework/middleware/builtin/context-storage.md +78 -0
  72. package/docs/framework/middleware/builtin/cors.md +135 -0
  73. package/docs/framework/middleware/builtin/csrf.md +105 -0
  74. package/docs/framework/middleware/builtin/etag.md +54 -0
  75. package/docs/framework/middleware/builtin/ip-restriction.md +69 -0
  76. package/docs/framework/middleware/builtin/jsx-renderer.md +286 -0
  77. package/docs/framework/middleware/builtin/jwk.md +192 -0
  78. package/docs/framework/middleware/builtin/jwt.md +145 -0
  79. package/docs/framework/middleware/builtin/language.md +232 -0
  80. package/docs/framework/middleware/builtin/logger.md +74 -0
  81. package/docs/framework/middleware/builtin/loopback-only.md +31 -0
  82. package/docs/framework/middleware/builtin/method-not-allowed.md +48 -0
  83. package/docs/framework/middleware/builtin/method-override.md +80 -0
  84. package/docs/framework/middleware/builtin/mount.md +106 -0
  85. package/docs/framework/middleware/builtin/pretty-json.md +53 -0
  86. package/docs/framework/middleware/builtin/request-id.md +94 -0
  87. package/docs/framework/middleware/builtin/secure-headers.md +247 -0
  88. package/docs/framework/middleware/builtin/timeout.md +98 -0
  89. package/docs/framework/middleware/builtin/timing.md +101 -0
  90. package/docs/framework/middleware/builtin/trailing-slash.md +74 -0
  91. package/docs/framework/middleware/third-party.md +4 -0
  92. package/docs/framework/upstream.json +9 -0
  93. package/lib/accepts-XBuZPVRI.js +52 -0
  94. package/lib/accepts.d.ts +33 -0
  95. package/lib/accepts.js +2 -0
  96. package/lib/acryl-base.d.ts +2 -0
  97. package/lib/acryl-base.js +332 -0
  98. package/lib/adapter.d.ts +12 -0
  99. package/lib/adapter.js +37 -0
  100. package/lib/app-BM0rTCp7.js +26 -0
  101. package/lib/app-CmAYW1Al.d.ts +21 -0
  102. package/lib/basic-auth.d.ts +72 -0
  103. package/lib/basic-auth.js +95 -0
  104. package/lib/bearer-auth.d.ts +89 -0
  105. package/lib/bearer-auth.js +94 -0
  106. package/lib/body-limit.d.ts +42 -0
  107. package/lib/body-limit.js +66 -0
  108. package/lib/client-BHTr318g.js +266 -0
  109. package/lib/client.d.ts +3 -0
  110. package/lib/client.js +2 -0
  111. package/lib/combine.d.ts +85 -0
  112. package/lib/combine.js +137 -0
  113. package/lib/compose-BuW2B6PZ.js +50 -0
  114. package/lib/compress.d.ts +38 -0
  115. package/lib/compress.js +79 -0
  116. package/lib/connection-C7xgP1hP.js +24 -0
  117. package/lib/conninfo.d.ts +25 -0
  118. package/lib/conninfo.js +2 -0
  119. package/lib/constants-Dy7ZvN1H.js +4 -0
  120. package/lib/context-Bm7rI-oK.d.ts +1234 -0
  121. package/lib/context-DDO0cfuP.js +365 -0
  122. package/lib/context-storage.d.ts +38 -0
  123. package/lib/context-storage.js +52 -0
  124. package/lib/cookie-ClMaKDQf.d.ts +40 -0
  125. package/lib/cookie.d.ts +24 -0
  126. package/lib/cookie.js +82 -0
  127. package/lib/cors.d.ts +52 -0
  128. package/lib/cors.js +106 -0
  129. package/lib/csrf.d.ts +75 -0
  130. package/lib/csrf.js +101 -0
  131. package/lib/etag.d.ts +40 -0
  132. package/lib/etag.js +135 -0
  133. package/lib/factory.d.ts +35 -0
  134. package/lib/factory.js +30 -0
  135. package/lib/headers-tu2YYN8T.d.ts +11 -0
  136. package/lib/html.d.ts +6 -0
  137. package/lib/html.js +31 -0
  138. package/lib/http-exception.d.ts +57 -0
  139. package/lib/http-exception.js +54 -0
  140. package/lib/http-status-DaAwGAFL.d.ts +35 -0
  141. package/lib/index-B-3F4wt2.d.ts +12 -0
  142. package/lib/index-UTBmM3GX.d.ts +68 -0
  143. package/lib/index-oOClm_Xk.d.ts +45 -0
  144. package/lib/index.d.ts +5 -0
  145. package/lib/index.js +28 -0
  146. package/lib/ip-restriction.d.ts +68 -0
  147. package/lib/ip-restriction.js +166 -0
  148. package/lib/jwk.d.ts +52 -0
  149. package/lib/jwk.js +117 -0
  150. package/lib/jwt-BGJBw5AF.js +403 -0
  151. package/lib/jwt-Dg493Tai.d.ts +51 -0
  152. package/lib/jwt.d.ts +72 -0
  153. package/lib/jwt.js +115 -0
  154. package/lib/language.d.ts +88 -0
  155. package/lib/language.js +198 -0
  156. package/lib/logger.d.ts +22 -0
  157. package/lib/logger.js +54 -0
  158. package/lib/loopback-only.d.ts +45 -0
  159. package/lib/loopback-only.js +90 -0
  160. package/lib/method-not-allowed.d.ts +47 -0
  161. package/lib/method-not-allowed.js +82 -0
  162. package/lib/method-override.d.ts +46 -0
  163. package/lib/method-override.js +103 -0
  164. package/lib/mime-BQOYHl-l.d.ts +72 -0
  165. package/lib/node-server/conninfo.d.ts +11 -0
  166. package/lib/node-server/conninfo.js +19 -0
  167. package/lib/node-server/serve-static.d.ts +19 -0
  168. package/lib/node-server/serve-static.js +2 -0
  169. package/lib/node-server.d.ts +103 -0
  170. package/lib/node-server.js +1325 -0
  171. package/lib/powered-by.d.ts +28 -0
  172. package/lib/powered-by.js +24 -0
  173. package/lib/pretty-json.d.ts +40 -0
  174. package/lib/pretty-json.js +39 -0
  175. package/lib/reg-exp-router-DC1R3qoY.js +363 -0
  176. package/lib/request-id.d.ts +45 -0
  177. package/lib/request-id.js +34 -0
  178. package/lib/request.d.ts +2 -0
  179. package/lib/request.js +323 -0
  180. package/lib/route.d.ts +78 -0
  181. package/lib/route.js +126 -0
  182. package/lib/router/linear-router.d.ts +11 -0
  183. package/lib/router/linear-router.js +96 -0
  184. package/lib/router/pattern-router.d.ts +11 -0
  185. package/lib/router/pattern-router.js +41 -0
  186. package/lib/router/reg-exp-router.d.ts +35 -0
  187. package/lib/router/reg-exp-router.js +2 -0
  188. package/lib/router/smart-router.d.ts +15 -0
  189. package/lib/router/smart-router.js +2 -0
  190. package/lib/router/trie-router.d.ts +11 -0
  191. package/lib/router/trie-router.js +2 -0
  192. package/lib/router-CvUxUTOq.d.ts +99 -0
  193. package/lib/router.d.ts +2 -0
  194. package/lib/router.js +35 -0
  195. package/lib/schema-validator.d.ts +28 -0
  196. package/lib/schema-validator.js +22 -0
  197. package/lib/secure-headers.d.ts +125 -0
  198. package/lib/secure-headers.js +163 -0
  199. package/lib/serve-static-Dw_m6KxR.js +213 -0
  200. package/lib/serve-static.d.ts +31 -0
  201. package/lib/serve-static.js +77 -0
  202. package/lib/smart-router-C5CZ5XSM.js +49 -0
  203. package/lib/stream-CPdqZ0Fp.d.ts +34 -0
  204. package/lib/streaming-DHST3Scg.js +96 -0
  205. package/lib/streaming.d.ts +23 -0
  206. package/lib/streaming.js +2 -0
  207. package/lib/testing.d.ts +10 -0
  208. package/lib/testing.js +17 -0
  209. package/lib/timeout.d.ts +30 -0
  210. package/lib/timeout.js +42 -0
  211. package/lib/timing.d.ts +130 -0
  212. package/lib/timing.js +183 -0
  213. package/lib/trailing-slash.d.ts +91 -0
  214. package/lib/trailing-slash.js +85 -0
  215. package/lib/trie-router-CfFbapzL.js +146 -0
  216. package/lib/types-CDO6I3B0.d.ts +40 -0
  217. package/lib/types-CHesD1KG.d.ts +195 -0
  218. package/lib/types-CwrQjlaM.d.ts +105 -0
  219. package/lib/types-DR95J00T.d.ts +29 -0
  220. package/lib/types.d.ts +2 -0
  221. package/lib/types.js +4 -0
  222. package/lib/utils/accept.d.ts +9 -0
  223. package/lib/utils/accept.js +193 -0
  224. package/lib/utils/basic-auth.d.ts +8 -0
  225. package/lib/utils/basic-auth.js +20 -0
  226. package/lib/utils/body.d.ts +2 -0
  227. package/lib/utils/body.js +94 -0
  228. package/lib/utils/buffer.d.ts +19 -0
  229. package/lib/utils/buffer.js +51 -0
  230. package/lib/utils/color.d.ts +24 -0
  231. package/lib/utils/color.js +37 -0
  232. package/lib/utils/compress.d.ts +11 -0
  233. package/lib/utils/compress.js +11 -0
  234. package/lib/utils/concurrent.d.ts +17 -0
  235. package/lib/utils/concurrent.js +30 -0
  236. package/lib/utils/constants.d.ts +7 -0
  237. package/lib/utils/constants.js +7 -0
  238. package/lib/utils/cookie.d.ts +2 -0
  239. package/lib/utils/cookie.js +125 -0
  240. package/lib/utils/crypto.d.ts +14 -0
  241. package/lib/utils/crypto.js +34 -0
  242. package/lib/utils/encode.d.ts +11 -0
  243. package/lib/utils/encode.js +33 -0
  244. package/lib/utils/filepath.d.ts +14 -0
  245. package/lib/utils/filepath.js +25 -0
  246. package/lib/utils/handler.d.ts +9 -0
  247. package/lib/utils/handler.js +12 -0
  248. package/lib/utils/headers.d.ts +2 -0
  249. package/lib/utils/headers.js +1 -0
  250. package/lib/utils/html.d.ts +46 -0
  251. package/lib/utils/html.js +103 -0
  252. package/lib/utils/http-status.d.ts +2 -0
  253. package/lib/utils/http-status.js +1 -0
  254. package/lib/utils/ipaddr.d.ts +56 -0
  255. package/lib/utils/ipaddr.js +238 -0
  256. package/lib/utils/jwt.d.ts +24 -0
  257. package/lib/utils/jwt.js +2 -0
  258. package/lib/utils/mime.d.ts +2 -0
  259. package/lib/utils/mime.js +77 -0
  260. package/lib/utils/stream.d.ts +2 -0
  261. package/lib/utils/stream.js +88 -0
  262. package/lib/utils/types.d.ts +2 -0
  263. package/lib/utils/types.js +1 -0
  264. package/lib/utils/url.d.ts +46 -0
  265. package/lib/utils/url.js +197 -0
  266. package/lib/utils-dw8lxqjL.js +4 -0
  267. package/lib/validator-D84mq-dO.js +65 -0
  268. package/lib/validator.d.ts +42 -0
  269. package/lib/validator.js +2 -0
  270. package/lib/ws.d.ts +2 -0
  271. package/lib/ws.js +53 -0
  272. package/package.json +315 -15
  273. package/plugins/acryl-inspect/src/report.ts +15 -10
  274. package/plugins/acryl-inspect/src/service.ts +2 -2
  275. package/plugins/acryl-inspect/tests/report.spec.ts +9 -3
  276. package/plugins/acryl-inspect/tests/service.spec.ts +3 -3
  277. package/plugins/acryl-inspect/upstream.json +2 -2
  278. package/plugins/acryl-node-server/tests/adapter/app.ts +2 -2
  279. package/plugins/acryl-node-server/tests/adapter/assets/static/acryl.html +1 -1
  280. package/plugins/acryl-node-server/tests/adapter/assets/static/index.html +1 -1
  281. package/plugins/acryl-node-server/tests/adapter/conninfo.test.ts +2 -2
  282. package/plugins/acryl-node-server/tests/adapter/content-length.test.ts +4 -4
  283. package/plugins/acryl-node-server/tests/adapter/early-hints.test.ts +5 -5
  284. package/plugins/acryl-node-server/tests/adapter/request-client-disconnect.test.ts +2 -2
  285. package/plugins/acryl-node-server/tests/adapter/serve-static.test.ts +20 -20
  286. package/plugins/acryl-node-server/tests/adapter/server-socket.test.ts +2 -2
  287. package/plugins/acryl-node-server/tests/adapter/server.test.ts +50 -50
  288. package/plugins/acryl-node-server/tests/adapter/utils/response.test.ts +4 -4
  289. package/plugins/acryl-node-server/tests/adapter/websocket.test.ts +9 -9
  290. package/plugins/acryl-node-server/tests/server.spec.ts +6 -6
  291. package/plugins/acryl-node-server/upstream.json +2 -2
  292. package/plugins/acryl-router/src/client.ts +1 -1
  293. package/plugins/acryl-router/src/connection.ts +2 -2
  294. package/plugins/acryl-router/src/core/app-base.ts +29 -29
  295. package/plugins/acryl-router/src/core/app.test.ts +175 -175
  296. package/plugins/acryl-router/src/core/app.ts +8 -8
  297. package/plugins/acryl-router/src/core/client/client.test.ts +76 -76
  298. package/plugins/acryl-router/src/core/client/client.ts +2 -2
  299. package/plugins/acryl-router/src/core/client/fetch-result-please.ts +1 -1
  300. package/plugins/acryl-router/src/core/client/index.ts +1 -1
  301. package/plugins/acryl-router/src/core/client/types.test.ts +6 -6
  302. package/plugins/acryl-router/src/core/client/types.ts +16 -16
  303. package/plugins/acryl-router/src/core/client/utils.test.ts +2 -2
  304. package/plugins/acryl-router/src/core/context.test.ts +4 -4
  305. package/plugins/acryl-router/src/core/context.ts +6 -6
  306. package/plugins/acryl-router/src/core/helper/accepts/accepts.test.ts +3 -3
  307. package/plugins/acryl-router/src/core/helper/accepts/index.ts +1 -1
  308. package/plugins/acryl-router/src/core/helper/adapter/index.test.ts +6 -6
  309. package/plugins/acryl-router/src/core/helper/adapter/index.ts +1 -1
  310. package/plugins/acryl-router/src/core/helper/conninfo/index.ts +1 -1
  311. package/plugins/acryl-router/src/core/helper/cookie/index.test.ts +8 -8
  312. package/plugins/acryl-router/src/core/helper/cookie/index.ts +1 -1
  313. package/plugins/acryl-router/src/core/helper/factory/index.test.ts +9 -9
  314. package/plugins/acryl-router/src/core/helper/factory/index.ts +10 -10
  315. package/plugins/acryl-router/src/core/helper/html/index.test.ts +4 -4
  316. package/plugins/acryl-router/src/core/helper/html/index.ts +1 -1
  317. package/plugins/acryl-router/src/core/helper/route/index.test.ts +7 -7
  318. package/plugins/acryl-router/src/core/helper/route/index.ts +8 -8
  319. package/plugins/acryl-router/src/core/helper/streaming/index.ts +1 -1
  320. package/plugins/acryl-router/src/core/helper/testing/index.test.ts +6 -6
  321. package/plugins/acryl-router/src/core/helper/testing/index.ts +4 -4
  322. package/plugins/acryl-router/src/core/helper/websocket/index.ts +2 -1
  323. package/plugins/acryl-router/src/core/http-exception.ts +1 -1
  324. package/plugins/acryl-router/src/core/index.ts +9 -9
  325. package/plugins/acryl-router/src/core/middleware/basic-auth/index.test.ts +6 -6
  326. package/plugins/acryl-router/src/core/middleware/basic-auth/index.ts +3 -3
  327. package/plugins/acryl-router/src/core/middleware/bearer-auth/index.test.ts +5 -5
  328. package/plugins/acryl-router/src/core/middleware/bearer-auth/index.ts +3 -3
  329. package/plugins/acryl-router/src/core/middleware/body-limit/index.test.ts +6 -6
  330. package/plugins/acryl-router/src/core/middleware/body-limit/index.ts +3 -3
  331. package/plugins/acryl-router/src/core/middleware/combine/index.test.ts +9 -9
  332. package/plugins/acryl-router/src/core/middleware/combine/index.ts +7 -7
  333. package/plugins/acryl-router/src/core/middleware/compress/index.test.ts +9 -9
  334. package/plugins/acryl-router/src/core/middleware/compress/index.ts +3 -3
  335. package/plugins/acryl-router/src/core/middleware/context-storage/index.test.ts +5 -5
  336. package/plugins/acryl-router/src/core/middleware/context-storage/index.ts +4 -4
  337. package/plugins/acryl-router/src/core/middleware/cors/index.test.ts +7 -7
  338. package/plugins/acryl-router/src/core/middleware/cors/index.ts +3 -3
  339. package/plugins/acryl-router/src/core/middleware/csrf/index.test.ts +10 -10
  340. package/plugins/acryl-router/src/core/middleware/csrf/index.ts +3 -3
  341. package/plugins/acryl-router/src/core/middleware/etag/index.test.ts +42 -42
  342. package/plugins/acryl-router/src/core/middleware/etag/index.ts +4 -4
  343. package/plugins/acryl-router/src/core/middleware/ip-restriction/index.test.ts +4 -4
  344. package/plugins/acryl-router/src/core/middleware/ip-restriction/index.ts +6 -6
  345. package/plugins/acryl-router/src/core/middleware/jwk/index.test.ts +16 -16
  346. package/plugins/acryl-router/src/core/middleware/jwk/jwk.ts +7 -6
  347. package/plugins/acryl-router/src/core/middleware/jwt/index.test.ts +16 -16
  348. package/plugins/acryl-router/src/core/middleware/jwt/jwt.ts +4 -3
  349. package/plugins/acryl-router/src/core/middleware/language/index.test.ts +3 -3
  350. package/plugins/acryl-router/src/core/middleware/language/language.ts +3 -3
  351. package/plugins/acryl-router/src/core/middleware/logger/index.test.ts +5 -5
  352. package/plugins/acryl-router/src/core/middleware/logger/index.ts +4 -4
  353. package/plugins/acryl-router/src/core/middleware/method-not-allowed/index.test.ts +24 -24
  354. package/plugins/acryl-router/src/core/middleware/method-not-allowed/index.ts +8 -8
  355. package/plugins/acryl-router/src/core/middleware/method-override/index.test.ts +5 -5
  356. package/plugins/acryl-router/src/core/middleware/method-override/index.ts +6 -6
  357. package/plugins/acryl-router/src/core/middleware/powered-by/index.test.ts +4 -4
  358. package/plugins/acryl-router/src/core/middleware/powered-by/index.ts +6 -6
  359. package/plugins/acryl-router/src/core/middleware/pretty-json/index.test.ts +30 -30
  360. package/plugins/acryl-router/src/core/middleware/pretty-json/index.ts +4 -4
  361. package/plugins/acryl-router/src/core/middleware/request-id/index.test.ts +15 -15
  362. package/plugins/acryl-router/src/core/middleware/request-id/request-id.ts +3 -3
  363. package/plugins/acryl-router/src/core/middleware/secure-headers/index.test.ts +35 -35
  364. package/plugins/acryl-router/src/core/middleware/secure-headers/secure-headers.ts +3 -3
  365. package/plugins/acryl-router/src/core/middleware/serve-static/index.test.ts +23 -23
  366. package/plugins/acryl-router/src/core/middleware/serve-static/index.ts +1 -1
  367. package/plugins/acryl-router/src/core/middleware/timeout/index.test.ts +2 -2
  368. package/plugins/acryl-router/src/core/middleware/timeout/index.ts +3 -3
  369. package/plugins/acryl-router/src/core/middleware/timing/index.test.ts +14 -14
  370. package/plugins/acryl-router/src/core/middleware/timing/timing.ts +3 -3
  371. package/plugins/acryl-router/src/core/middleware/trailing-slash/index.test.ts +7 -7
  372. package/plugins/acryl-router/src/core/middleware/trailing-slash/index.ts +7 -7
  373. package/plugins/acryl-router/src/core/request.test.ts +42 -42
  374. package/plugins/acryl-router/src/core/request.ts +6 -6
  375. package/plugins/acryl-router/src/core/router/linear-router/index.ts +1 -1
  376. package/plugins/acryl-router/src/core/router/pattern-router/index.ts +1 -1
  377. package/plugins/acryl-router/src/core/router/reg-exp-router/index.ts +1 -1
  378. package/plugins/acryl-router/src/core/router/smart-router/index.ts +1 -1
  379. package/plugins/acryl-router/src/core/router/trie-router/index.ts +1 -1
  380. package/plugins/acryl-router/src/core/types.test.ts +115 -115
  381. package/plugins/acryl-router/src/core/types.ts +72 -72
  382. package/plugins/acryl-router/src/core/utils/body.ts +8 -8
  383. package/plugins/acryl-router/src/core/utils/html.ts +1 -1
  384. package/plugins/acryl-router/src/core/utils/jwt/jws.ts +3 -3
  385. package/plugins/acryl-router/src/core/utils/jwt/jwt.test.ts +2 -2
  386. package/plugins/acryl-router/src/core/utils/jwt/jwt.ts +3 -3
  387. package/plugins/acryl-router/src/core/utils/jwt/types.ts +1 -1
  388. package/plugins/acryl-router/src/core/utils/types.ts +42 -0
  389. package/plugins/acryl-router/src/core/utils/url.test.ts +2 -2
  390. package/plugins/acryl-router/src/core/validator/index.ts +1 -1
  391. package/plugins/acryl-router/src/core/validator/validator.test.ts +28 -28
  392. package/plugins/acryl-router/src/deprecated.ts +15 -0
  393. package/plugins/acryl-router/src/index.ts +3 -2
  394. package/plugins/acryl-router/src/middleware/index.ts +1 -1
  395. package/plugins/acryl-router/src/middleware/loopback-only.ts +34 -1
  396. package/plugins/acryl-router/src/service.ts +19 -13
  397. package/plugins/acryl-router/tests/docs-examples.spec.ts +11 -11
  398. package/plugins/acryl-router/tests/live-install.spec.ts +2 -2
  399. package/plugins/acryl-router/tests/loader.spec.ts +3 -3
  400. package/plugins/acryl-router/tests/loopback-only.spec.ts +80 -0
  401. package/plugins/acryl-router/tests/no-dom/consumer.ts +11 -0
  402. package/plugins/acryl-router/tests/no-dom/tsconfig.json +13 -0
  403. package/plugins/acryl-router/tests/service.spec.ts +39 -22
  404. package/plugins/acryl-router/upstream.json +3 -3
  405. package/plugins/acryl-router-http/tests/adapter.spec.ts +3 -3
  406. package/plugins/acryl-router-http/tests/surfaces.spec.ts +2 -2
  407. package/plugins/acryl-router-http/upstream.json +2 -2
  408. package/plugins/acryl-state/src/service.ts +2 -2
  409. package/plugins/acryl-state/upstream.json +2 -2
  410. package/plugins/builder/script.ts +3 -3
  411. package/plugins/extensions/index.ts +4 -3
  412. package/plugins/extensions/resolve.ts +70 -10
  413. package/plugins/openai/prompt.ts +7 -7
  414. package/shared/AGENTS.core.md +61 -0
  415. package/templates/agent/AGENTS.md +6 -0
  416. package/templates/agent/README.md +2 -2
  417. package/templates/agent/blend.yaml +1 -1
  418. package/templates/agent/src/routes.ts +2 -2
  419. package/templates/agent/test/model.test.ts +2 -2
  420. package/templates/agent/tsconfig.json +40 -0
  421. package/templates/blank/AGENTS.md +5 -0
  422. package/templates/blank/README.md +1 -1
  423. package/templates/blank/src/routes.ts +2 -2
  424. package/templates/todo/AGENTS.md +5 -0
  425. package/templates/todo/src/todo.ts +6 -4
  426. package/templates/todo/upstream.json +2 -2
@@ -6,7 +6,7 @@ import { Context } from '@deepseek-ai/cordis'
6
6
  import z from '@deepseek-ai/schemastery'
7
7
  import { afterEach, describe, expect, it } from 'vitest'
8
8
  import { createMiddleware, schemaValidator, testClient } from '../src/helpers/index.ts'
9
- import { App, HTTPException, apply, name } from '../src/index.ts'
9
+ import { Acryl, HTTPException, apply, name } from '../src/index.ts'
10
10
  import { methodNotAllowed } from '../src/middleware/index.ts'
11
11
 
12
12
  const contexts: Context[] = []
@@ -30,7 +30,7 @@ describe('getting-started.md', () => {
30
30
  name: 'todo',
31
31
  inject: ['router'],
32
32
  apply(inner) {
33
- const app = new App()
33
+ const app = new Acryl()
34
34
  .get('/', c => c.json({ ok: true }))
35
35
  .get('/todos/:id', c => c.json({ id: c.req.param('id') }))
36
36
  inner.router.route('/todo', app)
@@ -45,7 +45,7 @@ describe('getting-started.md', () => {
45
45
 
46
46
  describe('routing.md', () => {
47
47
  it('serves the routes an app had at the mount, not ones added later', async () => {
48
- const app = new App().get('/a', c => c.text('a'))
48
+ const app = new Acryl().get('/a', c => c.text('a'))
49
49
  const ctx = await withPlugin({ name: 'late', inject: ['router'], apply(inner) { inner.router.route('/x', app) } })
50
50
  app.get('/b', c => c.text('b'))
51
51
  expect((await ctx.router.request('/x/a')).status).toBe(200)
@@ -53,9 +53,9 @@ describe('routing.md', () => {
53
53
  })
54
54
 
55
55
  it('answers a wrong method with 404, or 405 and Allow with methodNotAllowed', async () => {
56
- const plain = new App().get('/item', c => c.text('item'))
56
+ const plain = new Acryl().get('/item', c => c.text('item'))
57
57
  expect((await plain.request('/item', { method: 'POST' })).status).toBe(404)
58
- const strict = new App()
58
+ const strict = new Acryl()
59
59
  strict.use(methodNotAllowed({ app: strict })).get('/item', c => c.text('item'))
60
60
  const response = await strict.request('/item', { method: 'POST' })
61
61
  expect(response.status).toBe(405)
@@ -69,7 +69,7 @@ describe('routing.md', () => {
69
69
  })
70
70
 
71
71
  it('uses a mounted app\'s onError for its routes', async () => {
72
- const app = new App().get('/boom', () => { throw new Error('boom') })
72
+ const app = new Acryl().get('/boom', () => { throw new Error('boom') })
73
73
  app.onError((_error, c) => c.json({ error: 'internal_error' }, 500))
74
74
  const ctx = await withPlugin({ name: 'errors', inject: ['router'], apply(inner) { inner.router.route('/', app) } })
75
75
  expect(await (await ctx.router.request('/boom')).json()).toEqual({ error: 'internal_error' })
@@ -82,14 +82,14 @@ describe('context.md and middleware.md', () => {
82
82
  c.set('user', { name: 'ada' })
83
83
  await next()
84
84
  })
85
- const app = new App().use(withUser).get('/me', c => c.json(c.var.user))
85
+ const app = new Acryl().use(withUser).get('/me', c => c.json(c.var.user))
86
86
  expect(await (await app.request('/me')).json()).toEqual({ name: 'ada' })
87
87
  })
88
88
 
89
89
  it('never throws from next(): the error is already a response and is left in c.error', async () => {
90
90
  let caught = false
91
91
  let seen: unknown
92
- const app = new App()
92
+ const app = new Acryl()
93
93
  .use(async (c, next) => {
94
94
  try {
95
95
  await next()
@@ -105,7 +105,7 @@ describe('context.md and middleware.md', () => {
105
105
  })
106
106
 
107
107
  it('turns an HTTPException into its own response', async () => {
108
- const app = new App().get('/', () => { throw new HTTPException(403, { message: 'not yours' }) })
108
+ const app = new Acryl().get('/', () => { throw new HTTPException(403, { message: 'not yours' }) })
109
109
  const response = await app.request('/')
110
110
  expect(response.status).toBe(403)
111
111
  expect(await response.text()).toBe('not yours')
@@ -117,7 +117,7 @@ describe('validation.md', () => {
117
117
  title: z.string().required(),
118
118
  done: z.boolean().default(false),
119
119
  })
120
- const app = new App().post('/todos', schemaValidator('json', NewTodo), c => c.json({ todo: c.req.valid('json') }, 201))
120
+ const app = new Acryl().post('/todos', schemaValidator('json', NewTodo), c => c.json({ todo: c.req.valid('json') }, 201))
121
121
  const post = (body: string, type?: string): Promise<Response> =>
122
122
  Promise.resolve(app.request('/todos', { method: 'POST', body, headers: type === undefined ? {} : { 'content-type': type } }))
123
123
 
@@ -144,7 +144,7 @@ describe('validation.md', () => {
144
144
 
145
145
  describe('helpers.md', () => {
146
146
  it('calls a chained app through the typed test client', async () => {
147
- const app = new App().get('/search', c => c.json({ query: c.req.query('q') }))
147
+ const app = new Acryl().get('/search', c => c.json({ query: c.req.query('q') }))
148
148
  const client = testClient(app)
149
149
  const response = await client.search.$get({ query: { q: 'cordis' } })
150
150
  expect(await response.json()).toEqual({ query: 'cordis' })
@@ -7,7 +7,7 @@
7
7
  */
8
8
  import { Context, Service } from '@deepseek-ai/cordis'
9
9
  import { afterEach, describe, expect, it } from 'vitest'
10
- import { App, apply, name } from '../src/index.ts'
10
+ import { Acryl, apply, name } from '../src/index.ts'
11
11
 
12
12
  const contexts: Context[] = []
13
13
  const settle = (): Promise<void> => new Promise(resolve => setTimeout(resolve, 0))
@@ -46,7 +46,7 @@ const extension = (): object => ({
46
46
  name: 'live-ext',
47
47
  inject: ['router'],
48
48
  apply(ctx: Context): void {
49
- ctx.router.route('/', new App().get('/live', c => c.json({ ok: true })))
49
+ ctx.router.route('/', new Acryl().get('/live', c => c.json({ ok: true })))
50
50
  },
51
51
  })
52
52
 
@@ -11,7 +11,7 @@ import { join } from 'node:path'
11
11
  import { Context, type FiberState } from '@deepseek-ai/cordis'
12
12
  import { Loader } from '@deepseek-ai/cordis-plugin-loader'
13
13
  import { afterEach, describe, expect, it } from 'vitest'
14
- import { App } from '../src/index.ts'
14
+ import { Acryl } from '../src/index.ts'
15
15
 
16
16
  const ACTIVE = 2 as FiberState.ACTIVE
17
17
  const DISPOSED = 4 as FiberState.DISPOSED
@@ -46,7 +46,7 @@ describe('acryl-router through a real Loader', () => {
46
46
  it('activates from the built entry and serves a request in process', async () => {
47
47
  const { ctx, entryId } = await mount()
48
48
  expect(entryOf(ctx, entryId).fiber?.state).toBe(ACTIVE)
49
- ctx.router.route('/', new App().get('/ping', c => c.json({ ok: true })))
49
+ ctx.router.route('/', new Acryl().get('/ping', c => c.json({ ok: true })))
50
50
  const response = await ctx.router.fetch(new Request('http://acryl.test/ping'))
51
51
  expect(await response.json()).toEqual({ ok: true })
52
52
  })
@@ -66,7 +66,7 @@ describe('acryl-router through a real Loader', () => {
66
66
  expect(ctx.router).not.toBe(first)
67
67
  expect(ctx.router.size).toBe(0)
68
68
  expect((await first.fetch(new Request('http://acryl.test/x'))).status).toBe(503)
69
- ctx.router.route('/', new App().get('/again', c => c.text('ok')))
69
+ ctx.router.route('/', new Acryl().get('/again', c => c.text('ok')))
70
70
  expect(await (await ctx.router.fetch(new Request('http://acryl.test/again'))).text()).toBe('ok')
71
71
  })
72
72
  })
@@ -0,0 +1,80 @@
1
+ /**
2
+ * `loopbackOnly`, the guard of a local app's own routes: the peer and port come from the transport, the `Host` names this app, and the
3
+ * `callers` rule decides whether any local program or only the app's own page may call.
4
+ */
5
+
6
+ import { describe, expect, it } from 'vitest'
7
+ import { Acryl, type ConnectionInfo } from '../src/index.ts'
8
+ import { loopbackOnly, type LoopbackOptions } from '../src/middleware/index.ts'
9
+
10
+ const PORT = 43_120
11
+ const SELF = `http://127.0.0.1:${String(PORT)}`
12
+ const LOCAL: ConnectionInfo = { remoteAddress: '127.0.0.1', localPort: PORT }
13
+
14
+ function guarded(options: LoopbackOptions = {}): Acryl {
15
+ return new Acryl().use(loopbackOnly(options)).get('/x', c => c.text('read')).post('/x', c => c.text('changed'))
16
+ }
17
+
18
+ async function status(
19
+ app: Acryl,
20
+ method: string,
21
+ headers: Record<string, string>,
22
+ connection: ConnectionInfo = LOCAL,
23
+ ): Promise<number> {
24
+ const response = await app.fetch(new Request(`${SELF}/x`, { method, headers: { host: `127.0.0.1:${String(PORT)}`, ...headers } }), { connection })
25
+ return response.status
26
+ }
27
+
28
+ describe('loopbackOnly, callers: local (the default)', () => {
29
+ const app = guarded()
30
+
31
+ it('lets a local program read and change, and a page of this app change', async () => {
32
+ expect(await status(app, 'GET', {})).toBe(200)
33
+ expect(await status(app, 'POST', {})).toBe(200)
34
+ expect(await status(app, 'POST', { origin: SELF })).toBe(200)
35
+ })
36
+
37
+ it('refuses another origin\'s change, a foreign peer, a wrong port, a foreign Host and an in-process call', async () => {
38
+ expect(await status(app, 'POST', { origin: 'http://evil.test' })).toBe(403)
39
+ expect(await status(app, 'GET', {}, { remoteAddress: '192.168.1.5', localPort: PORT })).toBe(403)
40
+ expect(await status(app, 'GET', {}, { remoteAddress: '127.0.0.1', localPort: PORT + 1 })).toBe(403)
41
+ expect(await status(app, 'GET', { host: 'evil.test' })).toBe(403)
42
+ expect(await status(app, 'GET', {}, {})).toBe(403)
43
+ })
44
+
45
+ it('lets an in-process call through only when the host says so', async () => {
46
+ expect(await status(guarded({ inProcess: 'allow' }), 'GET', {}, {})).toBe(200)
47
+ })
48
+ })
49
+
50
+ describe('loopbackOnly, callers: own-page', () => {
51
+ const app = guarded({ callers: 'own-page', hostnames: ['127.0.0.1'] })
52
+
53
+ it('lets this app\'s page read and change with its exact Origin', async () => {
54
+ expect(await status(app, 'GET', { origin: SELF, 'sec-fetch-site': 'same-origin' })).toBe(200)
55
+ expect(await status(app, 'POST', { origin: SELF, 'sec-fetch-site': 'same-origin' })).toBe(200)
56
+ expect(await status(app, 'POST', { origin: SELF })).toBe(200)
57
+ })
58
+
59
+ it('lets a same-origin read without Origin through by its Referer, as browsers send it', async () => {
60
+ expect(await status(app, 'GET', { 'sec-fetch-site': 'same-origin', referer: `${SELF}/page?tab=1` })).toBe(200)
61
+ })
62
+
63
+ it('refuses a local program (no Origin), even for a read', async () => {
64
+ expect(await status(app, 'GET', {})).toBe(403)
65
+ expect(await status(app, 'POST', {})).toBe(403)
66
+ })
67
+
68
+ it('refuses a change that only has the read evidence, and Origin forms that are not exact', async () => {
69
+ expect(await status(app, 'POST', { 'sec-fetch-site': 'same-origin', referer: `${SELF}/` })).toBe(403)
70
+ expect(await status(app, 'POST', { origin: `${SELF}/` })).toBe(403)
71
+ expect(await status(app, 'POST', { origin: 'null' })).toBe(403)
72
+ expect(await status(app, 'POST', { origin: SELF, 'sec-fetch-site': 'same-site' })).toBe(403)
73
+ })
74
+
75
+ it('refuses another page, a Referer from elsewhere and a Host this app does not answer to', async () => {
76
+ expect(await status(app, 'GET', { origin: 'http://evil.test', 'sec-fetch-site': 'cross-site' })).toBe(403)
77
+ expect(await status(app, 'GET', { 'sec-fetch-site': 'same-origin', referer: 'http://evil.test/' })).toBe(403)
78
+ expect(await status(app, 'GET', { host: `localhost:${String(PORT)}`, origin: `http://localhost:${String(PORT)}` })).toBe(403)
79
+ })
80
+ })
@@ -0,0 +1,11 @@
1
+ /**
2
+ * A Host-only consumer: Node types and no DOM library. Typechecking this against the built `lib/types` proves every public entry point's
3
+ * declarations stand without the DOM library, which a server plugin should not need to load.
4
+ */
5
+
6
+ import type * as Router from '../../src/index.ts'
7
+ import type * as Client from '../../src/client.ts'
8
+ import type * as Helpers from '../../src/helpers/index.ts'
9
+ import type * as Middleware from '../../src/middleware/index.ts'
10
+
11
+ export type Entries = [typeof Router, typeof Client, typeof Helpers, typeof Middleware]
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2024",
4
+ "lib": ["ES2024"],
5
+ "module": "NodeNext",
6
+ "moduleResolution": "NodeNext",
7
+ "strict": true,
8
+ "exactOptionalPropertyTypes": true,
9
+ "noEmit": true,
10
+ "types": ["node"]
11
+ },
12
+ "files": ["consumer.ts"]
13
+ }
@@ -7,7 +7,7 @@
7
7
  import { Context, type FiberState } from '@deepseek-ai/cordis'
8
8
  import { afterEach, describe, expect, it } from 'vitest'
9
9
  import { streamSSE } from '../src/helpers/index.ts'
10
- import { apply, App, Config, connectionOf, HTTPException, name, type Router } from '../src/index.ts'
10
+ import { apply, Acryl, Config, connectionOf, HTTPException, name, type Router } from '../src/index.ts'
11
11
 
12
12
  const PENDING = 0 as FiberState.PENDING
13
13
  const ACTIVE = 2 as FiberState.ACTIVE
@@ -42,7 +42,7 @@ function consumer(path: string, label: string) {
42
42
  name: `consumer${label}`,
43
43
  inject: ['router'],
44
44
  apply(ctx: Context): void {
45
- ctx.router.route('/', new App().get(path, c => c.text(label)))
45
+ ctx.router.route('/', new Acryl().get(path, c => c.text(label)))
46
46
  },
47
47
  }
48
48
  }
@@ -52,7 +52,7 @@ const get = (router: Router, path: string): Promise<Response> => router.fetch(ne
52
52
  describe('acryl-router mounts', () => {
53
53
  it('serves a mounted app under its path, with the app\'s own routes, middleware and error handler', async () => {
54
54
  const { router } = await mountRouter()
55
- const api = new App()
55
+ const api = new Acryl()
56
56
  .use(async (c, next) => { await next(); c.header('x-api', 'yes') })
57
57
  .get('/items/:id', c => c.json({ id: c.req.param('id') }))
58
58
  .get('/boom', () => { throw new Error('boom') })
@@ -70,28 +70,28 @@ describe('acryl-router mounts', () => {
70
70
 
71
71
  it('removes a mount with its remover, idempotently, and keeps the others', async () => {
72
72
  const { router } = await mountRouter()
73
- const removeA = router.route('/', new App().get('/a', c => c.text('a')))
74
- router.route('/', new App().get('/b', c => c.text('b')))
73
+ const removeA = router.route('/', new Acryl().get('/a', c => c.text('a')))
74
+ router.route('/', new Acryl().get('/b', c => c.text('b')))
75
75
  expect((await get(router, '/a')).status).toBe(200)
76
76
  removeA()
77
77
  removeA()
78
78
  expect((await get(router, '/a')).status).toBe(404)
79
79
  expect(await (await get(router, '/b')).text()).toBe('b')
80
- router.route('/', new App().get('/a', c => c.text('a2')))
80
+ router.route('/', new Acryl().get('/a', c => c.text('a2')))
81
81
  expect(await (await get(router, '/a')).text()).toBe('a2')
82
82
  })
83
83
 
84
84
  it('runs mounts in the order they were made, so an earlier mount\'s middleware wraps a later mount\'s routes', async () => {
85
85
  const { router } = await mountRouter()
86
- router.route('/', new App().use('/guarded/*', async c => c.text('denied', 401)))
87
- router.route('/', new App().get('/guarded/x', c => c.text('secret')).get('/open', c => c.text('open')))
86
+ router.route('/', new Acryl().use('/guarded/*', async c => c.text('denied', 401)))
87
+ router.route('/', new Acryl().get('/guarded/x', c => c.text('secret')).get('/open', c => c.text('open')))
88
88
  expect((await get(router, '/guarded/x')).status).toBe(401)
89
89
  expect(await (await get(router, '/open')).text()).toBe('open')
90
90
  })
91
91
 
92
92
  it('takes one router-wide error handler and one not-found handler, owned by the caller', async () => {
93
93
  const { router } = await mountRouter()
94
- router.route('/', new App().get('/boom', () => { throw new Error('boom') }).get('/teapot', () => { throw new HTTPException(418, { message: 'short' }) }))
94
+ router.route('/', new Acryl().get('/boom', () => { throw new Error('boom') }).get('/teapot', () => { throw new HTTPException(418, { message: 'short' }) }))
95
95
  expect((await get(router, '/boom')).status).toBe(500)
96
96
  expect((await get(router, '/teapot')).status).toBe(418)
97
97
 
@@ -109,13 +109,13 @@ describe('acryl-router mounts', () => {
109
109
  const ctx = root()
110
110
  ctx.plugin(provider, { trailingSlash: 'ignore', disposeGraceMs: 2000 })
111
111
  await settle()
112
- ctx.router.route('/', new App().get('/a', c => c.text('a')))
112
+ ctx.router.route('/', new Acryl().get('/a', c => c.text('a')))
113
113
  expect((await get(ctx.router, '/a/')).status).toBe(200)
114
114
  })
115
115
 
116
116
  it('hands the transport\'s connection info to the handler and gives an in-process call an empty one', async () => {
117
117
  const { router } = await mountRouter()
118
- router.route('/', new App().get('/who', c => c.json({ remote: connectionOf(c).remoteAddress ?? null, port: connectionOf(c).localPort ?? null })))
118
+ router.route('/', new Acryl().get('/who', c => c.json({ remote: connectionOf(c).remoteAddress ?? null, port: connectionOf(c).localPort ?? null })))
119
119
  const over = await router.fetch(new Request('http://acryl.test/who'), { remoteAddress: '127.0.0.1', localPort: 4000 })
120
120
  expect(await over.json()).toEqual({ remote: '127.0.0.1', port: 4000 })
121
121
  const inProcess = await router.request('/who')
@@ -152,6 +152,23 @@ describe('acryl-router lifecycle through Cordis', () => {
152
152
  expect(router.size).toBe(1)
153
153
  })
154
154
 
155
+ it('labels a mount by its path with one child per route, so no label grows with the app (an inspector caps label length)', async () => {
156
+ const { ctx, router } = await mountRouter()
157
+ const big = new Acryl().use('/api/*', async (_c, next) => { await next() })
158
+ for (let i = 0; i < 60; i += 1) big.get(`/api/a-fairly-long-resource-name-${String(i)}`, c => c.text('ok'))
159
+ const fiber = ctx.plugin({ name: 'big', inject: ['router'], apply: (inner: Context) => { inner.router.route('/', big) } })
160
+ await settle()
161
+ const [mount, ...rest] = fiber.getEffects()
162
+ expect(rest).toEqual([])
163
+ expect(mount?.label).toBe('acryl-router: route /')
164
+ expect(mount?.children).toHaveLength(61)
165
+ expect(mount?.children[0]?.label).toBe('acryl-router: serves ALL /api/*')
166
+ expect(mount?.children.every(child => child.label.length < 100 && child.children.length === 0)).toBe(true)
167
+ expect(router.size).toBe(1)
168
+ await fiber.dispose()
169
+ expect(router.size).toBe(0)
170
+ })
171
+
155
172
  it('replaces the provider without leaving doubled registrations: consumers re-register exactly once', async () => {
156
173
  const ctx = root()
157
174
  const first = ctx.plugin(provider, config)
@@ -175,7 +192,7 @@ describe('acryl-router lifecycle through Cordis', () => {
175
192
 
176
193
  it('serves the routes an app had when it was mounted, whatever else changes later', async () => {
177
194
  const { ctx, router } = await mountRouter()
178
- const app = new App().get('/early', c => c.text('early'))
195
+ const app = new Acryl().get('/early', c => c.text('early'))
179
196
  router.route('/', app)
180
197
  app.get('/late', c => c.text('late'))
181
198
  expect((await get(router, '/early')).status).toBe(200)
@@ -206,7 +223,7 @@ describe('acryl-router lifecycle through Cordis', () => {
206
223
  name: 'counting',
207
224
  inject: ['router'],
208
225
  apply(inner: Context): void {
209
- inner.router.route('/', new App().use(async (_c, next) => { calls += 1; await next() }).get('/count', c => c.text(String(calls))))
226
+ inner.router.route('/', new Acryl().use(async (_c, next) => { calls += 1; await next() }).get('/count', c => c.text(String(calls))))
210
227
  },
211
228
  }
212
229
  for (let round = 0; round < 10; round += 1) {
@@ -244,7 +261,7 @@ describe('acryl-router lifecycle through Cordis', () => {
244
261
  await settle()
245
262
  const router = ctx.router
246
263
  let aborted = false
247
- router.route('/', new App().get('/slow', async c => {
264
+ router.route('/', new Acryl().get('/slow', async c => {
248
265
  await new Promise<void>((_resolve, reject) => { c.req.raw.signal.addEventListener('abort', () => { aborted = true; reject(new Error('aborted')) }, { once: true }) })
249
266
  return c.text('late')
250
267
  }))
@@ -261,7 +278,7 @@ describe('acryl-router lifecycle through Cordis', () => {
261
278
  it('forwards the caller\'s own abort to the handler signal', async () => {
262
279
  const { router } = await mountRouter()
263
280
  let seen = false
264
- router.route('/', new App().get('/slow', async c => {
281
+ router.route('/', new Acryl().get('/slow', async c => {
265
282
  await new Promise<void>((_resolve, reject) => { c.req.raw.signal.addEventListener('abort', () => { seen = true; reject(new Error('aborted')) }, { once: true }) })
266
283
  return c.text('x')
267
284
  }))
@@ -278,7 +295,7 @@ describe('acryl-router lifecycle through Cordis', () => {
278
295
  const fiber = ctx.plugin(provider, config)
279
296
  await settle()
280
297
  let cleaned = false
281
- ctx.router.route('/', new App().get('/events', c => streamSSE(c, async stream => {
298
+ ctx.router.route('/', new Acryl().get('/events', c => streamSSE(c, async stream => {
282
299
  stream.onAbort(() => { cleaned = true })
283
300
  await stream.writeSSE({ data: 'first' })
284
301
  while (!stream.aborted) await stream.sleep(10)
@@ -302,7 +319,7 @@ describe('acryl-router lifecycle through Cordis', () => {
302
319
  name: 'streaming',
303
320
  inject: ['router'],
304
321
  apply(inner: Context): void {
305
- inner.router.route('/', new App().get('/events', c => streamSSE(c, async stream => {
322
+ inner.router.route('/', new Acryl().get('/events', c => streamSSE(c, async stream => {
306
323
  stream.onAbort(() => { cleaned = true })
307
324
  await stream.writeSSE({ data: 'first' })
308
325
  while (!stream.aborted) await stream.sleep(10)
@@ -313,7 +330,7 @@ describe('acryl-router lifecycle through Cordis', () => {
313
330
  name: 'other',
314
331
  inject: ['router'],
315
332
  apply(inner: Context): void {
316
- inner.router.route('/', new App().get('/slow', async c => {
333
+ inner.router.route('/', new Acryl().get('/slow', async c => {
317
334
  c.req.raw.signal.addEventListener('abort', () => { otherAborted = true }, { once: true })
318
335
  await new Promise<void>(resolve => { release = resolve })
319
336
  return c.text('done')
@@ -344,7 +361,7 @@ describe('acryl-router lifecycle through Cordis', () => {
344
361
  name: 'guard',
345
362
  inject: ['router'],
346
363
  apply(inner: Context): void {
347
- inner.router.route('/', new App()
364
+ inner.router.route('/', new Acryl()
348
365
  .use('/held/*', async (c, next) => {
349
366
  c.req.raw.signal.addEventListener('abort', () => { guardAborted = true }, { once: true })
350
367
  await new Promise<void>((_resolve, reject) => { c.req.raw.signal.addEventListener('abort', () => { reject(new Error('aborted')) }, { once: true }) })
@@ -357,7 +374,7 @@ describe('acryl-router lifecycle through Cordis', () => {
357
374
  name: 'handler',
358
375
  inject: ['router'],
359
376
  apply(inner: Context): void {
360
- inner.router.route('/', new App()
377
+ inner.router.route('/', new Acryl()
361
378
  .get('/held/x', c => c.text('held'))
362
379
  .get('/passed/x', c => streamSSE(c, async stream => {
363
380
  stream.onAbort(() => { handlerAborted = true })
@@ -385,7 +402,7 @@ describe('acryl-router lifecycle through Cordis', () => {
385
402
  const fiber = ctx.plugin(provider, config)
386
403
  await settle()
387
404
  let finished = false
388
- ctx.router.route('/', new App().get('/later', c => {
405
+ ctx.router.route('/', new Acryl().get('/later', c => {
389
406
  c.executionCtx.waitUntil(new Promise<void>(resolve => { setTimeout(() => { finished = true; resolve() }, 30) }))
390
407
  return c.text('now')
391
408
  }))
@@ -399,7 +416,7 @@ describe('acryl-router lifecycle through Cordis', () => {
399
416
  const ctx = root()
400
417
  const fiber = ctx.plugin(provider, { trailingSlash: 'strict', disposeGraceMs: 30 })
401
418
  await settle()
402
- ctx.router.route('/', new App().get('/stuck', c => { c.executionCtx.waitUntil(new Promise(() => undefined)); return c.text('x') }))
419
+ ctx.router.route('/', new Acryl().get('/stuck', c => { c.executionCtx.waitUntil(new Promise(() => undefined)); return c.text('x') }))
403
420
  await get(ctx.router, '/stuck')
404
421
  const started = Date.now()
405
422
  await fiber.dispose()
@@ -2,15 +2,15 @@
2
2
  "source": {
3
3
  "repo": "acryldev/acryl",
4
4
  "path": "plugins/acryl-router",
5
- "commit": "df79b188398b6d4f8cceda35a72b88300a10a9ce"
5
+ "commit": "f5151e7f0c4de3b81bddb5cc686b5ef329e48e1c"
6
6
  },
7
7
  "vendored": [
8
8
  "src",
9
9
  "tests",
10
10
  "LICENSE"
11
11
  ],
12
- "rewrittenSpecifiers": 0,
13
- "digest": "sha256:6e15f8ce0499b94c95649342f6fe984bc9b2aec0f5a142e8853a5badcc3b85f5",
12
+ "rewrittenSpecifiers": 4,
13
+ "digest": "sha256:543a5ca99518eea1fe2e319b7708d62af34a6c24ab3daeb17dde48d67d319d6a",
14
14
  "testsNotRun": {
15
15
  "tests/loader.spec.ts": "loads the built lib/ entry through the real Loader; the library ships source and has no build"
16
16
  },
@@ -8,7 +8,7 @@ import { Context, type FiberState } from '@deepseek-ai/cordis'
8
8
  import WebServer from '@deepseek-ai/dsh-host-webserver'
9
9
  import { apply as routerApply, name as routerName } from '../../acryl-router/src/index.ts'
10
10
  import { afterEach, describe, expect, it } from 'vitest'
11
- import { App, connectionOf, type RouterHttpService } from '../../acryl-router/src/index.ts'
11
+ import { Acryl, connectionOf, type RouterHttpService } from '../../acryl-router/src/index.ts'
12
12
  import { methodNotAllowed } from '../../acryl-router/src/middleware/index.ts'
13
13
  import { apply, inject, name } from '../src/index.ts'
14
14
 
@@ -40,13 +40,13 @@ async function stack(): Promise<{ ctx: Context, base: () => string }> {
40
40
  return { ctx, base: () => `http://127.0.0.1:${String((ctx as unknown as { webServer: { port: number } }).webServer.port)}` }
41
41
  }
42
42
 
43
- function demo(prefix: string, extra?: (app: App) => void) {
43
+ function demo(prefix: string, extra?: (app: Acryl) => void) {
44
44
  return {
45
45
  name: `demo${prefix.replaceAll('/', '-')}`,
46
46
  inject: ['router', 'routerHttp'],
47
47
  apply(ctx: Context): void {
48
48
  ctx.routerHttp.mount(prefix)
49
- const app = new App()
49
+ const app = new Acryl()
50
50
  app.use(methodNotAllowed({ app }))
51
51
  app.get('/hello/:who', c => c.json({ hello: c.req.param('who'), q: c.req.query('q') ?? null, host: c.req.header('host') !== undefined }))
52
52
  app.post('/echo', async c => c.json({ got: await c.req.text(), type: c.req.header('content-type') ?? null }, 201))
@@ -7,7 +7,7 @@
7
7
 
8
8
  import { Context } from '@deepseek-ai/cordis'
9
9
  import WebServer from '@deepseek-ai/dsh-host-webserver'
10
- import { App, apply as routerApply, name as routerName } from '../../acryl-router/src/index.ts'
10
+ import { Acryl, apply as routerApply, name as routerName } from '../../acryl-router/src/index.ts'
11
11
  import { afterEach, describe, expect, it } from 'vitest'
12
12
  import { apply as httpApply, inject as httpInject, name as httpName } from '../src/index.ts'
13
13
 
@@ -28,7 +28,7 @@ const notes = {
28
28
  inject: ['router'],
29
29
  apply(ctx: Context): void {
30
30
  const store = new Map<string, string>()
31
- const app = new App()
31
+ const app = new Acryl()
32
32
  .post('/:id', async c => {
33
33
  const text = await c.req.text()
34
34
  store.set(c.req.param('id'), text)
@@ -2,7 +2,7 @@
2
2
  "source": {
3
3
  "repo": "acryldev/acryl",
4
4
  "path": "plugins/acryl-router-http",
5
- "commit": "df79b188398b6d4f8cceda35a72b88300a10a9ce"
5
+ "commit": "f5151e7f0c4de3b81bddb5cc686b5ef329e48e1c"
6
6
  },
7
7
  "vendored": [
8
8
  "src",
@@ -10,7 +10,7 @@
10
10
  "LICENSE"
11
11
  ],
12
12
  "rewrittenSpecifiers": 7,
13
- "digest": "sha256:29ba7834cf9d29e9af2f4fac265c7e5e220e06da3346e65c29bcbae41fdfaf9e",
13
+ "digest": "sha256:394994528a104c8f788e2d911f8f940ff9f74e9c5a72835ea3ea751651aa9fbd",
14
14
  "testsNotRun": {
15
15
  "tests/adapter.spec.ts": "needs @deepseek-ai/dsh-host-webserver (the DSH web server); acryl-node-server has its own tests",
16
16
  "tests/stream.spec.ts": "needs @deepseek-ai/dsh-host-webserver (the DSH web server)",
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import { Context, Service } from '@deepseek-ai/cordis'
9
- import { App, type Context as RequestContext } from '../../acryl-router/src/index.ts'
9
+ import { Acryl, type Context as RequestContext } from '../../acryl-router/src/index.ts'
10
10
  import { streamSSE } from '../../acryl-router/src/helpers/index.ts'
11
11
  import { loopbackOnly, methodNotAllowed } from '../../acryl-router/src/middleware/index.ts'
12
12
  import { createCell, createDerived, type Cell, type ReadonlyCell } from './cell.ts'
@@ -104,7 +104,7 @@ export function apply(ctx: Context): void {
104
104
  // The same cells on the router, read-only, for any surface; present only where a router is. A request over a socket must come from this machine to this
105
105
  // app; the terminal calls the router in process and is the host's own code.
106
106
  ctx.inject(['router'], inner => {
107
- const api = new App()
107
+ const api = new Acryl()
108
108
  api.use(loopbackOnly({ inProcess: 'allow' }), methodNotAllowed({ app: api }))
109
109
  .get('/', c => c.json({ keys: state.keys() }))
110
110
  .get('/:key', c => {
@@ -2,7 +2,7 @@
2
2
  "source": {
3
3
  "repo": "acryldev/acryl",
4
4
  "path": "plugins/acryl-state",
5
- "commit": "df79b188398b6d4f8cceda35a72b88300a10a9ce"
5
+ "commit": "f5151e7f0c4de3b81bddb5cc686b5ef329e48e1c"
6
6
  },
7
7
  "vendored": [
8
8
  "src",
@@ -11,7 +11,7 @@
11
11
  "NOTICE"
12
12
  ],
13
13
  "rewrittenSpecifiers": 4,
14
- "digest": "sha256:8feb067a67ea2628ff31a9fd2201361be4be5a8c217be3b7169277c0ce472b69",
14
+ "digest": "sha256:18df69d338a1928fc0f7497c14c245afb98f0c39d4fe6a24bda62bb845d96030",
15
15
  "testsNotRun": {
16
16
  "tests/react.spec.ts": "needs react and @testing-library/react, which generated apps do not install"
17
17
  },
@@ -18,7 +18,7 @@ const PATH = /^\/[a-z0-9/-]*$/
18
18
 
19
19
  export function routeSource(name: string, path: string, text: string): string {
20
20
  return `import type { Context } from '@deepseek-ai/cordis'
21
- import { App } from 'acryl-router'
21
+ import { Acryl } from 'acrylblends'
22
22
 
23
23
  /** Written by the builder: answers GET ${path}, and checks that it does before the install counts. */
24
24
  export const name = '${name}'
@@ -26,11 +26,11 @@ export const inject = ['router']
26
26
 
27
27
  export function apply(ctx: Context) {
28
28
  // The app is mounted through this plugin's context, so the route is removed when the plugin is unloaded.
29
- ctx.router.route('/', new App().get(${JSON.stringify(path)}, (c) => c.json({ text: ${JSON.stringify(text)} })))
29
+ ctx.router.route('/', new Acryl().get(${JSON.stringify(path)}, (c) => c.json({ text: ${JSON.stringify(text)} })))
30
30
  }
31
31
 
32
32
  export async function check(ctx: Context) {
33
- const reply = await ctx.get('router')!.request(${JSON.stringify(path)})
33
+ const reply = await ctx.router.request(${JSON.stringify(path)})
34
34
  if (!reply.ok) throw new Error(\`GET ${path} answered \${reply.status}\`)
35
35
  }
36
36
  `
@@ -109,7 +109,7 @@ interface PluginModule {
109
109
  readonly name?: unknown
110
110
  readonly inject?: unknown
111
111
  readonly apply: (...args: unknown[]) => unknown
112
- /** Optional self-check, run after the plugin starts: it throws (or rejects) when the plugin does not work. */
112
+ /** Optional self-check, run after the plugin starts with the plugin's own context (so `ctx.router` reads as in `apply`): it throws (or rejects) when the plugin does not work. */
113
113
  readonly check?: (ctx: Context) => unknown
114
114
  }
115
115
 
@@ -507,6 +507,7 @@ export class Extensions extends Service {
507
507
  /** Run the extension's own `check`, if it has one, with a time limit. Returns the failure, or `undefined` when it passes. */
508
508
  private async verify(
509
509
  module: PluginModule,
510
+ ctx: Context,
510
511
  ): Promise<{ stage: 'verify'; error: string } | undefined> {
511
512
  if (typeof module.check !== 'function') return undefined
512
513
  let timer: ReturnType<typeof setTimeout> | undefined
@@ -517,7 +518,7 @@ export class Extensions extends Service {
517
518
  CHECK_TIMEOUT_MS,
518
519
  )
519
520
  })
520
- await Promise.race([Promise.resolve(module.check(this.ctx)), limit])
521
+ await Promise.race([Promise.resolve(module.check(ctx)), limit])
521
522
  return undefined
522
523
  } catch (error) {
523
524
  return { stage: 'verify', error: `its check failed: ${message(error)}` }
@@ -615,7 +616,7 @@ export class Extensions extends Service {
615
616
  error: message(mounted.error ?? 'the plugin failed to start'),
616
617
  }
617
618
  : state === 'active'
618
- ? await this.verify(module)
619
+ ? await this.verify(module, mounted.fiber.ctx)
619
620
  : undefined
620
621
  if (failure) {
621
622
  await mounted.fiber.dispose().catch(() => undefined)