acrylblends 0.6.2 → 0.6.4

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 (316) hide show
  1. package/README.md +5 -1
  2. package/docs/framework/README.md +3 -2
  3. package/docs/framework/UPSTREAM.md +1 -1
  4. package/docs/framework/cordis/acryl-in-a-cordis-app.md +6 -0
  5. package/docs/framework/getting-started/basic.md +1 -0
  6. package/docs/framework/getting-started/desktop.md +41 -0
  7. package/docs/framework/guides/jsx-dom.md +0 -3
  8. package/docs/framework/guides/jsx.md +24 -3
  9. package/docs/framework/helpers/css.md +0 -3
  10. package/docs/framework/helpers/ssg.md +3 -6
  11. package/docs/framework/middleware/builtin/jsx-renderer.md +0 -3
  12. package/docs/framework/upstream.json +3 -3
  13. package/lib/accepts.d.ts +1 -1
  14. package/lib/acryl-base.d.ts +1 -1
  15. package/lib/adapter.d.ts +1 -1
  16. package/lib/{app-BdrVHBlJ.d.ts → app-D8riatvv.d.ts} +1 -1
  17. package/lib/base-BpQy2Rk7.d.ts +783 -0
  18. package/lib/base-ddiVmiE7.js +721 -0
  19. package/lib/basic-auth.d.ts +1 -1
  20. package/lib/bearer-auth.d.ts +1 -1
  21. package/lib/body-limit.d.ts +1 -1
  22. package/lib/cache.d.ts +1 -1
  23. package/lib/cache.js +4 -4
  24. package/lib/{client-BHTr318g.js → client-DDLXwhBo.js} +2 -125
  25. package/lib/client.d.ts +2 -2
  26. package/lib/client.js +2 -1
  27. package/lib/combine.d.ts +1 -1
  28. package/lib/common-D8Z4JRCw.d.ts +46 -0
  29. package/lib/components-0o0CBYNv.js +268 -0
  30. package/lib/components-DdI7-ns7.js +24 -0
  31. package/lib/compress.d.ts +1 -1
  32. package/lib/conninfo.d.ts +2 -2
  33. package/lib/constants-BR9jM9BC.js +9 -0
  34. package/lib/{context-B5Y1lLha.d.ts → context-DS3fYyG-.d.ts} +2 -2
  35. package/lib/context-DxFdY_un.d.ts +250 -0
  36. package/lib/context-storage.d.ts +1 -1
  37. package/lib/{cookie-D9_nQbsM.d.ts → cookie-CCelEqky.d.ts} +1 -1
  38. package/lib/cookie.d.ts +3 -3
  39. package/lib/cors.d.ts +1 -1
  40. package/lib/csrf.d.ts +1 -1
  41. package/lib/css-D2jb7X8b.js +320 -0
  42. package/lib/css.d.ts +82 -0
  43. package/lib/css.js +134 -0
  44. package/lib/dev.d.ts +2 -2
  45. package/lib/etag.d.ts +1 -1
  46. package/lib/factory.d.ts +2 -2
  47. package/lib/hooks-BMw6BAyP.js +762 -0
  48. package/lib/html-SllAe0YT.d.ts +46 -0
  49. package/lib/html.d.ts +1 -1
  50. package/lib/{index-m12zcmph.d.ts → index-Bb7YJKCq.d.ts} +3 -3
  51. package/lib/{index-Kqou8Fjh.d.ts → index-BvFh1Gfx.d.ts} +2 -2
  52. package/lib/index-C850MeBA.d.ts +94 -0
  53. package/lib/{index-DtEoak1i.d.ts → index-CTy4N7ng.d.ts} +2 -2
  54. package/lib/index.d.ts +4 -4
  55. package/lib/ip-restriction.d.ts +2 -2
  56. package/lib/jsx/dom/client.d.ts +30 -0
  57. package/lib/jsx/dom/client.js +49 -0
  58. package/lib/jsx/dom/css.d.ts +84 -0
  59. package/lib/jsx/dom/css.js +2 -0
  60. package/lib/jsx/dom/jsx-dev-runtime.d.ts +3 -0
  61. package/lib/jsx/dom/jsx-dev-runtime.js +2 -0
  62. package/lib/jsx/dom/jsx-runtime.d.ts +3 -0
  63. package/lib/jsx/dom/jsx-runtime.js +2 -0
  64. package/lib/jsx/dom/server.d.ts +105 -0
  65. package/lib/jsx/dom/server.js +37 -0
  66. package/lib/jsx/dom.d.ts +5 -0
  67. package/lib/jsx/dom.js +74 -0
  68. package/lib/jsx/jsx-dev-runtime.d.ts +6 -0
  69. package/lib/jsx/jsx-dev-runtime.js +14 -0
  70. package/lib/jsx/jsx-runtime.d.ts +10 -0
  71. package/lib/jsx/jsx-runtime.js +30 -0
  72. package/lib/jsx/streaming.d.ts +2 -0
  73. package/lib/jsx/streaming.js +2 -0
  74. package/lib/jsx-dev-runtime-B22O-Npk.d.ts +7 -0
  75. package/lib/jsx-dev-runtime-D18Y35TI.js +354 -0
  76. package/lib/jsx-renderer.d.ts +75 -0
  77. package/lib/jsx-renderer.js +99 -0
  78. package/lib/jsx-runtime-BVVgDSdq.d.ts +1 -0
  79. package/lib/jsx.d.ts +3 -0
  80. package/lib/jsx.js +47 -0
  81. package/lib/jwk.d.ts +4 -4
  82. package/lib/{jwt-BA65QhnJ.d.ts → jwt-BARhotDz.d.ts} +1 -1
  83. package/lib/jwt.d.ts +4 -4
  84. package/lib/language.d.ts +1 -1
  85. package/lib/logger.d.ts +1 -1
  86. package/lib/loopback-only.d.ts +1 -1
  87. package/lib/method-not-allowed.d.ts +2 -2
  88. package/lib/method-override.d.ts +2 -2
  89. package/lib/mount.d.ts +1 -1
  90. package/lib/node-server/conninfo.d.ts +1 -1
  91. package/lib/node-server/serve-static.d.ts +1 -1
  92. package/lib/node-server/serve-static.js +1 -1
  93. package/lib/node-server.d.ts +1 -1
  94. package/lib/node-server.js +1 -1
  95. package/lib/powered-by.d.ts +1 -1
  96. package/lib/pretty-json.d.ts +1 -1
  97. package/lib/proxy.d.ts +1 -1
  98. package/lib/quick.d.ts +1 -1
  99. package/lib/request-id.d.ts +1 -1
  100. package/lib/request.d.ts +1 -1
  101. package/lib/route.d.ts +1 -1
  102. package/lib/schema-validator.d.ts +1 -1
  103. package/lib/secure-headers.d.ts +1 -1
  104. package/lib/{serve-static-Dw_m6KxR.js → serve-static-B14MF1Va.js} +2 -0
  105. package/lib/serve-static.d.ts +1 -1
  106. package/lib/ssg-BwjcA-Ge.js +389 -0
  107. package/lib/ssg.d.ts +156 -0
  108. package/lib/ssg.js +2 -0
  109. package/lib/streaming.d.ts +1 -1
  110. package/lib/testing.d.ts +4 -4
  111. package/lib/testing.js +1 -1
  112. package/lib/timeout.d.ts +1 -1
  113. package/lib/timing.d.ts +1 -1
  114. package/lib/tiny.d.ts +1 -1
  115. package/lib/trailing-slash.d.ts +1 -1
  116. package/lib/{types-ZM9gIiKh.d.ts → types-Dv_nV2cv.d.ts} +1 -1
  117. package/lib/{types-D9DEXMjE.d.ts → types-Lqx5BxQF.d.ts} +8 -1
  118. package/lib/{types-Bd29gh2o.d.ts → types-zNxyEpG2.d.ts} +3 -3
  119. package/lib/types.d.ts +1 -1
  120. package/lib/utils/body.d.ts +1 -1
  121. package/lib/utils/cookie.d.ts +1 -1
  122. package/lib/utils/crypto.d.ts +1 -1
  123. package/lib/utils/html.d.ts +1 -45
  124. package/lib/utils/ipaddr.d.ts +1 -1
  125. package/lib/utils/jwt.d.ts +1 -1
  126. package/lib/utils/types.d.ts +2 -2
  127. package/lib/utils-Cx61GFMC.js +125 -0
  128. package/lib/validator.d.ts +2 -2
  129. package/lib/ws.d.ts +1 -1
  130. package/package.json +53 -1
  131. package/plugins/acryl-inspect/upstream.json +1 -1
  132. package/plugins/acryl-node-server/upstream.json +1 -1
  133. package/plugins/acryl-router/src/core/helper/css/common.ts +337 -0
  134. package/plugins/acryl-router/src/core/helper/css/index.ts +262 -0
  135. package/plugins/acryl-router/src/core/helper/ssg/index.ts +14 -0
  136. package/plugins/acryl-router/src/core/helper/ssg/middleware.ts +83 -0
  137. package/plugins/acryl-router/src/core/helper/ssg/plugins.ts +72 -0
  138. package/plugins/acryl-router/src/core/helper/ssg/ssg.ts +471 -0
  139. package/plugins/acryl-router/src/core/helper/ssg/utils.ts +118 -0
  140. package/plugins/acryl-router/src/core/jsx/base.ts +490 -0
  141. package/plugins/acryl-router/src/core/jsx/children.ts +20 -0
  142. package/plugins/acryl-router/src/core/jsx/components.ts +257 -0
  143. package/plugins/acryl-router/src/core/jsx/constants.ts +6 -0
  144. package/plugins/acryl-router/src/core/jsx/context.ts +266 -0
  145. package/plugins/acryl-router/src/core/jsx/dom/client.ts +89 -0
  146. package/plugins/acryl-router/src/core/jsx/dom/components.ts +39 -0
  147. package/plugins/acryl-router/src/core/jsx/dom/context.ts +52 -0
  148. package/plugins/acryl-router/src/core/jsx/dom/css.ts +263 -0
  149. package/plugins/acryl-router/src/core/jsx/dom/hooks/index.ts +91 -0
  150. package/plugins/acryl-router/src/core/jsx/dom/index.ts +170 -0
  151. package/plugins/acryl-router/src/core/jsx/dom/intrinsic-element/components.ts +438 -0
  152. package/plugins/acryl-router/src/core/jsx/dom/jsx-dev-runtime.ts +23 -0
  153. package/plugins/acryl-router/src/core/jsx/dom/jsx-runtime.ts +8 -0
  154. package/plugins/acryl-router/src/core/jsx/dom/render.ts +879 -0
  155. package/plugins/acryl-router/src/core/jsx/dom/server.ts +76 -0
  156. package/plugins/acryl-router/src/core/jsx/dom/utils.ts +7 -0
  157. package/plugins/acryl-router/src/core/jsx/hooks/index.ts +437 -0
  158. package/plugins/acryl-router/src/core/jsx/index.ts +115 -0
  159. package/plugins/acryl-router/src/core/jsx/intrinsic-element/common.ts +23 -0
  160. package/plugins/acryl-router/src/core/jsx/intrinsic-element/components.ts +210 -0
  161. package/plugins/acryl-router/src/core/jsx/intrinsic-elements.ts +938 -0
  162. package/plugins/acryl-router/src/core/jsx/jsx-dev-runtime.ts +26 -0
  163. package/plugins/acryl-router/src/core/jsx/jsx-runtime.ts +51 -0
  164. package/plugins/acryl-router/src/core/jsx/streaming.ts +231 -0
  165. package/plugins/acryl-router/src/core/jsx/types.ts +41 -0
  166. package/plugins/acryl-router/src/core/jsx/utils.ts +202 -0
  167. package/plugins/acryl-router/src/core/middleware/cache/index.ts +4 -4
  168. package/plugins/acryl-router/src/core/middleware/jsx-renderer/index.ts +162 -0
  169. package/plugins/acryl-router/src/core/utils/types.ts +6 -0
  170. package/plugins/acryl-router/src/helpers/index.ts +2 -0
  171. package/plugins/acryl-router/src/middleware/index.ts +1 -0
  172. package/plugins/acryl-router/upstream.json +3 -3
  173. package/plugins/acryl-router-http/upstream.json +1 -1
  174. package/plugins/acryl-state/upstream.json +1 -1
  175. package/plugins/desktop/upstream.json +1 -1
  176. package/plugins/desktop-web/upstream.json +1 -1
  177. package/plugins/extensions/resolve.ts +4 -0
  178. package/plugins/save/plugin.json +1 -1
  179. package/shared/AGENTS.core.md +4 -0
  180. package/templates/agent/tsconfig.json +7 -1
  181. package/templates/todo/upstream.json +1 -1
  182. package/plugins/acryl-inspect/tests/report.spec.ts +0 -69
  183. package/plugins/acryl-inspect/tests/service.spec.ts +0 -121
  184. package/plugins/acryl-node-server/tests/adapter/app.ts +0 -76
  185. package/plugins/acryl-node-server/tests/adapter/assets/.static/plain.txt +0 -1
  186. package/plugins/acryl-node-server/tests/adapter/assets/favicon.ico +0 -0
  187. package/plugins/acryl-node-server/tests/adapter/assets/secret.txt +0 -1
  188. package/plugins/acryl-node-server/tests/adapter/assets/static/acryl.html +0 -1
  189. package/plugins/acryl-node-server/tests/adapter/assets/static/admin/secret.txt +0 -1
  190. package/plugins/acryl-node-server/tests/adapter/assets/static/data.json +0 -5
  191. package/plugins/acryl-node-server/tests/adapter/assets/static/extensionless +0 -1
  192. package/plugins/acryl-node-server/tests/adapter/assets/static/foo..bar.txt +0 -0
  193. package/plugins/acryl-node-server/tests/adapter/assets/static/index.html +0 -1
  194. package/plugins/acryl-node-server/tests/adapter/assets/static/plain.txt +0 -1
  195. package/plugins/acryl-node-server/tests/adapter/assets/static-with-context-path/plain.txt +0 -1
  196. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.bin +0 -1
  197. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.bin.br +0 -1
  198. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt +0 -1
  199. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt.br +0 -1
  200. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt.zst +0 -1
  201. package/plugins/acryl-node-server/tests/adapter/conninfo.test.ts +0 -34
  202. package/plugins/acryl-node-server/tests/adapter/content-length.test.ts +0 -248
  203. package/plugins/acryl-node-server/tests/adapter/early-hints.test.ts +0 -457
  204. package/plugins/acryl-node-server/tests/adapter/fixtures/keys/agent1-cert.pem +0 -23
  205. package/plugins/acryl-node-server/tests/adapter/fixtures/keys/agent1-key.pem +0 -27
  206. package/plugins/acryl-node-server/tests/adapter/headers.test.ts +0 -252
  207. package/plugins/acryl-node-server/tests/adapter/helpers/request.ts +0 -190
  208. package/plugins/acryl-node-server/tests/adapter/listener.test.ts +0 -727
  209. package/plugins/acryl-node-server/tests/adapter/request-client-disconnect.test.ts +0 -189
  210. package/plugins/acryl-node-server/tests/adapter/request.test.ts +0 -1230
  211. package/plugins/acryl-node-server/tests/adapter/response.test.ts +0 -285
  212. package/plugins/acryl-node-server/tests/adapter/serve-static.test.ts +0 -849
  213. package/plugins/acryl-node-server/tests/adapter/server-socket.test.ts +0 -790
  214. package/plugins/acryl-node-server/tests/adapter/server.test.ts +0 -1312
  215. package/plugins/acryl-node-server/tests/adapter/setup.ts +0 -16
  216. package/plugins/acryl-node-server/tests/adapter/url.test.ts +0 -76
  217. package/plugins/acryl-node-server/tests/adapter/utils/response.test.ts +0 -23
  218. package/plugins/acryl-node-server/tests/adapter/utils/stream.test.ts +0 -75
  219. package/plugins/acryl-node-server/tests/adapter/utils.test.ts +0 -235
  220. package/plugins/acryl-node-server/tests/adapter/websocket.test.ts +0 -260
  221. package/plugins/acryl-node-server/tests/server.spec.ts +0 -141
  222. package/plugins/acryl-router/src/core/app.test.ts +0 -3833
  223. package/plugins/acryl-router/src/core/client/client.test.ts +0 -2313
  224. package/plugins/acryl-router/src/core/client/types.test.ts +0 -149
  225. package/plugins/acryl-router/src/core/client/utils.test.ts +0 -405
  226. package/plugins/acryl-router/src/core/compose.test.ts +0 -751
  227. package/plugins/acryl-router/src/core/context.test.ts +0 -629
  228. package/plugins/acryl-router/src/core/helper/accepts/accepts.test.ts +0 -344
  229. package/plugins/acryl-router/src/core/helper/adapter/index.test.ts +0 -55
  230. package/plugins/acryl-router/src/core/helper/cookie/index.test.ts +0 -510
  231. package/plugins/acryl-router/src/core/helper/dev/index.test.ts +0 -181
  232. package/plugins/acryl-router/src/core/helper/factory/index.test.ts +0 -434
  233. package/plugins/acryl-router/src/core/helper/html/index.test.ts +0 -78
  234. package/plugins/acryl-router/src/core/helper/proxy/index.test.ts +0 -356
  235. package/plugins/acryl-router/src/core/helper/route/index.test.ts +0 -259
  236. package/plugins/acryl-router/src/core/helper/streaming/stream.test.ts +0 -128
  237. package/plugins/acryl-router/src/core/helper/streaming/text.test.ts +0 -34
  238. package/plugins/acryl-router/src/core/helper/testing/index.test.ts +0 -42
  239. package/plugins/acryl-router/src/core/helper/websocket/index.test.ts +0 -124
  240. package/plugins/acryl-router/src/core/http-exception.test.ts +0 -45
  241. package/plugins/acryl-router/src/core/middleware/basic-auth/index.test.ts +0 -434
  242. package/plugins/acryl-router/src/core/middleware/bearer-auth/index.test.ts +0 -1018
  243. package/plugins/acryl-router/src/core/middleware/body-limit/index.test.ts +0 -273
  244. package/plugins/acryl-router/src/core/middleware/cache/index.test.ts +0 -1211
  245. package/plugins/acryl-router/src/core/middleware/combine/index.test.ts +0 -355
  246. package/plugins/acryl-router/src/core/middleware/compress/index.test.ts +0 -535
  247. package/plugins/acryl-router/src/core/middleware/context-storage/index.test.ts +0 -43
  248. package/plugins/acryl-router/src/core/middleware/cors/index.test.ts +0 -532
  249. package/plugins/acryl-router/src/core/middleware/csrf/index.test.ts +0 -534
  250. package/plugins/acryl-router/src/core/middleware/etag/digest.test.ts +0 -21
  251. package/plugins/acryl-router/src/core/middleware/etag/index.test.ts +0 -503
  252. package/plugins/acryl-router/src/core/middleware/ip-restriction/index.test.ts +0 -217
  253. package/plugins/acryl-router/src/core/middleware/jwk/index.test.ts +0 -1135
  254. package/plugins/acryl-router/src/core/middleware/jwk/keys.test.json +0 -48
  255. package/plugins/acryl-router/src/core/middleware/jwt/index.test.ts +0 -802
  256. package/plugins/acryl-router/src/core/middleware/language/index.test.ts +0 -552
  257. package/plugins/acryl-router/src/core/middleware/logger/index.test.ts +0 -204
  258. package/plugins/acryl-router/src/core/middleware/method-not-allowed/index.test.ts +0 -292
  259. package/plugins/acryl-router/src/core/middleware/method-override/index.test.ts +0 -221
  260. package/plugins/acryl-router/src/core/middleware/mount/index.test.ts +0 -334
  261. package/plugins/acryl-router/src/core/middleware/powered-by/index.test.ts +0 -37
  262. package/plugins/acryl-router/src/core/middleware/pretty-json/index.test.ts +0 -218
  263. package/plugins/acryl-router/src/core/middleware/request-id/index.test.ts +0 -168
  264. package/plugins/acryl-router/src/core/middleware/secure-headers/index.test.ts +0 -671
  265. package/plugins/acryl-router/src/core/middleware/serve-static/index.test.ts +0 -450
  266. package/plugins/acryl-router/src/core/middleware/serve-static/path.test.ts +0 -50
  267. package/plugins/acryl-router/src/core/middleware/timeout/index.test.ts +0 -70
  268. package/plugins/acryl-router/src/core/middleware/timing/index.test.ts +0 -310
  269. package/plugins/acryl-router/src/core/middleware/trailing-slash/index.test.ts +0 -381
  270. package/plugins/acryl-router/src/core/preset/quick.test.ts +0 -42
  271. package/plugins/acryl-router/src/core/preset/tiny.test.ts +0 -42
  272. package/plugins/acryl-router/src/core/request.test.ts +0 -751
  273. package/plugins/acryl-router/src/core/router/common.case.test.ts +0 -1132
  274. package/plugins/acryl-router/src/core/router/linear-router/router.test.ts +0 -80
  275. package/plugins/acryl-router/src/core/router/pattern-router/router.test.ts +0 -46
  276. package/plugins/acryl-router/src/core/router/reg-exp-router/prepared-router.test.ts +0 -154
  277. package/plugins/acryl-router/src/core/router/reg-exp-router/router.test.ts +0 -265
  278. package/plugins/acryl-router/src/core/router/smart-router/router.test.ts +0 -13
  279. package/plugins/acryl-router/src/core/router/trie-router/node.test.ts +0 -896
  280. package/plugins/acryl-router/src/core/router/trie-router/router.test.ts +0 -8
  281. package/plugins/acryl-router/src/core/types.test.ts +0 -4134
  282. package/plugins/acryl-router/src/core/utils/accept.test.ts +0 -299
  283. package/plugins/acryl-router/src/core/utils/basic-auth.test.ts +0 -56
  284. package/plugins/acryl-router/src/core/utils/body.test.ts +0 -496
  285. package/plugins/acryl-router/src/core/utils/buffer.test.ts +0 -147
  286. package/plugins/acryl-router/src/core/utils/color.test.ts +0 -23
  287. package/plugins/acryl-router/src/core/utils/concurrent.test.ts +0 -69
  288. package/plugins/acryl-router/src/core/utils/cookie.test.ts +0 -460
  289. package/plugins/acryl-router/src/core/utils/crypto.test.ts +0 -43
  290. package/plugins/acryl-router/src/core/utils/encode.test.ts +0 -61
  291. package/plugins/acryl-router/src/core/utils/filepath.test.ts +0 -78
  292. package/plugins/acryl-router/src/core/utils/html.test.ts +0 -16
  293. package/plugins/acryl-router/src/core/utils/ipaddr.test.ts +0 -149
  294. package/plugins/acryl-router/src/core/utils/jwt/jwa.test.ts +0 -86
  295. package/plugins/acryl-router/src/core/utils/jwt/jwt.test.ts +0 -1788
  296. package/plugins/acryl-router/src/core/utils/mime.test.ts +0 -64
  297. package/plugins/acryl-router/src/core/utils/stream.test.ts +0 -212
  298. package/plugins/acryl-router/src/core/utils/types.test.ts +0 -295
  299. package/plugins/acryl-router/src/core/utils/url.test.ts +0 -384
  300. package/plugins/acryl-router/src/core/validator/utils.test.ts +0 -82
  301. package/plugins/acryl-router/src/core/validator/validator.test.ts +0 -1488
  302. package/plugins/acryl-router/tests/docs-examples.spec.ts +0 -152
  303. package/plugins/acryl-router/tests/live-install.spec.ts +0 -64
  304. package/plugins/acryl-router/tests/loader.spec.ts +0 -72
  305. package/plugins/acryl-router/tests/loopback-only.spec.ts +0 -80
  306. package/plugins/acryl-router/tests/no-dom/consumer.ts +0 -11
  307. package/plugins/acryl-router/tests/no-dom/tsconfig.json +0 -13
  308. package/plugins/acryl-router/tests/service.spec.ts +0 -426
  309. package/plugins/acryl-router/tests/shadow-state.spec.ts +0 -79
  310. package/plugins/acryl-router-http/tests/adapter.spec.ts +0 -174
  311. package/plugins/acryl-router-http/tests/stream.spec.ts +0 -133
  312. package/plugins/acryl-router-http/tests/surfaces.spec.ts +0 -101
  313. package/plugins/acryl-state/src/react.ts +0 -11
  314. package/plugins/acryl-state/tests/cell.spec.ts +0 -71
  315. package/plugins/acryl-state/tests/react.spec.ts +0 -39
  316. package/plugins/acryl-state/tests/service.spec.ts +0 -132
@@ -0,0 +1,257 @@
1
+ import { raw } from '../helper/html/index.ts'
2
+ import type { HtmlEscapedCallback, HtmlEscapedString } from '../utils/html.ts'
3
+ import { HtmlEscapedCallbackPhase, resolveCallback } from '../utils/html.ts'
4
+ import type { Child, FC, PropsWithChildren } from './index.ts'
5
+ import { jsx, Fragment, isUntrustedObject, renderChildren, renderUntrustedObject } from './base.ts'
6
+ import { DOM_RENDERER } from './constants.ts'
7
+ import { captureRenderContext, useContext } from './context.ts'
8
+ import { ErrorBoundary as ErrorBoundaryDomRenderer } from './dom/components.ts'
9
+ import type { HasRenderToDom } from './dom/render.ts'
10
+ import { StreamingContext } from './streaming.ts'
11
+
12
+ let errorBoundaryCounter = 0
13
+
14
+ export const childrenToString = async (children: Child[]): Promise<HtmlEscapedString[]> => {
15
+ try {
16
+ return children.flat().map(resolveChildEarly) as HtmlEscapedString[]
17
+ } catch (e) {
18
+ if (e instanceof Promise) {
19
+ // Capture before `await`: on the fallback path the render context is
20
+ // only observable during this synchronous window.
21
+ const resume = captureRenderContext()
22
+ await e
23
+ return resume(() => childrenToString(children))
24
+ } else {
25
+ throw e
26
+ }
27
+ }
28
+ }
29
+
30
+ const resolveChildEarly = (child: Child): HtmlEscapedString | Promise<HtmlEscapedString> => {
31
+ if (child == null || typeof child === 'boolean') {
32
+ return '' as HtmlEscapedString
33
+ } else if (typeof child === 'string' || Array.isArray(child)) {
34
+ return renderChildren([child])
35
+ } else if (isUntrustedObject(child)) {
36
+ return renderUntrustedObject(child)
37
+ } else {
38
+ const str = child.toString()
39
+ return str instanceof Promise ? (str as Promise<HtmlEscapedString>) : raw(str)
40
+ }
41
+ }
42
+
43
+ export type ErrorHandler = (error: Error) => void
44
+ export type FallbackRender = (error: Error) => Child
45
+
46
+ /**
47
+ * @experimental
48
+ * `ErrorBoundary` is an experimental feature.
49
+ * The API might be changed.
50
+ */
51
+ export const ErrorBoundary: FC<
52
+ PropsWithChildren<{
53
+ fallback?: Child
54
+ fallbackRender?: FallbackRender
55
+ onError?: ErrorHandler
56
+ }>
57
+ > = async ({ children, fallback, fallbackRender, onError }) => {
58
+ if (!children) {
59
+ return raw('')
60
+ }
61
+
62
+ if (!Array.isArray(children)) {
63
+ children = [children]
64
+ }
65
+
66
+ const nonce = useContext(StreamingContext)?.scriptNonce
67
+
68
+ let resume: ReturnType<typeof captureRenderContext> | undefined
69
+ const getResume = () => (resume ||= captureRenderContext())
70
+
71
+ let fallbackStrPromise: Promise<HtmlEscapedString | string | undefined> | undefined
72
+ const resolveFallbackStr = (): Promise<HtmlEscapedString | string | undefined> =>
73
+ (fallbackStrPromise ||= (async () => {
74
+ const awaitedFallback = await fallback
75
+ if (awaitedFallback === null || awaitedFallback === undefined) {
76
+ return
77
+ }
78
+ if (typeof awaitedFallback === 'string' || Array.isArray(awaitedFallback)) {
79
+ return getResume()(() => renderChildren([awaitedFallback]))
80
+ }
81
+ if (isUntrustedObject(awaitedFallback)) {
82
+ return getResume()(() => renderUntrustedObject(awaitedFallback))
83
+ }
84
+ const fallbackResult = await getResume()(() => awaitedFallback.toString())
85
+ return raw(
86
+ fallbackResult,
87
+ (fallbackResult as HtmlEscapedString).callbacks ||
88
+ (awaitedFallback as unknown as HtmlEscapedString).callbacks
89
+ )
90
+ })())
91
+ const renderFallback = async (error: Error): Promise<HtmlEscapedString> => {
92
+ const fallbackStr = await resolveFallbackStr()
93
+ return getResume()(async () => {
94
+ onError?.(error)
95
+ const fallbackRes = (
96
+ fallbackStr !== undefined
97
+ ? fallbackStr
98
+ : (fallbackRender && jsx(Fragment, {}, fallbackRender(error) as HtmlEscapedString)) || ''
99
+ ) as HtmlEscapedString
100
+ const fallbackResString = await Fragment({ children: fallbackRes }).toString()
101
+ return raw(
102
+ fallbackResString,
103
+ (fallbackResString as HtmlEscapedString).callbacks || fallbackRes.callbacks
104
+ )
105
+ })
106
+ }
107
+ let resArray: HtmlEscapedString[] | Promise<HtmlEscapedString[]>[] = []
108
+ try {
109
+ resArray = children.map(resolveChildEarly) as unknown as HtmlEscapedString[]
110
+ } catch (e) {
111
+ const resume = getResume()
112
+ if (e instanceof Promise) {
113
+ resArray = [
114
+ e
115
+ .then(() => resume(() => childrenToString(children as Child[])))
116
+ .catch((e) => renderFallback(e)),
117
+ ] as Promise<HtmlEscapedString[]>[]
118
+ } else {
119
+ resArray = [await renderFallback(e as Error)]
120
+ }
121
+ }
122
+
123
+ if (resArray.some((res) => (res as {}) instanceof Promise)) {
124
+ // Prime the context capture while still synchronous: a child that returned
125
+ // a Promise from `resolveChildEarly` skipped the `catch`, so the deferred
126
+ // `catchCallback` would otherwise capture too late.
127
+ getResume()
128
+ const index = errorBoundaryCounter++
129
+ const replaceRe = RegExp(`(<template id="E:${index}"></template>)(.*?)(<!--E:${index}-->)`, 's')
130
+ let caught = false
131
+ const catchCallback = async ({ error, buffer }: { error: Error; buffer?: [string] }) => {
132
+ if (caught) {
133
+ return ''
134
+ }
135
+ caught = true
136
+
137
+ const fallbackResString = await renderFallback(error)
138
+ const fallbackCallbacks = fallbackResString.callbacks
139
+ if (buffer) {
140
+ buffer[0] = buffer[0].replace(replaceRe, () => fallbackResString)
141
+ return fallbackCallbacks?.length ? raw('', fallbackCallbacks) : ''
142
+ }
143
+ return raw(
144
+ `<template data-acryl-target="E:${index}">${fallbackResString}</template><script>
145
+ ((d,c,n) => {
146
+ c=d.currentScript.previousSibling
147
+ d=d.getElementById('E:${index}')
148
+ if(!d)return
149
+ do{n=d.nextSibling;n.remove()}while(n.nodeType!=8||n.nodeValue!='E:${index}')
150
+ d.replaceWith(c.content)
151
+ })(document)
152
+ </script>`,
153
+ fallbackCallbacks
154
+ )
155
+ }
156
+
157
+ let error: unknown
158
+ const promiseAll = Promise.all(resArray).catch((e) => (error = e))
159
+ return raw(`<template id="E:${index}"></template><!--E:${index}-->`, [
160
+ ({ phase, buffer, context }) => {
161
+ if (phase === HtmlEscapedCallbackPhase.BeforeStream) {
162
+ return
163
+ }
164
+ return promiseAll
165
+ .then(async (htmlArray: HtmlEscapedString[]) => {
166
+ if (error) {
167
+ throw error
168
+ }
169
+ htmlArray = htmlArray.flat()
170
+ const content = htmlArray.join('')
171
+ let html = buffer
172
+ ? ''
173
+ : `<template data-acryl-target="E:${index}">${content}</template><script${
174
+ nonce ? ` nonce="${nonce}"` : ''
175
+ }>
176
+ ((d,c) => {
177
+ c=d.currentScript.previousSibling
178
+ d=d.getElementById('E:${index}')
179
+ if(!d)return
180
+ d.parentElement.insertBefore(c.content,d.nextSibling)
181
+ })(document)
182
+ </script>`
183
+
184
+ if (htmlArray.every((html) => !(html as HtmlEscapedString).callbacks?.length)) {
185
+ if (buffer) {
186
+ buffer[0] = buffer[0].replace(replaceRe, () => content)
187
+ }
188
+ return html
189
+ }
190
+
191
+ if (buffer) {
192
+ buffer[0] = buffer[0].replace(
193
+ replaceRe,
194
+ (_all, pre, _, post) => `${pre}${content}${post}`
195
+ )
196
+ }
197
+
198
+ const callbacks = htmlArray
199
+ .map((html) => (html as HtmlEscapedString).callbacks || [])
200
+ .flat()
201
+
202
+ if (phase === HtmlEscapedCallbackPhase.Stream) {
203
+ html = await resolveCallback(
204
+ html,
205
+ HtmlEscapedCallbackPhase.BeforeStream,
206
+ true,
207
+ context
208
+ )
209
+ }
210
+
211
+ let resolvedCount = 0
212
+ const promises = callbacks.map<HtmlEscapedCallback>(
213
+ (c) =>
214
+ (...args) =>
215
+ c(...args)
216
+ ?.then((content) => {
217
+ resolvedCount++
218
+
219
+ if (buffer) {
220
+ if (resolvedCount === callbacks.length) {
221
+ buffer[0] = buffer[0].replace(replaceRe, (_all, _pre, content) => content)
222
+ }
223
+ buffer[0] += content
224
+ return raw('', (content as HtmlEscapedString).callbacks)
225
+ }
226
+
227
+ return raw(
228
+ content +
229
+ (resolvedCount !== callbacks.length
230
+ ? ''
231
+ : `<script>
232
+ ((d,c,n) => {
233
+ d=d.getElementById('E:${index}')
234
+ if(!d)return
235
+ n=d.nextSibling
236
+ while(n.nodeType!=8||n.nodeValue!='E:${index}'){n=n.nextSibling}
237
+ n.remove()
238
+ d.remove()
239
+ })(document)
240
+ </script>`),
241
+ (content as HtmlEscapedString).callbacks
242
+ )
243
+ })
244
+ .catch((error) => catchCallback({ error, buffer }))
245
+ )
246
+
247
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
248
+ return raw(html, promises as any)
249
+ })
250
+ .catch((error) => catchCallback({ error, buffer }))
251
+ },
252
+ ])
253
+ } else {
254
+ return Fragment({ children: resArray as Child[] })
255
+ }
256
+ }
257
+ ;(ErrorBoundary as HasRenderToDom)[DOM_RENDERER] = ErrorBoundaryDomRenderer
@@ -0,0 +1,6 @@
1
+ export const DOM_RENDERER = Symbol('RENDERER')
2
+ export const DOM_ERROR_HANDLER = Symbol('ERROR_HANDLER')
3
+ export const DOM_STASH = Symbol('STASH')
4
+ export const DOM_INTERNAL_TAG = Symbol('INTERNAL')
5
+ export const DOM_MEMO = Symbol('MEMO')
6
+ export const PERMALINK = Symbol('PERMALINK')
@@ -0,0 +1,266 @@
1
+ import { raw } from '../helper/html/index.ts'
2
+ import type { HtmlEscapedString } from '../utils/html.ts'
3
+ import type { FC, PropsWithChildren } from './index.ts'
4
+ import { isUntrustedObject, JSXFragmentNode, renderChildren, renderUntrustedObject } from './base.ts'
5
+ import { DOM_RENDERER } from './constants.ts'
6
+ import { createContextProviderFunction } from './dom/context.ts'
7
+
8
+ export interface Context<T> extends FC<PropsWithChildren<{ value: T }>> {
9
+ values: T[]
10
+ Provider: FC<PropsWithChildren<{ value: T }>>
11
+ }
12
+
13
+ export const globalContexts: Context<unknown>[] = []
14
+
15
+ /** Per-render context store, isolated per request so values never leak across renders. */
16
+ type RenderStore = WeakMap<Context<unknown>, unknown[]>
17
+
18
+ type AsyncLocalStorageLike<T> = {
19
+ getStore(): T | undefined
20
+ run<R>(store: T, callback: () => R): R
21
+ }
22
+ type AsyncLocalStorageClass = new <T>() => AsyncLocalStorageLike<T>
23
+ type AsyncHooksModule = { AsyncLocalStorage?: AsyncLocalStorageClass }
24
+
25
+ let alsProbed = false
26
+ let asyncLocalStorage: AsyncLocalStorageLike<RenderStore> | undefined
27
+ let fallbackStore: RenderStore | undefined
28
+ // Unsettled async renders on the fallback path. While > 0, a store-less
29
+ // context access is a post-`await` read that degraded to the default value
30
+ // — worth a one-time warning. Stays 0 in the browser DOM renderer.
31
+ let fallbackRendersInFlight = 0
32
+ let warnedFallbackDefault = false
33
+
34
+ const loadAsyncLocalStorage = (): AsyncLocalStorageLike<RenderStore> | undefined => {
35
+ if (alsProbed) {
36
+ return asyncLocalStorage
37
+ }
38
+ alsProbed = true
39
+
40
+ const global = globalThis as unknown as {
41
+ process?: {
42
+ getBuiltinModule?: (specifier: string) => AsyncHooksModule
43
+ mainModule?: { require?: (specifier: string) => AsyncHooksModule }
44
+ }
45
+ }
46
+ let AsyncLocalStorage: AsyncLocalStorageClass | undefined
47
+ for (const probe of [
48
+ // Node.js >= 20.16, Deno, Bun, Cloudflare Workers (nodejs_compat). Property
49
+ // access only, so bundlers don't statically resolve `node:async_hooks`.
50
+ () => global.process?.getBuiltinModule?.('node:async_hooks')?.AsyncLocalStorage,
51
+ // Node.js < 20.16 has no `process.getBuiltinModule`, but a CJS entrypoint
52
+ // exposes the main module's `require` here.
53
+ () => global.process?.mainModule?.require?.('node:async_hooks')?.AsyncLocalStorage,
54
+ ]) {
55
+ try {
56
+ AsyncLocalStorage = probe()
57
+ } catch {}
58
+ if (AsyncLocalStorage) {
59
+ break
60
+ }
61
+ }
62
+
63
+ if (AsyncLocalStorage) {
64
+ asyncLocalStorage = new AsyncLocalStorage<RenderStore>()
65
+ }
66
+ return asyncLocalStorage
67
+ }
68
+
69
+ const getCurrentStore = (): RenderStore | undefined => {
70
+ return loadAsyncLocalStorage()?.getStore() || fallbackStore
71
+ }
72
+
73
+ const warnIfStorelessAccess = (): void => {
74
+ if (fallbackRendersInFlight > 0 && !warnedFallbackDefault) {
75
+ warnedFallbackDefault = true
76
+ console.warn(
77
+ 'acryl-router/jsx: AsyncLocalStorage is unavailable in this runtime, so useContext() after an await in an async component falls back to the context default value during server-side rendering. To get provided values across await boundaries, use a runtime with AsyncLocalStorage (Node.js >= 20.16, Deno, Bun, or Cloudflare Workers with the nodejs_compat flag).'
78
+ )
79
+ }
80
+ }
81
+
82
+ // Write path: materializes the store entry so a Provider's push/pop pair
83
+ // operates on a per-render array.
84
+ const getContextValuesIn = <T>(store: RenderStore | undefined, context: Context<T>): T[] => {
85
+ if (!store) {
86
+ warnIfStorelessAccess()
87
+ return context.values
88
+ }
89
+
90
+ let values = store.get(context as Context<unknown>) as T[] | undefined
91
+ if (!values) {
92
+ values = [context.values[0]]
93
+ store.set(context as Context<unknown>, values)
94
+ }
95
+ return values
96
+ }
97
+
98
+ // Read path: never materializes a store entry — a missing entry always
99
+ // resolves to the default value, so reads stay allocation-free.
100
+ const readContextValueIn = <T>(store: RenderStore | undefined, context: Context<T>): T => {
101
+ if (!store) {
102
+ warnIfStorelessAccess()
103
+ return context.values.at(-1) as T
104
+ }
105
+ const values = store.get(context as Context<unknown>) as T[] | undefined
106
+ return values?.length ? (values.at(-1) as T) : context.values[0]
107
+ }
108
+
109
+ type LocalContexts = [Context<unknown>, unknown][]
110
+
111
+ const captureContextValues = (store: RenderStore | undefined): LocalContexts =>
112
+ (store ? globalContexts.filter((c) => store.has(c)) : globalContexts).map((c) => [
113
+ c,
114
+ readContextValueIn(store, c),
115
+ ])
116
+
117
+ const resumeWithContextValues = <T>(
118
+ callback: () => T,
119
+ store: RenderStore | undefined,
120
+ contexts: LocalContexts
121
+ ): T =>
122
+ runWithRenderContext(() => {
123
+ // Resolve each target array once so every pop operates on the same array
124
+ // as its push, as the Provider in `createContext` does.
125
+ const currentStore = getCurrentStore()
126
+ const valuesPerContext = contexts.map(([context, value]) => {
127
+ const values = getContextValuesIn(currentStore, context)
128
+ values.push(value)
129
+ return values
130
+ })
131
+ const popContextValues = () => {
132
+ valuesPerContext.forEach((values) => {
133
+ values.pop()
134
+ })
135
+ }
136
+ try {
137
+ const result = callback()
138
+ if (result instanceof Promise) {
139
+ return result.finally(popContextValues) as T
140
+ }
141
+ popContextValues()
142
+ return result
143
+ } catch (e) {
144
+ popContextValues()
145
+ throw e
146
+ }
147
+ }, store)
148
+
149
+ /**
150
+ * Establish the request-scoped context store for a render.
151
+ *
152
+ * `resumeStore` continues a suspended subtree in the same store on the fallback
153
+ * path (ignored when `AsyncLocalStorage` is available, where isolation is
154
+ * automatic).
155
+ *
156
+ * Without `AsyncLocalStorage` a render can't be followed across `await`, so the
157
+ * store lives in `fallbackStore` only during synchronous work (mirroring
158
+ * React's request storage). Reading context after `await` then finds no store
159
+ * and falls back to the default value — never another request's value.
160
+ */
161
+ export const runWithRenderContext = <T>(callback: () => T, resumeStore?: RenderStore): T => {
162
+ if (getCurrentStore()) {
163
+ // Already inside a render (synchronously nested or resumed subtree).
164
+ return callback()
165
+ }
166
+
167
+ const store = resumeStore ?? new WeakMap()
168
+ const storage = loadAsyncLocalStorage()
169
+ if (storage) {
170
+ // AsyncLocalStorage path: request isolation propagates across `await`.
171
+ return storage.run(store, callback)
172
+ }
173
+
174
+ // Fallback path (no AsyncLocalStorage).
175
+ fallbackStore = store
176
+ let result: T
177
+ try {
178
+ result = callback()
179
+ } finally {
180
+ fallbackStore = undefined
181
+ }
182
+ if (!warnedFallbackDefault && result instanceof Promise) {
183
+ fallbackRendersInFlight++
184
+ result = result.finally(() => {
185
+ fallbackRendersInFlight--
186
+ }) as T
187
+ }
188
+ return result
189
+ }
190
+
191
+ /**
192
+ * Capture the current render store and return a resumer that re-establishes it
193
+ * around a deferred continuation (e.g. a re-render after a suspended promise
194
+ * settles). Shared by every suspension point so none reimplements it.
195
+ */
196
+ export const captureRenderContext = (): (<T>(callback: () => T) => T) => {
197
+ const store = getCurrentStore()
198
+ const contexts = captureContextValues(store)
199
+ return (callback) => resumeWithContextValues(callback, store, contexts)
200
+ }
201
+
202
+ /**
203
+ * Create a context whose value can be provided with `<Context.Provider>` and
204
+ * read with {@link useContext}.
205
+ *
206
+ * Server-side renders are isolated per request, so a provided value never leaks
207
+ * into a concurrent request — even across `await` in an async component, when
208
+ * `AsyncLocalStorage` is available (Node.js >= 20.16, Deno, Bun, Cloudflare
209
+ * Workers with `nodejs_compat`). Without it, reading context after `await`
210
+ * returns the default value; synchronous components and `use()`-based
211
+ * suspension are unaffected.
212
+ */
213
+ export const createContext = <T>(defaultValue: T): Context<T> => {
214
+ const values = [defaultValue]
215
+ const context: Context<T> = ((props): HtmlEscapedString | Promise<HtmlEscapedString> => {
216
+ // Resolve the target array once, synchronously: the pop in `.finally()`
217
+ // below may run later and must operate on the same array as the push.
218
+ const contextValues = getContextValuesIn(getCurrentStore(), context)
219
+ contextValues.push(props.value)
220
+ let rendered
221
+ try {
222
+ rendered =
223
+ typeof props.children === 'string'
224
+ ? renderChildren([props.children])
225
+ : isUntrustedObject(props.children)
226
+ ? renderUntrustedObject(props.children)
227
+ : props.children
228
+ ? (Array.isArray(props.children)
229
+ ? new JSXFragmentNode('', {}, props.children)
230
+ : props.children
231
+ ).toString()
232
+ : raw('')
233
+ } catch (e) {
234
+ contextValues.pop()
235
+ throw e
236
+ }
237
+
238
+ if (rendered instanceof Promise) {
239
+ return rendered
240
+ .finally(() => contextValues.pop())
241
+ .then((resString) => raw(resString, (resString as HtmlEscapedString).callbacks))
242
+ } else {
243
+ contextValues.pop()
244
+ return raw(rendered)
245
+ }
246
+ }) as Context<T>
247
+ context.values = values
248
+ context.Provider = context
249
+
250
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
251
+ ;(context as any)[DOM_RENDERER] = createContextProviderFunction(values)
252
+
253
+ globalContexts.push(context as Context<unknown>)
254
+
255
+ return context
256
+ }
257
+
258
+ /**
259
+ * Read the current value of a context created with {@link createContext}.
260
+ *
261
+ * Safe to call from async components after `await`. See {@link createContext}
262
+ * for the per-runtime isolation guarantees.
263
+ */
264
+ export const useContext = <T>(context: Context<T>): T => {
265
+ return readContextValueIn(getCurrentStore(), context)
266
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * @module
3
+ * This module provides APIs for `acryl-router/jsx/dom/client`, which is compatible with `react-dom/client`.
4
+ */
5
+
6
+ import type { Child } from '../base.ts'
7
+ import { useState } from '../hooks/index.ts'
8
+ import { buildNode, renderNode } from './render.ts'
9
+ import type { NodeObject } from './render.ts'
10
+
11
+ export interface Root {
12
+ render(children: Child): void
13
+ unmount(): void
14
+ }
15
+ export type RootOptions = Record<string, unknown>
16
+
17
+ /**
18
+ * Create a root object for rendering
19
+ * @param element Render target
20
+ * @param options Options for createRoot (not supported yet)
21
+ * @returns Root object has `render` and `unmount` methods
22
+ */
23
+ export const createRoot = (
24
+ element: HTMLElement | DocumentFragment,
25
+ options: RootOptions = {}
26
+ ): Root => {
27
+ let setJsxNode:
28
+ | undefined // initial state
29
+ | ((jsxNode: unknown) => void) // rendered
30
+ | null = // unmounted
31
+ undefined
32
+
33
+ if (Object.keys(options).length > 0) {
34
+ console.warn('createRoot options are not supported yet')
35
+ }
36
+
37
+ return {
38
+ render(jsxNode: unknown) {
39
+ if (setJsxNode === null) {
40
+ // unmounted
41
+ throw new Error('Cannot update an unmounted root')
42
+ }
43
+ if (setJsxNode) {
44
+ // rendered
45
+ setJsxNode(jsxNode)
46
+ } else {
47
+ renderNode(
48
+ buildNode({
49
+ tag: () => {
50
+ const [_jsxNode, _setJsxNode] = useState(jsxNode)
51
+ setJsxNode = _setJsxNode
52
+ return _jsxNode
53
+ },
54
+ props: {},
55
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
56
+ } as any) as NodeObject,
57
+ element
58
+ )
59
+ }
60
+ },
61
+ unmount() {
62
+ setJsxNode?.(null)
63
+ setJsxNode = null
64
+ },
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Create a root object and hydrate app to the target element.
70
+ * In acryl-router/jsx/dom, hydrate is equivalent to render.
71
+ * @param element Render target
72
+ * @param reactNode A JSXNode to render
73
+ * @param options Options for createRoot (not supported yet)
74
+ * @returns Root object has `render` and `unmount` methods
75
+ */
76
+ export const hydrateRoot = (
77
+ element: HTMLElement | DocumentFragment,
78
+ reactNode: Child,
79
+ options: RootOptions = {}
80
+ ): Root => {
81
+ const root = createRoot(element, options)
82
+ root.render(reactNode)
83
+ return root
84
+ }
85
+
86
+ export default {
87
+ createRoot,
88
+ hydrateRoot,
89
+ }
@@ -0,0 +1,39 @@
1
+ import type { Child, FC, PropsWithChildren } from '../index.ts'
2
+ import type { ErrorHandler, FallbackRender } from '../components.ts'
3
+ import { DOM_ERROR_HANDLER } from '../constants.ts'
4
+ import { Fragment } from './jsx-runtime.ts'
5
+
6
+ /* eslint-disable @typescript-eslint/no-explicit-any */
7
+ export const ErrorBoundary: FC<
8
+ PropsWithChildren<{
9
+ fallback?: Child
10
+ fallbackRender?: FallbackRender
11
+ onError?: ErrorHandler
12
+ }>
13
+ > = (({ children, fallback, fallbackRender, onError }: any) => {
14
+ const res = Fragment({ children })
15
+ ;(res as any)[DOM_ERROR_HANDLER] = (err: any) => {
16
+ if (err instanceof Promise) {
17
+ throw err
18
+ }
19
+ onError?.(err)
20
+ return fallbackRender?.(err) || fallback
21
+ }
22
+ return res
23
+ }) as any
24
+
25
+ export const Suspense: FC<PropsWithChildren<{ fallback: any }>> = (({
26
+ children,
27
+ fallback,
28
+ }: any) => {
29
+ const res = Fragment({ children })
30
+ ;(res as any)[DOM_ERROR_HANDLER] = (err: any, retry: () => void) => {
31
+ if (!(err instanceof Promise)) {
32
+ throw err
33
+ }
34
+ err.finally(retry)
35
+ return fallback
36
+ }
37
+ return res
38
+ }) as any
39
+ /* eslint-enable @typescript-eslint/no-explicit-any */