@nifrajs/web 4.0.2 → 5.0.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 (384) hide show
  1. package/README.md +11 -0
  2. package/dist/build-vite.d.ts +19 -0
  3. package/dist/build-vite.d.ts.map +1 -1
  4. package/dist/build-vite.js +31 -10
  5. package/dist/build-vite.js.map +1 -1
  6. package/dist/build.d.ts +8 -10
  7. package/dist/build.d.ts.map +1 -1
  8. package/dist/build.js +90 -31
  9. package/dist/build.js.map +1 -1
  10. package/dist/cdn.d.ts +8 -0
  11. package/dist/cdn.d.ts.map +1 -1
  12. package/dist/cdn.js +14 -15
  13. package/dist/cdn.js.map +1 -1
  14. package/dist/client.d.ts +1 -1
  15. package/dist/client.d.ts.map +1 -1
  16. package/dist/client.js +25 -7
  17. package/dist/client.js.map +1 -1
  18. package/dist/deferred.d.ts +2 -2
  19. package/dist/deferred.d.ts.map +1 -1
  20. package/dist/deferred.js +28 -7
  21. package/dist/deferred.js.map +1 -1
  22. package/dist/dev-feed.d.ts.map +1 -1
  23. package/dist/dev-feed.js +24 -5
  24. package/dist/dev-feed.js.map +1 -1
  25. package/dist/dev-session.d.ts.map +1 -1
  26. package/dist/dev-session.js +4 -14
  27. package/dist/dev-session.js.map +1 -1
  28. package/dist/dev.d.ts +7 -17
  29. package/dist/dev.d.ts.map +1 -1
  30. package/dist/dev.js +13 -1
  31. package/dist/dev.js.map +1 -1
  32. package/dist/draft.d.ts +8 -5
  33. package/dist/draft.d.ts.map +1 -1
  34. package/dist/draft.js +30 -13
  35. package/dist/draft.js.map +1 -1
  36. package/dist/fonts-google.d.ts.map +1 -1
  37. package/dist/fonts-google.js +7 -0
  38. package/dist/fonts-google.js.map +1 -1
  39. package/dist/index.d.ts +2 -2
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +2 -2
  42. package/dist/index.js.map +1 -1
  43. package/dist/internal/codegen.d.ts +1 -0
  44. package/dist/internal/codegen.d.ts.map +1 -1
  45. package/dist/internal/codegen.js +56 -29
  46. package/dist/internal/codegen.js.map +1 -1
  47. package/dist/internal/disposable-dir.d.ts +8 -0
  48. package/dist/internal/disposable-dir.d.ts.map +1 -0
  49. package/dist/internal/disposable-dir.js +25 -0
  50. package/dist/internal/disposable-dir.js.map +1 -0
  51. package/dist/internal/entry-chunks.d.ts +9 -0
  52. package/dist/internal/entry-chunks.d.ts.map +1 -0
  53. package/dist/internal/entry-chunks.js +15 -0
  54. package/dist/internal/entry-chunks.js.map +1 -0
  55. package/dist/internal/evidence-traversal.d.ts +12 -0
  56. package/dist/internal/evidence-traversal.d.ts.map +1 -0
  57. package/dist/internal/evidence-traversal.js +48 -0
  58. package/dist/internal/evidence-traversal.js.map +1 -0
  59. package/dist/internal/head-merge.d.ts +6 -0
  60. package/dist/internal/head-merge.d.ts.map +1 -1
  61. package/dist/internal/head-merge.js +9 -0
  62. package/dist/internal/head-merge.js.map +1 -1
  63. package/dist/internal/js-string.d.ts +2 -0
  64. package/dist/internal/js-string.d.ts.map +1 -1
  65. package/dist/internal/js-string.js +5 -1
  66. package/dist/internal/js-string.js.map +1 -1
  67. package/dist/internal/mount-shadow.js +1 -1
  68. package/dist/internal/mount-shadow.js.map +1 -1
  69. package/dist/internal/output-guard.d.ts +7 -0
  70. package/dist/internal/output-guard.d.ts.map +1 -1
  71. package/dist/internal/output-guard.js +80 -8
  72. package/dist/internal/output-guard.js.map +1 -1
  73. package/dist/internal/page-execution.d.ts +6 -0
  74. package/dist/internal/page-execution.d.ts.map +1 -1
  75. package/dist/internal/page-execution.js +377 -108
  76. package/dist/internal/page-execution.js.map +1 -1
  77. package/dist/internal/progressive-cookies.d.ts +14 -0
  78. package/dist/internal/progressive-cookies.d.ts.map +1 -0
  79. package/dist/internal/progressive-cookies.js +43 -0
  80. package/dist/internal/progressive-cookies.js.map +1 -0
  81. package/dist/internal/public-env.d.ts +2 -0
  82. package/dist/internal/public-env.d.ts.map +1 -0
  83. package/dist/internal/public-env.js +13 -0
  84. package/dist/internal/public-env.js.map +1 -0
  85. package/dist/internal/render-document.d.ts +1 -0
  86. package/dist/internal/render-document.d.ts.map +1 -1
  87. package/dist/internal/render-document.js +28 -5
  88. package/dist/internal/render-document.js.map +1 -1
  89. package/dist/internal/response-controls.d.ts +2 -0
  90. package/dist/internal/response-controls.d.ts.map +1 -1
  91. package/dist/internal/response-controls.js +26 -16
  92. package/dist/internal/response-controls.js.map +1 -1
  93. package/dist/internal/revive-dates.d.ts +16 -0
  94. package/dist/internal/revive-dates.d.ts.map +1 -0
  95. package/dist/internal/revive-dates.js +32 -0
  96. package/dist/internal/revive-dates.js.map +1 -0
  97. package/dist/internal/route-path.d.ts +8 -0
  98. package/dist/internal/route-path.d.ts.map +1 -0
  99. package/dist/internal/route-path.js +8 -0
  100. package/dist/internal/route-path.js.map +1 -0
  101. package/dist/internal/secret-scan.d.ts.map +1 -1
  102. package/dist/internal/secret-scan.js +88 -20
  103. package/dist/internal/secret-scan.js.map +1 -1
  104. package/dist/internal/server-boundary.d.ts.map +1 -1
  105. package/dist/internal/server-boundary.js +33 -5
  106. package/dist/internal/server-boundary.js.map +1 -1
  107. package/dist/internal/web-app.d.ts +21 -16
  108. package/dist/internal/web-app.d.ts.map +1 -1
  109. package/dist/internal/web-app.js +73 -21
  110. package/dist/internal/web-app.js.map +1 -1
  111. package/dist/internal/wire-date.d.ts +3 -0
  112. package/dist/internal/wire-date.d.ts.map +1 -0
  113. package/dist/internal/wire-date.js +14 -0
  114. package/dist/internal/wire-date.js.map +1 -0
  115. package/dist/internal/zone-graph.d.ts +4 -0
  116. package/dist/internal/zone-graph.d.ts.map +1 -1
  117. package/dist/internal/zone-graph.js +54 -14
  118. package/dist/internal/zone-graph.js.map +1 -1
  119. package/dist/isr.d.ts +58 -2
  120. package/dist/isr.d.ts.map +1 -1
  121. package/dist/isr.js +296 -39
  122. package/dist/isr.js.map +1 -1
  123. package/dist/llms-txt.d.ts +2 -0
  124. package/dist/llms-txt.d.ts.map +1 -1
  125. package/dist/llms-txt.js +29 -3
  126. package/dist/llms-txt.js.map +1 -1
  127. package/dist/manifest.d.ts +28 -4
  128. package/dist/manifest.d.ts.map +1 -1
  129. package/dist/manifest.js +3 -0
  130. package/dist/manifest.js.map +1 -1
  131. package/dist/open-resolved.d.ts +18 -0
  132. package/dist/open-resolved.d.ts.map +1 -0
  133. package/dist/open-resolved.js +62 -0
  134. package/dist/open-resolved.js.map +1 -0
  135. package/dist/plugins/kit.d.ts +1 -0
  136. package/dist/plugins/kit.d.ts.map +1 -1
  137. package/dist/plugins/kit.js +26 -11
  138. package/dist/plugins/kit.js.map +1 -1
  139. package/dist/plugins/vite-leak-guard.d.ts +3 -1
  140. package/dist/plugins/vite-leak-guard.d.ts.map +1 -1
  141. package/dist/plugins/vite-leak-guard.js +4 -0
  142. package/dist/plugins/vite-leak-guard.js.map +1 -1
  143. package/dist/public-dir.d.ts +8 -1
  144. package/dist/public-dir.d.ts.map +1 -1
  145. package/dist/public-dir.js +17 -6
  146. package/dist/public-dir.js.map +1 -1
  147. package/dist/query.d.ts +13 -4
  148. package/dist/query.d.ts.map +1 -1
  149. package/dist/query.js +89 -26
  150. package/dist/query.js.map +1 -1
  151. package/dist/render-seam.d.ts +2 -0
  152. package/dist/render-seam.d.ts.map +1 -1
  153. package/dist/render-seam.js +1 -0
  154. package/dist/render-seam.js.map +1 -1
  155. package/dist/route-types.d.ts.map +1 -1
  156. package/dist/route-types.js +20 -9
  157. package/dist/route-types.js.map +1 -1
  158. package/dist/router.d.ts +9 -2
  159. package/dist/router.d.ts.map +1 -1
  160. package/dist/router.js +39 -46
  161. package/dist/router.js.map +1 -1
  162. package/dist/server-components/build-info.d.ts +3 -0
  163. package/dist/server-components/build-info.d.ts.map +1 -0
  164. package/dist/server-components/build-info.js +7 -0
  165. package/dist/server-components/build-info.js.map +1 -0
  166. package/dist/server-components/build-target.d.ts +12 -0
  167. package/dist/server-components/build-target.d.ts.map +1 -0
  168. package/dist/server-components/build-target.js +153 -0
  169. package/dist/server-components/build-target.js.map +1 -0
  170. package/dist/server-components/build.d.ts +24 -0
  171. package/dist/server-components/build.d.ts.map +1 -0
  172. package/dist/server-components/build.js +366 -0
  173. package/dist/server-components/build.js.map +1 -0
  174. package/dist/server-components/bundle-proof.d.ts +3 -0
  175. package/dist/server-components/bundle-proof.d.ts.map +1 -0
  176. package/dist/server-components/bundle-proof.js +23 -0
  177. package/dist/server-components/bundle-proof.js.map +1 -0
  178. package/dist/server-components/client.d.ts +46 -0
  179. package/dist/server-components/client.d.ts.map +1 -0
  180. package/dist/server-components/client.js +594 -0
  181. package/dist/server-components/client.js.map +1 -0
  182. package/dist/server-components/definition.d.ts +11 -0
  183. package/dist/server-components/definition.d.ts.map +1 -0
  184. package/dist/server-components/definition.js +94 -0
  185. package/dist/server-components/definition.js.map +1 -0
  186. package/dist/server-components/dev-http.d.ts +5 -0
  187. package/dist/server-components/dev-http.d.ts.map +1 -0
  188. package/dist/server-components/dev-http.js +71 -0
  189. package/dist/server-components/dev-http.js.map +1 -0
  190. package/dist/server-components/error.d.ts +9 -0
  191. package/dist/server-components/error.d.ts.map +1 -0
  192. package/dist/server-components/error.js +24 -0
  193. package/dist/server-components/error.js.map +1 -0
  194. package/dist/server-components/frames.d.ts +7 -0
  195. package/dist/server-components/frames.d.ts.map +1 -0
  196. package/dist/server-components/frames.js +169 -0
  197. package/dist/server-components/frames.js.map +1 -0
  198. package/dist/server-components/html-policy.d.ts +6 -0
  199. package/dist/server-components/html-policy.d.ts.map +1 -0
  200. package/dist/server-components/html-policy.js +75 -0
  201. package/dist/server-components/html-policy.js.map +1 -0
  202. package/dist/server-components/html.d.ts +4 -0
  203. package/dist/server-components/html.d.ts.map +1 -0
  204. package/dist/server-components/html.js +67 -0
  205. package/dist/server-components/html.js.map +1 -0
  206. package/dist/server-components/islands.d.ts +44 -0
  207. package/dist/server-components/islands.d.ts.map +1 -0
  208. package/dist/server-components/islands.js +42 -0
  209. package/dist/server-components/islands.js.map +1 -0
  210. package/dist/server-components/native.d.ts +4 -0
  211. package/dist/server-components/native.d.ts.map +1 -0
  212. package/dist/server-components/native.js +81 -0
  213. package/dist/server-components/native.js.map +1 -0
  214. package/dist/server-components/portal.d.ts +6 -0
  215. package/dist/server-components/portal.d.ts.map +1 -0
  216. package/dist/server-components/portal.js +35 -0
  217. package/dist/server-components/portal.js.map +1 -0
  218. package/dist/server-components/props.d.ts +3 -0
  219. package/dist/server-components/props.d.ts.map +1 -0
  220. package/dist/server-components/props.js +14 -0
  221. package/dist/server-components/props.js.map +1 -0
  222. package/dist/server-components/protocol.d.ts +44 -0
  223. package/dist/server-components/protocol.d.ts.map +1 -0
  224. package/dist/server-components/protocol.js +139 -0
  225. package/dist/server-components/protocol.js.map +1 -0
  226. package/dist/server-components/range.d.ts +24 -0
  227. package/dist/server-components/range.d.ts.map +1 -0
  228. package/dist/server-components/range.js +263 -0
  229. package/dist/server-components/range.js.map +1 -0
  230. package/dist/server-components/registry.d.ts +6 -0
  231. package/dist/server-components/registry.d.ts.map +1 -0
  232. package/dist/server-components/registry.js +30 -0
  233. package/dist/server-components/registry.js.map +1 -0
  234. package/dist/server-components/response.d.ts +4 -0
  235. package/dist/server-components/response.d.ts.map +1 -0
  236. package/dist/server-components/response.js +32 -0
  237. package/dist/server-components/response.js.map +1 -0
  238. package/dist/server-components/router-integration.d.ts +9 -0
  239. package/dist/server-components/router-integration.d.ts.map +1 -0
  240. package/dist/server-components/router-integration.js +29 -0
  241. package/dist/server-components/router-integration.js.map +1 -0
  242. package/dist/server-components/runtime.d.ts +14 -0
  243. package/dist/server-components/runtime.d.ts.map +1 -0
  244. package/dist/server-components/runtime.js +276 -0
  245. package/dist/server-components/runtime.js.map +1 -0
  246. package/dist/server-components/scheduler.d.ts +14 -0
  247. package/dist/server-components/scheduler.d.ts.map +1 -0
  248. package/dist/server-components/scheduler.js +74 -0
  249. package/dist/server-components/scheduler.js.map +1 -0
  250. package/dist/server-components/schema.d.ts +4 -0
  251. package/dist/server-components/schema.d.ts.map +1 -0
  252. package/dist/server-components/schema.js +77 -0
  253. package/dist/server-components/schema.js.map +1 -0
  254. package/dist/server-components/server-entry.d.ts +12 -0
  255. package/dist/server-components/server-entry.d.ts.map +1 -0
  256. package/dist/server-components/server-entry.js +90 -0
  257. package/dist/server-components/server-entry.js.map +1 -0
  258. package/dist/server-components/source.d.ts +15 -0
  259. package/dist/server-components/source.d.ts.map +1 -0
  260. package/dist/server-components/source.js +269 -0
  261. package/dist/server-components/source.js.map +1 -0
  262. package/dist/server-components/state.d.ts +4 -0
  263. package/dist/server-components/state.d.ts.map +1 -0
  264. package/dist/server-components/state.js +123 -0
  265. package/dist/server-components/state.js.map +1 -0
  266. package/dist/server-components/stream.d.ts +6 -0
  267. package/dist/server-components/stream.d.ts.map +1 -0
  268. package/dist/server-components/stream.js +70 -0
  269. package/dist/server-components/stream.js.map +1 -0
  270. package/dist/server-components/trusted-html.d.ts +3 -0
  271. package/dist/server-components/trusted-html.d.ts.map +1 -0
  272. package/dist/server-components/trusted-html.js +14 -0
  273. package/dist/server-components/trusted-html.js.map +1 -0
  274. package/dist/server-components/types.d.ts +107 -0
  275. package/dist/server-components/types.d.ts.map +1 -0
  276. package/dist/server-components/types.js +2 -0
  277. package/dist/server-components/types.js.map +1 -0
  278. package/dist/server-components/typescript.d.ts +11 -0
  279. package/dist/server-components/typescript.d.ts.map +1 -0
  280. package/dist/server-components/typescript.js +28 -0
  281. package/dist/server-components/typescript.js.map +1 -0
  282. package/dist/server-components/value.d.ts +5 -0
  283. package/dist/server-components/value.d.ts.map +1 -0
  284. package/dist/server-components/value.js +77 -0
  285. package/dist/server-components/value.js.map +1 -0
  286. package/dist/server-components/vite.d.ts +12 -0
  287. package/dist/server-components/vite.d.ts.map +1 -0
  288. package/dist/server-components/vite.js +197 -0
  289. package/dist/server-components/vite.js.map +1 -0
  290. package/dist/server-components.d.ts +5 -0
  291. package/dist/server-components.d.ts.map +1 -0
  292. package/dist/server-components.js +4 -0
  293. package/dist/server-components.js.map +1 -0
  294. package/dist/service-worker.d.ts +8 -1
  295. package/dist/service-worker.d.ts.map +1 -1
  296. package/dist/service-worker.js +20 -6
  297. package/dist/service-worker.js.map +1 -1
  298. package/dist/vite.d.ts +12 -0
  299. package/dist/vite.d.ts.map +1 -1
  300. package/dist/vite.js +7 -1
  301. package/dist/vite.js.map +1 -1
  302. package/dist/zones.d.ts +40 -0
  303. package/dist/zones.d.ts.map +1 -1
  304. package/dist/zones.js +170 -6
  305. package/dist/zones.js.map +1 -1
  306. package/package.json +97 -4
  307. package/src/build-vite.ts +31 -9
  308. package/src/build.ts +109 -31
  309. package/src/cdn.ts +18 -12
  310. package/src/client.ts +25 -6
  311. package/src/deferred.ts +27 -9
  312. package/src/dev-feed.ts +24 -5
  313. package/src/dev-session.ts +4 -13
  314. package/src/dev.ts +29 -1
  315. package/src/draft.ts +35 -14
  316. package/src/fonts-google.ts +10 -0
  317. package/src/index.ts +3 -2
  318. package/src/internal/codegen.ts +60 -29
  319. package/src/internal/disposable-dir.ts +24 -0
  320. package/src/internal/entry-chunks.ts +18 -0
  321. package/src/internal/evidence-traversal.ts +57 -0
  322. package/src/internal/head-merge.ts +16 -0
  323. package/src/internal/js-string.ts +6 -1
  324. package/src/internal/mount-shadow.ts +1 -1
  325. package/src/internal/output-guard.ts +88 -7
  326. package/src/internal/page-execution.ts +506 -108
  327. package/src/internal/progressive-cookies.ts +60 -0
  328. package/src/internal/public-env.ts +14 -0
  329. package/src/internal/render-document.ts +26 -11
  330. package/src/internal/response-controls.ts +31 -14
  331. package/src/internal/revive-dates.ts +35 -0
  332. package/src/internal/route-path.ts +8 -0
  333. package/src/internal/secret-scan.ts +90 -18
  334. package/src/internal/server-boundary.ts +29 -5
  335. package/src/internal/web-app.ts +116 -37
  336. package/src/internal/wire-date.ts +16 -0
  337. package/src/internal/zone-graph.ts +59 -19
  338. package/src/isr.ts +351 -43
  339. package/src/llms-txt.ts +32 -3
  340. package/src/manifest.ts +28 -4
  341. package/src/open-resolved.ts +64 -0
  342. package/src/plugins/kit.ts +31 -11
  343. package/src/plugins/vite-leak-guard.ts +5 -1
  344. package/src/public-dir.ts +27 -8
  345. package/src/query.ts +107 -26
  346. package/src/render-seam.ts +2 -0
  347. package/src/route-types.ts +34 -7
  348. package/src/router.ts +58 -55
  349. package/src/server-components/build-info.ts +11 -0
  350. package/src/server-components/build-target.ts +194 -0
  351. package/src/server-components/build.ts +419 -0
  352. package/src/server-components/bundle-proof.ts +33 -0
  353. package/src/server-components/client.ts +663 -0
  354. package/src/server-components/definition.ts +125 -0
  355. package/src/server-components/dev-http.ts +75 -0
  356. package/src/server-components/error.ts +24 -0
  357. package/src/server-components/frames.ts +181 -0
  358. package/src/server-components/html-policy.ts +94 -0
  359. package/src/server-components/html.ts +81 -0
  360. package/src/server-components/islands.ts +111 -0
  361. package/src/server-components/native.ts +101 -0
  362. package/src/server-components/portal.ts +42 -0
  363. package/src/server-components/props.ts +19 -0
  364. package/src/server-components/protocol.ts +167 -0
  365. package/src/server-components/range.ts +313 -0
  366. package/src/server-components/registry.ts +34 -0
  367. package/src/server-components/response.ts +44 -0
  368. package/src/server-components/router-integration.ts +40 -0
  369. package/src/server-components/runtime.ts +376 -0
  370. package/src/server-components/scheduler.ts +84 -0
  371. package/src/server-components/schema.ts +69 -0
  372. package/src/server-components/server-entry.ts +98 -0
  373. package/src/server-components/source.ts +324 -0
  374. package/src/server-components/state.ts +143 -0
  375. package/src/server-components/stream.ts +71 -0
  376. package/src/server-components/trusted-html.ts +21 -0
  377. package/src/server-components/types.ts +138 -0
  378. package/src/server-components/typescript.ts +39 -0
  379. package/src/server-components/value.ts +82 -0
  380. package/src/server-components/vite.ts +242 -0
  381. package/src/server-components.ts +32 -0
  382. package/src/service-worker.ts +24 -7
  383. package/src/vite.ts +8 -1
  384. package/src/zones.ts +208 -7
@@ -0,0 +1,663 @@
1
+ import { ServerComponentError } from "./error.ts"
2
+ import { readComponentFrames } from "./frames.ts"
3
+ import {
4
+ COMPONENT_HEADER,
5
+ COMPONENT_LIMITS,
6
+ COMPONENT_MEDIA_TYPE,
7
+ componentRequestHeader,
8
+ } from "./protocol.ts"
9
+ import { registerComponentController, unregisterComponentController } from "./registry.ts"
10
+ import { componentResponseFailure } from "./response.ts"
11
+ import { BoundedComponentTasks } from "./scheduler.ts"
12
+ import { parseComponentState } from "./state.ts"
13
+ import { readComponentBytes } from "./stream.ts"
14
+ import type { ComponentKey, ComponentRecord, ComponentState, ServerComponent } from "./types.ts"
15
+ import { componentInputKey, componentValue } from "./value.ts"
16
+
17
+ declare const __NIFRA_COMPONENT_BUILD__: string
18
+
19
+ export type CurrentComponent = () => boolean
20
+ export interface ComponentClientRoot {
21
+ update(record: ComponentRecord, current: CurrentComponent): void | Promise<void>
22
+ dispose(): void | Promise<void>
23
+ }
24
+ export interface ComponentClientAdapter {
25
+ readonly name: ComponentState["adapter"]
26
+ prepare(
27
+ record: ComponentRecord,
28
+ current: CurrentComponent,
29
+ host?: Element,
30
+ context?: { readonly records: readonly ComponentRecord[] },
31
+ ): unknown | Promise<unknown>
32
+ hydrate(
33
+ host: Element,
34
+ record: ComponentRecord,
35
+ prepared: unknown,
36
+ current: CurrentComponent,
37
+ ): ComponentClientRoot | Promise<ComponentClientRoot>
38
+ create?(
39
+ host: Element,
40
+ record: ComponentRecord,
41
+ prepared: unknown,
42
+ current: CurrentComponent,
43
+ ): ComponentClientRoot | Promise<ComponentClientRoot>
44
+ update(
45
+ root: ComponentClientRoot,
46
+ record: ComponentRecord,
47
+ prepared: unknown,
48
+ current: CurrentComponent,
49
+ ): void | Promise<void>
50
+ }
51
+ export interface ComponentControllerOptions {
52
+ readonly state: ComponentState
53
+ readonly adapter: ComponentClientAdapter
54
+ readonly ownerUrl?: string
55
+ readonly fetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>
56
+ readonly navigate?: (url: string) => void
57
+ readonly onError?: (code: string) => void
58
+ readonly reload?: () => void
59
+ }
60
+ interface Mounted {
61
+ readonly host: Element
62
+ record: ComponentRecord
63
+ generation: number
64
+ abort: AbortController
65
+ root?: ComponentClientRoot
66
+ ready: Promise<void>
67
+ }
68
+ function sameRecord(a: ComponentRecord, b: ComponentRecord): boolean {
69
+ return (
70
+ a.instance === b.instance &&
71
+ a.reference === b.reference &&
72
+ a.key === b.key &&
73
+ a.parent === b.parent &&
74
+ componentInputKey(a.input) === componentInputKey(b.input)
75
+ )
76
+ }
77
+ function cancelled(): ServerComponentError {
78
+ return new ServerComponentError("component_cancelled", 499)
79
+ }
80
+
81
+ export class ComponentController {
82
+ #state: ComponentState
83
+ #source: ComponentState | undefined
84
+ #ownerUrl: string | undefined
85
+ get state(): ComponentState {
86
+ return this.#state
87
+ }
88
+ reportError(code: string): void {
89
+ this.#options.onError?.(code)
90
+ }
91
+ readonly #options: ComponentControllerOptions
92
+ readonly #mounted = new Map<string, Mounted>()
93
+ // Structural outlet anchors survive logout; their private records and native roots do not.
94
+ readonly #restorable = new Map<string, Element>()
95
+ readonly #teardown = new WeakMap<Element, Promise<void>>()
96
+ readonly #mountWaiters = new Map<string, Set<() => void>>()
97
+ readonly #tasks = new BoundedComponentTasks(4, COMPONENT_LIMITS.instances)
98
+ #routeEpoch = 0
99
+ #authEpoch = 0
100
+ #adoption = 0
101
+ #disposed = false
102
+ constructor(options: ComponentControllerOptions) {
103
+ this.#state = parseComponentState(options.state)
104
+ this.#options = options
105
+ this.#source = options.state
106
+ this.#ownerUrl = options.ownerUrl
107
+ if (this.state.adapter !== options.adapter.name)
108
+ throw new ServerComponentError("component_version", 409)
109
+ if (
110
+ typeof __NIFRA_COMPONENT_BUILD__ === "string" &&
111
+ this.state.build !== __NIFRA_COMPONENT_BUILD__
112
+ ) {
113
+ this.#versionReload()
114
+ this.#state = { ...this.#state, records: [] }
115
+ this.#source = undefined
116
+ throw new ServerComponentError("component_version", 409)
117
+ }
118
+ registerComponentController(this)
119
+ }
120
+ async mount(host: Element, instance: string, fresh = false): Promise<void> {
121
+ await this.#mount(host, instance, fresh)
122
+ }
123
+ async #mount(host: Element, instance: string, fresh: boolean): Promise<void> {
124
+ await this.#teardown.get(host)
125
+ if (this.#disposed || this.#mounted.has(instance))
126
+ throw new ServerComponentError("component_contract")
127
+ const record = this.state.records.find((record) => record.instance === instance)
128
+ if (record === undefined) throw new ServerComponentError("component_unavailable", 404)
129
+ const mounted: Mounted = {
130
+ host,
131
+ record,
132
+ generation: 0,
133
+ abort: new AbortController(),
134
+ ready: Promise.resolve(),
135
+ }
136
+ this.#mounted.set(instance, mounted)
137
+ this.#restorable.delete(instance)
138
+ const signal = AbortSignal.any([
139
+ mounted.abort.signal,
140
+ AbortSignal.timeout(COMPONENT_LIMITS.operationMs),
141
+ ])
142
+ const valid = this.#current(mounted)
143
+ const current = () => valid() && !signal.aborted
144
+ mounted.ready = this.#tasks
145
+ .run(signal, async () => {
146
+ const prepared = await this.#options.adapter.prepare(record, current, host, this.state)
147
+ if (!current()) throw cancelled()
148
+ const mount = fresh ? this.#options.adapter.create : this.#options.adapter.hydrate
149
+ if (mount === undefined) throw new ServerComponentError("component_contract")
150
+ const root = await mount.call(this.#options.adapter, host, record, prepared, current)
151
+ if (!current()) {
152
+ await root.dispose()
153
+ throw cancelled()
154
+ }
155
+ mounted.root = root
156
+ })
157
+ .then(async () => {
158
+ if (current()) await this.#syncChildren(mounted, this.#state.records, current)
159
+ })
160
+ for (const ready of this.#mountWaiters.get(instance) ?? []) ready()
161
+ this.#mountWaiters.delete(instance)
162
+ try {
163
+ await mounted.ready
164
+ } catch (error) {
165
+ if (this.#mounted.get(instance) === mounted) await this.unmount(instance)
166
+ throw error
167
+ }
168
+ }
169
+ async whenReady(instance: string): Promise<void> {
170
+ const mounted = this.#mounted.get(instance)
171
+ if (mounted !== undefined) {
172
+ await mounted.ready
173
+ return
174
+ }
175
+ if (this.#disposed || !this.state.records.some((record) => record.instance === instance))
176
+ throw new ServerComponentError("component_unavailable", 404)
177
+ await new Promise<void>((resolve, reject) => {
178
+ const waiters = this.#mountWaiters.get(instance) ?? new Set<() => void>()
179
+ const ready = () => {
180
+ clearTimeout(timer)
181
+ waiters.delete(ready)
182
+ const mounted = this.#mounted.get(instance)
183
+ if (mounted === undefined) reject(cancelled())
184
+ else mounted.ready.then(resolve, reject)
185
+ }
186
+ const timer = setTimeout(() => {
187
+ waiters.delete(ready)
188
+ reject(new ServerComponentError("component_timeout", 504))
189
+ }, COMPONENT_LIMITS.operationMs)
190
+ waiters.add(ready)
191
+ this.#mountWaiters.set(instance, waiters)
192
+ })
193
+ }
194
+ #current(mounted: Mounted): CurrentComponent {
195
+ const generation = mounted.generation
196
+ const route = this.#routeEpoch
197
+ const auth = this.#authEpoch
198
+ return () =>
199
+ !this.#disposed &&
200
+ !mounted.abort.signal.aborted &&
201
+ this.#mounted.get(mounted.record.instance) === mounted &&
202
+ mounted.generation === generation &&
203
+ this.#routeEpoch === route &&
204
+ this.#authEpoch === auth
205
+ }
206
+ async refresh(instance: string, ownerUrl?: string): Promise<boolean> {
207
+ const mounted = this.#mounted.get(instance)
208
+ if (mounted === undefined || this.#disposed) return false
209
+ try {
210
+ await mounted.ready
211
+ } catch {
212
+ return false
213
+ }
214
+ if (this.#mounted.get(instance) !== mounted || this.#disposed) return false
215
+ // A newer child intent must not be overwritten by an older in-flight ancestor subtree.
216
+ let parent = mounted.record.parent
217
+ while (parent !== null) {
218
+ const ancestor = this.#mounted.get(parent)
219
+ if (ancestor === undefined) break
220
+ ancestor.abort.abort()
221
+ ancestor.abort = new AbortController()
222
+ ancestor.generation++
223
+ parent = ancestor.record.parent
224
+ }
225
+ mounted.abort.abort()
226
+ mounted.abort = new AbortController()
227
+ mounted.generation++
228
+ for (const child of this.#descendants(instance)) {
229
+ child.abort.abort()
230
+ child.abort = new AbortController()
231
+ child.generation++
232
+ }
233
+ const valid = this.#current(mounted)
234
+ const request = {
235
+ v: 1 as const,
236
+ build: this.state.build,
237
+ reference: mounted.record.reference,
238
+ key: mounted.record.key,
239
+ parent: mounted.record.parent,
240
+ input: mounted.record.input,
241
+ generation: mounted.generation,
242
+ routeEpoch: this.#routeEpoch,
243
+ authEpoch: this.#authEpoch,
244
+ }
245
+ const owner = new URL(ownerUrl ?? this.#ownerUrl ?? location.href, location.href)
246
+ if (owner.origin !== location.origin) throw new ServerComponentError("component_denied", 403)
247
+ const signal = AbortSignal.any([
248
+ mounted.abort.signal,
249
+ AbortSignal.timeout(COMPONENT_LIMITS.operationMs),
250
+ ])
251
+ const current = () => valid() && !signal.aborted
252
+ let updating = false
253
+ try {
254
+ const committed = await this.#tasks.run(signal, async () => {
255
+ const response = await (this.#options.fetch ?? fetch)(owner, {
256
+ method: "GET",
257
+ credentials: "same-origin",
258
+ cache: "no-store",
259
+ redirect: "manual",
260
+ signal,
261
+ headers: {
262
+ [COMPONENT_HEADER]: componentRequestHeader(request),
263
+ accept: COMPONENT_MEDIA_TYPE,
264
+ },
265
+ })
266
+ if (!current()) return false
267
+ if (
268
+ response.type === "opaqueredirect" ||
269
+ (response.status >= 300 && response.status < 400)
270
+ ) {
271
+ this.#options.navigate?.(owner.href)
272
+ if (this.#options.navigate === undefined) location.assign(owner.href)
273
+ return false
274
+ }
275
+ if (!response.ok) {
276
+ if ([401, 403, 404].includes(response.status)) {
277
+ this.#options.onError?.("component_denied")
278
+ await this.invalidateScope()
279
+ return false
280
+ }
281
+ const failure = await componentResponseFailure(response, signal)
282
+ if (failure.code === "component_version") {
283
+ this.#versionReload()
284
+ return false
285
+ }
286
+ throw failure
287
+ }
288
+ if (response.headers.has("x-nifra-component-control")) {
289
+ if (response.body === null) throw new ServerComponentError("component_protocol", 400)
290
+ let value: unknown
291
+ try {
292
+ value = JSON.parse(
293
+ new TextDecoder("utf-8", { fatal: true }).decode(
294
+ await readComponentBytes(response.body, signal, COMPONENT_LIMITS.headerBytes),
295
+ ),
296
+ )
297
+ } catch {
298
+ throw new ServerComponentError("component_protocol", 400)
299
+ }
300
+ const checked = componentValue(value, COMPONENT_LIMITS.headerBytes)
301
+ if (typeof checked !== "object" || checked === null || Array.isArray(checked))
302
+ throw new ServerComponentError("component_protocol", 400)
303
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: Only this adapter produces its prepared tree and native root; component records and public props are validated before native preparation.
304
+ const control = checked as Record<string, unknown>
305
+ if (
306
+ Object.keys(control).length !== 2 ||
307
+ control.v !== 1 ||
308
+ typeof control.control !== "object" ||
309
+ control.control === null ||
310
+ Array.isArray(control.control)
311
+ )
312
+ throw new ServerComponentError("component_protocol", 400)
313
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: Only this adapter produces its prepared tree and native root; component records and public props are validated before native preparation.
314
+ const terminal = control.control as Record<string, unknown>
315
+ if (
316
+ Object.keys(terminal).length !== 1 ||
317
+ typeof terminal.status !== "number" ||
318
+ !Number.isSafeInteger(terminal.status) ||
319
+ terminal.status < 200 ||
320
+ terminal.status > 299 ||
321
+ response.headers.get("x-nifra-component-control") !== String(terminal.status)
322
+ )
323
+ throw new ServerComponentError("component_protocol", 400)
324
+ if (current()) {
325
+ this.#options.navigate?.(owner.href)
326
+ if (this.#options.navigate === undefined) location.assign(owner.href)
327
+ }
328
+ return false
329
+ }
330
+ if (
331
+ response.headers.get("content-type")?.split(";")[0]?.trim() !== COMPONENT_MEDIA_TYPE ||
332
+ response.body === null
333
+ )
334
+ throw new ServerComponentError("component_protocol", 400)
335
+ const decoded = await readComponentFrames(response.body, signal)
336
+ const next = parseComponentState(
337
+ decoded.value,
338
+ COMPONENT_LIMITS.outputBytes,
339
+ mounted.record.parent,
340
+ )
341
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: Only this adapter produces its prepared tree and native root; component records and public props are validated before native preparation.
342
+ const meta = componentValue(decoded.metadata, COMPONENT_LIMITS.headerBytes) as Record<
343
+ string,
344
+ unknown
345
+ >
346
+ const expected = {
347
+ ...request,
348
+ input: undefined,
349
+ adapter: next.adapter,
350
+ owner: next.owner,
351
+ scope: next.scope,
352
+ instance: next.records[0]?.instance,
353
+ }
354
+ const keys = Object.keys(expected).filter((key) => key !== "input")
355
+ if (
356
+ typeof meta !== "object" ||
357
+ meta === null ||
358
+ Array.isArray(meta) ||
359
+ Object.keys(meta).length !== keys.length ||
360
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: Only this adapter produces its prepared tree and native root; component records and public props are validated before native preparation.
361
+ keys.some((key) => meta[key] !== expected[key as keyof typeof expected]) ||
362
+ next.build !== this.state.build ||
363
+ next.adapter !== this.state.adapter ||
364
+ next.owner !== this.state.owner ||
365
+ next.records.length < 1 ||
366
+ !sameRecord(mounted.record, next.records[0]!)
367
+ )
368
+ throw new ServerComponentError("component_protocol", 400)
369
+ const incoming = new Map(next.records.map((record) => [record.instance, record]))
370
+ for (const record of next.records.slice(1)) {
371
+ let parent = record.parent
372
+ let depth = 0
373
+ while (parent !== instance) {
374
+ if (parent === null || ++depth >= 16 || !incoming.has(parent))
375
+ throw new ServerComponentError("component_protocol", 400)
376
+ parent = incoming.get(parent)!.parent
377
+ }
378
+ }
379
+ if (next.scope !== this.state.scope) {
380
+ this.#options.onError?.("component_denied")
381
+ await this.invalidateScope()
382
+ return false
383
+ }
384
+ if (!current()) return false
385
+ const record = next.records[0]!
386
+ const prepared = await this.#options.adapter.prepare(record, current, mounted.host, next)
387
+ if (!current() || signal.aborted) return false
388
+ if (mounted.root === undefined) throw new ServerComponentError("component_contract")
389
+ updating = true
390
+ await this.#options.adapter.update(mounted.root, record, prepared, current)
391
+ if (!current()) return false
392
+ const replaced = recordSubtree(this.#state.records, instance)
393
+ this.#state = {
394
+ ...this.#state,
395
+ records: [
396
+ ...this.#state.records.filter((item) => !replaced.has(item.instance)),
397
+ ...next.records,
398
+ ],
399
+ }
400
+ mounted.record = record
401
+ return true
402
+ })
403
+ if (committed && current()) await this.#syncChildren(mounted, this.#state.records, current)
404
+ return committed && current()
405
+ } catch (error) {
406
+ if (!current() || signal.aborted) return false
407
+ const failure =
408
+ error instanceof ServerComponentError ? error : new ServerComponentError("component_failed")
409
+ this.#options.onError?.(failure.code)
410
+ if (updating) await this.unmount(instance)
411
+ throw failure
412
+ }
413
+ }
414
+ #versionReload(): void {
415
+ const key = `nifra-component-reload:${this.state.build}`
416
+ let used = false
417
+ try {
418
+ used = sessionStorage.getItem(key) === "1"
419
+ if (!used) sessionStorage.setItem(key, "1")
420
+ } catch {
421
+ used = reloadedBuilds.has(this.state.build)
422
+ }
423
+ reloadedBuilds.add(this.state.build)
424
+ if (used) {
425
+ this.#options.onError?.("component_version")
426
+ return
427
+ }
428
+ this.cancelPending()
429
+ if (this.#options.reload !== undefined) this.#options.reload()
430
+ else location.reload()
431
+ }
432
+ /** Abort networking immediately while retaining the tree until verified navigation arrives. */
433
+ cancelPending(): void {
434
+ this.#adoption++
435
+ this.#routeEpoch++
436
+ for (const mounted of this.#mounted.values()) {
437
+ mounted.abort.abort()
438
+ mounted.abort = new AbortController()
439
+ mounted.generation++
440
+ }
441
+ }
442
+ async adopt(state: ComponentState, ownerUrl?: string): Promise<void> {
443
+ if (ownerUrl !== undefined) this.#ownerUrl = ownerUrl
444
+ if (this.#disposed || state === this.#source) return
445
+ const next = parseComponentState(state)
446
+ if (next.build !== this.state.build || next.adapter !== this.state.adapter) {
447
+ this.#versionReload()
448
+ return
449
+ }
450
+ const targets = new Map(
451
+ [...this.#restorable].map(([id, host]) => [id, { host, mounted: this.#mounted.get(id) }]),
452
+ )
453
+ for (const mounted of this.#mounted.values())
454
+ if (mounted.record.parent === null || !this.#mounted.has(mounted.record.parent))
455
+ targets.set(mounted.record.instance, { host: mounted.host, mounted })
456
+ this.cancelPending()
457
+ const adoption = this.#adoption
458
+ const changedScope = next.owner !== this.state.owner || next.scope !== this.state.scope
459
+ if (changedScope) await this.#clearScope()
460
+ if (this.#disposed || adoption !== this.#adoption) return
461
+ this.#state = next
462
+ this.#source = state
463
+ this.#restorable.clear()
464
+ for (const [id, target] of targets) {
465
+ if (this.#disposed || adoption !== this.#adoption) return
466
+ const mounted = target.mounted
467
+ const record = next.records.find((item) => item.instance === id)
468
+ if (record === undefined) {
469
+ await this.unmount(id)
470
+ continue
471
+ }
472
+ if (mounted !== undefined && !changedScope && !sameRecord(mounted.record, record))
473
+ await this.unmount(id)
474
+ try {
475
+ await mounted?.ready
476
+ } catch {
477
+ /* Cancelled initial hydration owns its error and can retry from verified state. */
478
+ }
479
+ if (this.#disposed || adoption !== this.#adoption) return
480
+ if (!this.#mounted.has(id)) {
481
+ try {
482
+ await this.#mount(target.host, id, true)
483
+ } catch (error) {
484
+ if (adoption === this.#adoption) throw error
485
+ }
486
+ continue
487
+ }
488
+ if (mounted === undefined || this.#mounted.get(id) !== mounted) continue
489
+ const current = this.#current(mounted)
490
+ if (!current()) continue
491
+ const prepared = await this.#options.adapter.prepare(record, current, mounted.host, next)
492
+ if (!current() || mounted.root === undefined) continue
493
+ await this.#options.adapter.update(mounted.root, record, prepared, current)
494
+ if (current()) {
495
+ mounted.record = record
496
+ await this.#syncChildren(mounted, next.records, current)
497
+ }
498
+ }
499
+ }
500
+ #descendants(instance: string): Mounted[] {
501
+ const ids = new Set([instance])
502
+ const descendants: Mounted[] = []
503
+ for (let depth = 0; depth < 16; depth++)
504
+ for (const mounted of this.#mounted.values()) {
505
+ if (
506
+ mounted.record.parent !== null &&
507
+ ids.has(mounted.record.parent) &&
508
+ !ids.has(mounted.record.instance)
509
+ ) {
510
+ ids.add(mounted.record.instance)
511
+ descendants.push(mounted)
512
+ }
513
+ }
514
+ return descendants
515
+ }
516
+ async #syncChildren(
517
+ parent: Mounted,
518
+ records: readonly ComponentRecord[],
519
+ current: CurrentComponent,
520
+ ): Promise<void> {
521
+ if (typeof parent.host.querySelectorAll !== "function") return
522
+ const targets = new Map<string, Element>()
523
+ for (const host of parent.host.querySelectorAll("[data-nifra-component]")) {
524
+ if (host.parentElement?.closest("[data-nifra-component]") !== parent.host) continue
525
+ const id = host.getAttribute("data-nifra-component")
526
+ if (
527
+ id === null ||
528
+ targets.has(id) ||
529
+ !records.some(
530
+ (record) => record.instance === id && record.parent === parent.record.instance,
531
+ )
532
+ )
533
+ throw new ServerComponentError("component_contract")
534
+ targets.set(id, host)
535
+ }
536
+ for (const child of [...this.#mounted.values()].filter(
537
+ (item) => item.record.parent === parent.record.instance,
538
+ )) {
539
+ const record = records.find((item) => item.instance === child.record.instance)
540
+ if (
541
+ record === undefined ||
542
+ targets.get(child.record.instance) !== child.host ||
543
+ !sameRecord(child.record, record)
544
+ )
545
+ await this.unmount(child.record.instance)
546
+ }
547
+ for (const [id, host] of targets) {
548
+ if (!current()) return
549
+ const record = records.find((item) => item.instance === id)!
550
+ const child = this.#mounted.get(id)
551
+ if (child === undefined) {
552
+ await this.#mount(host, id, host.childNodes.length === 0)
553
+ continue
554
+ }
555
+ await child.ready
556
+ const valid = this.#current(child)
557
+ const prepared = await this.#options.adapter.prepare(
558
+ record,
559
+ () => current() && valid(),
560
+ host,
561
+ { records },
562
+ )
563
+ if (!current() || !valid() || child.root === undefined) return
564
+ await this.#options.adapter.update(child.root, record, prepared, () => current() && valid())
565
+ if (current() && valid()) {
566
+ child.record = record
567
+ await this.#syncChildren(child, records, () => current() && valid())
568
+ }
569
+ }
570
+ }
571
+ async unmount(instance: string): Promise<void> {
572
+ this.#restorable.delete(instance)
573
+ await this.#unmount(instance)
574
+ }
575
+ async #unmount(instance: string): Promise<void> {
576
+ const mounted = this.#mounted.get(instance)
577
+ if (mounted === undefined) return
578
+ this.#mounted.delete(instance)
579
+ mounted.abort.abort()
580
+ const children = [...this.#mounted.values()]
581
+ .filter((child) => child.record.parent === instance)
582
+ .map((child) => this.#unmount(child.record.instance))
583
+ const teardown = (async () => {
584
+ await Promise.all(children)
585
+ await mounted.ready.catch(() => {
586
+ /* Unmounted hydration cannot publish; its primary error belongs to mount. */
587
+ })
588
+ await mounted.root?.dispose()
589
+ })()
590
+ this.#teardown.set(mounted.host, teardown)
591
+ try {
592
+ await teardown
593
+ } finally {
594
+ if (this.#teardown.get(mounted.host) === teardown) this.#teardown.delete(mounted.host)
595
+ }
596
+ }
597
+ async #clearScope(preserveOutlets = false): Promise<void> {
598
+ this.#authEpoch++
599
+ if (preserveOutlets) {
600
+ for (const mounted of this.#mounted.values())
601
+ if (mounted.record.parent === null || !this.#mounted.has(mounted.record.parent))
602
+ this.#restorable.set(mounted.record.instance, mounted.host)
603
+ } else this.#restorable.clear()
604
+ this.#state = { ...this.#state, records: [] }
605
+ this.#source = undefined
606
+ await Promise.all([...this.#mounted.keys()].map((instance) => this.#unmount(instance)))
607
+ }
608
+ async invalidateScope(): Promise<void> {
609
+ this.#adoption++
610
+ await this.#clearScope(true)
611
+ }
612
+ async navigate(): Promise<void> {
613
+ this.cancelPending()
614
+ await this.#clearScope()
615
+ }
616
+ async dispose(): Promise<void> {
617
+ this.#disposed = true
618
+ unregisterComponentController(this)
619
+ for (const waiters of this.#mountWaiters.values()) for (const ready of waiters) ready()
620
+ this.#mountWaiters.clear()
621
+ await this.navigate()
622
+ }
623
+ }
624
+ const reloadedBuilds = new Set<string>()
625
+
626
+ function recordSubtree(records: readonly ComponentRecord[], instance: string): Set<string> {
627
+ const ids = new Set([instance])
628
+ for (let depth = 0; depth < 16; depth++)
629
+ for (const record of records)
630
+ if (record.parent !== null && ids.has(record.parent)) ids.add(record.instance)
631
+ return ids
632
+ }
633
+
634
+ export function resolveComponentRecord<Input>(
635
+ state: ComponentState,
636
+ component: Pick<ServerComponent<Input>, "reference" | "input">,
637
+ key: ComponentKey,
638
+ input: NoInfer<Input>,
639
+ parent: string | null = null,
640
+ ): ComponentRecord {
641
+ const checked = parseComponentState(state)
642
+ const serialized = componentInputKey(componentValue(input, COMPONENT_LIMITS.inputBytes))
643
+ const matches = checked.records.filter(
644
+ (record) =>
645
+ record.reference === component.reference &&
646
+ record.key === key &&
647
+ record.parent === parent &&
648
+ componentInputKey(record.input) === serialized,
649
+ )
650
+ if (matches.length !== 1) throw new ServerComponentError("component_unavailable", 404)
651
+ return matches[0]!
652
+ }
653
+
654
+ export { invalidateComponentScope } from "./registry.ts"
655
+ export { bindComponentRouter, refreshAfterComponentMutation } from "./router-integration.ts"
656
+ export { parseComponentState } from "./state.ts"
657
+ export type {
658
+ ComponentKey,
659
+ ComponentRecord,
660
+ ComponentState,
661
+ ServerComponent,
662
+ ServerComponentHandle,
663
+ } from "./types.ts"