@nifrajs/web 5.5.1 → 5.7.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 (481) hide show
  1. package/CHANGELOG.md +243 -0
  2. package/README.md +21 -11
  3. package/dist/boundary.d.ts +67 -7
  4. package/dist/boundary.d.ts.map +1 -1
  5. package/dist/boundary.js +6 -4
  6. package/dist/boundary.js.map +1 -1
  7. package/dist/build-plan.d.ts +3 -0
  8. package/dist/build-plan.d.ts.map +1 -1
  9. package/dist/build-plan.js.map +1 -1
  10. package/dist/build-vite.d.ts.map +1 -1
  11. package/dist/build-vite.js +2 -0
  12. package/dist/build-vite.js.map +1 -1
  13. package/dist/build.d.ts +9 -2
  14. package/dist/build.d.ts.map +1 -1
  15. package/dist/build.js +10 -0
  16. package/dist/build.js.map +1 -1
  17. package/dist/client.d.ts +3 -1
  18. package/dist/client.d.ts.map +1 -1
  19. package/dist/client.js +174 -23
  20. package/dist/client.js.map +1 -1
  21. package/dist/csp.d.ts +8 -0
  22. package/dist/csp.d.ts.map +1 -1
  23. package/dist/csp.js +11 -2
  24. package/dist/csp.js.map +1 -1
  25. package/dist/deferred.d.ts +6 -4
  26. package/dist/deferred.d.ts.map +1 -1
  27. package/dist/deferred.js +10 -6
  28. package/dist/deferred.js.map +1 -1
  29. package/dist/dev-feed.d.ts.map +1 -1
  30. package/dist/dev-feed.js +11 -2
  31. package/dist/dev-feed.js.map +1 -1
  32. package/dist/dev-session.d.ts.map +1 -1
  33. package/dist/dev-session.js +13 -6
  34. package/dist/dev-session.js.map +1 -1
  35. package/dist/dev.d.ts +4 -1
  36. package/dist/dev.d.ts.map +1 -1
  37. package/dist/dev.js +12 -5
  38. package/dist/dev.js.map +1 -1
  39. package/dist/diagnostic-catalog.d.ts +4 -0
  40. package/dist/diagnostic-catalog.d.ts.map +1 -1
  41. package/dist/diagnostic-catalog.js +48 -0
  42. package/dist/diagnostic-catalog.js.map +1 -1
  43. package/dist/diagnostic.d.ts +1 -1
  44. package/dist/diagnostic.d.ts.map +1 -1
  45. package/dist/diagnostic.js +1 -1
  46. package/dist/diagnostic.js.map +1 -1
  47. package/dist/fn.d.ts +5 -3
  48. package/dist/fn.d.ts.map +1 -1
  49. package/dist/fn.js +20 -5
  50. package/dist/fn.js.map +1 -1
  51. package/dist/fs.d.ts.map +1 -1
  52. package/dist/fs.js +13 -3
  53. package/dist/fs.js.map +1 -1
  54. package/dist/identity.d.ts +22 -0
  55. package/dist/identity.d.ts.map +1 -0
  56. package/dist/identity.js +60 -0
  57. package/dist/identity.js.map +1 -0
  58. package/dist/index.d.ts +8 -6
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +6 -3
  61. package/dist/index.js.map +1 -1
  62. package/dist/internal/cdn/cloudflare.d.ts +2 -1
  63. package/dist/internal/cdn/cloudflare.d.ts.map +1 -1
  64. package/dist/internal/cdn/cloudflare.js +2 -1
  65. package/dist/internal/cdn/cloudflare.js.map +1 -1
  66. package/dist/internal/certified-components.d.ts +1 -1
  67. package/dist/internal/certified-components.js +1 -1
  68. package/dist/internal/codegen.d.ts +11 -10
  69. package/dist/internal/codegen.d.ts.map +1 -1
  70. package/dist/internal/codegen.js +73 -10
  71. package/dist/internal/codegen.js.map +1 -1
  72. package/dist/internal/dev-websocket.d.ts +73 -0
  73. package/dist/internal/dev-websocket.d.ts.map +1 -0
  74. package/dist/internal/dev-websocket.js +145 -0
  75. package/dist/internal/dev-websocket.js.map +1 -0
  76. package/dist/internal/hole-runtime.d.ts +66 -0
  77. package/dist/internal/hole-runtime.d.ts.map +1 -0
  78. package/dist/internal/hole-runtime.js +211 -0
  79. package/dist/internal/hole-runtime.js.map +1 -0
  80. package/dist/internal/identity-epoch.d.ts +50 -0
  81. package/dist/internal/identity-epoch.d.ts.map +1 -0
  82. package/dist/internal/identity-epoch.js +180 -0
  83. package/dist/internal/identity-epoch.js.map +1 -0
  84. package/dist/internal/invalidation-contract.d.ts +23 -0
  85. package/dist/internal/invalidation-contract.d.ts.map +1 -0
  86. package/dist/internal/invalidation-contract.js +11 -0
  87. package/dist/internal/invalidation-contract.js.map +1 -0
  88. package/dist/internal/invalidation.d.ts +62 -0
  89. package/dist/internal/invalidation.d.ts.map +1 -0
  90. package/dist/internal/invalidation.js +149 -0
  91. package/dist/internal/invalidation.js.map +1 -0
  92. package/dist/internal/island-bindings.d.ts +51 -0
  93. package/dist/internal/island-bindings.d.ts.map +1 -0
  94. package/dist/internal/island-bindings.js +147 -0
  95. package/dist/internal/island-bindings.js.map +1 -0
  96. package/dist/internal/island-list.d.ts +26 -0
  97. package/dist/internal/island-list.d.ts.map +1 -0
  98. package/dist/internal/island-list.js +78 -0
  99. package/dist/internal/island-list.js.map +1 -0
  100. package/dist/internal/island-mount.d.ts +4 -0
  101. package/dist/internal/island-mount.d.ts.map +1 -0
  102. package/dist/internal/island-mount.js +66 -0
  103. package/dist/internal/island-mount.js.map +1 -0
  104. package/dist/internal/island-owner.d.ts +5 -0
  105. package/dist/internal/island-owner.d.ts.map +1 -0
  106. package/dist/internal/island-owner.js +20 -0
  107. package/dist/internal/island-owner.js.map +1 -0
  108. package/dist/internal/island-resource.d.ts +30 -0
  109. package/dist/internal/island-resource.d.ts.map +1 -0
  110. package/dist/internal/island-resource.js +57 -0
  111. package/dist/internal/island-resource.js.map +1 -0
  112. package/dist/internal/island-signals.d.ts +51 -0
  113. package/dist/internal/island-signals.d.ts.map +1 -0
  114. package/dist/internal/island-signals.js +220 -0
  115. package/dist/internal/island-signals.js.map +1 -0
  116. package/dist/internal/keep-alive-table.d.ts +23 -0
  117. package/dist/internal/keep-alive-table.d.ts.map +1 -0
  118. package/dist/internal/keep-alive-table.js +53 -0
  119. package/dist/internal/keep-alive-table.js.map +1 -0
  120. package/dist/internal/live-contract.d.ts +101 -0
  121. package/dist/internal/live-contract.d.ts.map +1 -0
  122. package/dist/internal/live-contract.js +5 -0
  123. package/dist/internal/live-contract.js.map +1 -0
  124. package/dist/internal/live-queries.d.ts +22 -0
  125. package/dist/internal/live-queries.d.ts.map +1 -0
  126. package/dist/internal/live-queries.js +54 -0
  127. package/dist/internal/live-queries.js.map +1 -0
  128. package/dist/internal/page-events.d.ts +10 -0
  129. package/dist/internal/page-events.d.ts.map +1 -0
  130. package/dist/internal/page-events.js +26 -0
  131. package/dist/internal/page-events.js.map +1 -0
  132. package/dist/internal/page-execution.d.ts +16 -1
  133. package/dist/internal/page-execution.d.ts.map +1 -1
  134. package/dist/internal/page-execution.js +432 -31
  135. package/dist/internal/page-execution.js.map +1 -1
  136. package/dist/internal/parity.d.ts +3 -0
  137. package/dist/internal/parity.d.ts.map +1 -1
  138. package/dist/internal/parity.js +28 -5
  139. package/dist/internal/parity.js.map +1 -1
  140. package/dist/internal/ppr/admission.d.ts +83 -0
  141. package/dist/internal/ppr/admission.d.ts.map +1 -0
  142. package/dist/internal/ppr/admission.js +221 -0
  143. package/dist/internal/ppr/admission.js.map +1 -0
  144. package/dist/internal/ppr/canary.d.ts +24 -0
  145. package/dist/internal/ppr/canary.d.ts.map +1 -0
  146. package/dist/internal/ppr/canary.js +102 -0
  147. package/dist/internal/ppr/canary.js.map +1 -0
  148. package/dist/internal/ppr/compile-shell.d.ts +39 -0
  149. package/dist/internal/ppr/compile-shell.d.ts.map +1 -0
  150. package/dist/internal/ppr/compile-shell.js +298 -0
  151. package/dist/internal/ppr/compile-shell.js.map +1 -0
  152. package/dist/internal/ppr/contract.d.ts +177 -0
  153. package/dist/internal/ppr/contract.d.ts.map +1 -0
  154. package/dist/internal/ppr/contract.js +80 -0
  155. package/dist/internal/ppr/contract.js.map +1 -0
  156. package/dist/internal/ppr/frames.d.ts +71 -0
  157. package/dist/internal/ppr/frames.d.ts.map +1 -0
  158. package/dist/internal/ppr/frames.js +119 -0
  159. package/dist/internal/ppr/frames.js.map +1 -0
  160. package/dist/internal/ppr/integration.d.ts +19 -0
  161. package/dist/internal/ppr/integration.d.ts.map +1 -0
  162. package/dist/internal/ppr/integration.js +106 -0
  163. package/dist/internal/ppr/integration.js.map +1 -0
  164. package/dist/internal/ppr/options.d.ts +96 -0
  165. package/dist/internal/ppr/options.d.ts.map +1 -0
  166. package/dist/internal/ppr/options.js +125 -0
  167. package/dist/internal/ppr/options.js.map +1 -0
  168. package/dist/internal/ppr/route-rules.d.ts +12 -0
  169. package/dist/internal/ppr/route-rules.d.ts.map +1 -0
  170. package/dist/internal/ppr/route-rules.js +53 -0
  171. package/dist/internal/ppr/route-rules.js.map +1 -0
  172. package/dist/internal/ppr/runtime.d.ts +29 -0
  173. package/dist/internal/ppr/runtime.d.ts.map +1 -0
  174. package/dist/internal/ppr/runtime.js +35 -0
  175. package/dist/internal/ppr/runtime.js.map +1 -0
  176. package/dist/internal/ppr/server.d.ts +29 -0
  177. package/dist/internal/ppr/server.d.ts.map +1 -0
  178. package/dist/internal/ppr/server.js +1409 -0
  179. package/dist/internal/ppr/server.js.map +1 -0
  180. package/dist/internal/ppr/shell-store.d.ts +39 -0
  181. package/dist/internal/ppr/shell-store.d.ts.map +1 -0
  182. package/dist/internal/ppr/shell-store.js +106 -0
  183. package/dist/internal/ppr/shell-store.js.map +1 -0
  184. package/dist/internal/prerendered-pages.d.ts +1 -1
  185. package/dist/internal/prerendered-pages.d.ts.map +1 -1
  186. package/dist/internal/prerendered-pages.js +7 -4
  187. package/dist/internal/prerendered-pages.js.map +1 -1
  188. package/dist/internal/present.d.ts +11 -0
  189. package/dist/internal/present.d.ts.map +1 -0
  190. package/dist/internal/present.js +13 -0
  191. package/dist/internal/present.js.map +1 -0
  192. package/dist/internal/query-digest.d.ts +9 -0
  193. package/dist/internal/query-digest.d.ts.map +1 -0
  194. package/dist/internal/query-digest.js +23 -0
  195. package/dist/internal/query-digest.js.map +1 -0
  196. package/dist/internal/query-runtime.d.ts +6 -1
  197. package/dist/internal/query-runtime.d.ts.map +1 -1
  198. package/dist/internal/query-runtime.js +9 -0
  199. package/dist/internal/query-runtime.js.map +1 -1
  200. package/dist/internal/query-seam.d.ts +29 -0
  201. package/dist/internal/query-seam.d.ts.map +1 -0
  202. package/dist/internal/query-seam.js +23 -0
  203. package/dist/internal/query-seam.js.map +1 -0
  204. package/dist/internal/render-document.d.ts +34 -64
  205. package/dist/internal/render-document.d.ts.map +1 -1
  206. package/dist/internal/render-document.js +82 -64
  207. package/dist/internal/render-document.js.map +1 -1
  208. package/dist/internal/request-abort.d.ts +8 -0
  209. package/dist/internal/request-abort.d.ts.map +1 -0
  210. package/dist/internal/request-abort.js +22 -0
  211. package/dist/internal/request-abort.js.map +1 -0
  212. package/dist/internal/server-boundary.d.ts +2 -0
  213. package/dist/internal/server-boundary.d.ts.map +1 -1
  214. package/dist/internal/server-boundary.js +8 -1
  215. package/dist/internal/server-boundary.js.map +1 -1
  216. package/dist/internal/share-data.d.ts +10 -0
  217. package/dist/internal/share-data.d.ts.map +1 -0
  218. package/dist/internal/share-data.js +135 -0
  219. package/dist/internal/share-data.js.map +1 -0
  220. package/dist/internal/status-page.d.ts +68 -0
  221. package/dist/internal/status-page.d.ts.map +1 -0
  222. package/dist/internal/status-page.js +66 -0
  223. package/dist/internal/status-page.js.map +1 -0
  224. package/dist/internal/store-signature.d.ts +15 -0
  225. package/dist/internal/store-signature.d.ts.map +1 -0
  226. package/dist/internal/store-signature.js +114 -0
  227. package/dist/internal/store-signature.js.map +1 -0
  228. package/dist/internal/vite-plugin-compat.d.ts +2 -2
  229. package/dist/internal/vite-plugin-compat.js +2 -2
  230. package/dist/internal/web-app.d.ts +26 -0
  231. package/dist/internal/web-app.d.ts.map +1 -1
  232. package/dist/internal/web-app.js +79 -3
  233. package/dist/internal/web-app.js.map +1 -1
  234. package/dist/islands-swap.d.ts +41 -0
  235. package/dist/islands-swap.d.ts.map +1 -0
  236. package/dist/islands-swap.js +270 -0
  237. package/dist/islands-swap.js.map +1 -0
  238. package/dist/islands.d.ts +86 -24
  239. package/dist/islands.d.ts.map +1 -1
  240. package/dist/islands.js +138 -78
  241. package/dist/islands.js.map +1 -1
  242. package/dist/isr.d.ts +67 -13
  243. package/dist/isr.d.ts.map +1 -1
  244. package/dist/isr.js +247 -99
  245. package/dist/isr.js.map +1 -1
  246. package/dist/keep-alive.d.ts +147 -0
  247. package/dist/keep-alive.d.ts.map +1 -0
  248. package/dist/keep-alive.js +1102 -0
  249. package/dist/keep-alive.js.map +1 -0
  250. package/dist/live/bus.d.ts +70 -0
  251. package/dist/live/bus.d.ts.map +1 -0
  252. package/dist/live/bus.js +55 -0
  253. package/dist/live/bus.js.map +1 -0
  254. package/dist/live/core.d.ts +25 -0
  255. package/dist/live/core.d.ts.map +1 -0
  256. package/dist/live/core.js +29 -0
  257. package/dist/live/core.js.map +1 -0
  258. package/dist/live/history.d.ts +38 -0
  259. package/dist/live/history.d.ts.map +1 -0
  260. package/dist/live/history.js +104 -0
  261. package/dist/live/history.js.map +1 -0
  262. package/dist/live/hub-protocol.d.ts +46 -0
  263. package/dist/live/hub-protocol.d.ts.map +1 -0
  264. package/dist/live/hub-protocol.js +108 -0
  265. package/dist/live/hub-protocol.js.map +1 -0
  266. package/dist/live/hub.d.ts +185 -0
  267. package/dist/live/hub.d.ts.map +1 -0
  268. package/dist/live/hub.js +544 -0
  269. package/dist/live/hub.js.map +1 -0
  270. package/dist/live/keyring.d.ts +92 -0
  271. package/dist/live/keyring.d.ts.map +1 -0
  272. package/dist/live/keyring.js +265 -0
  273. package/dist/live/keyring.js.map +1 -0
  274. package/dist/live/read-capped.d.ts +6 -0
  275. package/dist/live/read-capped.d.ts.map +1 -0
  276. package/dist/live/read-capped.js +42 -0
  277. package/dist/live/read-capped.js.map +1 -0
  278. package/dist/live/revocations.d.ts +52 -0
  279. package/dist/live/revocations.d.ts.map +1 -0
  280. package/dist/live/revocations.js +116 -0
  281. package/dist/live/revocations.js.map +1 -0
  282. package/dist/live/settings.d.ts +14 -0
  283. package/dist/live/settings.d.ts.map +1 -0
  284. package/dist/live/settings.js +38 -0
  285. package/dist/live/settings.js.map +1 -0
  286. package/dist/live-client.d.ts +34 -0
  287. package/dist/live-client.d.ts.map +1 -0
  288. package/dist/live-client.js +878 -0
  289. package/dist/live-client.js.map +1 -0
  290. package/dist/live-hub.d.ts +130 -0
  291. package/dist/live-hub.d.ts.map +1 -0
  292. package/dist/live-hub.js +507 -0
  293. package/dist/live-hub.js.map +1 -0
  294. package/dist/live-redis.d.ts +60 -0
  295. package/dist/live-redis.d.ts.map +1 -0
  296. package/dist/live-redis.js +212 -0
  297. package/dist/live-redis.js.map +1 -0
  298. package/dist/live.d.ts +150 -0
  299. package/dist/live.d.ts.map +1 -0
  300. package/dist/live.js +972 -0
  301. package/dist/live.js.map +1 -0
  302. package/dist/manifest.d.ts +113 -2
  303. package/dist/manifest.d.ts.map +1 -1
  304. package/dist/manifest.js +109 -5
  305. package/dist/manifest.js.map +1 -1
  306. package/dist/navigation.d.ts +9 -3
  307. package/dist/navigation.d.ts.map +1 -1
  308. package/dist/navigation.js +13 -3
  309. package/dist/navigation.js.map +1 -1
  310. package/dist/plugins/vite-zone-guard.d.ts +6 -0
  311. package/dist/plugins/vite-zone-guard.d.ts.map +1 -1
  312. package/dist/plugins/vite-zone-guard.js +8 -2
  313. package/dist/plugins/vite-zone-guard.js.map +1 -1
  314. package/dist/ppr.d.ts +73 -0
  315. package/dist/ppr.d.ts.map +1 -0
  316. package/dist/ppr.js +76 -0
  317. package/dist/ppr.js.map +1 -0
  318. package/dist/query.d.ts +8 -13
  319. package/dist/query.d.ts.map +1 -1
  320. package/dist/query.js +112 -18
  321. package/dist/query.js.map +1 -1
  322. package/dist/render-seam.d.ts +32 -0
  323. package/dist/render-seam.d.ts.map +1 -1
  324. package/dist/render-seam.js +7 -0
  325. package/dist/render-seam.js.map +1 -1
  326. package/dist/route-types.d.ts.map +1 -1
  327. package/dist/route-types.js +2 -2
  328. package/dist/route-types.js.map +1 -1
  329. package/dist/router.d.ts +72 -4
  330. package/dist/router.d.ts.map +1 -1
  331. package/dist/router.js +416 -129
  332. package/dist/router.js.map +1 -1
  333. package/dist/server-components/build.d.ts.map +1 -1
  334. package/dist/server-components/build.js +3 -3
  335. package/dist/server-components/build.js.map +1 -1
  336. package/dist/server-components/client.d.ts +23 -1
  337. package/dist/server-components/client.d.ts.map +1 -1
  338. package/dist/server-components/client.js +92 -5
  339. package/dist/server-components/client.js.map +1 -1
  340. package/dist/server-components/definition.d.ts.map +1 -1
  341. package/dist/server-components/definition.js +2 -1
  342. package/dist/server-components/definition.js.map +1 -1
  343. package/dist/server-components/hole.d.ts +8 -0
  344. package/dist/server-components/hole.d.ts.map +1 -0
  345. package/dist/server-components/hole.js +20 -0
  346. package/dist/server-components/hole.js.map +1 -0
  347. package/dist/server-components/html-policy.d.ts.map +1 -1
  348. package/dist/server-components/html-policy.js +62 -4
  349. package/dist/server-components/html-policy.js.map +1 -1
  350. package/dist/server-components/html-scan.d.ts +17 -0
  351. package/dist/server-components/html-scan.d.ts.map +1 -0
  352. package/dist/server-components/html-scan.js +180 -0
  353. package/dist/server-components/html-scan.js.map +1 -0
  354. package/dist/server-components/html.d.ts.map +1 -1
  355. package/dist/server-components/html.js +19 -4
  356. package/dist/server-components/html.js.map +1 -1
  357. package/dist/server-components/router-integration.d.ts +4 -1
  358. package/dist/server-components/router-integration.d.ts.map +1 -1
  359. package/dist/server-components/router-integration.js +23 -2
  360. package/dist/server-components/router-integration.js.map +1 -1
  361. package/dist/server-components/runtime.d.ts +6 -1
  362. package/dist/server-components/runtime.d.ts.map +1 -1
  363. package/dist/server-components/runtime.js +88 -29
  364. package/dist/server-components/runtime.js.map +1 -1
  365. package/dist/server-components/state.d.ts +3 -1
  366. package/dist/server-components/state.d.ts.map +1 -1
  367. package/dist/server-components/state.js +50 -38
  368. package/dist/server-components/state.js.map +1 -1
  369. package/dist/server-components/types.d.ts +9 -0
  370. package/dist/server-components/types.d.ts.map +1 -1
  371. package/dist/server-components/vite.d.ts.map +1 -1
  372. package/dist/server-components/vite.js +26 -9
  373. package/dist/server-components/vite.js.map +1 -1
  374. package/dist/vite.d.ts +5 -1
  375. package/dist/vite.d.ts.map +1 -1
  376. package/dist/vite.js +58 -4
  377. package/dist/vite.js.map +1 -1
  378. package/package.json +48 -8
  379. package/src/boundary.ts +77 -5
  380. package/src/build-plan.ts +3 -0
  381. package/src/build-vite.ts +2 -0
  382. package/src/build.ts +20 -1
  383. package/src/client.ts +183 -23
  384. package/src/csp.ts +16 -2
  385. package/src/deferred.ts +11 -5
  386. package/src/dev-feed.ts +11 -2
  387. package/src/dev-session.ts +22 -11
  388. package/src/dev.ts +29 -8
  389. package/src/diagnostic-catalog.ts +53 -0
  390. package/src/diagnostic.ts +1 -0
  391. package/src/fn.ts +24 -7
  392. package/src/fs.ts +22 -8
  393. package/src/identity.ts +61 -0
  394. package/src/index.ts +20 -4
  395. package/src/internal/cdn/cloudflare.ts +2 -1
  396. package/src/internal/certified-components.ts +1 -1
  397. package/src/internal/codegen.ts +95 -9
  398. package/src/internal/dev-websocket.ts +230 -0
  399. package/src/internal/hole-runtime.ts +286 -0
  400. package/src/internal/identity-epoch.ts +239 -0
  401. package/src/internal/invalidation-contract.ts +35 -0
  402. package/src/internal/invalidation.ts +213 -0
  403. package/src/internal/island-bindings.ts +191 -0
  404. package/src/internal/island-list.ts +92 -0
  405. package/src/internal/island-mount.ts +68 -0
  406. package/src/internal/island-owner.ts +20 -0
  407. package/src/internal/island-resource.ts +76 -0
  408. package/src/internal/island-signals.ts +249 -0
  409. package/src/internal/keep-alive-table.ts +72 -0
  410. package/src/internal/live-contract.ts +111 -0
  411. package/src/internal/live-queries.ts +60 -0
  412. package/src/internal/page-events.ts +27 -0
  413. package/src/internal/page-execution.ts +605 -45
  414. package/src/internal/parity.ts +35 -6
  415. package/src/internal/ppr/admission.ts +300 -0
  416. package/src/internal/ppr/canary.ts +122 -0
  417. package/src/internal/ppr/compile-shell.ts +379 -0
  418. package/src/internal/ppr/contract.ts +265 -0
  419. package/src/internal/ppr/frames.ts +170 -0
  420. package/src/internal/ppr/integration.ts +137 -0
  421. package/src/internal/ppr/options.ts +221 -0
  422. package/src/internal/ppr/route-rules.ts +77 -0
  423. package/src/internal/ppr/runtime.ts +39 -0
  424. package/src/internal/ppr/server.ts +1766 -0
  425. package/src/internal/ppr/shell-store.ts +152 -0
  426. package/src/internal/prerendered-pages.ts +7 -4
  427. package/src/internal/present.ts +20 -0
  428. package/src/internal/query-digest.ts +34 -0
  429. package/src/internal/query-runtime.ts +14 -1
  430. package/src/internal/query-seam.ts +48 -0
  431. package/src/internal/render-document.ts +158 -113
  432. package/src/internal/request-abort.ts +26 -0
  433. package/src/internal/server-boundary.ts +10 -1
  434. package/src/internal/share-data.ts +136 -0
  435. package/src/internal/status-page.ts +95 -0
  436. package/src/internal/store-signature.ts +149 -0
  437. package/src/internal/vite-plugin-compat.ts +2 -2
  438. package/src/internal/web-app.ts +123 -3
  439. package/src/islands-swap.ts +283 -0
  440. package/src/islands.ts +169 -72
  441. package/src/isr.ts +348 -106
  442. package/src/keep-alive.ts +1354 -0
  443. package/src/live/bus.ts +104 -0
  444. package/src/live/core.ts +52 -0
  445. package/src/live/history.ts +109 -0
  446. package/src/live/hub-protocol.ts +133 -0
  447. package/src/live/hub.ts +620 -0
  448. package/src/live/keyring.ts +384 -0
  449. package/src/live/read-capped.ts +36 -0
  450. package/src/live/revocations.ts +155 -0
  451. package/src/live/settings.ts +46 -0
  452. package/src/live-client.ts +1011 -0
  453. package/src/live-hub.ts +658 -0
  454. package/src/live-redis.ts +284 -0
  455. package/src/live.ts +1257 -0
  456. package/src/manifest.ts +274 -6
  457. package/src/navigation.ts +14 -2
  458. package/src/plugins/vite-zone-guard.ts +15 -2
  459. package/src/ppr.ts +153 -0
  460. package/src/query.ts +147 -20
  461. package/src/render-seam.ts +37 -0
  462. package/src/route-types.ts +3 -1
  463. package/src/router.ts +534 -132
  464. package/src/server-components/build.ts +9 -3
  465. package/src/server-components/client.ts +89 -5
  466. package/src/server-components/definition.ts +2 -1
  467. package/src/server-components/hole.ts +23 -0
  468. package/src/server-components/html-policy.ts +63 -4
  469. package/src/server-components/html-scan.ts +159 -0
  470. package/src/server-components/html.ts +18 -4
  471. package/src/server-components/router-integration.ts +21 -2
  472. package/src/server-components/runtime.ts +114 -31
  473. package/src/server-components/state.ts +66 -44
  474. package/src/server-components/types.ts +16 -0
  475. package/src/server-components/vite.ts +33 -14
  476. package/src/vite.ts +75 -6
  477. package/dist/nano.d.ts +0 -125
  478. package/dist/nano.d.ts.map +0 -1
  479. package/dist/nano.js +0 -190
  480. package/dist/nano.js.map +0 -1
  481. package/src/nano.ts +0 -246
package/src/query.ts CHANGED
@@ -11,6 +11,7 @@
11
11
  * + navigation use the path cache; component-level interactive data (search, infinite scroll, polling)
12
12
  * uses this arbitrary-keyed query cache. The two coexist.
13
13
  */
14
+ import { adoptClient, type LiveQuery, querySeam } from "./internal/query-seam.ts"
14
15
 
15
16
  /** A query's lifecycle status. `pending` = no data yet; `success`/`error` once it has settled once. */
16
17
  export type QueryStatus = "pending" | "success" | "error"
@@ -43,6 +44,11 @@ export interface QueryOptions {
43
44
  /** Data stays fresh this long (ms) after a fetch; older ⇒ refetch on the next `fetch()`. Overrides
44
45
  * the client default for this key. */
45
46
  readonly staleTime?: number
47
+ /**
48
+ * Live keys the data depends on, as the page's loaders declare them with `ctx.live()`. While the
49
+ * query is mounted, a change to one refetches it. Needs `live({ queryKeys: true })`.
50
+ */
51
+ readonly live?: readonly string[]
46
52
  }
47
53
 
48
54
  export interface QueryClientOptions {
@@ -147,6 +153,9 @@ export interface QueryClient {
147
153
  dehydrate: () => DehydratedState
148
154
  /** Seed the cache from a {@link dehydrate} snapshot (client boot). Existing fresher entries win. */
149
155
  hydrate: (state: DehydratedState) => void
156
+ /** Drop every cached query: mounted ones go back to pending and refetch, the rest are removed. A
157
+ * live app calls it on every query client in the page when the viewer changes. */
158
+ clear: () => void
150
159
  }
151
160
 
152
161
  /**
@@ -180,10 +189,33 @@ function keyMatchesPrefix(key: unknown, prefix: unknown): boolean {
180
189
  )
181
190
  }
182
191
 
183
- interface Entry {
192
+ /** What both kinds of entry carry for a page with live refresh. */
193
+ interface LiveFields {
194
+ live: readonly string[]
195
+ subscribers: number
196
+ readonly lq: LiveQuery
197
+ }
198
+
199
+ /** The live tick now; 0 on a page without live refresh. */
200
+ const liveTick = (): number => querySeam()?.tick() ?? 0
201
+
202
+ /** New keys while mounted are tracked again: a change to a new key before now never reached it. */
203
+ function relive(entry: LiveFields, live: readonly string[] | undefined): void {
204
+ if (live === undefined) return
205
+ if (live.length === entry.live.length && live.every((key, i) => key === entry.live[i])) return
206
+ entry.live = [...live]
207
+ if (entry.subscribers === 0) return
208
+ querySeam()?.track(entry.lq, false)
209
+ querySeam()?.track(entry.lq, true)
210
+ }
211
+
212
+ interface Entry extends LiveFields {
184
213
  readonly key: unknown
185
214
  fn: () => Promise<unknown>
186
215
  staleTime: number
216
+ /** The {@link liveTick} at the start of the latest fetch, and of the fetch that produced `state`. */
217
+ startTick: number
218
+ dataTick: number
187
219
  state: QueryState
188
220
  promise: Promise<unknown> | undefined
189
221
  /** The {@link generation} the in-flight {@link promise} started at - so a fetch begun *before* an
@@ -192,7 +224,6 @@ interface Entry {
192
224
  /** Bumped by `invalidateQueries`/`refetch` to supersede any in-flight fetch begun before it. */
193
225
  generation: number
194
226
  invalidated: boolean
195
- subscribers: number
196
227
  /** Scheduled eviction time (set when subscribers hit 0; cleared on resubscribe). */
197
228
  gcAt: number | undefined
198
229
  readonly listeners: Set<() => void>
@@ -248,10 +279,22 @@ export function createQueryClient(options: QueryClientOptions): QueryClient {
248
279
  // between renders). All the fetch/dedup/generation machinery lives here so query(), prefetchQuery(),
249
280
  // setQueryData(), and invalidateQueries() share exactly one implementation.
250
281
  const makeEntry = (key: unknown, fn: () => Promise<unknown>, staleTime: number): Entry => {
282
+ const lq: LiveQuery = {
283
+ keys: () => entry.live,
284
+ tick: () =>
285
+ entry.promise !== undefined && entry.promiseGen === entry.generation
286
+ ? entry.startTick
287
+ : entry.dataTick,
288
+ refresh: () => invalidateEntry(entry),
289
+ }
251
290
  const entry: Entry = {
252
291
  key,
253
292
  fn,
254
293
  staleTime,
294
+ live: [],
295
+ lq,
296
+ startTick: 0,
297
+ dataTick: 0,
255
298
  state: PENDING,
256
299
  promise: undefined,
257
300
  promiseGen: 0,
@@ -287,6 +330,8 @@ export function createQueryClient(options: QueryClientOptions): QueryClient {
287
330
  if (!force && entry.state.status === "success" && !isStale())
288
331
  return Promise.resolve(entry.state.data) // fresh cache hit - no fn call
289
332
  const gen = entry.generation
333
+ const started = liveTick()
334
+ entry.startTick = started
290
335
  entry.setState({ ...entry.state, isFetching: true }) // show the background/initial fetch
291
336
  const promise = (async () => {
292
337
  try {
@@ -296,6 +341,7 @@ export function createQueryClient(options: QueryClientOptions): QueryClient {
296
341
  // that superseded this one publishes the post-mutation data.
297
342
  if (gen !== entry.generation) return data
298
343
  entry.invalidated = false
344
+ entry.dataTick = started
299
345
  entry.setState(successState(data, now()))
300
346
  return data
301
347
  } catch (error) {
@@ -320,10 +366,14 @@ export function createQueryClient(options: QueryClientOptions): QueryClient {
320
366
  entry.subscribers++
321
367
  entry.gcAt = undefined // in use again - cancel any pending eviction
322
368
  entry.listeners.add(listener)
369
+ if (entry.subscribers === 1) querySeam()?.track(lq, true)
323
370
  return () => {
324
371
  entry.listeners.delete(listener)
325
372
  entry.subscribers--
326
- if (entry.subscribers === 0) entry.gcAt = now() + gcTime
373
+ if (entry.subscribers === 0) {
374
+ entry.gcAt = now() + gcTime
375
+ querySeam()?.track(lq, false)
376
+ }
327
377
  }
328
378
  },
329
379
  fetch: () => entry.run(false),
@@ -332,35 +382,50 @@ export function createQueryClient(options: QueryClientOptions): QueryClient {
332
382
  return entry
333
383
  }
334
384
 
335
- // Get an entry by key, or create + insert a fresh one. `fn`/`staleTime` re-bind on an existing entry.
336
- const upsert = (key: unknown, fn: () => Promise<unknown>, staleTime: number): Entry => {
385
+ const invalidateEntry = (entry: Entry): void => {
386
+ entry.invalidated = true // next fetch refetches even within staleTime
387
+ // Supersede any in-flight fetch begun before this point - its result is pre-mutation and must
388
+ // not resolve as "fresh" or clear `invalidated`. The refetch below (or the next `fetch()`)
389
+ // starts a new generation that publishes the post-mutation data.
390
+ entry.generation++
391
+ if (entry.subscribers > 0) void entry.handle.refetch().catch(() => {}) // refresh mounted now (best-effort)
392
+ }
393
+
394
+ // Get an entry by key, or create + insert a fresh one. `fn`/`staleTime`/`live` re-bind on an
395
+ // existing entry; `live` stays as it is when the call names none (a write, a hydration).
396
+ const upsert = (
397
+ key: unknown,
398
+ fn: () => Promise<unknown>,
399
+ staleTime: number,
400
+ live?: readonly string[],
401
+ ): Entry => {
337
402
  const h = hashQueryKey(key)
338
403
  const existing = cache.get(h)
339
404
  if (existing !== undefined) {
340
405
  existing.fn = fn
341
406
  existing.staleTime = staleTime
407
+ relive(existing, live)
342
408
  return existing
343
409
  }
344
410
  const entry = makeEntry(key, fn, staleTime)
411
+ relive(entry, live)
345
412
  cache.set(h, entry)
346
413
  return entry
347
414
  }
348
415
 
349
416
  const query = <T>(key: unknown, fn: () => Promise<T>, opts?: QueryOptions): QueryHandle<T> => {
350
417
  sweep()
351
- return upsert(key, fn as () => Promise<unknown>, opts?.staleTime ?? defaultStaleTime)
352
- .handle as QueryHandle<T>
418
+ return upsert(
419
+ key,
420
+ fn as () => Promise<unknown>,
421
+ opts?.staleTime ?? defaultStaleTime,
422
+ opts?.live ?? [],
423
+ ).handle as QueryHandle<T>
353
424
  }
354
425
 
355
426
  const invalidateQueries = (keyOrPrefix: unknown): void => {
356
427
  for (const entry of cache.values()) {
357
- if (!keyMatchesPrefix(entry.key, keyOrPrefix)) continue
358
- entry.invalidated = true // next fetch refetches even within staleTime
359
- // Supersede any in-flight fetch begun before this point - its result is pre-mutation and must
360
- // not resolve as "fresh" or clear `invalidated`. The refetch below (or the next `fetch()`)
361
- // starts a new generation that publishes the post-mutation data.
362
- entry.generation++
363
- if (entry.subscribers > 0) void entry.handle.refetch().catch(() => {}) // refresh mounted now (best-effort)
428
+ if (keyMatchesPrefix(entry.key, keyOrPrefix)) invalidateEntry(entry)
364
429
  }
365
430
  for (const entry of infinite.values()) {
366
431
  if (keyMatchesPrefix(entry.key, keyOrPrefix)) entry.invalidate()
@@ -391,6 +456,7 @@ export function createQueryClient(options: QueryClientOptions): QueryClient {
391
456
  // and clear the invalidated flag (the caller just supplied fresh data).
392
457
  entry.generation++
393
458
  entry.invalidated = false
459
+ entry.dataTick = liveTick()
394
460
  entry.setState(successState(data, now()))
395
461
  }
396
462
 
@@ -400,7 +466,12 @@ export function createQueryClient(options: QueryClientOptions): QueryClient {
400
466
  opts?: QueryOptions,
401
467
  ): Promise<void> => {
402
468
  sweep()
403
- await upsert(key, fn as () => Promise<unknown>, opts?.staleTime ?? defaultStaleTime).run(false)
469
+ await upsert(
470
+ key,
471
+ fn as () => Promise<unknown>,
472
+ opts?.staleTime ?? defaultStaleTime,
473
+ opts?.live,
474
+ ).run(false)
404
475
  }
405
476
 
406
477
  const dehydrate = (): DehydratedState => {
@@ -433,8 +504,11 @@ export function createQueryClient(options: QueryClientOptions): QueryClient {
433
504
  // Don't clobber a client entry that's already fresher than the server snapshot (the client may
434
505
  // have refetched between SSR and hydration). Seed only when we have no successful data or the
435
506
  // incoming snapshot is newer.
436
- if (entry.state.status !== "success" || q.updatedAt > entry.state.updatedAt)
507
+ if (entry.state.status !== "success" || q.updatedAt > entry.state.updatedAt) {
508
+ // Read before any live load took a watermark: the first grant of its keys refetches it.
509
+ entry.dataTick = 0
437
510
  entry.setState(successState(q.data, q.updatedAt))
511
+ }
438
512
  }
439
513
  // An infinite entry needs its page options before it can exist, so a snapshot for a key not yet
440
514
  // queried waits here until `infiniteQuery` creates it.
@@ -473,15 +547,16 @@ export function createQueryClient(options: QueryClientOptions): QueryClient {
473
547
  }
474
548
 
475
549
  // Infinite entries live in their own map (their data shape + fetch model differ from plain queries).
476
- interface InfiniteEntry<T, P> {
550
+ interface InfiniteEntry<T, P> extends LiveFields {
477
551
  readonly key: unknown
478
552
  handle: InfiniteQueryHandle<T, P>
479
553
  rebind: (fn: (p: P) => Promise<T>, opts: InfiniteQueryOptions<T, P>) => void
480
554
  /** Mark stale, drop an in-flight fetch's result, and refetch when mounted. */
481
555
  invalidate: () => void
556
+ /** Back to pending, dropping an in-flight fetch's result; refetch the first page when mounted. */
557
+ reset: () => void
482
558
  /** Take a dehydrated snapshot unless the entry already holds fresher data. */
483
559
  seed: (data: InfiniteData<unknown, unknown>, updatedAt: number) => void
484
- subscribers: number
485
560
  gcAt: number | undefined
486
561
  }
487
562
  const infinite = new Map<string, InfiniteEntry<unknown, unknown>>()
@@ -495,9 +570,12 @@ export function createQueryClient(options: QueryClientOptions): QueryClient {
495
570
  let fn = fetchPage
496
571
  let opts = initialOpts
497
572
  let state: QueryState<InfiniteData<T, P>> = PENDING as QueryState<InfiniteData<T, P>>
573
+ const pending = state
498
574
  let generation = 0
499
575
  let invalidated = false
500
576
  let promise: Promise<InfiniteData<T, P>> | undefined
577
+ let startTick = 0
578
+ let dataTick = 0
501
579
  const listeners = new Set<() => void>()
502
580
  const emit = (): void => {
503
581
  for (const l of listeners) l()
@@ -532,12 +610,15 @@ export function createQueryClient(options: QueryClientOptions): QueryClient {
532
610
  const runPaged = (build: () => Promise<InfiniteData<T, P>>): Promise<InfiniteData<T, P>> => {
533
611
  if (promise !== undefined) return promise
534
612
  const gen = ++generation
613
+ const started = liveTick()
614
+ startTick = started
535
615
  setState({ ...state, isFetching: true })
536
616
  const p = (async () => {
537
617
  try {
538
618
  const data = await build()
539
619
  if (gen !== generation) return data
540
620
  invalidated = false
621
+ dataTick = started
541
622
  setState(successState(data, now()))
542
623
  return data
543
624
  } catch (error) {
@@ -566,10 +647,14 @@ export function createQueryClient(options: QueryClientOptions): QueryClient {
566
647
  entry.subscribers++
567
648
  entry.gcAt = undefined
568
649
  listeners.add(listener)
650
+ if (entry.subscribers === 1) querySeam()?.track(lq, true)
569
651
  return () => {
570
652
  listeners.delete(listener)
571
653
  entry.subscribers--
572
- if (entry.subscribers === 0) entry.gcAt = now() + gcTime
654
+ if (entry.subscribers === 0) {
655
+ entry.gcAt = now() + gcTime
656
+ querySeam()?.track(lq, false)
657
+ }
573
658
  }
574
659
  },
575
660
  fetch: () => fetchFirst(false),
@@ -614,12 +699,18 @@ export function createQueryClient(options: QueryClientOptions): QueryClient {
614
699
  return p !== undefined && p !== null
615
700
  },
616
701
  }
702
+ const lq: LiveQuery = {
703
+ keys: () => entry.live,
704
+ tick: () => (promise !== undefined ? startTick : dataTick),
705
+ refresh: () => entry.invalidate(),
706
+ }
617
707
  const entry: InfiniteEntry<T, P> = {
618
708
  key,
619
709
  handle,
620
710
  rebind: (nextFn, nextOpts) => {
621
711
  fn = nextFn
622
712
  opts = nextOpts
713
+ relive(entry, nextOpts.live ?? [])
623
714
  },
624
715
  invalidate: () => {
625
716
  invalidated = true
@@ -629,18 +720,50 @@ export function createQueryClient(options: QueryClientOptions): QueryClient {
629
720
  promise = undefined
630
721
  if (entry.subscribers > 0) void handle.refetch().catch(() => {})
631
722
  },
723
+ reset: () => {
724
+ invalidated = true
725
+ generation++
726
+ promise = undefined
727
+ dataTick = 0
728
+ setState(pending)
729
+ if (entry.subscribers > 0) void handle.refetch().catch(() => {})
730
+ },
632
731
  seed: (data, updatedAt) => {
633
732
  if (state.status === "success" && state.updatedAt >= updatedAt) return
733
+ dataTick = 0
634
734
  // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: the snapshot was dehydrated from an entry under this key, whose pages and params are T and P
635
735
  setState(successState(data as InfiniteData<T, P>, updatedAt))
636
736
  },
637
737
  subscribers: 0,
638
738
  gcAt: undefined,
739
+ live: [],
740
+ lq,
639
741
  }
742
+ relive(entry, initialOpts.live)
640
743
  return entry
641
744
  }
642
745
 
643
- return {
746
+ const clear = (): void => {
747
+ seeded.clear()
748
+ for (const [h, entry] of cache) {
749
+ // An in-flight fetch carried the last viewer's credentials: its result may not publish.
750
+ entry.generation++
751
+ entry.invalidated = true
752
+ entry.dataTick = 0
753
+ if (entry.subscribers === 0) {
754
+ cache.delete(h)
755
+ continue
756
+ }
757
+ entry.setState(PENDING)
758
+ void entry.handle.refetch().catch(() => {})
759
+ }
760
+ for (const [h, entry] of infinite) {
761
+ if (entry.subscribers === 0) infinite.delete(h)
762
+ entry.reset()
763
+ }
764
+ }
765
+
766
+ const client: QueryClient = {
644
767
  query,
645
768
  infiniteQuery,
646
769
  invalidateQueries,
@@ -649,7 +772,11 @@ export function createQueryClient(options: QueryClientOptions): QueryClient {
649
772
  prefetchQuery,
650
773
  dehydrate,
651
774
  hydrate,
775
+ clear,
652
776
  }
777
+ // A page with live refresh clears every client it made when the viewer changes.
778
+ adoptClient(client)
779
+ return client
653
780
  }
654
781
 
655
782
  // --- Standalone mutations -----------------------------------------------------------------------
@@ -135,6 +135,13 @@ export interface RenderStreamOptions {
135
135
  readonly nonce?: string
136
136
  /** Present only for an adapter that sets {@link RenderAdapter.documentParts}. */
137
137
  readonly parts?: DocumentParts
138
+ /**
139
+ * Chain indices to render inside the two slot wrappers a kept page needs (`keepAlive`), each
140
+ * `<slot data-nifra-entry><slot data-nifra-page>` around that chain entry (a `<slot>` lays out as
141
+ * `display: contents`), so the browser's keep-alive view hydrates the same markup. Present only
142
+ * in an app with a `keepAlive` route; an adapter without keep-alive support ignores it.
143
+ */
144
+ readonly slots?: readonly number[]
138
145
  }
139
146
 
140
147
  /**
@@ -167,13 +174,41 @@ export interface RenderAdapter {
167
174
  ): string | undefined | Promise<string | undefined>
168
175
  /** Per-document bootstrap markup injected into `<head>` for client hydration. */
169
176
  hydrationHead(nonce?: string): string
177
+ /**
178
+ * Render one piece of UI (a boundary's `render` result or `fallback`) to an HTML string on its own,
179
+ * outside the route chain. `ui` produces it: call it inside the render, since a framework whose
180
+ * markup is made as it is evaluated (Solid) needs its render context there. `idPrefix` namespaces
181
+ * any ids the framework generates, so two fragments rendered separately never collide. `hydrates`
182
+ * is set when the client hydrates the fragment as a root of its own (an interactive hole). Required
183
+ * for partial prerendering; optional otherwise.
184
+ */
185
+ renderFragment?(
186
+ ui: () => unknown,
187
+ options: { readonly idPrefix?: string; readonly hydrates?: boolean },
188
+ ): string | Promise<string>
170
189
  /**
171
190
  * Set when a render can produce markup that belongs outside the app root. Every render is then
172
191
  * passed `options.parts` to fill; an adapter without it never is, and pays nothing for the seam.
173
192
  */
174
193
  readonly documentParts?: boolean
194
+ /**
195
+ * Render the chain and return only the HTML of the slot named `name`, or `undefined` when the
196
+ * render has no such slot. Called for an {@link SWAP_HEADER} request; an adapter without it, or a
197
+ * render without the slot, answers with the whole document, which the client loads normally.
198
+ */
199
+ renderSlot?(
200
+ chain: readonly unknown[],
201
+ props: RenderProps,
202
+ name: string,
203
+ ): string | undefined | Promise<string | undefined>
175
204
  }
176
205
 
206
+ /**
207
+ * Request header of a `data-swap` link or form: the name of the slot it refreshes. A response that
208
+ * carries only that slot echoes it; any other response is a whole document.
209
+ */
210
+ export const SWAP_HEADER = "x-nifra-swap"
211
+
177
212
  /** Loads a module through the dev server's module graph rather than the runtime's resolver. */
178
213
  export type SsrModuleLoader = (id: string) => Promise<unknown>
179
214
 
@@ -209,6 +244,8 @@ export const ACTION_GLOBAL = "__NIFRA_ACTION__"
209
244
  /** Dynamic-boundary states for hydration; absent when a route declares no boundaries. */
210
245
  export const BOUNDARY_GLOBAL = "__NIFRA_BOUNDARIES__"
211
246
  export const COMPONENT_GLOBAL = "__NIFRA_COMPONENTS__"
247
+ /** A live app's seed for the page: the client exchanges it for a grant once its socket opens. */
248
+ export const LIVE_GLOBAL = "__NIFRA_LIVE__"
212
249
  /** Locale key -> copy chunk URL, in a production build that split copy tables by locale. The name
213
250
  * `@nifrajs/i18n/copy` reads. */
214
251
  export const COPY_CHUNKS_GLOBAL = "__NIFRA_COPY__"
@@ -144,7 +144,7 @@ function routeTypesModule(
144
144
  )
145
145
  lines.push(
146
146
  route.kind === "page"
147
- ? 'import type { MetaArgs as WebMetaArgs, RenderProps } from "@nifrajs/web"'
147
+ ? 'import type { BoundaryLoadersOf, MetaArgs as WebMetaArgs, RenderProps } from "@nifrajs/web"'
148
148
  : 'import type { MetaArgs as WebMetaArgs } from "@nifrajs/web"',
149
149
  )
150
150
  lines.push("")
@@ -181,6 +181,8 @@ function routeTypesModule(
181
181
  " readonly actionData?: ActionData",
182
182
  " readonly params: Params",
183
183
  " }",
184
+ " /** The backend half's `boundaryLoaders`. Check it, `export const boundaryLoaders = { ... } satisfies Route.BoundaryLoaders`, and a key must name a boundary the page declares; with a `shellLoader`, every hole needs one. */",
185
+ ` export type BoundaryLoaders = BoundaryLoadersOf<${frontend === undefined ? "unknown" : "Frontend"}, ${backend === undefined ? "unknown" : "Backend"}>`,
184
186
  )
185
187
  } else {
186
188
  lines.push(