@nifrajs/web 5.5.0 → 5.6.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 (457) hide show
  1. package/CHANGELOG.md +162 -0
  2. package/dist/boundary.d.ts +67 -7
  3. package/dist/boundary.d.ts.map +1 -1
  4. package/dist/boundary.js +6 -4
  5. package/dist/boundary.js.map +1 -1
  6. package/dist/build-plan.d.ts +3 -0
  7. package/dist/build-plan.d.ts.map +1 -1
  8. package/dist/build-plan.js.map +1 -1
  9. package/dist/build-vite.d.ts.map +1 -1
  10. package/dist/build-vite.js +2 -0
  11. package/dist/build-vite.js.map +1 -1
  12. package/dist/build.d.ts +9 -2
  13. package/dist/build.d.ts.map +1 -1
  14. package/dist/build.js +9 -0
  15. package/dist/build.js.map +1 -1
  16. package/dist/client.d.ts +2 -1
  17. package/dist/client.d.ts.map +1 -1
  18. package/dist/client.js +140 -12
  19. package/dist/client.js.map +1 -1
  20. package/dist/csp.d.ts +8 -0
  21. package/dist/csp.d.ts.map +1 -1
  22. package/dist/csp.js +11 -2
  23. package/dist/csp.js.map +1 -1
  24. package/dist/deferred.d.ts +6 -4
  25. package/dist/deferred.d.ts.map +1 -1
  26. package/dist/deferred.js +10 -6
  27. package/dist/deferred.js.map +1 -1
  28. package/dist/dev-feed.d.ts.map +1 -1
  29. package/dist/dev-feed.js +11 -2
  30. package/dist/dev-feed.js.map +1 -1
  31. package/dist/dev-session.d.ts.map +1 -1
  32. package/dist/dev-session.js +13 -6
  33. package/dist/dev-session.js.map +1 -1
  34. package/dist/dev.d.ts +4 -1
  35. package/dist/dev.d.ts.map +1 -1
  36. package/dist/dev.js +12 -5
  37. package/dist/dev.js.map +1 -1
  38. package/dist/diagnostic-catalog.d.ts +4 -0
  39. package/dist/diagnostic-catalog.d.ts.map +1 -1
  40. package/dist/diagnostic-catalog.js +48 -0
  41. package/dist/diagnostic-catalog.js.map +1 -1
  42. package/dist/diagnostic.d.ts +1 -1
  43. package/dist/diagnostic.d.ts.map +1 -1
  44. package/dist/diagnostic.js +1 -1
  45. package/dist/diagnostic.js.map +1 -1
  46. package/dist/fn.d.ts +5 -3
  47. package/dist/fn.d.ts.map +1 -1
  48. package/dist/fn.js +20 -5
  49. package/dist/fn.js.map +1 -1
  50. package/dist/fs.d.ts.map +1 -1
  51. package/dist/fs.js +13 -3
  52. package/dist/fs.js.map +1 -1
  53. package/dist/identity.d.ts +22 -0
  54. package/dist/identity.d.ts.map +1 -0
  55. package/dist/identity.js +60 -0
  56. package/dist/identity.js.map +1 -0
  57. package/dist/index.d.ts +7 -6
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +4 -2
  60. package/dist/index.js.map +1 -1
  61. package/dist/internal/cdn/cloudflare.d.ts +2 -1
  62. package/dist/internal/cdn/cloudflare.d.ts.map +1 -1
  63. package/dist/internal/cdn/cloudflare.js +2 -1
  64. package/dist/internal/cdn/cloudflare.js.map +1 -1
  65. package/dist/internal/certified-components.d.ts +1 -1
  66. package/dist/internal/certified-components.js +1 -1
  67. package/dist/internal/codegen.d.ts +11 -10
  68. package/dist/internal/codegen.d.ts.map +1 -1
  69. package/dist/internal/codegen.js +65 -8
  70. package/dist/internal/codegen.js.map +1 -1
  71. package/dist/internal/dev-websocket.d.ts +73 -0
  72. package/dist/internal/dev-websocket.d.ts.map +1 -0
  73. package/dist/internal/dev-websocket.js +145 -0
  74. package/dist/internal/dev-websocket.js.map +1 -0
  75. package/dist/internal/hole-runtime.d.ts +66 -0
  76. package/dist/internal/hole-runtime.d.ts.map +1 -0
  77. package/dist/internal/hole-runtime.js +211 -0
  78. package/dist/internal/hole-runtime.js.map +1 -0
  79. package/dist/internal/identity-epoch.d.ts +50 -0
  80. package/dist/internal/identity-epoch.d.ts.map +1 -0
  81. package/dist/internal/identity-epoch.js +179 -0
  82. package/dist/internal/identity-epoch.js.map +1 -0
  83. package/dist/internal/invalidation-contract.d.ts +23 -0
  84. package/dist/internal/invalidation-contract.d.ts.map +1 -0
  85. package/dist/internal/invalidation-contract.js +11 -0
  86. package/dist/internal/invalidation-contract.js.map +1 -0
  87. package/dist/internal/invalidation.d.ts +62 -0
  88. package/dist/internal/invalidation.d.ts.map +1 -0
  89. package/dist/internal/invalidation.js +149 -0
  90. package/dist/internal/invalidation.js.map +1 -0
  91. package/dist/internal/island-bindings.d.ts +51 -0
  92. package/dist/internal/island-bindings.d.ts.map +1 -0
  93. package/dist/internal/island-bindings.js +147 -0
  94. package/dist/internal/island-bindings.js.map +1 -0
  95. package/dist/internal/island-list.d.ts +26 -0
  96. package/dist/internal/island-list.d.ts.map +1 -0
  97. package/dist/internal/island-list.js +78 -0
  98. package/dist/internal/island-list.js.map +1 -0
  99. package/dist/internal/island-mount.d.ts +4 -0
  100. package/dist/internal/island-mount.d.ts.map +1 -0
  101. package/dist/internal/island-mount.js +66 -0
  102. package/dist/internal/island-mount.js.map +1 -0
  103. package/dist/internal/island-owner.d.ts +5 -0
  104. package/dist/internal/island-owner.d.ts.map +1 -0
  105. package/dist/internal/island-owner.js +20 -0
  106. package/dist/internal/island-owner.js.map +1 -0
  107. package/dist/internal/island-resource.d.ts +30 -0
  108. package/dist/internal/island-resource.d.ts.map +1 -0
  109. package/dist/internal/island-resource.js +57 -0
  110. package/dist/internal/island-resource.js.map +1 -0
  111. package/dist/internal/island-signals.d.ts +51 -0
  112. package/dist/internal/island-signals.d.ts.map +1 -0
  113. package/dist/internal/island-signals.js +220 -0
  114. package/dist/internal/island-signals.js.map +1 -0
  115. package/dist/internal/keep-alive-table.d.ts +23 -0
  116. package/dist/internal/keep-alive-table.d.ts.map +1 -0
  117. package/dist/internal/keep-alive-table.js +53 -0
  118. package/dist/internal/keep-alive-table.js.map +1 -0
  119. package/dist/internal/live-contract.d.ts +96 -0
  120. package/dist/internal/live-contract.d.ts.map +1 -0
  121. package/dist/internal/live-contract.js +5 -0
  122. package/dist/internal/live-contract.js.map +1 -0
  123. package/dist/internal/live-queries.d.ts +22 -0
  124. package/dist/internal/live-queries.d.ts.map +1 -0
  125. package/dist/internal/live-queries.js +54 -0
  126. package/dist/internal/live-queries.js.map +1 -0
  127. package/dist/internal/page-events.d.ts +10 -0
  128. package/dist/internal/page-events.d.ts.map +1 -0
  129. package/dist/internal/page-events.js +26 -0
  130. package/dist/internal/page-events.js.map +1 -0
  131. package/dist/internal/page-execution.d.ts +14 -1
  132. package/dist/internal/page-execution.d.ts.map +1 -1
  133. package/dist/internal/page-execution.js +415 -31
  134. package/dist/internal/page-execution.js.map +1 -1
  135. package/dist/internal/ppr/admission.d.ts +83 -0
  136. package/dist/internal/ppr/admission.d.ts.map +1 -0
  137. package/dist/internal/ppr/admission.js +221 -0
  138. package/dist/internal/ppr/admission.js.map +1 -0
  139. package/dist/internal/ppr/canary.d.ts +24 -0
  140. package/dist/internal/ppr/canary.d.ts.map +1 -0
  141. package/dist/internal/ppr/canary.js +102 -0
  142. package/dist/internal/ppr/canary.js.map +1 -0
  143. package/dist/internal/ppr/compile-shell.d.ts +39 -0
  144. package/dist/internal/ppr/compile-shell.d.ts.map +1 -0
  145. package/dist/internal/ppr/compile-shell.js +298 -0
  146. package/dist/internal/ppr/compile-shell.js.map +1 -0
  147. package/dist/internal/ppr/contract.d.ts +177 -0
  148. package/dist/internal/ppr/contract.d.ts.map +1 -0
  149. package/dist/internal/ppr/contract.js +80 -0
  150. package/dist/internal/ppr/contract.js.map +1 -0
  151. package/dist/internal/ppr/frames.d.ts +71 -0
  152. package/dist/internal/ppr/frames.d.ts.map +1 -0
  153. package/dist/internal/ppr/frames.js +119 -0
  154. package/dist/internal/ppr/frames.js.map +1 -0
  155. package/dist/internal/ppr/integration.d.ts +19 -0
  156. package/dist/internal/ppr/integration.d.ts.map +1 -0
  157. package/dist/internal/ppr/integration.js +106 -0
  158. package/dist/internal/ppr/integration.js.map +1 -0
  159. package/dist/internal/ppr/options.d.ts +96 -0
  160. package/dist/internal/ppr/options.d.ts.map +1 -0
  161. package/dist/internal/ppr/options.js +125 -0
  162. package/dist/internal/ppr/options.js.map +1 -0
  163. package/dist/internal/ppr/route-rules.d.ts +12 -0
  164. package/dist/internal/ppr/route-rules.d.ts.map +1 -0
  165. package/dist/internal/ppr/route-rules.js +53 -0
  166. package/dist/internal/ppr/route-rules.js.map +1 -0
  167. package/dist/internal/ppr/runtime.d.ts +29 -0
  168. package/dist/internal/ppr/runtime.d.ts.map +1 -0
  169. package/dist/internal/ppr/runtime.js +32 -0
  170. package/dist/internal/ppr/runtime.js.map +1 -0
  171. package/dist/internal/ppr/server.d.ts +29 -0
  172. package/dist/internal/ppr/server.d.ts.map +1 -0
  173. package/dist/internal/ppr/server.js +1407 -0
  174. package/dist/internal/ppr/server.js.map +1 -0
  175. package/dist/internal/ppr/shell-store.d.ts +38 -0
  176. package/dist/internal/ppr/shell-store.d.ts.map +1 -0
  177. package/dist/internal/ppr/shell-store.js +90 -0
  178. package/dist/internal/ppr/shell-store.js.map +1 -0
  179. package/dist/internal/prerendered-pages.d.ts +1 -1
  180. package/dist/internal/prerendered-pages.d.ts.map +1 -1
  181. package/dist/internal/prerendered-pages.js +7 -4
  182. package/dist/internal/prerendered-pages.js.map +1 -1
  183. package/dist/internal/query-digest.d.ts +9 -0
  184. package/dist/internal/query-digest.d.ts.map +1 -0
  185. package/dist/internal/query-digest.js +23 -0
  186. package/dist/internal/query-digest.js.map +1 -0
  187. package/dist/internal/query-runtime.d.ts +6 -1
  188. package/dist/internal/query-runtime.d.ts.map +1 -1
  189. package/dist/internal/query-runtime.js +9 -0
  190. package/dist/internal/query-runtime.js.map +1 -1
  191. package/dist/internal/query-seam.d.ts +29 -0
  192. package/dist/internal/query-seam.d.ts.map +1 -0
  193. package/dist/internal/query-seam.js +23 -0
  194. package/dist/internal/query-seam.js.map +1 -0
  195. package/dist/internal/render-document.d.ts +28 -1
  196. package/dist/internal/render-document.d.ts.map +1 -1
  197. package/dist/internal/render-document.js +83 -18
  198. package/dist/internal/render-document.js.map +1 -1
  199. package/dist/internal/request-abort.d.ts +8 -0
  200. package/dist/internal/request-abort.d.ts.map +1 -0
  201. package/dist/internal/request-abort.js +22 -0
  202. package/dist/internal/request-abort.js.map +1 -0
  203. package/dist/internal/server-boundary.d.ts +2 -0
  204. package/dist/internal/server-boundary.d.ts.map +1 -1
  205. package/dist/internal/server-boundary.js +8 -1
  206. package/dist/internal/server-boundary.js.map +1 -1
  207. package/dist/internal/share-data.d.ts +10 -0
  208. package/dist/internal/share-data.d.ts.map +1 -0
  209. package/dist/internal/share-data.js +135 -0
  210. package/dist/internal/share-data.js.map +1 -0
  211. package/dist/internal/store-signature.d.ts +15 -0
  212. package/dist/internal/store-signature.d.ts.map +1 -0
  213. package/dist/internal/store-signature.js +114 -0
  214. package/dist/internal/store-signature.js.map +1 -0
  215. package/dist/internal/vite-plugin-compat.d.ts +2 -2
  216. package/dist/internal/vite-plugin-compat.js +2 -2
  217. package/dist/internal/web-app.d.ts +25 -0
  218. package/dist/internal/web-app.d.ts.map +1 -1
  219. package/dist/internal/web-app.js +78 -3
  220. package/dist/internal/web-app.js.map +1 -1
  221. package/dist/islands-swap.d.ts +41 -0
  222. package/dist/islands-swap.d.ts.map +1 -0
  223. package/dist/islands-swap.js +270 -0
  224. package/dist/islands-swap.js.map +1 -0
  225. package/dist/islands.d.ts +86 -24
  226. package/dist/islands.d.ts.map +1 -1
  227. package/dist/islands.js +138 -78
  228. package/dist/islands.js.map +1 -1
  229. package/dist/isr.d.ts +67 -13
  230. package/dist/isr.d.ts.map +1 -1
  231. package/dist/isr.js +247 -99
  232. package/dist/isr.js.map +1 -1
  233. package/dist/keep-alive.d.ts +147 -0
  234. package/dist/keep-alive.d.ts.map +1 -0
  235. package/dist/keep-alive.js +1069 -0
  236. package/dist/keep-alive.js.map +1 -0
  237. package/dist/live/bus.d.ts +56 -0
  238. package/dist/live/bus.d.ts.map +1 -0
  239. package/dist/live/bus.js +32 -0
  240. package/dist/live/bus.js.map +1 -0
  241. package/dist/live/core.d.ts +25 -0
  242. package/dist/live/core.d.ts.map +1 -0
  243. package/dist/live/core.js +29 -0
  244. package/dist/live/core.js.map +1 -0
  245. package/dist/live/history.d.ts +38 -0
  246. package/dist/live/history.d.ts.map +1 -0
  247. package/dist/live/history.js +104 -0
  248. package/dist/live/history.js.map +1 -0
  249. package/dist/live/hub-protocol.d.ts +46 -0
  250. package/dist/live/hub-protocol.d.ts.map +1 -0
  251. package/dist/live/hub-protocol.js +108 -0
  252. package/dist/live/hub-protocol.js.map +1 -0
  253. package/dist/live/hub.d.ts +181 -0
  254. package/dist/live/hub.d.ts.map +1 -0
  255. package/dist/live/hub.js +539 -0
  256. package/dist/live/hub.js.map +1 -0
  257. package/dist/live/keyring.d.ts +87 -0
  258. package/dist/live/keyring.d.ts.map +1 -0
  259. package/dist/live/keyring.js +260 -0
  260. package/dist/live/keyring.js.map +1 -0
  261. package/dist/live/read-capped.d.ts +6 -0
  262. package/dist/live/read-capped.d.ts.map +1 -0
  263. package/dist/live/read-capped.js +42 -0
  264. package/dist/live/read-capped.js.map +1 -0
  265. package/dist/live/revocations.d.ts +52 -0
  266. package/dist/live/revocations.d.ts.map +1 -0
  267. package/dist/live/revocations.js +116 -0
  268. package/dist/live/revocations.js.map +1 -0
  269. package/dist/live/settings.d.ts +14 -0
  270. package/dist/live/settings.d.ts.map +1 -0
  271. package/dist/live/settings.js +38 -0
  272. package/dist/live/settings.js.map +1 -0
  273. package/dist/live-client.d.ts +34 -0
  274. package/dist/live-client.d.ts.map +1 -0
  275. package/dist/live-client.js +840 -0
  276. package/dist/live-client.js.map +1 -0
  277. package/dist/live-hub.d.ts +130 -0
  278. package/dist/live-hub.d.ts.map +1 -0
  279. package/dist/live-hub.js +507 -0
  280. package/dist/live-hub.js.map +1 -0
  281. package/dist/live-redis.d.ts +48 -0
  282. package/dist/live-redis.d.ts.map +1 -0
  283. package/dist/live-redis.js +201 -0
  284. package/dist/live-redis.js.map +1 -0
  285. package/dist/live.d.ts +94 -0
  286. package/dist/live.d.ts.map +1 -0
  287. package/dist/live.js +869 -0
  288. package/dist/live.js.map +1 -0
  289. package/dist/manifest.d.ts +113 -2
  290. package/dist/manifest.d.ts.map +1 -1
  291. package/dist/manifest.js +109 -5
  292. package/dist/manifest.js.map +1 -1
  293. package/dist/navigation.d.ts +9 -3
  294. package/dist/navigation.d.ts.map +1 -1
  295. package/dist/navigation.js +13 -3
  296. package/dist/navigation.js.map +1 -1
  297. package/dist/plugins/vite-zone-guard.d.ts +6 -0
  298. package/dist/plugins/vite-zone-guard.d.ts.map +1 -1
  299. package/dist/plugins/vite-zone-guard.js +8 -2
  300. package/dist/plugins/vite-zone-guard.js.map +1 -1
  301. package/dist/ppr.d.ts +73 -0
  302. package/dist/ppr.d.ts.map +1 -0
  303. package/dist/ppr.js +76 -0
  304. package/dist/ppr.js.map +1 -0
  305. package/dist/query.d.ts +8 -13
  306. package/dist/query.d.ts.map +1 -1
  307. package/dist/query.js +112 -18
  308. package/dist/query.js.map +1 -1
  309. package/dist/render-seam.d.ts +30 -0
  310. package/dist/render-seam.d.ts.map +1 -1
  311. package/dist/render-seam.js +5 -0
  312. package/dist/render-seam.js.map +1 -1
  313. package/dist/route-types.d.ts.map +1 -1
  314. package/dist/route-types.js +2 -2
  315. package/dist/route-types.js.map +1 -1
  316. package/dist/router.d.ts +72 -4
  317. package/dist/router.d.ts.map +1 -1
  318. package/dist/router.js +374 -99
  319. package/dist/router.js.map +1 -1
  320. package/dist/server-components/build.d.ts.map +1 -1
  321. package/dist/server-components/build.js +3 -3
  322. package/dist/server-components/build.js.map +1 -1
  323. package/dist/server-components/client.d.ts +23 -1
  324. package/dist/server-components/client.d.ts.map +1 -1
  325. package/dist/server-components/client.js +92 -5
  326. package/dist/server-components/client.js.map +1 -1
  327. package/dist/server-components/hole.d.ts +8 -0
  328. package/dist/server-components/hole.d.ts.map +1 -0
  329. package/dist/server-components/hole.js +20 -0
  330. package/dist/server-components/hole.js.map +1 -0
  331. package/dist/server-components/html-policy.d.ts.map +1 -1
  332. package/dist/server-components/html-policy.js +62 -4
  333. package/dist/server-components/html-policy.js.map +1 -1
  334. package/dist/server-components/html-scan.d.ts +17 -0
  335. package/dist/server-components/html-scan.d.ts.map +1 -0
  336. package/dist/server-components/html-scan.js +180 -0
  337. package/dist/server-components/html-scan.js.map +1 -0
  338. package/dist/server-components/html.d.ts.map +1 -1
  339. package/dist/server-components/html.js +19 -4
  340. package/dist/server-components/html.js.map +1 -1
  341. package/dist/server-components/router-integration.d.ts +4 -1
  342. package/dist/server-components/router-integration.d.ts.map +1 -1
  343. package/dist/server-components/router-integration.js +23 -2
  344. package/dist/server-components/router-integration.js.map +1 -1
  345. package/dist/server-components/runtime.d.ts +6 -1
  346. package/dist/server-components/runtime.d.ts.map +1 -1
  347. package/dist/server-components/runtime.js +86 -28
  348. package/dist/server-components/runtime.js.map +1 -1
  349. package/dist/server-components/state.d.ts +3 -1
  350. package/dist/server-components/state.d.ts.map +1 -1
  351. package/dist/server-components/state.js +50 -38
  352. package/dist/server-components/state.js.map +1 -1
  353. package/dist/server-components/types.d.ts +9 -0
  354. package/dist/server-components/types.d.ts.map +1 -1
  355. package/dist/vite.d.ts +5 -1
  356. package/dist/vite.d.ts.map +1 -1
  357. package/dist/vite.js +58 -4
  358. package/dist/vite.js.map +1 -1
  359. package/package.json +48 -8
  360. package/src/boundary.ts +77 -5
  361. package/src/build-plan.ts +3 -0
  362. package/src/build-vite.ts +2 -0
  363. package/src/build.ts +19 -1
  364. package/src/client.ts +147 -10
  365. package/src/csp.ts +16 -2
  366. package/src/deferred.ts +11 -5
  367. package/src/dev-feed.ts +11 -2
  368. package/src/dev-session.ts +22 -11
  369. package/src/dev.ts +29 -8
  370. package/src/diagnostic-catalog.ts +53 -0
  371. package/src/diagnostic.ts +1 -0
  372. package/src/fn.ts +24 -7
  373. package/src/fs.ts +22 -8
  374. package/src/identity.ts +61 -0
  375. package/src/index.ts +19 -0
  376. package/src/internal/cdn/cloudflare.ts +2 -1
  377. package/src/internal/certified-components.ts +1 -1
  378. package/src/internal/codegen.ts +87 -8
  379. package/src/internal/dev-websocket.ts +230 -0
  380. package/src/internal/hole-runtime.ts +286 -0
  381. package/src/internal/identity-epoch.ts +235 -0
  382. package/src/internal/invalidation-contract.ts +35 -0
  383. package/src/internal/invalidation.ts +213 -0
  384. package/src/internal/island-bindings.ts +191 -0
  385. package/src/internal/island-list.ts +92 -0
  386. package/src/internal/island-mount.ts +68 -0
  387. package/src/internal/island-owner.ts +20 -0
  388. package/src/internal/island-resource.ts +76 -0
  389. package/src/internal/island-signals.ts +249 -0
  390. package/src/internal/keep-alive-table.ts +72 -0
  391. package/src/internal/live-contract.ts +106 -0
  392. package/src/internal/live-queries.ts +60 -0
  393. package/src/internal/page-events.ts +27 -0
  394. package/src/internal/page-execution.ts +579 -40
  395. package/src/internal/ppr/admission.ts +300 -0
  396. package/src/internal/ppr/canary.ts +122 -0
  397. package/src/internal/ppr/compile-shell.ts +379 -0
  398. package/src/internal/ppr/contract.ts +265 -0
  399. package/src/internal/ppr/frames.ts +170 -0
  400. package/src/internal/ppr/integration.ts +137 -0
  401. package/src/internal/ppr/options.ts +221 -0
  402. package/src/internal/ppr/route-rules.ts +77 -0
  403. package/src/internal/ppr/runtime.ts +36 -0
  404. package/src/internal/ppr/server.ts +1764 -0
  405. package/src/internal/ppr/shell-store.ts +139 -0
  406. package/src/internal/prerendered-pages.ts +7 -4
  407. package/src/internal/query-digest.ts +34 -0
  408. package/src/internal/query-runtime.ts +14 -1
  409. package/src/internal/query-seam.ts +48 -0
  410. package/src/internal/render-document.ts +149 -22
  411. package/src/internal/request-abort.ts +26 -0
  412. package/src/internal/server-boundary.ts +10 -1
  413. package/src/internal/share-data.ts +136 -0
  414. package/src/internal/store-signature.ts +149 -0
  415. package/src/internal/vite-plugin-compat.ts +2 -2
  416. package/src/internal/web-app.ts +121 -3
  417. package/src/islands-swap.ts +283 -0
  418. package/src/islands.ts +169 -72
  419. package/src/isr.ts +348 -106
  420. package/src/keep-alive.ts +1315 -0
  421. package/src/live/bus.ts +77 -0
  422. package/src/live/core.ts +52 -0
  423. package/src/live/history.ts +109 -0
  424. package/src/live/hub-protocol.ts +133 -0
  425. package/src/live/hub.ts +615 -0
  426. package/src/live/keyring.ts +379 -0
  427. package/src/live/read-capped.ts +36 -0
  428. package/src/live/revocations.ts +155 -0
  429. package/src/live/settings.ts +46 -0
  430. package/src/live-client.ts +973 -0
  431. package/src/live-hub.ts +658 -0
  432. package/src/live-redis.ts +259 -0
  433. package/src/live.ts +1096 -0
  434. package/src/manifest.ts +274 -6
  435. package/src/navigation.ts +14 -2
  436. package/src/plugins/vite-zone-guard.ts +15 -2
  437. package/src/ppr.ts +153 -0
  438. package/src/query.ts +147 -20
  439. package/src/render-seam.ts +35 -0
  440. package/src/route-types.ts +3 -1
  441. package/src/router.ts +494 -101
  442. package/src/server-components/build.ts +9 -3
  443. package/src/server-components/client.ts +89 -5
  444. package/src/server-components/hole.ts +23 -0
  445. package/src/server-components/html-policy.ts +63 -4
  446. package/src/server-components/html-scan.ts +159 -0
  447. package/src/server-components/html.ts +18 -4
  448. package/src/server-components/router-integration.ts +21 -2
  449. package/src/server-components/runtime.ts +112 -30
  450. package/src/server-components/state.ts +66 -44
  451. package/src/server-components/types.ts +16 -0
  452. package/src/vite.ts +75 -6
  453. package/dist/nano.d.ts +0 -125
  454. package/dist/nano.d.ts.map +0 -1
  455. package/dist/nano.js +0 -190
  456. package/dist/nano.js.map +0 -1
  457. package/src/nano.ts +0 -246
package/dist/nano.js DELETED
@@ -1,190 +0,0 @@
1
- /**
2
- * `@nifrajs/web/nano` - the smallest state layer that still lets an island drive a keyed list.
3
- *
4
- * The islands lane (`@nifrajs/web/islands`) is imperative and correct-by-construction, but it has no
5
- * state primitive: every DOM update is hand-written, and a list means rebuilding the whole container.
6
- * nano adds a small, closed set of primitives and stops:
7
- *
8
- * - `signal(v)` - a current-value cell you read with `.get()` and write with `.set()`.
9
- * - `computed(fn, [deps])` - a derived cell. Its dependencies are DECLARED, never auto-tracked.
10
- * - `resource(fetcher, [deps])` - an async cell with an explicit pending/error/ready value. This is
11
- * nano's answer to "suspense": no thrown promise, no magic boundary, just a value you can bind.
12
- * - `bind` / `bindList` / `bindResource` - explicit, one-directional DOM bindings that each return
13
- * their teardown.
14
- *
15
- * The design rule is the whole point: every reactive edge is a call you can see - a `bind(...)` or a
16
- * `computed(fn, [deps])`. There is no re-render scope, no VDOM, no template compiler, and no effect
17
- * that silently subscribes to whatever it happened to read. That is what removes the three failure
18
- * modes an AI (or a human) hits in a framework - stale closures, wrong effect deps, hydration
19
- * mismatch - and, just as importantly, it is what makes every remaining mistake STATICALLY
20
- * detectable: a missing dep is `[deps]` that doesn't list a signal the body reads; a leak is a `bind`
21
- * whose cleanup isn't returned. A framework's reactivity can't be linted like that; nano's can.
22
- *
23
- * Where nano stops: no client router and no nested/composable view state (a component tree that mounts
24
- * and unmounts its own children). When an app needs those, it has outgrown a vanilla page - reach for
25
- * a framework adapter (`@nifrajs/web-preact`), not more nano.
26
- *
27
- * Browser code: it touches the DOM and is meant to run in an island enhancer, never under SSR.
28
- */
29
- /** Notify a snapshot of the current listeners, so a listener that (un)subscribes mid-dispatch does not
30
- * corrupt the walk. A throwing listener is isolated - it never blocks the others. */
31
- function notify(listeners, value) {
32
- for (const listener of [...listeners]) {
33
- try {
34
- listener(value);
35
- }
36
- catch (error) {
37
- console.error("[nifra/nano] a subscriber threw:", error);
38
- }
39
- }
40
- }
41
- /** A current-value cell. `signal(0)` -> `.get()` reads, `.set(1)` writes and notifies. */
42
- export function signal(initial) {
43
- let value = initial;
44
- const listeners = new Set();
45
- return {
46
- get: () => value,
47
- set(next) {
48
- if (Object.is(next, value))
49
- return;
50
- value = next;
51
- notify(listeners, value);
52
- },
53
- subscribe(listener) {
54
- listeners.add(listener);
55
- return () => {
56
- listeners.delete(listener);
57
- };
58
- },
59
- };
60
- }
61
- /**
62
- * A derived cell. `computed(() => a.get() + b.get(), [a, b])` recomputes whenever a declared
63
- * dependency changes and notifies its own subscribers when the derived value changes.
64
- *
65
- * The `deps` array is mandatory and explicit ON PURPOSE - it is the contract a linter checks against
66
- * the signals the body reads (`NF-C023`). Auto-tracking would remove the array but reintroduce the
67
- * exact "why didn't this update" bug that makes framework reactivity hard to get right.
68
- */
69
- export function computed(compute, deps) {
70
- let value = compute();
71
- const listeners = new Set();
72
- const recompute = () => {
73
- const next = compute();
74
- if (Object.is(next, value))
75
- return;
76
- value = next;
77
- notify(listeners, value);
78
- };
79
- // A computed with a live subscriber must itself be subscribed to its deps; to keep the prototype
80
- // simple it subscribes eagerly for its whole life (fine for the island-scoped, few-signal case).
81
- for (const dep of deps)
82
- dep.subscribe(recompute);
83
- return {
84
- get: () => value,
85
- subscribe(listener) {
86
- listeners.add(listener);
87
- return () => {
88
- listeners.delete(listener);
89
- };
90
- },
91
- };
92
- }
93
- /**
94
- * An async derived cell. `resource(fetcher, [deps])` runs `fetcher` immediately and again whenever a
95
- * declared dependency changes, exposing the result as an explicit `{ status, value, error }`.
96
- *
97
- * Two footguns are handled here so they cannot be got wrong by hand:
98
- * - Races: each run holds a generation; a stale run's resolution is dropped, and its `AbortSignal` is
99
- * aborted, so an earlier-started-later-finishing fetch never overwrites a newer one.
100
- * - Deps: like `computed`, dependencies are DECLARED (`NF-C023` checks the fetcher's `.get()` reads
101
- * against the array). A `resource` that reads a signal its deps omit won't refetch when it changes.
102
- */
103
- export function resource(fetcher, deps = []) {
104
- const state = signal({ status: "pending", value: undefined, error: undefined });
105
- let generation = 0;
106
- let controller;
107
- const run = () => {
108
- const mine = ++generation;
109
- controller?.abort();
110
- controller = new AbortController();
111
- const active = controller;
112
- state.set({ status: "pending", value: undefined, error: undefined });
113
- fetcher(active.signal).then((value) => {
114
- if (mine === generation)
115
- state.set({ status: "ready", value, error: undefined });
116
- }, (error) => {
117
- // A stale run (superseded) or one we aborted must not surface as an error state.
118
- if (mine === generation && !active.signal.aborted)
119
- state.set({ status: "error", value: undefined, error });
120
- });
121
- };
122
- for (const dep of deps)
123
- dep.subscribe(run);
124
- run();
125
- return {
126
- get: () => state.get(),
127
- subscribe: (listener) => state.subscribe(listener),
128
- refetch: run,
129
- };
130
- }
131
- /**
132
- * Bind one element to a source: `apply(el, value)` runs once immediately and again on every change.
133
- * Returns the unsubscribe - hand it back as the island's cleanup (or collect several).
134
- *
135
- * bind(count, remaining, (el, n) => { el.textContent = String(n) })
136
- */
137
- export function bind(el, source, apply) {
138
- apply(el, source.get());
139
- return source.subscribe((value) => apply(el, value));
140
- }
141
- /**
142
- * Bind a list signal to a container with keyed reconciliation: new items are `create`d, surviving
143
- * items are `update`d in place (keeping focus, scroll, and selection), removed items are detached,
144
- * and the children are ordered to match the array. Returns the unsubscribe.
145
- *
146
- * This is the one imperative task too error-prone to hand-write each time - it is why nano exists on
147
- * top of islands rather than leaving you to rebuild the container on every change.
148
- */
149
- export function bindList(source, container, options) {
150
- let nodes = new Map();
151
- const render = (items) => {
152
- const next = new Map();
153
- for (const item of items) {
154
- const key = options.key(item);
155
- const el = nodes.get(key) ?? options.create(item);
156
- options.update?.(el, item);
157
- next.set(key, el);
158
- }
159
- for (const [key, el] of nodes)
160
- if (!next.has(key))
161
- el.remove();
162
- // Append in item order; appending an already-attached node MOVES it, so the final order matches.
163
- for (const item of items)
164
- container.appendChild(next.get(options.key(item)));
165
- nodes = next;
166
- };
167
- render(source.get());
168
- return source.subscribe(render);
169
- }
170
- /**
171
- * Bind an element to a `resource`, dispatching on `status`. Like `bind`, it applies immediately and
172
- * on every change and returns the unsubscribe - collect it (a discarded disposer is `NF-C021`).
173
- *
174
- * bindResource(el, user, {
175
- * pending: (n) => { n.textContent = "Loading…" },
176
- * ready: (n, u) => { n.textContent = u.name },
177
- * error: (n) => { n.textContent = "Failed to load" },
178
- * })
179
- */
180
- export function bindResource(el, source, handlers) {
181
- return bind(el, source, (node, state) => {
182
- if (state.status === "pending")
183
- handlers.pending?.(node);
184
- else if (state.status === "error")
185
- handlers.error?.(node, state.error);
186
- else
187
- handlers.ready(node, state.value);
188
- });
189
- }
190
- //# sourceMappingURL=nano.js.map
package/dist/nano.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"nano.js","sourceRoot":"","sources":["../src/nano.ts"],"names":[],"mappings":"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"}
package/src/nano.ts DELETED
@@ -1,246 +0,0 @@
1
- /**
2
- * `@nifrajs/web/nano` - the smallest state layer that still lets an island drive a keyed list.
3
- *
4
- * The islands lane (`@nifrajs/web/islands`) is imperative and correct-by-construction, but it has no
5
- * state primitive: every DOM update is hand-written, and a list means rebuilding the whole container.
6
- * nano adds a small, closed set of primitives and stops:
7
- *
8
- * - `signal(v)` - a current-value cell you read with `.get()` and write with `.set()`.
9
- * - `computed(fn, [deps])` - a derived cell. Its dependencies are DECLARED, never auto-tracked.
10
- * - `resource(fetcher, [deps])` - an async cell with an explicit pending/error/ready value. This is
11
- * nano's answer to "suspense": no thrown promise, no magic boundary, just a value you can bind.
12
- * - `bind` / `bindList` / `bindResource` - explicit, one-directional DOM bindings that each return
13
- * their teardown.
14
- *
15
- * The design rule is the whole point: every reactive edge is a call you can see - a `bind(...)` or a
16
- * `computed(fn, [deps])`. There is no re-render scope, no VDOM, no template compiler, and no effect
17
- * that silently subscribes to whatever it happened to read. That is what removes the three failure
18
- * modes an AI (or a human) hits in a framework - stale closures, wrong effect deps, hydration
19
- * mismatch - and, just as importantly, it is what makes every remaining mistake STATICALLY
20
- * detectable: a missing dep is `[deps]` that doesn't list a signal the body reads; a leak is a `bind`
21
- * whose cleanup isn't returned. A framework's reactivity can't be linted like that; nano's can.
22
- *
23
- * Where nano stops: no client router and no nested/composable view state (a component tree that mounts
24
- * and unmounts its own children). When an app needs those, it has outgrown a vanilla page - reach for
25
- * a framework adapter (`@nifrajs/web-preact`), not more nano.
26
- *
27
- * Browser code: it touches the DOM and is meant to run in an island enhancer, never under SSR.
28
- */
29
-
30
- /** A value you can read now and be told about later. Both `signal` and `computed` are `Readable`, so
31
- * `bind`/`bindList` accept either. */
32
- export interface Readable<T> {
33
- get(): T
34
- subscribe(listener: (value: T) => void): () => void
35
- }
36
-
37
- /** A writable cell. `set` notifies subscribers only when the value actually changes (`Object.is`), so
38
- * a redundant write costs nothing. */
39
- export interface Signal<T> extends Readable<T> {
40
- set(value: T): void
41
- }
42
-
43
- /** Notify a snapshot of the current listeners, so a listener that (un)subscribes mid-dispatch does not
44
- * corrupt the walk. A throwing listener is isolated - it never blocks the others. */
45
- function notify<T>(listeners: Set<(value: T) => void>, value: T): void {
46
- for (const listener of [...listeners]) {
47
- try {
48
- listener(value)
49
- } catch (error) {
50
- console.error("[nifra/nano] a subscriber threw:", error)
51
- }
52
- }
53
- }
54
-
55
- /** A current-value cell. `signal(0)` -> `.get()` reads, `.set(1)` writes and notifies. */
56
- export function signal<T>(initial: T): Signal<T> {
57
- let value = initial
58
- const listeners = new Set<(value: T) => void>()
59
- return {
60
- get: () => value,
61
- set(next: T) {
62
- if (Object.is(next, value)) return
63
- value = next
64
- notify(listeners, value)
65
- },
66
- subscribe(listener) {
67
- listeners.add(listener)
68
- return () => {
69
- listeners.delete(listener)
70
- }
71
- },
72
- }
73
- }
74
-
75
- /**
76
- * A derived cell. `computed(() => a.get() + b.get(), [a, b])` recomputes whenever a declared
77
- * dependency changes and notifies its own subscribers when the derived value changes.
78
- *
79
- * The `deps` array is mandatory and explicit ON PURPOSE - it is the contract a linter checks against
80
- * the signals the body reads (`NF-C023`). Auto-tracking would remove the array but reintroduce the
81
- * exact "why didn't this update" bug that makes framework reactivity hard to get right.
82
- */
83
- export function computed<T>(compute: () => T, deps: readonly Readable<unknown>[]): Readable<T> {
84
- let value = compute()
85
- const listeners = new Set<(value: T) => void>()
86
- const recompute = (): void => {
87
- const next = compute()
88
- if (Object.is(next, value)) return
89
- value = next
90
- notify(listeners, value)
91
- }
92
- // A computed with a live subscriber must itself be subscribed to its deps; to keep the prototype
93
- // simple it subscribes eagerly for its whole life (fine for the island-scoped, few-signal case).
94
- for (const dep of deps) dep.subscribe(recompute)
95
- return {
96
- get: () => value,
97
- subscribe(listener) {
98
- listeners.add(listener)
99
- return () => {
100
- listeners.delete(listener)
101
- }
102
- },
103
- }
104
- }
105
-
106
- /** The three states of an async cell. A discriminated union on `status` so a consumer must handle
107
- * every branch - there is no "value that might secretly be loading", the failure mode of ad-hoc
108
- * `isLoading` booleans. `value`/`error` are narrowed by `status`. */
109
- export type ResourceState<T> =
110
- | { readonly status: "pending"; readonly value: undefined; readonly error: undefined }
111
- | { readonly status: "ready"; readonly value: T; readonly error: undefined }
112
- | { readonly status: "error"; readonly value: undefined; readonly error: unknown }
113
-
114
- /** An async cell: a `Readable` of `ResourceState<T>` plus `refetch()`. This is nano's answer to
115
- * "suspense" - an explicit pending/error/ready value, never a thrown promise or a magic boundary. */
116
- export interface Resource<T> extends Readable<ResourceState<T>> {
117
- refetch(): void
118
- }
119
-
120
- /**
121
- * An async derived cell. `resource(fetcher, [deps])` runs `fetcher` immediately and again whenever a
122
- * declared dependency changes, exposing the result as an explicit `{ status, value, error }`.
123
- *
124
- * Two footguns are handled here so they cannot be got wrong by hand:
125
- * - Races: each run holds a generation; a stale run's resolution is dropped, and its `AbortSignal` is
126
- * aborted, so an earlier-started-later-finishing fetch never overwrites a newer one.
127
- * - Deps: like `computed`, dependencies are DECLARED (`NF-C023` checks the fetcher's `.get()` reads
128
- * against the array). A `resource` that reads a signal its deps omit won't refetch when it changes.
129
- */
130
- export function resource<T>(
131
- fetcher: (signal: AbortSignal) => Promise<T>,
132
- deps: readonly Readable<unknown>[] = [],
133
- ): Resource<T> {
134
- const state = signal<ResourceState<T>>({ status: "pending", value: undefined, error: undefined })
135
- let generation = 0
136
- let controller: AbortController | undefined
137
- const run = (): void => {
138
- const mine = ++generation
139
- controller?.abort()
140
- controller = new AbortController()
141
- const active = controller
142
- state.set({ status: "pending", value: undefined, error: undefined })
143
- fetcher(active.signal).then(
144
- (value) => {
145
- if (mine === generation) state.set({ status: "ready", value, error: undefined })
146
- },
147
- (error) => {
148
- // A stale run (superseded) or one we aborted must not surface as an error state.
149
- if (mine === generation && !active.signal.aborted)
150
- state.set({ status: "error", value: undefined, error })
151
- },
152
- )
153
- }
154
- for (const dep of deps) dep.subscribe(run)
155
- run()
156
- return {
157
- get: () => state.get(),
158
- subscribe: (listener) => state.subscribe(listener),
159
- refetch: run,
160
- }
161
- }
162
-
163
- /**
164
- * Bind one element to a source: `apply(el, value)` runs once immediately and again on every change.
165
- * Returns the unsubscribe - hand it back as the island's cleanup (or collect several).
166
- *
167
- * bind(count, remaining, (el, n) => { el.textContent = String(n) })
168
- */
169
- export function bind<T>(
170
- el: HTMLElement,
171
- source: Readable<T>,
172
- apply: (el: HTMLElement, value: T) => void,
173
- ): () => void {
174
- apply(el, source.get())
175
- return source.subscribe((value) => apply(el, value))
176
- }
177
-
178
- /** How a `bindList` turns items into keyed DOM. `key` MUST be stable and unique per item (never the
179
- * array index) - it is what lets add/remove/reorder touch only the changed rows. */
180
- export interface BindListOptions<T> {
181
- key(item: T): string | number
182
- create(item: T): HTMLElement
183
- update?(el: HTMLElement, item: T): void
184
- }
185
-
186
- /**
187
- * Bind a list signal to a container with keyed reconciliation: new items are `create`d, surviving
188
- * items are `update`d in place (keeping focus, scroll, and selection), removed items are detached,
189
- * and the children are ordered to match the array. Returns the unsubscribe.
190
- *
191
- * This is the one imperative task too error-prone to hand-write each time - it is why nano exists on
192
- * top of islands rather than leaving you to rebuild the container on every change.
193
- */
194
- export function bindList<T>(
195
- source: Readable<readonly T[]>,
196
- container: HTMLElement,
197
- options: BindListOptions<T>,
198
- ): () => void {
199
- let nodes = new Map<string | number, HTMLElement>()
200
- const render = (items: readonly T[]): void => {
201
- const next = new Map<string | number, HTMLElement>()
202
- for (const item of items) {
203
- const key = options.key(item)
204
- const el = nodes.get(key) ?? options.create(item)
205
- options.update?.(el, item)
206
- next.set(key, el)
207
- }
208
- for (const [key, el] of nodes) if (!next.has(key)) el.remove()
209
- // Append in item order; appending an already-attached node MOVES it, so the final order matches.
210
- for (const item of items) container.appendChild(next.get(options.key(item)) as HTMLElement)
211
- nodes = next
212
- }
213
- render(source.get())
214
- return source.subscribe(render)
215
- }
216
-
217
- /** How a `bindResource` maps each async state to the DOM. `ready` is required (there is always a
218
- * success shape to render); `pending`/`error` are optional. Every branch is explicit - no hidden
219
- * "still loading" state can slip through as a rendered `undefined`. */
220
- export interface BindResourceHandlers<T> {
221
- ready(el: HTMLElement, value: T): void
222
- pending?(el: HTMLElement): void
223
- error?(el: HTMLElement, error: unknown): void
224
- }
225
-
226
- /**
227
- * Bind an element to a `resource`, dispatching on `status`. Like `bind`, it applies immediately and
228
- * on every change and returns the unsubscribe - collect it (a discarded disposer is `NF-C021`).
229
- *
230
- * bindResource(el, user, {
231
- * pending: (n) => { n.textContent = "Loading…" },
232
- * ready: (n, u) => { n.textContent = u.name },
233
- * error: (n) => { n.textContent = "Failed to load" },
234
- * })
235
- */
236
- export function bindResource<T>(
237
- el: HTMLElement,
238
- source: Readable<ResourceState<T>>,
239
- handlers: BindResourceHandlers<T>,
240
- ): () => void {
241
- return bind(el, source, (node, state) => {
242
- if (state.status === "pending") handlers.pending?.(node)
243
- else if (state.status === "error") handlers.error?.(node, state.error)
244
- else handlers.ready(node, state.value)
245
- })
246
- }