@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
@@ -0,0 +1,300 @@
1
+ import type { ResolvedPprOptions } from "./options.ts"
2
+
3
+ /** Which admission level denied an invocation, checked in this order. */
4
+ export type DenialLevel = "stop" | "process" | "boundary"
5
+
6
+ /** How one hole invocation ended, as its response sees it. */
7
+ export type HoleRun<T> =
8
+ | { readonly ok: true; readonly value: T }
9
+ | {
10
+ readonly ok: false
11
+ readonly reason: "denied" | "failed" | "aborted"
12
+ readonly error?: unknown
13
+ }
14
+
15
+ export interface PprAdmissionStats {
16
+ /** Responses holding a hole-work slot. */
17
+ readonly slotsInUse: number
18
+ /** Every invocation whose promise has not settled, abandoned ones included (`ppr.inflight_loaders`). */
19
+ readonly pending: number
20
+ /** The abandoned subset of {@link pending} (`ppr.abandoned_inflight`). */
21
+ readonly abandoned: number
22
+ readonly pendingBy: Readonly<Record<string, number>>
23
+ readonly abandonedBy: Readonly<Record<string, number>>
24
+ readonly denied: Readonly<Record<DenialLevel, number>>
25
+ /** Responses that found no free slot and invoked no hole (`ppr.shed`). */
26
+ readonly shed: number
27
+ /** True while the global admission stop holds (`ppr.admission_stopped`). */
28
+ readonly admissionStopped: boolean
29
+ }
30
+
31
+ type AdmissionOptions = Pick<
32
+ ResolvedPprOptions,
33
+ | "boundaryAbandonedTrip"
34
+ | "processAbandonedTrip"
35
+ | "admissionStop"
36
+ | "abandonGraceMs"
37
+ | "maxHoleWork"
38
+ >
39
+
40
+ /** The clock and timer admission reads; injected by tests so no test waits on the wall clock. */
41
+ export interface AdmissionTimers {
42
+ now(): number
43
+ setTimeout(callback: () => void, ms: number): unknown
44
+ clearTimeout(handle: unknown): void
45
+ }
46
+
47
+ const SYSTEM_TIMERS: AdmissionTimers = {
48
+ now: () => performance.now(),
49
+ setTimeout: (callback, ms) => setTimeout(callback, ms),
50
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: the handle came from this object's own setTimeout.
51
+ clearTimeout: (handle) => clearTimeout(handle as ReturnType<typeof setTimeout>),
52
+ }
53
+
54
+ const watchers = new WeakMap<AbortSignal, Set<() => void>>()
55
+
56
+ /**
57
+ * Calls `onAbort` when `signal` aborts, until the returned function is called. A signal gets one
58
+ * native listener that every watcher shares: adding and removing a native listener per hole
59
+ * invocation cost more under load than the invocation's own bookkeeping.
60
+ */
61
+ export function watchAbort(signal: AbortSignal, onAbort: () => void): () => void {
62
+ let watching = watchers.get(signal)
63
+ if (watching === undefined) {
64
+ const created = new Set<() => void>()
65
+ watching = created
66
+ watchers.set(signal, created)
67
+ signal.addEventListener(
68
+ "abort",
69
+ () => {
70
+ for (const watcher of created) watcher()
71
+ },
72
+ { once: true },
73
+ )
74
+ }
75
+ const set = watching
76
+ set.add(onAbort)
77
+ return () => {
78
+ set.delete(onAbort)
79
+ }
80
+ }
81
+
82
+ /** A boundary's count, kept once made: an invocation changes its own count with no map lookup. */
83
+ interface Count {
84
+ n: number
85
+ }
86
+
87
+ const countOf = (counts: Map<string, Count>, key: string): Count => {
88
+ let count = counts.get(key)
89
+ if (count === undefined) {
90
+ count = { n: 0 }
91
+ counts.set(key, count)
92
+ }
93
+ return count
94
+ }
95
+
96
+ const nonZero = (counts: Map<string, Count>): Record<string, number> => {
97
+ const out: Record<string, number> = {}
98
+ for (const [key, count] of counts) if (count.n !== 0) out[key] = count.n
99
+ return out
100
+ }
101
+
102
+ /**
103
+ * Bounds the hole work a process runs without letting one bad loader starve the rest.
104
+ *
105
+ * An invocation is abandoned once its signal aborted more than `abandonGraceMs` ago and its promise
106
+ * is still pending; it stays counted until the promise settles. Only abandoned invocations count
107
+ * toward a denial level, so a well-behaved boundary is denied only at the global stop. Each response
108
+ * holds one hole-work slot from before its first invocation until every invocation has settled,
109
+ * been denied or been abandoned, which bounds live invocations by `32 * maxHoleWork`.
110
+ */
111
+ export class HoleAdmission {
112
+ readonly #options: AdmissionOptions
113
+ readonly #onLateSettle: ((key: string, msAfterAbort: number) => void) | undefined
114
+ readonly #timers: AdmissionTimers
115
+ readonly #pendingBy = new Map<string, Count>()
116
+ readonly #abandonedBy = new Map<string, Count>()
117
+ readonly #denied: Record<DenialLevel, number> = { stop: 0, process: 0, boundary: 0 }
118
+ #pending = 0
119
+ #abandoned = 0
120
+ #slotsInUse = 0
121
+ #shed = 0
122
+
123
+ constructor(
124
+ options: AdmissionOptions,
125
+ onLateSettle?: (key: string, msAfterAbort: number) => void,
126
+ timers: AdmissionTimers = SYSTEM_TIMERS,
127
+ ) {
128
+ this.#options = options
129
+ this.#onLateSettle = onLateSettle
130
+ this.#timers = timers
131
+ }
132
+
133
+ /** A hole-work slot for one response, or `undefined` when none is free (the response is shed). */
134
+ slot(): HoleSlot | undefined {
135
+ if (this.#slotsInUse >= this.#options.maxHoleWork) {
136
+ this.#shed++
137
+ return undefined
138
+ }
139
+ this.#slotsInUse++
140
+ return new HoleSlot(this, () => this.#slotsInUse--)
141
+ }
142
+
143
+ /** The level that denies boundary `key` now, or `undefined` when it is admitted. */
144
+ denial(key: string): DenialLevel | undefined {
145
+ if (this.#abandoned >= this.#options.admissionStop) return "stop"
146
+ const mine = this.#abandonedBy.get(key)?.n ?? 0
147
+ if (this.#abandoned >= this.#options.processAbandonedTrip && mine > 0) return "process"
148
+ if (mine >= this.#options.boundaryAbandonedTrip) return "boundary"
149
+ return undefined
150
+ }
151
+
152
+ /** @internal Counts one denial. */
153
+ countDenied(level: DenialLevel): void {
154
+ this.#denied[level]++
155
+ }
156
+
157
+ /**
158
+ * @internal Runs one admitted invocation, counted until its promise settles. Once its signal has
159
+ * been aborted for `abandonGraceMs` it is abandoned and `onDone` fires; `onDone` fires again when
160
+ * it settles. The result settles with the loader, or at once when the signal aborts first: a
161
+ * response never waits on a loader that ignores its signal. One promise and one abort watcher per
162
+ * invocation; a chain of them cost more than the loaders the bench page runs.
163
+ */
164
+ invoke<T>(
165
+ key: string,
166
+ signal: AbortSignal,
167
+ load: () => T | Promise<T>,
168
+ onDone: () => void,
169
+ watch: (onAbort: () => void) => () => void,
170
+ ): Promise<HoleRun<T>> {
171
+ this.#pending++
172
+ const pending = countOf(this.#pendingBy, key)
173
+ pending.n++
174
+ return new Promise<HoleRun<T>>((resolve) => {
175
+ let answered = false
176
+ const answer = (run: HoleRun<T>): void => {
177
+ if (answered) return
178
+ answered = true
179
+ resolve(run)
180
+ }
181
+ let abandoned: Count | undefined
182
+ let abortedAt: number | undefined
183
+ let timer: unknown
184
+ const onAbort = (): void => {
185
+ abortedAt = this.#timers.now()
186
+ timer = this.#timers.setTimeout(() => {
187
+ abandoned = countOf(this.#abandonedBy, key)
188
+ abandoned.n++
189
+ this.#abandoned++
190
+ onDone()
191
+ }, this.#options.abandonGraceMs)
192
+ answer({ ok: false, reason: "aborted", error: signal.reason })
193
+ }
194
+ let unwatch: (() => void) | undefined
195
+ if (signal.aborted) onAbort()
196
+ else unwatch = watch(onAbort)
197
+ const settle = (): void => {
198
+ unwatch?.()
199
+ if (timer !== undefined) this.#timers.clearTimeout(timer)
200
+ this.#pending--
201
+ pending.n--
202
+ if (abandoned !== undefined) {
203
+ this.#abandoned--
204
+ abandoned.n--
205
+ }
206
+ if (abortedAt !== undefined) {
207
+ const late = this.#timers.now() - abortedAt
208
+ if (late > this.#options.abandonGraceMs) this.#onLateSettle?.(key, late)
209
+ }
210
+ onDone()
211
+ }
212
+ // `then` turns a synchronous throw into this invocation's rejection.
213
+ Promise.resolve()
214
+ .then(load)
215
+ .then(
216
+ (value) => {
217
+ settle()
218
+ answer({ ok: true, value })
219
+ },
220
+ (error: unknown) => {
221
+ settle()
222
+ answer({ ok: false, reason: signal.aborted ? "aborted" : "failed", error })
223
+ },
224
+ )
225
+ })
226
+ }
227
+
228
+ stats(): PprAdmissionStats {
229
+ return {
230
+ slotsInUse: this.#slotsInUse,
231
+ pending: this.#pending,
232
+ abandoned: this.#abandoned,
233
+ pendingBy: nonZero(this.#pendingBy),
234
+ abandonedBy: nonZero(this.#abandonedBy),
235
+ denied: { ...this.#denied },
236
+ shed: this.#shed,
237
+ admissionStopped: this.#abandoned >= this.#options.admissionStop,
238
+ }
239
+ }
240
+ }
241
+
242
+ /** One response's hole-work slot. Released exactly once. */
243
+ export class HoleSlot {
244
+ readonly #admission: HoleAdmission
245
+ readonly #release: () => void
246
+ #outstanding = 0
247
+ #sealed = false
248
+ #released = false
249
+
250
+ constructor(admission: HoleAdmission, release: () => void) {
251
+ this.#admission = admission
252
+ this.#release = release
253
+ }
254
+
255
+ /**
256
+ * Invoke one hole loader under admission. Denied, it is never invoked. Otherwise the result
257
+ * settles with the loader, or at once when `signal` aborts while the loader is still pending.
258
+ * `watch` reports that abort; the caller that aborts the signal itself passes its own.
259
+ */
260
+ run<T>(
261
+ key: string,
262
+ signal: AbortSignal,
263
+ load: () => T | Promise<T>,
264
+ watch: (onAbort: () => void) => () => void = (onAbort) => watchAbort(signal, onAbort),
265
+ ): Promise<HoleRun<T>> {
266
+ if (this.#sealed) throw new Error("[nifra/web] ppr: a sealed hole slot cannot start a hole")
267
+ if (signal.aborted) return Promise.resolve({ ok: false, reason: "aborted" })
268
+ const level = this.#admission.denial(key)
269
+ if (level !== undefined) {
270
+ this.#admission.countDenied(level)
271
+ return Promise.resolve({ ok: false, reason: "denied" })
272
+ }
273
+ this.#outstanding++
274
+ let done = false
275
+ return this.#admission.invoke(
276
+ key,
277
+ signal,
278
+ load,
279
+ () => {
280
+ if (done) return
281
+ done = true
282
+ this.#outstanding--
283
+ this.#maybeRelease()
284
+ },
285
+ watch,
286
+ )
287
+ }
288
+
289
+ /** No more holes will start for this response; the slot frees once the started ones are done. */
290
+ seal(): void {
291
+ this.#sealed = true
292
+ this.#maybeRelease()
293
+ }
294
+
295
+ #maybeRelease(): void {
296
+ if (this.#released || !this.#sealed || this.#outstanding > 0) return
297
+ this.#released = true
298
+ this.#release()
299
+ }
300
+ }
@@ -0,0 +1,122 @@
1
+ import { MAX_SHELL_AFTER_REQUESTS } from "./options.ts"
2
+
3
+ /** Where a canary value went into the request whose shell carried it. */
4
+ export type CanaryCarrier = "cookie" | "authorization" | "header" | "query"
5
+
6
+ export interface ShellCanaryFinding {
7
+ readonly path: string
8
+ readonly via: CanaryCarrier
9
+ /** The shared hole whose frame carried it; absent when the shell did. */
10
+ readonly hole?: string
11
+ }
12
+
13
+ export interface ShellCanaryOptions {
14
+ /** The origin the requests are made against. Default `http://canary.invalid`. */
15
+ readonly origin?: string
16
+ }
17
+
18
+ const CARRIERS: readonly CanaryCarrier[] = ["cookie", "authorization", "header", "query"]
19
+
20
+ /** Where a streamed document's per-response part starts: everything before it is the shared shell. */
21
+ const REQUEST_SCRIPT = '<script type="application/json" id="nifra-ppr-req">'
22
+ const SCRIPT_END = "</script>"
23
+
24
+ /** Requests per path: enough for a shell to be built, then for each shared hole's entry. */
25
+ const MAX_ATTEMPTS = 2 * MAX_SHELL_AFTER_REQUESTS
26
+
27
+ /** The JSON text of the inert script element opened by `opener`, or `undefined` without one. */
28
+ const scriptText = (html: string, opener: string): string | undefined => {
29
+ const start = html.indexOf(opener)
30
+ if (start === -1) return undefined
31
+ const end = html.indexOf(SCRIPT_END, start + opener.length)
32
+ return end === -1 ? undefined : html.slice(start + opener.length, end)
33
+ }
34
+
35
+ const parsed = (text: string | undefined): unknown => {
36
+ if (text === undefined) return undefined
37
+ try {
38
+ return JSON.parse(text)
39
+ } catch {
40
+ return undefined
41
+ }
42
+ }
43
+
44
+ /** The shared holes' frames a response carries, by name, when every one carries its entry. */
45
+ function sharedFrames(html: string): ReadonlyMap<string, string> | undefined {
46
+ const request = parsed(scriptText(html, REQUEST_SCRIPT))
47
+ const names =
48
+ typeof request === "object" && request !== null ? Reflect.get(request, "shared") : undefined
49
+ const frames = new Map<string, string>()
50
+ if (!Array.isArray(names)) return frames
51
+ for (const name of names) {
52
+ if (typeof name !== "string") return undefined
53
+ const text = scriptText(html, `<script type="application/json" id="nifra-ppr:${name}">`)
54
+ const frame = parsed(text)
55
+ // An error frame holds nothing built: the entry is not there yet.
56
+ if (text === undefined || typeof frame !== "object" || frame === null) return undefined
57
+ if (Reflect.get(frame, "error") === true) return undefined
58
+ frames.set(name, text)
59
+ }
60
+ return frames
61
+ }
62
+
63
+ const randomHex = (): string =>
64
+ Array.from(crypto.getRandomValues(new Uint8Array(12)), (byte) =>
65
+ byte.toString(16).padStart(2, "0"),
66
+ ).join("")
67
+
68
+ /**
69
+ * The canary check. Each path is requested through the app's own middleware and gates with a
70
+ * unique value in a cookie, an `Authorization` header, a custom header and the query, until a
71
+ * response carries a shell (`shellAfterRequests` decides which request builds it) and a built frame
72
+ * for each shared hole, and those are scanned for every value the path's requests carried. It
73
+ * catches visitor data the app keeps in module state, which no import check sees. Run it against a
74
+ * fresh app before it serves traffic: a shell or frame already cached was built without the canaries.
75
+ */
76
+ export async function checkShellCanaries(
77
+ app: { fetch(request: Request): Response | Promise<Response> },
78
+ paths: readonly string[],
79
+ options: ShellCanaryOptions = {},
80
+ ): Promise<readonly ShellCanaryFinding[]> {
81
+ const findings: ShellCanaryFinding[] = []
82
+ for (const path of paths) {
83
+ const sent: Array<Record<CanaryCarrier, string>> = []
84
+ // A route that renders whole every time reports nothing.
85
+ for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
86
+ const values: Record<CanaryCarrier, string> = {
87
+ cookie: `nifra-canary-${randomHex()}`,
88
+ authorization: `nifra-canary-${randomHex()}`,
89
+ header: `nifra-canary-${randomHex()}`,
90
+ query: `nifra-canary-${randomHex()}`,
91
+ }
92
+ sent.push(values)
93
+ const url = new URL(path, options.origin ?? "http://canary.invalid")
94
+ url.searchParams.set("nifra_canary", values.query)
95
+ const response = await app.fetch(
96
+ new Request(url.href, {
97
+ headers: {
98
+ cookie: `nifra_canary=${values.cookie}`,
99
+ authorization: `Bearer ${values.authorization}`,
100
+ "x-nifra-canary": values.header,
101
+ },
102
+ }),
103
+ )
104
+ const html = await response.text()
105
+ const end = html.indexOf(REQUEST_SCRIPT)
106
+ // No shared shell yet: this request rendered whole, so nothing it saw is shared.
107
+ if (end === -1) continue
108
+ const frames = sharedFrames(html)
109
+ // A shared hole whose entry is still being built: a later request gets it.
110
+ if (frames === undefined && attempt < MAX_ATTEMPTS - 1) continue
111
+ const shell = html.slice(0, end)
112
+ const carried = (text: string, via: CanaryCarrier): boolean =>
113
+ sent.some((earlier) => text.includes(earlier[via]))
114
+ for (const via of CARRIERS) if (carried(shell, via)) findings.push({ path, via })
115
+ for (const [hole, frame] of frames ?? []) {
116
+ for (const via of CARRIERS) if (carried(frame, via)) findings.push({ path, via, hole })
117
+ }
118
+ break
119
+ }
120
+ }
121
+ return findings
122
+ }