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
@@ -12,21 +12,81 @@ import { registerHooks } from 'node:module'
12
12
  import { join } from 'node:path'
13
13
  import { fileURLToPath, pathToFileURL } from 'node:url'
14
14
 
15
+ const PACKAGE = 'acrylblends'
16
+
15
17
  const isBare = (specifier: string) => !/^[./]/.test(specifier) && !specifier.includes(':')
16
18
 
17
- /** The router's specifiers, as files of the vendored plugin that sits beside this one; empty when the app has no router. */
19
+ /**
20
+ * The framework's specifiers, as files of the vendored router plugin that sits beside this one. A plugin written while the app runs (by the agent, from the docs in the app)
21
+ * imports `acrylblends` and `acrylblends/<subpath>` as the docs write them; the old `acryl-router` names keep working. Empty when the app has no router.
22
+ */
23
+ const MIDDLEWARE = [
24
+ 'basic-auth',
25
+ 'bearer-auth',
26
+ 'body-limit',
27
+ 'combine',
28
+ 'compress',
29
+ 'context-storage',
30
+ 'cors',
31
+ 'csrf',
32
+ 'etag',
33
+ 'ip-restriction',
34
+ 'jwk',
35
+ 'jwt',
36
+ 'language',
37
+ 'logger',
38
+ 'loopback-only',
39
+ 'method-not-allowed',
40
+ 'method-override',
41
+ 'powered-by',
42
+ 'pretty-json',
43
+ 'request-id',
44
+ 'secure-headers',
45
+ 'serve-static',
46
+ 'timeout',
47
+ 'timing',
48
+ 'trailing-slash',
49
+ ]
50
+ const HELPERS = [
51
+ 'accepts',
52
+ 'cookie',
53
+ 'factory',
54
+ 'html',
55
+ 'route',
56
+ 'schema-validator',
57
+ 'streaming',
58
+ 'testing',
59
+ 'validator',
60
+ 'ws',
61
+ ]
62
+
63
+ /** The `acrylblends/...` names a plugin installed live may import, with the file each resolves to under `acryl-router/src`. */
64
+ export function frameworkSpecifiers(): Record<string, string> {
65
+ const map: Record<string, string> = {
66
+ '': 'index.ts',
67
+ conninfo: 'index.ts',
68
+ 'http-exception': 'index.ts',
69
+ types: 'index.ts',
70
+ client: 'client.ts',
71
+ }
72
+ for (const name of MIDDLEWARE) map[name] = 'middleware/index.ts'
73
+ for (const name of HELPERS) map[name] = 'helpers/index.ts'
74
+ return map
75
+ }
76
+
18
77
  export function routerAliases(): Record<string, string> {
19
78
  const src = new URL('../acryl-router/src/', import.meta.url)
20
- const files = {
21
- 'acryl-router': 'index.ts',
22
- 'acryl-router/middleware': 'middleware/index.ts',
23
- 'acryl-router/helpers': 'helpers/index.ts',
79
+ const aliases: Record<string, string> = {}
80
+ const add = (specifier: string, file: string) => {
81
+ const path = fileURLToPath(new URL(file, src))
82
+ if (existsSync(path)) aliases[specifier] = path
24
83
  }
25
- return Object.fromEntries(
26
- Object.entries(files)
27
- .map(([specifier, file]) => [specifier, fileURLToPath(new URL(file, src))] as const)
28
- .filter(([, path]) => existsSync(path)),
29
- )
84
+ for (const [sub, file] of Object.entries(frameworkSpecifiers()))
85
+ add(sub === '' ? PACKAGE : `${PACKAGE}/${sub}`, file)
86
+ add('acryl-router', 'index.ts')
87
+ add('acryl-router/middleware', 'middleware/index.ts')
88
+ add('acryl-router/helpers', 'helpers/index.ts')
89
+ return aliases
30
90
  }
31
91
 
32
92
  export function resolveFromApp(
@@ -10,21 +10,21 @@ To add a capability:
10
10
  A plugin's index.ts is TypeScript that Node runs directly (erasable syntax only: no enums, no parameter properties):
11
11
 
12
12
  import type { Context } from '@deepseek-ai/cordis'
13
- import { App } from 'acryl-router'
13
+ import { Acryl } from 'acrylblends'
14
14
  export const name = 'hello' // must equal the folder name
15
15
  export const inject = ['router'] // services it needs; it waits (pending) until they exist
16
16
  export function apply(ctx: Context) {
17
- ctx.router.route('/', new App().get('/hello', (c) => c.json({ text: 'Hello' }))) // removed when the plugin is unloaded
17
+ ctx.router.route('/', new Acryl().get('/hello', (c) => c.json({ text: 'Hello' }))) // removed when the plugin is unloaded
18
18
  }
19
19
  export async function check(ctx: Context) { // optional: must pass, or the install is rolled back
20
- const reply = await ctx.get('router')!.request('/hello')
20
+ const reply = await ctx.router.request('/hello')
21
21
  if (!reply.ok) throw new Error('GET /hello failed')
22
22
  }
23
23
 
24
- The router: build an App (import { App } from 'acryl-router'), register routes on it with app.get/post/put/patch/delete(path, handler) and mount it with
24
+ The router: build an Acryl app (import { Acryl } from 'acrylblends'), register routes on it with app.get/post/put/patch/delete(path, handler) and mount it with
25
25
  ctx.router.route(prefix, app). A path parameter is :id and is read with c.req.param('id'); c.json(body, status) answers JSON, c.text(...) answers text,
26
- await c.req.json() reads a JSON body, throw new HTTPException(404, { message }) (also from 'acryl-router') answers an error. Routes added to an app
27
- after route() are not served: build the whole app first. ctx.get('router')!.request('/path', { method: 'POST', body: ... }) calls a route in process (use it in
26
+ await c.req.json() reads a JSON body, throw new HTTPException(404, { message }) (also from 'acrylblends') answers an error. Routes added to an app
27
+ after route() are not served: build the whole app first. ctx.router.request('/path', { method: 'POST', body: ... }) calls a route in process (use it in
28
28
  check). The mount goes away when the plugin is unloaded.
29
29
 
30
30
  For a capability that is a pure function of data (parse, convert, compute), write a sandboxed TOOL instead of a plugin. It cannot touch the
@@ -32,6 +32,6 @@ host: it runs in a separate process that can read only its own folder. Files: in
32
32
  { "description": "what it does and its input", "examples": [ { "input": {...}, "output": {...} } ] }
33
33
  The examples are run in the sandbox at install and must match exactly, or the install is refused. Use write_plugin and install_plugin for tools too.
34
34
 
35
- Rules: import only relative files, node: built-ins, @deepseek-ai/cordis and acryl-router. Provide services with ctx.provide(name, value) and
35
+ Rules: import only relative files, node: built-ins, @deepseek-ai/cordis and acrylblends (and its subpaths such as acrylblends/cors, whose docs are in docs/framework). Provide services with ctx.provide(name, value) and
36
36
  use others with ctx.get(name). Put timers, listeners and routes inside ctx.effect(() => cleanup). Use list_plugins and
37
37
  read_plugin to see what exists. When you are done, answer in one or two plain sentences saying what you built.`
@@ -0,0 +1,61 @@
1
+ # __APP_NAME__
2
+
3
+ A Cordis app on the ACRYL framework: TypeScript that Node 22.19 or newer runs directly (no build step; erasable syntax only, so no enums and no
4
+ parameter properties). Every part of it is a Cordis plugin.
5
+
6
+ ## The framework's API is in `docs/framework/`
7
+
8
+ Start at `docs/framework/README.md`; `api/summary.md` is the one-page summary. Read before you write routes: `api/routing.md` (the `Acryl` app), `api/request.md` (`c.req`),
9
+ `api/context.md` (`c`), `cordis/validation.md` and `guides/validation.md`, `guides/helpers.md` (`streamSSE`), `concepts/middleware.md`, `cordis/plugin-lifecycle.md` (what a plugin
10
+ owns and when it is unloaded), `cordis/mounting.md` and `cordis/node-server.md`. Pages marked "not yet available" describe parts that are not in this app.
11
+
12
+ The docs write `import { Acryl } from 'acrylblends'` and `from 'acrylblends/<subpath>'`. In this app the framework is the vendored plugin source in `src/plugins/acryl-router/src`
13
+ (read it, do not edit it: a change belongs in ACRYL, upstream), so from a file in `src/` import:
14
+
15
+ - `./plugins/acryl-router/src/index.ts` for `Acryl`, `Context`, `HTTPException`, `connectionOf`, `getConnInfo` and the types;
16
+ - `./plugins/acryl-router/src/middleware/index.ts` for every middleware (`cors`, `bodyLimit`, `trimTrailingSlash`, `loopbackOnly`, ...);
17
+ - `./plugins/acryl-router/src/helpers/index.ts` for the helpers and validators (`streamSSE`, `schemaValidator`, `validator`, cookies, `testClient`, ...).
18
+
19
+ A plugin written into `extensions/` while the app runs may use the docs' own names instead: `acrylblends` and `acrylblends/<subpath>` resolve to the same files. `App` is the
20
+ deprecated name of `Acryl`.
21
+
22
+ ## How a feature is built
23
+
24
+ Routes are an ordinary `Acryl` app that a plugin mounts once with `ctx.router.route(prefix, app)`. The mount belongs to the plugin and goes away when it unloads.
25
+
26
+ ```ts
27
+ import type { Context } from '@deepseek-ai/cordis'
28
+ import { Acryl, HTTPException } from './plugins/acryl-router/src/index.ts'
29
+ import { schemaValidator, type StandardSchema } from './plugins/acryl-router/src/helpers/index.ts'
30
+
31
+ export const name = 'notes' // a plugin's name; blend.yaml refers to it
32
+ export const inject = ['router'] // services it needs
33
+ export function apply(ctx: Context) {
34
+ const app = new Acryl()
35
+ .get('/', (c) => c.json({ ok: true }))
36
+ .get('/:id', (c) => c.json({ id: c.req.param('id') }))
37
+ .post('/', schemaValidator('json', NewNote), (c) => c.json(c.req.valid('json'), 201)) // NewNote is a Standard Schema
38
+ ctx.router.route('/notes', app) // build the whole app first: routes added after route() are not served
39
+ }
40
+ ```
41
+
42
+ - A small route goes on the `App` in `src/routes.ts`. A new feature is a new plugin file: add it to `registry` in `src/app.ts`, add a row
43
+ (`- id: notes` / `name: notes`) to `blend.yaml` before the `acryl-node-server` row, and pass runtime values (a data file) as its config in `createApp`.
44
+ - State, when `src/plugins/acryl-state` exists: `const cell = ctx.state.cell<T>('notes.list', initial)` (add `'state'` to `inject`), then
45
+ `cell.get()`, `cell.set(value)`, `cell.update(fn)` and `cell.subscribe(fn)` (returns the unsubscribe). Every cell is also served as JSON at `/api/state/<key>`
46
+ and streamed at `/api/state/<key>/events`.
47
+ - Test a route without a socket: `await ctx.router.request('/notes')`, or boot the app as `test/app.test.ts` does and use `fetch`.
48
+
49
+ ## Things that surprise people
50
+
51
+ - A page mounted at `/x` answers `/x`, not `/x/` (trailing slashes are strict); `trimTrailingSlash()` (middleware) redirects `/x/` to `/x` for GET and HEAD.
52
+ - `schemaValidator('json', ...)` needs a JSON `Content-Type`; without it the schema sees `{}`. A failure answers 400 with
53
+ `{ "error": "invalid json", "issues": [{ "message", "path" }] }`: the human text is `issues[0].message`. A `jsonOnly` recipe in `docs/framework/validation.md` answers 415 instead.
54
+ - Throw `new HTTPException(404, { message })` for an error response; the body is the message as plain text (use `c.json({ error }, status)` when a page expects JSON).
55
+ - Handlers get the request as `c.req`: `c.req.param('id')`, `c.req.query('q')`, `await c.req.json()`, `c.req.raw` for the Web `Request`.
56
+ - A server-sent stream is `streamSSE(c, async (stream) => { await stream.writeSSE({ data: JSON.stringify(x) }) ... })`; call `stream.onAbort(cleanup)`.
57
+ - `npm run dev` restarts the app whenever a file changes.
58
+
59
+ ## Before you say you are done
60
+
61
+ `npm test` and `npm run check` (type-check) both pass. Put tests in `test/`, written with `node:test` like `test/app.test.ts`.
@@ -0,0 +1,6 @@
1
+
2
+ ## This app (agent)
3
+
4
+ Besides `src/routes.ts`, the app runs an agent (`POST /ask`) that can write plugins into `extensions/` while the app runs and install them with
5
+ rollback; `GET /extensions` and `GET /tasks` show what it did. A plugin there is a folder with an `index.ts` exporting `name`, `inject`, `apply(ctx)`
6
+ and an optional `check(ctx)` that receives the plugin's own context (so `ctx.router` reads as in `apply`). It may import `acryl-router`.
@@ -5,7 +5,7 @@ Cordis plugin.
5
5
 
6
6
  ```sh
7
7
  npm install
8
- npm run dev # prints its URL; data lives in .acrylblends/
8
+ npm run dev # prints its URL; data lives in .acrylblends/; it restarts when a file changes (a new port if ACRYLBLENDS_PORT=0)
9
9
  npm test
10
10
  npm run check # type-check
11
11
 
@@ -41,7 +41,7 @@ curl <url>/tasks # every run as a durable task, with the tool calls
41
41
 
42
42
  Other commands: `update route <name> <path> <text>`, `break plugin <name>` (shows the rollback), `remove plugin <name>`,
43
43
  `list plugins`. What the agent writes lands in `extensions/`, is part of your app, and starts again at the next start.
44
- - `src/routes.ts` is your own plugin: routes added inside `ctx.effect`.
44
+ - `src/routes.ts` is your own plugin: it builds an `App` (`import { App } from './plugins/acryl-router/src/index.ts'`) and mounts it with `ctx.router.route(prefix, app)`. Routes added to the app after it is mounted are not served. A page mounted at `/x` answers `/x`, not `/x/` (trailing slashes are strict). The router API is documented at https://acrylblends.github.io/docs/framework/the-acryl-framework and in `docs/framework` of the acrylblends repository.
45
45
  - `src/plugins/` holds the plugins copied from the ACRYLBLENDS library. They are yours now; edit them freely.
46
46
  Add more with `acrylblends add <plugin>`.
47
47
 
@@ -1,6 +1,6 @@
1
1
  # The app as data, in ACRYL's Blend format (blends.acryl.dev/v1alpha1): which plugins it mounts, in what order, with what config.
2
2
  # Edit this, not app.ts, to switch a plugin off (disabled: true) or change its config. `npm run lock` records what the app is built from.
3
- # Order matters: http comes before extensions, because installed extensions start again at boot and may add routes.
3
+ # Order matters: acryl-router comes before extensions, because installed extensions start again at boot and may add routes.
4
4
  apiVersion: blends.acryl.dev/v1alpha1
5
5
  kind: Blueprint
6
6
  metadata:
@@ -1,5 +1,5 @@
1
1
  import type { Context } from '@deepseek-ai/cordis'
2
- import { App } from './plugins/acryl-router/src/index.ts'
2
+ import { Acryl } from './plugins/acryl-router/src/index.ts'
3
3
  import { bodyLimit } from './plugins/acryl-router/src/middleware/index.ts'
4
4
  import type {} from './plugins/agent/index.ts'
5
5
  import type {} from './plugins/extensions/index.ts'
@@ -23,7 +23,7 @@ export const routes = {
23
23
  const body = await c.req.json().catch(() => undefined)
24
24
  return typeof body === 'object' && body !== null ? (body as Record<string, unknown>) : {}
25
25
  }
26
- const app = new App()
26
+ const app = new Acryl()
27
27
  .use(bodyLimit({ maxSize: 64 * 1024 }))
28
28
  .get('/', (c) => c.json({ app: APP_NAME, ok: true }))
29
29
  .get('/health', (c) => c.json({ ok: true }))
@@ -10,12 +10,12 @@ import { pinnedInstance, withPort } from '../src/plugins/instance/index.ts'
10
10
 
11
11
  /** A plugin no scripted command knows: the model writes it from scratch. */
12
12
  const COUNTER = `import type { Context } from '@deepseek-ai/cordis'
13
- import { App } from 'acryl-router'
13
+ import { Acryl } from 'acrylblends'
14
14
  export const name = 'counter'
15
15
  export const inject = ['router']
16
16
  export function apply(ctx: Context) {
17
17
  let count = 0
18
- ctx.router.route('/', new App().get('/count', (c) => c.json({ count: ++count })))
18
+ ctx.router.route('/', new Acryl().get('/count', (c) => c.json({ count: ++count })))
19
19
  }
20
20
  `
21
21
 
@@ -13,6 +13,46 @@
13
13
  "skipLibCheck": true,
14
14
  "types": ["node"],
15
15
  "paths": {
16
+ "acrylblends": ["./src/plugins/acryl-router/src/index.ts"],
17
+ "acrylblends/conninfo": ["./src/plugins/acryl-router/src/index.ts"],
18
+ "acrylblends/http-exception": ["./src/plugins/acryl-router/src/index.ts"],
19
+ "acrylblends/types": ["./src/plugins/acryl-router/src/index.ts"],
20
+ "acrylblends/client": ["./src/plugins/acryl-router/src/client.ts"],
21
+ "acrylblends/basic-auth": ["./src/plugins/acryl-router/src/middleware/index.ts"],
22
+ "acrylblends/bearer-auth": ["./src/plugins/acryl-router/src/middleware/index.ts"],
23
+ "acrylblends/body-limit": ["./src/plugins/acryl-router/src/middleware/index.ts"],
24
+ "acrylblends/combine": ["./src/plugins/acryl-router/src/middleware/index.ts"],
25
+ "acrylblends/compress": ["./src/plugins/acryl-router/src/middleware/index.ts"],
26
+ "acrylblends/context-storage": ["./src/plugins/acryl-router/src/middleware/index.ts"],
27
+ "acrylblends/cors": ["./src/plugins/acryl-router/src/middleware/index.ts"],
28
+ "acrylblends/csrf": ["./src/plugins/acryl-router/src/middleware/index.ts"],
29
+ "acrylblends/etag": ["./src/plugins/acryl-router/src/middleware/index.ts"],
30
+ "acrylblends/ip-restriction": ["./src/plugins/acryl-router/src/middleware/index.ts"],
31
+ "acrylblends/jwk": ["./src/plugins/acryl-router/src/middleware/index.ts"],
32
+ "acrylblends/jwt": ["./src/plugins/acryl-router/src/middleware/index.ts"],
33
+ "acrylblends/language": ["./src/plugins/acryl-router/src/middleware/index.ts"],
34
+ "acrylblends/logger": ["./src/plugins/acryl-router/src/middleware/index.ts"],
35
+ "acrylblends/loopback-only": ["./src/plugins/acryl-router/src/middleware/index.ts"],
36
+ "acrylblends/method-not-allowed": ["./src/plugins/acryl-router/src/middleware/index.ts"],
37
+ "acrylblends/method-override": ["./src/plugins/acryl-router/src/middleware/index.ts"],
38
+ "acrylblends/powered-by": ["./src/plugins/acryl-router/src/middleware/index.ts"],
39
+ "acrylblends/pretty-json": ["./src/plugins/acryl-router/src/middleware/index.ts"],
40
+ "acrylblends/request-id": ["./src/plugins/acryl-router/src/middleware/index.ts"],
41
+ "acrylblends/secure-headers": ["./src/plugins/acryl-router/src/middleware/index.ts"],
42
+ "acrylblends/serve-static": ["./src/plugins/acryl-router/src/middleware/index.ts"],
43
+ "acrylblends/timeout": ["./src/plugins/acryl-router/src/middleware/index.ts"],
44
+ "acrylblends/timing": ["./src/plugins/acryl-router/src/middleware/index.ts"],
45
+ "acrylblends/trailing-slash": ["./src/plugins/acryl-router/src/middleware/index.ts"],
46
+ "acrylblends/accepts": ["./src/plugins/acryl-router/src/helpers/index.ts"],
47
+ "acrylblends/cookie": ["./src/plugins/acryl-router/src/helpers/index.ts"],
48
+ "acrylblends/factory": ["./src/plugins/acryl-router/src/helpers/index.ts"],
49
+ "acrylblends/html": ["./src/plugins/acryl-router/src/helpers/index.ts"],
50
+ "acrylblends/route": ["./src/plugins/acryl-router/src/helpers/index.ts"],
51
+ "acrylblends/schema-validator": ["./src/plugins/acryl-router/src/helpers/index.ts"],
52
+ "acrylblends/streaming": ["./src/plugins/acryl-router/src/helpers/index.ts"],
53
+ "acrylblends/testing": ["./src/plugins/acryl-router/src/helpers/index.ts"],
54
+ "acrylblends/validator": ["./src/plugins/acryl-router/src/helpers/index.ts"],
55
+ "acrylblends/ws": ["./src/plugins/acryl-router/src/helpers/index.ts"],
16
56
  "acryl-router": ["./src/plugins/acryl-router/src/index.ts"],
17
57
  "acryl-router/middleware": ["./src/plugins/acryl-router/src/middleware/index.ts"],
18
58
  "acryl-router/helpers": ["./src/plugins/acryl-router/src/helpers/index.ts"]
@@ -0,0 +1,5 @@
1
+
2
+ ## This app (blank)
3
+
4
+ `src/routes.ts` is the app's own plugin: add routes to its `App`. `blend.yaml` lists the plugins that are mounted. `acrylblends add <plugin>` copies
5
+ another library plugin (for example `acryl-state`) into `src/plugins/`.
@@ -15,7 +15,7 @@ npm run check # type-check
15
15
  one off with `disabled: true`. `npm run lock` writes `blend.lock.json`: the manifest's digest and a digest of
16
16
  every plugin folder, so a diff of the lock shows what changed.
17
17
  - `src/app.ts` holds the registry of plugins the code provides, and mounts the manifest's rows.
18
- - `src/routes.ts` is your own plugin: routes added inside `ctx.effect`.
18
+ - `src/routes.ts` is your own plugin: it builds an `App` (`import { App } from './plugins/acryl-router/src/index.ts'`) and mounts it with `ctx.router.route(prefix, app)`. Routes added to the app after it is mounted are not served. A page mounted at `/x` answers `/x`, not `/x/` (trailing slashes are strict). The router API is documented at https://acrylblends.github.io/docs/framework/the-acryl-framework and in `docs/framework` of the acrylblends repository.
19
19
  - `src/plugins/` holds the plugins copied from the ACRYLBLENDS library. They are yours now; edit them freely.
20
20
  Add more with `acrylblends add <plugin>`.
21
21
 
@@ -1,5 +1,5 @@
1
1
  import type { Context } from '@deepseek-ai/cordis'
2
- import { App } from './plugins/acryl-router/src/index.ts'
2
+ import { Acryl } from './plugins/acryl-router/src/index.ts'
3
3
 
4
4
  export const APP_NAME = '__APP_NAME__'
5
5
 
@@ -8,7 +8,7 @@ export const routes = {
8
8
  name: 'routes',
9
9
  inject: ['router'],
10
10
  apply(ctx: Context) {
11
- const app = new App()
11
+ const app = new Acryl()
12
12
  .get('/', (c) => c.json({ app: APP_NAME, ok: true }))
13
13
  .get('/health', (c) => c.json({ ok: true }))
14
14
  ctx.router.route('/', app)
@@ -0,0 +1,5 @@
1
+
2
+ ## This app (todo)
3
+
4
+ `src/todo.ts` is the whole feature: a plugin that mounts an `App` (CRUD, a page, a server-sent stream) and keeps the list in a state cell that it
5
+ also writes to a file in the app's home. Use it as the reference for a new plugin, and keep its tests passing when you change it.
@@ -6,8 +6,8 @@
6
6
  import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs'
7
7
  import { dirname } from 'node:path'
8
8
  import type { Context } from '@deepseek-ai/cordis'
9
- import { App, HTTPException } from './plugins/acryl-router/src/index.ts'
10
- import { bodyLimit, loopbackOnly } from './plugins/acryl-router/src/middleware/index.ts'
9
+ import { Acryl, HTTPException } from './plugins/acryl-router/src/index.ts'
10
+ import { bodyLimit, loopbackOnly, trimTrailingSlash } from './plugins/acryl-router/src/middleware/index.ts'
11
11
  import { schemaValidator, streamSSE, type StandardSchema } from './plugins/acryl-router/src/helpers/index.ts'
12
12
  import type {} from './plugins/acryl-state/src/index.ts'
13
13
  import { PAGE } from './page.ts'
@@ -84,8 +84,10 @@ export function apply(ctx: Context, config: Config = {}): void {
84
84
  return todo
85
85
  }
86
86
 
87
- const api = new App()
88
- .use(loopbackOnly({ inProcess: 'allow' }), bodyLimit({ maxSize: MAX_BODY_BYTES }))
87
+ // `/todo/` is what people type: trimTrailingSlash answers a GET for an unknown path ending in `/` with a 301 to the path without it,
88
+ // so the page is found either way while each route keeps one canonical path (the router itself treats `/a` and `/a/` as different).
89
+ const api = new Acryl()
90
+ .use(loopbackOnly({ inProcess: 'allow' }), bodyLimit({ maxSize: MAX_BODY_BYTES }), trimTrailingSlash())
89
91
  .get('/', c => c.html(PAGE))
90
92
  .get('/api', c => c.json(todos.get()))
91
93
  .post('/api', schemaValidator('json', NewTodo), c => {
@@ -5,8 +5,8 @@
5
5
  "examples/acryl-todo/src/index.ts": "src/todo.ts",
6
6
  "examples/acryl-todo/src/page.ts": "src/page.ts"
7
7
  },
8
- "commit": "df79b188398b6d4f8cceda35a72b88300a10a9ce"
8
+ "commit": "f5151e7f0c4de3b81bddb5cc686b5ef329e48e1c"
9
9
  },
10
10
  "rewrittenSpecifiers": 4,
11
- "digest": "sha256:c4efede39387681003c6d2f0f2858e157a84bcaaf9ae94ebe23d10f72a538ffd"
11
+ "digest": "sha256:9093704776563001f7f472f94cc5c2fdd79206342c1cb59c11fce62aa74df3ed"
12
12
  }