@nifrajs/web 4.0.2 → 5.1.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 (426) hide show
  1. package/README.md +19 -0
  2. package/dist/build-vite.d.ts +19 -0
  3. package/dist/build-vite.d.ts.map +1 -1
  4. package/dist/build-vite.js +31 -10
  5. package/dist/build-vite.js.map +1 -1
  6. package/dist/build.d.ts +8 -10
  7. package/dist/build.d.ts.map +1 -1
  8. package/dist/build.js +90 -31
  9. package/dist/build.js.map +1 -1
  10. package/dist/cdn.d.ts +8 -0
  11. package/dist/cdn.d.ts.map +1 -1
  12. package/dist/cdn.js +14 -15
  13. package/dist/cdn.js.map +1 -1
  14. package/dist/client.d.ts +6 -6
  15. package/dist/client.d.ts.map +1 -1
  16. package/dist/client.js +46 -18
  17. package/dist/client.js.map +1 -1
  18. package/dist/deferred.d.ts +2 -2
  19. package/dist/deferred.d.ts.map +1 -1
  20. package/dist/deferred.js +28 -7
  21. package/dist/deferred.js.map +1 -1
  22. package/dist/dev-feed.d.ts.map +1 -1
  23. package/dist/dev-feed.js +24 -5
  24. package/dist/dev-feed.js.map +1 -1
  25. package/dist/dev-port.d.ts +3 -2
  26. package/dist/dev-port.d.ts.map +1 -1
  27. package/dist/dev-port.js +2 -6
  28. package/dist/dev-port.js.map +1 -1
  29. package/dist/dev-session.d.ts.map +1 -1
  30. package/dist/dev-session.js +4 -14
  31. package/dist/dev-session.js.map +1 -1
  32. package/dist/dev.d.ts +7 -17
  33. package/dist/dev.d.ts.map +1 -1
  34. package/dist/dev.js +13 -1
  35. package/dist/dev.js.map +1 -1
  36. package/dist/draft.d.ts +8 -5
  37. package/dist/draft.d.ts.map +1 -1
  38. package/dist/draft.js +30 -13
  39. package/dist/draft.js.map +1 -1
  40. package/dist/fonts-google.d.ts.map +1 -1
  41. package/dist/fonts-google.js +7 -0
  42. package/dist/fonts-google.js.map +1 -1
  43. package/dist/index.d.ts +5 -4
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +4 -3
  46. package/dist/index.js.map +1 -1
  47. package/dist/internal/codegen.d.ts +1 -0
  48. package/dist/internal/codegen.d.ts.map +1 -1
  49. package/dist/internal/codegen.js +56 -29
  50. package/dist/internal/codegen.js.map +1 -1
  51. package/dist/internal/disposable-dir.d.ts +8 -0
  52. package/dist/internal/disposable-dir.d.ts.map +1 -0
  53. package/dist/internal/disposable-dir.js +25 -0
  54. package/dist/internal/disposable-dir.js.map +1 -0
  55. package/dist/internal/entry-chunks.d.ts +9 -0
  56. package/dist/internal/entry-chunks.d.ts.map +1 -0
  57. package/dist/internal/entry-chunks.js +15 -0
  58. package/dist/internal/entry-chunks.js.map +1 -0
  59. package/dist/internal/evidence-traversal.d.ts +12 -0
  60. package/dist/internal/evidence-traversal.d.ts.map +1 -0
  61. package/dist/internal/evidence-traversal.js +48 -0
  62. package/dist/internal/evidence-traversal.js.map +1 -0
  63. package/dist/internal/head-merge.d.ts +6 -0
  64. package/dist/internal/head-merge.d.ts.map +1 -1
  65. package/dist/internal/head-merge.js +9 -0
  66. package/dist/internal/head-merge.js.map +1 -1
  67. package/dist/internal/js-string.d.ts +2 -0
  68. package/dist/internal/js-string.d.ts.map +1 -1
  69. package/dist/internal/js-string.js +5 -1
  70. package/dist/internal/js-string.js.map +1 -1
  71. package/dist/internal/mount-shadow.js +1 -1
  72. package/dist/internal/mount-shadow.js.map +1 -1
  73. package/dist/internal/output-guard.d.ts +7 -0
  74. package/dist/internal/output-guard.d.ts.map +1 -1
  75. package/dist/internal/output-guard.js +80 -8
  76. package/dist/internal/output-guard.js.map +1 -1
  77. package/dist/internal/package-entries.d.ts +34 -0
  78. package/dist/internal/package-entries.d.ts.map +1 -0
  79. package/dist/internal/package-entries.js +324 -0
  80. package/dist/internal/package-entries.js.map +1 -0
  81. package/dist/internal/page-execution.d.ts +6 -0
  82. package/dist/internal/page-execution.d.ts.map +1 -1
  83. package/dist/internal/page-execution.js +437 -114
  84. package/dist/internal/page-execution.js.map +1 -1
  85. package/dist/internal/parity.d.ts.map +1 -1
  86. package/dist/internal/parity.js +15 -2
  87. package/dist/internal/parity.js.map +1 -1
  88. package/dist/internal/private-env.d.ts +8 -0
  89. package/dist/internal/private-env.d.ts.map +1 -1
  90. package/dist/internal/private-env.js +25 -10
  91. package/dist/internal/private-env.js.map +1 -1
  92. package/dist/internal/progressive-cookies.d.ts +14 -0
  93. package/dist/internal/progressive-cookies.d.ts.map +1 -0
  94. package/dist/internal/progressive-cookies.js +43 -0
  95. package/dist/internal/progressive-cookies.js.map +1 -0
  96. package/dist/internal/public-env.d.ts +2 -0
  97. package/dist/internal/public-env.d.ts.map +1 -0
  98. package/dist/internal/public-env.js +13 -0
  99. package/dist/internal/public-env.js.map +1 -0
  100. package/dist/internal/render-document.d.ts +5 -44
  101. package/dist/internal/render-document.d.ts.map +1 -1
  102. package/dist/internal/render-document.js +51 -63
  103. package/dist/internal/render-document.js.map +1 -1
  104. package/dist/internal/response-controls.d.ts +2 -0
  105. package/dist/internal/response-controls.d.ts.map +1 -1
  106. package/dist/internal/response-controls.js +26 -16
  107. package/dist/internal/response-controls.js.map +1 -1
  108. package/dist/internal/revive-dates.d.ts +16 -0
  109. package/dist/internal/revive-dates.d.ts.map +1 -0
  110. package/dist/internal/revive-dates.js +32 -0
  111. package/dist/internal/revive-dates.js.map +1 -0
  112. package/dist/internal/route-path.d.ts +14 -0
  113. package/dist/internal/route-path.d.ts.map +1 -0
  114. package/dist/internal/route-path.js +19 -0
  115. package/dist/internal/route-path.js.map +1 -0
  116. package/dist/internal/secret-scan.d.ts.map +1 -1
  117. package/dist/internal/secret-scan.js +190 -20
  118. package/dist/internal/secret-scan.js.map +1 -1
  119. package/dist/internal/server-boundary.d.ts.map +1 -1
  120. package/dist/internal/server-boundary.js +33 -5
  121. package/dist/internal/server-boundary.js.map +1 -1
  122. package/dist/internal/web-app.d.ts +25 -18
  123. package/dist/internal/web-app.d.ts.map +1 -1
  124. package/dist/internal/web-app.js +81 -24
  125. package/dist/internal/web-app.js.map +1 -1
  126. package/dist/internal/wire-date.d.ts +3 -0
  127. package/dist/internal/wire-date.d.ts.map +1 -0
  128. package/dist/internal/wire-date.js +14 -0
  129. package/dist/internal/wire-date.js.map +1 -0
  130. package/dist/internal/without-query.d.ts +4 -0
  131. package/dist/internal/without-query.d.ts.map +1 -0
  132. package/dist/internal/without-query.js +9 -0
  133. package/dist/internal/without-query.js.map +1 -0
  134. package/dist/internal/zone-graph.d.ts +4 -0
  135. package/dist/internal/zone-graph.d.ts.map +1 -1
  136. package/dist/internal/zone-graph.js +71 -20
  137. package/dist/internal/zone-graph.js.map +1 -1
  138. package/dist/isr.d.ts +59 -3
  139. package/dist/isr.d.ts.map +1 -1
  140. package/dist/isr.js +311 -41
  141. package/dist/isr.js.map +1 -1
  142. package/dist/llms-txt.d.ts +2 -0
  143. package/dist/llms-txt.d.ts.map +1 -1
  144. package/dist/llms-txt.js +29 -3
  145. package/dist/llms-txt.js.map +1 -1
  146. package/dist/manifest.d.ts +37 -4
  147. package/dist/manifest.d.ts.map +1 -1
  148. package/dist/manifest.js +4 -0
  149. package/dist/manifest.js.map +1 -1
  150. package/dist/open-resolved.d.ts +18 -0
  151. package/dist/open-resolved.d.ts.map +1 -0
  152. package/dist/open-resolved.js +62 -0
  153. package/dist/open-resolved.js.map +1 -0
  154. package/dist/plugins/kit.d.ts +1 -0
  155. package/dist/plugins/kit.d.ts.map +1 -1
  156. package/dist/plugins/kit.js +26 -11
  157. package/dist/plugins/kit.js.map +1 -1
  158. package/dist/plugins/vite-leak-guard.d.ts +9 -1
  159. package/dist/plugins/vite-leak-guard.d.ts.map +1 -1
  160. package/dist/plugins/vite-leak-guard.js +84 -7
  161. package/dist/plugins/vite-leak-guard.js.map +1 -1
  162. package/dist/plugins/vite-zone-guard.d.ts.map +1 -1
  163. package/dist/plugins/vite-zone-guard.js +17 -21
  164. package/dist/plugins/vite-zone-guard.js.map +1 -1
  165. package/dist/plugins/zone-guard.d.ts.map +1 -1
  166. package/dist/plugins/zone-guard.js +44 -3
  167. package/dist/plugins/zone-guard.js.map +1 -1
  168. package/dist/prerender.d.ts +5 -1
  169. package/dist/prerender.d.ts.map +1 -1
  170. package/dist/prerender.js +41 -19
  171. package/dist/prerender.js.map +1 -1
  172. package/dist/public-dir.d.ts +8 -1
  173. package/dist/public-dir.d.ts.map +1 -1
  174. package/dist/public-dir.js +17 -6
  175. package/dist/public-dir.js.map +1 -1
  176. package/dist/query.d.ts +13 -4
  177. package/dist/query.d.ts.map +1 -1
  178. package/dist/query.js +89 -26
  179. package/dist/query.js.map +1 -1
  180. package/dist/render-seam.d.ts +25 -2
  181. package/dist/render-seam.d.ts.map +1 -1
  182. package/dist/render-seam.js +1 -0
  183. package/dist/render-seam.js.map +1 -1
  184. package/dist/route-types.d.ts.map +1 -1
  185. package/dist/route-types.js +20 -9
  186. package/dist/route-types.js.map +1 -1
  187. package/dist/router.d.ts +9 -2
  188. package/dist/router.d.ts.map +1 -1
  189. package/dist/router.js +47 -50
  190. package/dist/router.js.map +1 -1
  191. package/dist/seo.d.ts +49 -0
  192. package/dist/seo.d.ts.map +1 -0
  193. package/dist/seo.js +48 -0
  194. package/dist/seo.js.map +1 -0
  195. package/dist/server-components/build-info.d.ts +3 -0
  196. package/dist/server-components/build-info.d.ts.map +1 -0
  197. package/dist/server-components/build-info.js +7 -0
  198. package/dist/server-components/build-info.js.map +1 -0
  199. package/dist/server-components/build-target.d.ts +12 -0
  200. package/dist/server-components/build-target.d.ts.map +1 -0
  201. package/dist/server-components/build-target.js +153 -0
  202. package/dist/server-components/build-target.js.map +1 -0
  203. package/dist/server-components/build.d.ts +24 -0
  204. package/dist/server-components/build.d.ts.map +1 -0
  205. package/dist/server-components/build.js +366 -0
  206. package/dist/server-components/build.js.map +1 -0
  207. package/dist/server-components/bundle-proof.d.ts +3 -0
  208. package/dist/server-components/bundle-proof.d.ts.map +1 -0
  209. package/dist/server-components/bundle-proof.js +23 -0
  210. package/dist/server-components/bundle-proof.js.map +1 -0
  211. package/dist/server-components/client.d.ts +46 -0
  212. package/dist/server-components/client.d.ts.map +1 -0
  213. package/dist/server-components/client.js +594 -0
  214. package/dist/server-components/client.js.map +1 -0
  215. package/dist/server-components/definition.d.ts +11 -0
  216. package/dist/server-components/definition.d.ts.map +1 -0
  217. package/dist/server-components/definition.js +94 -0
  218. package/dist/server-components/definition.js.map +1 -0
  219. package/dist/server-components/dev-http.d.ts +5 -0
  220. package/dist/server-components/dev-http.d.ts.map +1 -0
  221. package/dist/server-components/dev-http.js +71 -0
  222. package/dist/server-components/dev-http.js.map +1 -0
  223. package/dist/server-components/error.d.ts +9 -0
  224. package/dist/server-components/error.d.ts.map +1 -0
  225. package/dist/server-components/error.js +24 -0
  226. package/dist/server-components/error.js.map +1 -0
  227. package/dist/server-components/frames.d.ts +7 -0
  228. package/dist/server-components/frames.d.ts.map +1 -0
  229. package/dist/server-components/frames.js +169 -0
  230. package/dist/server-components/frames.js.map +1 -0
  231. package/dist/server-components/html-policy.d.ts +6 -0
  232. package/dist/server-components/html-policy.d.ts.map +1 -0
  233. package/dist/server-components/html-policy.js +75 -0
  234. package/dist/server-components/html-policy.js.map +1 -0
  235. package/dist/server-components/html.d.ts +4 -0
  236. package/dist/server-components/html.d.ts.map +1 -0
  237. package/dist/server-components/html.js +67 -0
  238. package/dist/server-components/html.js.map +1 -0
  239. package/dist/server-components/islands.d.ts +44 -0
  240. package/dist/server-components/islands.d.ts.map +1 -0
  241. package/dist/server-components/islands.js +42 -0
  242. package/dist/server-components/islands.js.map +1 -0
  243. package/dist/server-components/native.d.ts +4 -0
  244. package/dist/server-components/native.d.ts.map +1 -0
  245. package/dist/server-components/native.js +81 -0
  246. package/dist/server-components/native.js.map +1 -0
  247. package/dist/server-components/portal.d.ts +6 -0
  248. package/dist/server-components/portal.d.ts.map +1 -0
  249. package/dist/server-components/portal.js +35 -0
  250. package/dist/server-components/portal.js.map +1 -0
  251. package/dist/server-components/props.d.ts +3 -0
  252. package/dist/server-components/props.d.ts.map +1 -0
  253. package/dist/server-components/props.js +14 -0
  254. package/dist/server-components/props.js.map +1 -0
  255. package/dist/server-components/protocol.d.ts +44 -0
  256. package/dist/server-components/protocol.d.ts.map +1 -0
  257. package/dist/server-components/protocol.js +139 -0
  258. package/dist/server-components/protocol.js.map +1 -0
  259. package/dist/server-components/range.d.ts +24 -0
  260. package/dist/server-components/range.d.ts.map +1 -0
  261. package/dist/server-components/range.js +263 -0
  262. package/dist/server-components/range.js.map +1 -0
  263. package/dist/server-components/registry.d.ts +6 -0
  264. package/dist/server-components/registry.d.ts.map +1 -0
  265. package/dist/server-components/registry.js +30 -0
  266. package/dist/server-components/registry.js.map +1 -0
  267. package/dist/server-components/response.d.ts +4 -0
  268. package/dist/server-components/response.d.ts.map +1 -0
  269. package/dist/server-components/response.js +32 -0
  270. package/dist/server-components/response.js.map +1 -0
  271. package/dist/server-components/router-integration.d.ts +9 -0
  272. package/dist/server-components/router-integration.d.ts.map +1 -0
  273. package/dist/server-components/router-integration.js +29 -0
  274. package/dist/server-components/router-integration.js.map +1 -0
  275. package/dist/server-components/runtime.d.ts +14 -0
  276. package/dist/server-components/runtime.d.ts.map +1 -0
  277. package/dist/server-components/runtime.js +276 -0
  278. package/dist/server-components/runtime.js.map +1 -0
  279. package/dist/server-components/scheduler.d.ts +14 -0
  280. package/dist/server-components/scheduler.d.ts.map +1 -0
  281. package/dist/server-components/scheduler.js +74 -0
  282. package/dist/server-components/scheduler.js.map +1 -0
  283. package/dist/server-components/schema.d.ts +4 -0
  284. package/dist/server-components/schema.d.ts.map +1 -0
  285. package/dist/server-components/schema.js +77 -0
  286. package/dist/server-components/schema.js.map +1 -0
  287. package/dist/server-components/server-entry.d.ts +12 -0
  288. package/dist/server-components/server-entry.d.ts.map +1 -0
  289. package/dist/server-components/server-entry.js +90 -0
  290. package/dist/server-components/server-entry.js.map +1 -0
  291. package/dist/server-components/source.d.ts +15 -0
  292. package/dist/server-components/source.d.ts.map +1 -0
  293. package/dist/server-components/source.js +269 -0
  294. package/dist/server-components/source.js.map +1 -0
  295. package/dist/server-components/state.d.ts +4 -0
  296. package/dist/server-components/state.d.ts.map +1 -0
  297. package/dist/server-components/state.js +123 -0
  298. package/dist/server-components/state.js.map +1 -0
  299. package/dist/server-components/stream.d.ts +6 -0
  300. package/dist/server-components/stream.d.ts.map +1 -0
  301. package/dist/server-components/stream.js +70 -0
  302. package/dist/server-components/stream.js.map +1 -0
  303. package/dist/server-components/trusted-html.d.ts +3 -0
  304. package/dist/server-components/trusted-html.d.ts.map +1 -0
  305. package/dist/server-components/trusted-html.js +14 -0
  306. package/dist/server-components/trusted-html.js.map +1 -0
  307. package/dist/server-components/types.d.ts +107 -0
  308. package/dist/server-components/types.d.ts.map +1 -0
  309. package/dist/server-components/types.js +2 -0
  310. package/dist/server-components/types.js.map +1 -0
  311. package/dist/server-components/typescript.d.ts +11 -0
  312. package/dist/server-components/typescript.d.ts.map +1 -0
  313. package/dist/server-components/typescript.js +28 -0
  314. package/dist/server-components/typescript.js.map +1 -0
  315. package/dist/server-components/value.d.ts +5 -0
  316. package/dist/server-components/value.d.ts.map +1 -0
  317. package/dist/server-components/value.js +77 -0
  318. package/dist/server-components/value.js.map +1 -0
  319. package/dist/server-components/vite.d.ts +12 -0
  320. package/dist/server-components/vite.d.ts.map +1 -0
  321. package/dist/server-components/vite.js +197 -0
  322. package/dist/server-components/vite.js.map +1 -0
  323. package/dist/server-components.d.ts +5 -0
  324. package/dist/server-components.d.ts.map +1 -0
  325. package/dist/server-components.js +4 -0
  326. package/dist/server-components.js.map +1 -0
  327. package/dist/service-worker.d.ts +8 -1
  328. package/dist/service-worker.d.ts.map +1 -1
  329. package/dist/service-worker.js +20 -6
  330. package/dist/service-worker.js.map +1 -1
  331. package/dist/vite.d.ts +12 -0
  332. package/dist/vite.d.ts.map +1 -1
  333. package/dist/vite.js +7 -1
  334. package/dist/vite.js.map +1 -1
  335. package/dist/zones.d.ts +64 -2
  336. package/dist/zones.d.ts.map +1 -1
  337. package/dist/zones.js +331 -28
  338. package/dist/zones.js.map +1 -1
  339. package/package.json +102 -4
  340. package/src/build-vite.ts +31 -9
  341. package/src/build.ts +109 -31
  342. package/src/cdn.ts +18 -12
  343. package/src/client.ts +48 -17
  344. package/src/deferred.ts +27 -9
  345. package/src/dev-feed.ts +24 -5
  346. package/src/dev-port.ts +4 -10
  347. package/src/dev-session.ts +4 -13
  348. package/src/dev.ts +29 -1
  349. package/src/draft.ts +35 -14
  350. package/src/fonts-google.ts +10 -0
  351. package/src/index.ts +5 -6
  352. package/src/internal/codegen.ts +60 -29
  353. package/src/internal/disposable-dir.ts +24 -0
  354. package/src/internal/entry-chunks.ts +18 -0
  355. package/src/internal/evidence-traversal.ts +57 -0
  356. package/src/internal/head-merge.ts +16 -0
  357. package/src/internal/js-string.ts +6 -1
  358. package/src/internal/mount-shadow.ts +1 -1
  359. package/src/internal/output-guard.ts +88 -7
  360. package/src/internal/package-entries.ts +331 -0
  361. package/src/internal/page-execution.ts +573 -114
  362. package/src/internal/parity.ts +15 -2
  363. package/src/internal/private-env.ts +27 -13
  364. package/src/internal/progressive-cookies.ts +60 -0
  365. package/src/internal/public-env.ts +14 -0
  366. package/src/internal/render-document.ts +66 -89
  367. package/src/internal/response-controls.ts +31 -14
  368. package/src/internal/revive-dates.ts +35 -0
  369. package/src/internal/route-path.ts +24 -0
  370. package/src/internal/secret-scan.ts +190 -18
  371. package/src/internal/server-boundary.ts +29 -5
  372. package/src/internal/web-app.ts +126 -42
  373. package/src/internal/wire-date.ts +16 -0
  374. package/src/internal/without-query.ts +8 -0
  375. package/src/internal/zone-graph.ts +78 -25
  376. package/src/isr.ts +364 -45
  377. package/src/llms-txt.ts +32 -3
  378. package/src/manifest.ts +38 -4
  379. package/src/open-resolved.ts +64 -0
  380. package/src/plugins/kit.ts +31 -11
  381. package/src/plugins/vite-leak-guard.ts +104 -8
  382. package/src/plugins/vite-zone-guard.ts +21 -19
  383. package/src/plugins/zone-guard.ts +41 -2
  384. package/src/prerender.ts +39 -18
  385. package/src/public-dir.ts +27 -8
  386. package/src/query.ts +107 -26
  387. package/src/render-seam.ts +30 -2
  388. package/src/route-types.ts +34 -7
  389. package/src/router.ts +66 -59
  390. package/src/seo.ts +68 -0
  391. package/src/server-components/build-info.ts +11 -0
  392. package/src/server-components/build-target.ts +194 -0
  393. package/src/server-components/build.ts +419 -0
  394. package/src/server-components/bundle-proof.ts +33 -0
  395. package/src/server-components/client.ts +663 -0
  396. package/src/server-components/definition.ts +125 -0
  397. package/src/server-components/dev-http.ts +75 -0
  398. package/src/server-components/error.ts +24 -0
  399. package/src/server-components/frames.ts +181 -0
  400. package/src/server-components/html-policy.ts +94 -0
  401. package/src/server-components/html.ts +81 -0
  402. package/src/server-components/islands.ts +111 -0
  403. package/src/server-components/native.ts +101 -0
  404. package/src/server-components/portal.ts +42 -0
  405. package/src/server-components/props.ts +19 -0
  406. package/src/server-components/protocol.ts +167 -0
  407. package/src/server-components/range.ts +313 -0
  408. package/src/server-components/registry.ts +34 -0
  409. package/src/server-components/response.ts +44 -0
  410. package/src/server-components/router-integration.ts +40 -0
  411. package/src/server-components/runtime.ts +376 -0
  412. package/src/server-components/scheduler.ts +84 -0
  413. package/src/server-components/schema.ts +69 -0
  414. package/src/server-components/server-entry.ts +98 -0
  415. package/src/server-components/source.ts +324 -0
  416. package/src/server-components/state.ts +143 -0
  417. package/src/server-components/stream.ts +71 -0
  418. package/src/server-components/trusted-html.ts +21 -0
  419. package/src/server-components/types.ts +138 -0
  420. package/src/server-components/typescript.ts +39 -0
  421. package/src/server-components/value.ts +82 -0
  422. package/src/server-components/vite.ts +242 -0
  423. package/src/server-components.ts +32 -0
  424. package/src/service-worker.ts +24 -7
  425. package/src/vite.ts +8 -1
  426. package/src/zones.ts +418 -32
@@ -1,5 +1,5 @@
1
1
  import { NIFRA_BACKEND_BIND_PLATFORM, NIFRA_PLATFORM_CLIENT_IP_DERIVED, } from "@nifrajs/core/mount";
2
- import { status as statusResult } from "@nifrajs/core/server";
2
+ import { isSameOriginForm, status as statusResult, } from "@nifrajs/core/server";
3
3
  import { boundaryDescriptors, resolveStaticBoundaries, startDynamicBoundaries, } from "../boundary.js";
4
4
  import { DEFAULT_CSS_LOADING, normalizeCssLoading } from "../css-contract.js";
5
5
  import { defer, ndjsonStream, prepareDeferred } from "../deferred.js";
@@ -7,7 +7,7 @@ import { isDraftEnabled } from "../draft.js";
7
7
  import { routeTags } from "../isr.js";
8
8
  import { createMatcher, DATA_HEADER, NAV_FROM_HEADER, REDIRECT_HEADER, RETAIN_HEADER, REVALIDATE_HEADER, STATUS_HEADER, } from "../router.js";
9
9
  import { searchOf, searchOfChain } from "../search.js";
10
- import { mergeHeads, resolveMeta } from "./head-merge.js";
10
+ import { mergeHeads, resolveChainMeta } from "./head-merge.js";
11
11
  import { EMPTY_RETAIN, isControlFlow, isResponseResult, isStatusSignal, layoutErrorId, renderPageResult, STATUS_SIGNAL, STATUS_TEXT, scopeParams, tagLayoutError, withDuplicateInstanceHint, } from "./render-document.js";
12
12
  import { urlPartsFor } from "./request-url.js";
13
13
  import { ACTION_SCOPE, DATA_RESPONSE_HEADERS, MIDDLEWARE_SCOPE, PAGE_SCOPE, PageResponseControls, PRIVATE_NO_STORE, PRIVATE_PAGE_HEADERS, privateOutcome, varyOnDataHeader, } from "./response-controls.js";
@@ -92,9 +92,12 @@ function appTarget(location, requestUrl) {
92
92
  catch {
93
93
  return location; // the browser fails on it the same way when it loads it
94
94
  }
95
- return target.origin === new URL(requestUrl).origin
96
- ? target.pathname + target.search + target.hash
97
- : location;
95
+ if (target.origin !== new URL(requestUrl).origin)
96
+ return location;
97
+ // A path starting `//` (from `/.//host`) is a protocol-relative URL to whoever resolves it next.
98
+ return target.pathname.startsWith("//")
99
+ ? target.href
100
+ : target.pathname + target.search + target.hash;
98
101
  }
99
102
  /**
100
103
  * Own the page request program behind one private seam.
@@ -107,6 +110,8 @@ function appTarget(location, requestUrl) {
107
110
  */
108
111
  export function createPageRequestExecutor(options) {
109
112
  const { adapter, manifest, clientEntry, api } = options;
113
+ // The action result is request-local; a browser cannot manufacture this handoff.
114
+ const progressiveActionData = new WeakMap();
110
115
  // Bound once per render, so every loader, action and boundary in that render shares one view of the
111
116
  // in-process client carrying the visitor's platform. An `api` without the seam passes through as-is.
112
117
  const bindApi = platformBinderOf(api);
@@ -165,9 +170,7 @@ export function createPageRequestExecutor(options) {
165
170
  return chunks ? { preload: chunks } : {};
166
171
  };
167
172
  const resolveNonce = (request, env) => options.nonce === undefined ? undefined : options.nonce({ request, env });
168
- const stylesOf = (id) => {
169
- const perRoute = options.routeStyles?.[id];
170
- const styles = perRoute ?? options.styles;
173
+ const stylesFrom = (styles) => {
171
174
  if (styles === undefined || styles.length === 0)
172
175
  return {};
173
176
  return {
@@ -175,6 +178,7 @@ export function createPageRequestExecutor(options) {
175
178
  ...(cssLoading === "deferred" ? { cssLoading } : {}),
176
179
  };
177
180
  };
181
+ const stylesOf = (id) => stylesFrom(options.routeStyles?.[id] ?? options.styles);
178
182
  // Load a route's layout modules once for the current execution. The render path needs each whole
179
183
  // module for its component + meta, while the data path also needs loader/gate/boundary declarations.
180
184
  const loadLayoutModules = async (route) => {
@@ -194,11 +198,49 @@ export function createPageRequestExecutor(options) {
194
198
  retained: [],
195
199
  pending: undefined,
196
200
  };
197
- /** Run layout loaders, awaiting gates before starting the page loader and retaining only safe data. */
198
- const runLayoutChain = async (route, ctx, controls, retain) => {
201
+ /**
202
+ * Await one gate. A returned redirect, status or Response refuses the request exactly as a thrown
203
+ * one does: stored as layout data it would let the page loader and the action run.
204
+ */
205
+ const runGate = async (route, index, loader, ctx) => {
206
+ let value;
207
+ try {
208
+ value = await loader(ctx);
209
+ }
210
+ catch (err) {
211
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: The loaded layout array is derived from these same manifest IDs in the same order.
212
+ throw tagLayoutError(err, route.layoutIds[index]);
213
+ }
214
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: The loaded layout array is derived from these same manifest IDs in the same order.
215
+ if (isControlFlow(value))
216
+ throw tagLayoutError(value, route.layoutIds[index]);
217
+ return value;
218
+ };
219
+ // Development only. A layout loader that refuses access reads as a guard, and is not one: a client
220
+ // navigation can skip it and an action below it runs without it.
221
+ const warnsUngatedRefusal = typeof process !== "undefined" && process.env?.NODE_ENV === "development";
222
+ const warnedUngated = new Set();
223
+ const warnUngatedRefusal = (layoutId, outcome) => {
224
+ if (warnedUngated.has(layoutId))
225
+ return;
226
+ // notFound() and gone() say the record is missing, which a layout may decide without guarding.
227
+ const refusesAccess = isStatusSignal(outcome)
228
+ ? outcome.status === 401 || outcome.status === 403
229
+ : isControlFlow(outcome);
230
+ if (!refusesAccess)
231
+ return;
232
+ warnedUngated.add(layoutId);
233
+ const file = manifest.layouts?.[layoutId]?.file ?? layoutId;
234
+ console.warn(`[nifra/web] the loader in "${file}" refused a request, but that layout is not a gate. A client navigation that keeps the layout on screen can skip its loader, and an action below it runs without it, so it does not protect the pages under it. Export \`gate = true\` from that file, or move the check into middleware. (Shown in development only.)`);
235
+ };
236
+ /**
237
+ * Run layout loaders, awaiting gates before starting the page loader and retaining only safe data.
238
+ * `gated` carries gates that already ran for this request, so none runs twice.
239
+ */
240
+ const runLayoutChain = async (route, ctx, controls, retain, gated) => {
199
241
  if (route.layoutIds.length === 0)
200
242
  return emptyLayoutRun;
201
- const modules = await loadLayoutModules(route);
243
+ const modules = gated?.modules ?? (await loadLayoutModules(route));
202
244
  if (!modules.some((m) => m.loader !== undefined)) {
203
245
  return { modules, layoutData: undefined, retained: [], pending: undefined };
204
246
  }
@@ -216,6 +258,7 @@ export function createPageRequestExecutor(options) {
216
258
  keep = keepsLayoutData(route, i, layout, ctx.params, retain);
217
259
  }
218
260
  catch (err) {
261
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: The loaded layout array is derived from these same manifest IDs in the same order.
219
262
  throw tagLayoutError(err, route.layoutIds[i]);
220
263
  }
221
264
  if (keep) {
@@ -229,12 +272,7 @@ export function createPageRequestExecutor(options) {
229
272
  set: controls.scope(i),
230
273
  };
231
274
  if (modules[i]?.gate === true) {
232
- try {
233
- results[i] = await loader(scoped);
234
- }
235
- catch (err) {
236
- throw tagLayoutError(err, route.layoutIds[i]);
237
- }
275
+ results[i] = gated === undefined ? await runGate(route, i, loader, scoped) : gated.gates[i];
238
276
  continue;
239
277
  }
240
278
  const layoutId = route.layoutIds[i];
@@ -245,18 +283,31 @@ export function createPageRequestExecutor(options) {
245
283
  // assimilation: property-presence and prototype probes are unsafe for proxies.
246
284
  if (value !== null && (typeof value === "object" || typeof value === "function")) {
247
285
  pending.push((async () => {
286
+ let settled;
248
287
  try {
249
- results[index] = await value;
288
+ settled = await value;
250
289
  }
251
290
  catch (err) {
291
+ if (warnsUngatedRefusal)
292
+ warnUngatedRefusal(layoutId, err);
252
293
  throw tagLayoutError(err, layoutId);
253
294
  }
295
+ // A returned redirect or Response refuses exactly as a thrown one: kept as layout data,
296
+ // it would ship beside the page loader's data.
297
+ if (isControlFlow(settled)) {
298
+ if (warnsUngatedRefusal)
299
+ warnUngatedRefusal(layoutId, settled);
300
+ throw tagLayoutError(settled, layoutId);
301
+ }
302
+ results[index] = settled;
254
303
  })());
255
304
  }
256
305
  else
257
306
  results[index] = value;
258
307
  }
259
308
  catch (err) {
309
+ if (warnsUngatedRefusal)
310
+ warnUngatedRefusal(layoutId, err);
260
311
  // Preserve a synchronous throw as a pending layout failure, just like the async lane.
261
312
  pending.push(Promise.reject(tagLayoutError(err, layoutId)));
262
313
  }
@@ -271,22 +322,18 @@ export function createPageRequestExecutor(options) {
271
322
  /** Run only authorization gates before an action; ordinary layout data runs after a native action. */
272
323
  const runLayoutGates = async (route, ctx, controls) => {
273
324
  const modules = await loadLayoutModules(route);
325
+ const gates = [];
274
326
  for (let i = 0; i < modules.length; i++) {
275
327
  const mod = modules[i];
276
328
  if (mod?.gate !== true || mod.loader === undefined)
277
329
  continue;
278
- try {
279
- await mod.loader({
280
- ...ctx,
281
- params: scopeParams(ctx.params, route.layoutParams?.[i]),
282
- set: controls.scope(i),
283
- });
284
- }
285
- catch (err) {
286
- throw tagLayoutError(err, route.layoutIds[i]);
287
- }
330
+ gates[i] = await runGate(route, i, mod.loader, {
331
+ ...ctx,
332
+ params: scopeParams(ctx.params, route.layoutParams?.[i]),
333
+ set: controls.scope(i),
334
+ });
288
335
  }
289
- return modules;
336
+ return { modules, gates };
290
337
  };
291
338
  /** Outermost first; a failure carries its directory so the nearest `_error` above it renders. */
292
339
  const runMiddleware = async (route, ctx, controls) => {
@@ -400,13 +447,10 @@ export function createPageRequestExecutor(options) {
400
447
  }
401
448
  return mod.HydrateFallback ?? EMPTY_LEAF;
402
449
  };
403
- const resolveChainAndHead = (layoutModules, page, metaArgs, leaf = page.default) => {
450
+ const resolveChainAndHead = (layoutModules, layoutData, page, metaArgs, leaf = page.default) => {
404
451
  const chain = [...layoutModules.map((m) => m.default), leaf];
405
- const heads = [
406
- ...layoutModules.map((m) => resolveMeta(m.meta, metaArgs)),
407
- resolveMeta(page.meta, metaArgs),
408
- ];
409
- return { chain, head: mergeHeads(heads) };
452
+ const metas = [...layoutModules.map((m) => m.meta), page.meta];
453
+ return { chain, head: mergeHeads(resolveChainMeta(metas, layoutData, metaArgs)) };
410
454
  };
411
455
  /** What `useMatches` reports for a chain: the layouts' ids and `handle`s, then the page's. */
412
456
  const matchChainOf = (layoutIds, layoutModules, pageId, page) => ({
@@ -444,34 +488,128 @@ export function createPageRequestExecutor(options) {
444
488
  return "/";
445
489
  }
446
490
  };
447
- const renderError = async (route, errorId, err, personalized, nonce) => {
491
+ /** What a status or error page's `meta` receives: no route params, and its own data. */
492
+ const statusMetaArgs = (req, data, nonce) => nonce === undefined
493
+ ? { data, pageData: data, params: {}, origin: originOf(req) }
494
+ : { data, pageData: data, params: {}, origin: originOf(req), nonce };
495
+ const renderError = async (req, route, errorId, err, personalized, nonce, layouts) => {
448
496
  const errDir = dirOfId(errorId, "_error");
449
497
  const keptLayoutIds = route.layoutIds.filter((id) => {
450
498
  const ld = dirOfId(id, "_layout");
451
499
  return ld === "" || ld === errDir || errDir.startsWith(`${ld}/`);
452
500
  });
453
- const layouts = await Promise.all(keptLayoutIds.map((id) => manifest.layouts[id].load().then((m) => m.default)));
454
- const { default: errComp } = await (manifest.errors?.[errorId]).load();
501
+ const errorModule = await (manifest.errors?.[errorId]).load();
455
502
  const e = err instanceof Error ? err : new Error(String(err));
456
- // An error's text can carry a connection string or a query, so a production page gets neither.
457
- const production = typeof process === "undefined" || process.env.NODE_ENV === "production";
458
- return renderPageResult({
459
- adapter,
460
- chain: [...layouts, errComp],
461
- data: production
462
- ? { name: "Error", message: "Internal Server Error" }
463
- : { name: e.name, message: e.message },
464
- clientEntry,
465
- routeId: errorId,
466
- status: 500,
467
- hydrate: false,
468
- ...(personalized ? { headers: PRIVATE_PAGE_HEADERS } : {}),
469
- ...(nonce === undefined ? {} : { nonce }),
470
- ...cspOption,
471
- ...titleOption,
472
- });
503
+ // An error's text can carry a connection string or a query, so only a run that says it is
504
+ // development or test shows it; an unset NODE_ENV (an unbuilt `bun app.ts`) counts as production.
505
+ const nodeEnv = typeof process === "undefined" ? undefined : process.env.NODE_ENV;
506
+ const data = nodeEnv === "development" || nodeEnv === "test"
507
+ ? { name: e.name, message: e.message }
508
+ : { name: "Error", message: "Internal Server Error" };
509
+ const inLayouts = errorModule.inLayouts !== false;
510
+ const chain = inLayouts && layouts !== undefined && keptLayoutIds.length > 0
511
+ ? await layouts(errorModule, keptLayoutIds.length)
512
+ : undefined;
513
+ const render = (components, wrapped) => {
514
+ const layoutData = wrapped?.chain.layoutData;
515
+ return renderPageResult({
516
+ adapter,
517
+ chain: components,
518
+ data,
519
+ ...(wrapped === undefined ? {} : { head: wrapped.head }),
520
+ clientEntry,
521
+ routeId: errorId,
522
+ status: 500,
523
+ hydrate: false,
524
+ ...(layoutData === undefined ? {} : { layoutData }),
525
+ ...(personalized || layoutData !== undefined ? { headers: PRIVATE_PAGE_HEADERS } : {}),
526
+ ...(nonce === undefined ? {} : { nonce }),
527
+ ...cspOption,
528
+ ...titleOption,
529
+ });
530
+ };
531
+ if (chain !== undefined) {
532
+ const resolved = resolveChainAndHead(chain.modules, chain.layoutData, errorModule, statusMetaArgs(req, data, nonce));
533
+ try {
534
+ return await render(resolved.chain, { head: resolved.head, chain });
535
+ }
536
+ catch {
537
+ // A layout that fails to render leaves the error page alone, never a second error.
538
+ return render([errorModule.default]);
539
+ }
540
+ }
541
+ // Layouts whose data did not settle still frame the error page, without their data.
542
+ const layoutComponents = await Promise.all(keptLayoutIds.map((id) => manifest.layouts[id].load().then((m) => m.default)));
543
+ return render([...layoutComponents, errorModule.default]);
473
544
  };
474
- const renderTerminalStatus = async (status, path, extraHeaders, nonce) => {
545
+ /** The leading `count` layouts of `modules`, unless one of them answered with a status or redirect. */
546
+ const settledLayouts = (modules, layoutData, count) => {
547
+ if (modules.length < count)
548
+ return undefined;
549
+ const data = layoutData?.slice(0, count);
550
+ if (data?.some((value) => isControlFlow(value) || isStatusSignal(value)))
551
+ return undefined;
552
+ return { modules: modules.slice(0, count), layoutData: data };
553
+ };
554
+ /** A run's leading layouts once all of its loaders settle - never when a layout or middleware refused. */
555
+ const leadingLayouts = (run, err) => async (_page, count) => {
556
+ if (run === undefined || (err !== undefined && layoutErrorId(err) !== undefined)) {
557
+ return undefined;
558
+ }
559
+ try {
560
+ await run.pending;
561
+ }
562
+ catch {
563
+ return undefined;
564
+ }
565
+ return settledLayouts(run.modules, run.layoutData, count);
566
+ };
567
+ const rootLayoutIds = manifest.layouts._layout === undefined ? [] : ["_layout"];
568
+ const rootMiddlewareIds = manifest.middlewares?._middleware === undefined ? [] : ["_middleware"];
569
+ /**
570
+ * Run the root middleware and layouts for a request no route ran them for (an unmatched URL), as a
571
+ * nested `_404` runs its own. Any refusal or failure renders the page alone.
572
+ */
573
+ const rootLayoutsFor = (c, controls) => async (page) => {
574
+ const entry = manifest.statusPages?.["404"] ?? manifest.notFound;
575
+ if (rootLayoutIds.length === 0 || entry === undefined)
576
+ return undefined;
577
+ const layoutsOnly = {
578
+ id: "_404",
579
+ pattern: "/*",
580
+ layoutIds: rootLayoutIds,
581
+ errorIds: [],
582
+ file: entry.file,
583
+ load: entry.load,
584
+ };
585
+ const route = rootMiddlewareIds.length === 0
586
+ ? layoutsOnly
587
+ : { ...layoutsOnly, middlewareIds: rootMiddlewareIds };
588
+ try {
589
+ const draft = await draftFlag(c.req);
590
+ if (draft)
591
+ controls.personalize();
592
+ const ctx = {
593
+ params: {},
594
+ request: c.req,
595
+ req: c.req,
596
+ api: apiFor(c),
597
+ env: c.env,
598
+ draft,
599
+ search: loaderSearch(page.searchSchema, c.req),
600
+ set: controls.scope(PAGE_SCOPE),
601
+ };
602
+ if (route.middlewareIds !== undefined)
603
+ await runMiddleware(route, ctx, controls);
604
+ const run = await runLayoutChain(route, ctx, controls);
605
+ await run.pending;
606
+ return settledLayouts(run.modules, run.layoutData, rootLayoutIds.length);
607
+ }
608
+ catch {
609
+ return undefined;
610
+ }
611
+ };
612
+ const renderTerminalStatus = async (status, req, extraHeaders, nonce, layouts) => {
475
613
  const page = manifest.statusPages?.[String(status)] ?? manifest.notFound;
476
614
  if (page === undefined) {
477
615
  const headers = new Headers(extraHeaders);
@@ -479,31 +617,55 @@ export function createPageRequestExecutor(options) {
479
617
  return new Response(STATUS_TEXT[status] ?? "Error", { status, headers });
480
618
  }
481
619
  const routeId = manifest.statusPages?.[String(status)] !== undefined ? `_${status}` : "_404";
482
- const loaded = (await page.load());
483
- return renderPageResult({
620
+ const loaded = await page.load();
621
+ const wraps = loaded.inLayouts !== false && rootLayoutIds.length > 0;
622
+ const chain = wraps && layouts !== undefined ? await layouts(loaded, rootLayoutIds.length) : undefined;
623
+ const modules = chain?.modules ?? [];
624
+ const layoutIds = chain === undefined ? [] : rootLayoutIds;
625
+ const layoutData = chain?.layoutData;
626
+ const { chain: components, head } = resolveChainAndHead(modules, layoutData, loaded, statusMetaArgs(req, null, nonce));
627
+ let headers = extraHeaders;
628
+ if (layoutData !== undefined) {
629
+ // The page now shows what a layout loaded for this visitor.
630
+ const privateHeaders = new Headers(extraHeaders);
631
+ privateHeaders.set("cache-control", PRIVATE_NO_STORE);
632
+ headers = privateHeaders;
633
+ }
634
+ const statusPage = {
484
635
  adapter,
485
- chain: [loaded.default],
636
+ chain: components,
486
637
  data: null,
638
+ head,
487
639
  clientEntry,
488
640
  routeId,
489
- matchChain: { ids: [routeId], handles: [loaded.handle] },
490
- ...(path !== undefined
491
- ? {
492
- search: searchOf(loaded.searchSchema, path.includes("?") ? path.slice(path.indexOf("?")) : ""),
493
- }
494
- : {}),
495
- ...(path !== undefined ? { path } : {}),
641
+ matchChain: matchChainOf(layoutIds, modules, routeId, loaded),
642
+ search: searchChainOf(modules, loaded, req),
643
+ path: pathOf(req),
496
644
  status,
497
- ...(extraHeaders !== undefined ? { headers: extraHeaders } : {}),
645
+ // The browser renders this page inside the root layout, so a page that had to render alone
646
+ // stays server-only, as a nested `_404` does.
647
+ ...(wraps && chain === undefined ? { hydrate: false } : {}),
648
+ ...(layoutData === undefined ? {} : { layoutData }),
649
+ ...(headers !== undefined ? { headers } : {}),
498
650
  ...(nonce === undefined ? {} : { nonce }),
499
651
  ...cspOption,
500
652
  ...preloadOf(routeId),
501
- ...stylesOf(routeId),
653
+ // The build keys stylesheets by route, so a page inside the root layout links the whole-app one.
654
+ ...(chain === undefined ? stylesOf(routeId) : stylesFrom(options.styles)),
502
655
  ...prerenderedHandover,
503
656
  ...titleOption,
504
- });
657
+ };
658
+ if (chain === undefined)
659
+ return renderPageResult(statusPage);
660
+ try {
661
+ return await renderPageResult(statusPage);
662
+ }
663
+ catch {
664
+ // A layout that fails to render leaves the status page alone, never a second error.
665
+ return renderTerminalStatus(status, req, extraHeaders, nonce);
666
+ }
505
667
  };
506
- const renderStatusSignal = async (req, env, signal, personalized) => {
668
+ const renderStatusSignal = async (req, env, signal, personalized, layouts) => {
507
669
  const { status, headers } = signal[STATUS_SIGNAL];
508
670
  if (req.headers.get(DATA_HEADER) !== null) {
509
671
  // Same URL as the status page, so it is never storable: a cache keyed on the URL alone would
@@ -516,7 +678,7 @@ export function createPageRequestExecutor(options) {
516
678
  }
517
679
  const nonce = await resolveNonce(req, env);
518
680
  if (headers === undefined && !personalized) {
519
- return renderTerminalStatus(status, pathOf(req), undefined, nonce);
681
+ return renderTerminalStatus(status, req, undefined, nonce, layouts);
520
682
  }
521
683
  const documentHeaders = new Headers(headers);
522
684
  if (headers !== undefined) {
@@ -525,7 +687,7 @@ export function createPageRequestExecutor(options) {
525
687
  // A cookie queued before the signal makes the status page specific to this visitor.
526
688
  if (personalized)
527
689
  documentHeaders.set("cache-control", PRIVATE_NO_STORE);
528
- return renderTerminalStatus(status, pathOf(req), documentHeaders, nonce);
690
+ return renderTerminalStatus(status, req, documentHeaders, nonce, layouts);
529
691
  };
530
692
  const reportLoaderError = (route, req, params, err) => {
531
693
  if (options.onLoaderError === undefined)
@@ -537,9 +699,11 @@ export function createPageRequestExecutor(options) {
537
699
  // A faulty reporter must never break error rendering.
538
700
  }
539
701
  };
540
- const handleDataOrDocument = async (req, env, route, params, mod, data, layoutData, layoutModules, layoutRetained, boundaryStates, assemblyCache, responseHeaders, personalized) => {
702
+ const handleDataOrDocument = async (req, env, route, params, mod, data, layoutData, layoutModules, layoutRetained, boundaryStates, assemblyCache, responseHeaders, personalized, serverComponents) => {
703
+ // Every layout loader settled before the page rendered, so a status or error page can reuse them.
704
+ const settled = async (_page, count) => settledLayouts(layoutModules, layoutData, count);
541
705
  if (isStatusSignal(data))
542
- return renderStatusSignal(req, env, data, personalized);
706
+ return renderStatusSignal(req, env, data, personalized, settled);
543
707
  if (isControlFlow(data))
544
708
  return data;
545
709
  if (req.headers.get(DATA_HEADER) !== null) {
@@ -557,7 +721,7 @@ export function createPageRequestExecutor(options) {
557
721
  ...deferred,
558
722
  ...(boundarySplit === undefined ? [] : boundarySplit.deferred),
559
723
  ];
560
- const payload = layoutData === undefined && boundarySplit === undefined
724
+ const payload = layoutData === undefined && boundarySplit === undefined && serverComponents === undefined
561
725
  ? pageSplit.forClient
562
726
  : {
563
727
  v: 1,
@@ -565,6 +729,7 @@ export function createPageRequestExecutor(options) {
565
729
  layoutData: layoutSplits.map((split) => split.forClient),
566
730
  retained: layoutRetained,
567
731
  ...(boundarySplit === undefined ? {} : { boundaries: boundarySplit.forClient }),
732
+ ...(serverComponents === undefined ? {} : { serverComponents }),
568
733
  };
569
734
  if (allDeferred.length === 0) {
570
735
  return Response.json(payload ?? null, { headers: DATA_RESPONSE_HEADERS });
@@ -577,8 +742,9 @@ export function createPageRequestExecutor(options) {
577
742
  });
578
743
  }
579
744
  const nonce = options.nonce === undefined ? undefined : await resolveNonce(req, env);
580
- const { chain, head } = resolveChainAndHead(layoutModules, mod, {
745
+ const { chain, head } = resolveChainAndHead(layoutModules, layoutData, mod, {
581
746
  data,
747
+ pageData: data,
582
748
  params,
583
749
  origin: originOf(req),
584
750
  ...(nonce === undefined ? {} : { nonce }),
@@ -595,6 +761,8 @@ export function createPageRequestExecutor(options) {
595
761
  path: pathOf(req),
596
762
  search: searchChainOf(layoutModules, mod, req),
597
763
  matchChain: matchChainOf(route.layoutIds, layoutModules, route.id, mod),
764
+ ...(serverComponents === undefined ? {} : { serverComponents }),
765
+ ...(progressiveActionData.has(req) ? { actionData: progressiveActionData.get(req) } : {}),
598
766
  hydrate: mod.hydrate !== false,
599
767
  ...(layoutData !== undefined ? { layoutData } : {}),
600
768
  ...(boundaryStates !== undefined ? { boundaries: boundaryStates } : {}),
@@ -622,31 +790,33 @@ export function createPageRequestExecutor(options) {
622
790
  }
623
791
  catch (err) {
624
792
  if (isStatusSignal(err))
625
- return renderStatusSignal(req, env, err, personalized);
793
+ return renderStatusSignal(req, env, err, personalized, settled);
626
794
  if (isControlFlow(err))
627
795
  throw err;
628
796
  reportLoaderError(route, req, { ...params }, err);
629
797
  const errorId = boundaryFor(route, err);
630
798
  if (errorId === undefined)
631
799
  throw err;
632
- return renderError(route, errorId, withDuplicateInstanceHint(err), personalized, nonce);
800
+ options.logBoundaryError?.(err, req);
801
+ return renderError(req, route, errorId, withDuplicateInstanceHint(err), personalized, nonce, settled);
633
802
  }
634
803
  };
635
804
  /** What a failed loader answers with, once a status signal has been ruled out. */
636
- const renderLoaderFailure = async (route, req, env, params, err, personalized) => {
805
+ const renderLoaderFailure = async (route, req, env, params, err, personalized, layouts) => {
637
806
  if (isControlFlow(err))
638
807
  throw err;
639
808
  reportLoaderError(route, req, params, err);
640
809
  const errorId = boundaryFor(route, err);
641
810
  if (errorId === undefined)
642
811
  throw err;
812
+ options.logBoundaryError?.(err, req);
643
813
  if (req.headers.get(DATA_HEADER) !== null) {
644
814
  return new Response("Internal Server Error", {
645
815
  status: 500,
646
816
  headers: { "content-type": "text/plain; charset=utf-8" },
647
817
  });
648
818
  }
649
- return renderError(route, errorId, withDuplicateInstanceHint(err), personalized, options.nonce === undefined ? undefined : await resolveNonce(req, env));
819
+ return renderError(req, route, errorId, withDuplicateInstanceHint(err), personalized, options.nonce === undefined ? undefined : await resolveNonce(req, env), layouts);
650
820
  };
651
821
  /**
652
822
  * Render a nested `_404` inside its layouts. `layoutModules` and `layoutData` are exactly the
@@ -665,8 +835,9 @@ export function createPageRequestExecutor(options) {
665
835
  }
666
836
  const mod = await page.load();
667
837
  const nonce = options.nonce === undefined ? undefined : await resolveNonce(req, env);
668
- const { chain, head } = resolveChainAndHead(layoutModules, mod, {
838
+ const { chain, head } = resolveChainAndHead(layoutModules, layoutData, mod, {
669
839
  data: null,
840
+ pageData: null,
670
841
  params,
671
842
  origin: originOf(req),
672
843
  ...(nonce === undefined ? {} : { nonce }),
@@ -721,11 +892,11 @@ export function createPageRequestExecutor(options) {
721
892
  const kept = page.layoutIds.length;
722
893
  return renderNestedNotFound(req, env, id, page, params, run.modules.slice(0, kept), run.layoutData?.slice(0, kept), personalized, signal[STATUS_SIGNAL].headers);
723
894
  };
724
- const renderNotFound = async (request, env, path) => {
895
+ const renderNotFound = async (c, controls) => {
725
896
  const page = manifest.statusPages?.["404"] ?? manifest.notFound;
726
897
  if (page === undefined)
727
- return renderTerminalStatus(404, path);
728
- return renderTerminalStatus(404, path, undefined, options.nonce === undefined ? undefined : await resolveNonce(request, env));
898
+ return renderTerminalStatus(404, c.req);
899
+ return renderTerminalStatus(404, c.req, undefined, options.nonce === undefined ? undefined : await resolveNonce(c.req, c.env), rootLayoutsFor(c, controls));
729
900
  };
730
901
  /**
731
902
  * Run a page handler with the request's `ctx.set`, sealed once the handler settles - whatever it
@@ -779,8 +950,19 @@ export function createPageRequestExecutor(options) {
779
950
  return assemblySlot;
780
951
  };
781
952
  return withResponseControls(async (c, controls) => {
782
- if (is404Fallback && !prerenderedSet.has(urlPartsFor(c.req).pathname)) {
783
- return renderNotFound(c.req, c.env, pathOf(c.req));
953
+ const componentRuntime = options.serverComponents;
954
+ const componentOwner = componentRuntime?.owns(route.id) === true;
955
+ let componentRequest;
956
+ try {
957
+ componentRequest = componentRuntime?.classify(c.req, route.id);
958
+ }
959
+ catch (error) {
960
+ return componentRuntime.failure(error);
961
+ }
962
+ if (componentOwner || componentRequest !== undefined)
963
+ controls.personalize();
964
+ if (!componentOwner && is404Fallback && !prerenderedSet.has(urlPartsFor(c.req).pathname)) {
965
+ return renderNotFound(c, controls);
784
966
  }
785
967
  const mod = await route.load();
786
968
  const draft = await draftFlag(c.req);
@@ -793,6 +975,7 @@ export function createPageRequestExecutor(options) {
793
975
  let layoutModules;
794
976
  let layoutRetained = [];
795
977
  let responseHeaders;
978
+ let componentState;
796
979
  const requestApi = apiFor(c);
797
980
  let run;
798
981
  try {
@@ -808,13 +991,15 @@ export function createPageRequestExecutor(options) {
808
991
  };
809
992
  if (route.middlewareIds !== undefined)
810
993
  await runMiddleware(route, ctx, controls);
811
- run = await runLayoutChain(route, ctx, controls, retainContextOf(c.req));
994
+ run = await runLayoutChain(route, ctx, controls, componentOwner ? undefined : retainContextOf(c.req));
812
995
  layoutModules = run.modules;
813
996
  layoutRetained = run.retained;
814
- const boundaryDefinitions = [
815
- ...run.modules.flatMap((layout) => layout.boundaries ?? []),
816
- ...(mod.boundaries ?? []),
817
- ];
997
+ const boundaryDefinitions = componentRequest === undefined
998
+ ? [
999
+ ...run.modules.flatMap((layout) => layout.boundaries ?? []),
1000
+ ...(mod.boundaries ?? []),
1001
+ ]
1002
+ : [];
818
1003
  if (boundaryDefinitions.length > 0)
819
1004
  boundaryDescriptors(boundaryDefinitions);
820
1005
  const effectiveSearch = searchChainOf(run.modules, mod, c.req);
@@ -840,6 +1025,25 @@ export function createPageRequestExecutor(options) {
840
1025
  : await Promise.all([pageResult, run.pending, staticBoundaryPromise]);
841
1026
  data = pageData;
842
1027
  layoutData = run.layoutData;
1028
+ if (componentOwner) {
1029
+ for (let i = 0; i < (layoutData?.length ?? 0); i++) {
1030
+ const outcome = layoutData?.[i];
1031
+ if (isControlFlow(outcome) || isStatusSignal(outcome))
1032
+ throw tagLayoutError(outcome, route.layoutIds[i]);
1033
+ }
1034
+ }
1035
+ if (componentRequest !== undefined && (isControlFlow(data) || isStatusSignal(data)))
1036
+ return componentRuntime.failure(data);
1037
+ if (componentOwner && !isControlFlow(data) && !isStatusSignal(data)) {
1038
+ const componentContext = Object.create(c, Object.getOwnPropertyDescriptors({
1039
+ ...ctx,
1040
+ search: effectiveSearch,
1041
+ signal: c.signal === undefined ? c.req.signal : AbortSignal.any([c.signal, c.req.signal]),
1042
+ data,
1043
+ layoutData: layoutData ?? [],
1044
+ }));
1045
+ componentState = await componentRuntime.execute(route.id, componentContext, componentRequest, options.nonce === undefined ? undefined : await resolveNonce(c.req, c.env));
1046
+ }
843
1047
  if (boundaryBatch !== undefined) {
844
1048
  const streamed = { ...boundaryBatch.initial, ...(staticBoundaries ?? {}) };
845
1049
  for (const entry of boundaryBatch.pending) {
@@ -857,17 +1061,28 @@ export function createPageRequestExecutor(options) {
857
1061
  responseHeaders = controls.documentHeaders();
858
1062
  }
859
1063
  catch (err) {
1064
+ if (componentRequest !== undefined)
1065
+ return componentRuntime.failure(err);
860
1066
  if (isStatusSignal(err)) {
861
- return ((await renderRouteNotFound(c.req, c.env, route, c.params, err, run, controls.personalized)) ?? renderStatusSignal(c.req, c.env, err, controls.personalized));
1067
+ return ((await renderRouteNotFound(c.req, c.env, route, c.params, err, run, controls.personalized)) ??
1068
+ renderStatusSignal(c.req, c.env, err, controls.personalized, leadingLayouts(run, err)));
862
1069
  }
863
- return renderLoaderFailure(route, c.req, c.env, c.params, err, controls.personalized);
1070
+ return renderLoaderFailure(route, c.req, c.env, c.params, err, controls.personalized, leadingLayouts(run, err));
1071
+ }
1072
+ if (componentRequest !== undefined && componentState !== undefined) {
1073
+ const answer = componentRuntime.response(componentState, componentRequest);
1074
+ const headers = new Headers(answer.headers);
1075
+ for (const [name, value] of Object.entries(responseHeaders ?? {}))
1076
+ if (name !== "cache-control" && name !== "content-type" && name !== "vary")
1077
+ headers.set(name, value);
1078
+ return new Response(answer.body, { status: answer.status, headers });
864
1079
  }
865
1080
  if (isStatusSignal(data)) {
866
1081
  const nested = await renderRouteNotFound(c.req, c.env, route, c.params, data, run, controls.personalized);
867
1082
  if (nested !== undefined)
868
1083
  return nested;
869
1084
  }
870
- return handleDataOrDocument(c.req, c.env, route, c.params, mod, data, layoutData, layoutModules ?? (await loadLayoutModules(route)), layoutRetained, boundaryStates, layoutModules === undefined ? undefined : assemblyCacheFor(mod, layoutModules), responseHeaders, controls.personalized);
1085
+ return handleDataOrDocument(c.req, c.env, route, c.params, mod, data, layoutData, layoutModules ?? (await loadLayoutModules(route)), layoutRetained, boundaryStates, layoutModules === undefined ? undefined : assemblyCacheFor(mod, layoutModules), responseHeaders, controls.personalized, componentState);
871
1086
  });
872
1087
  },
873
1088
  post: (route) => withResponseControls(async (c, controls) => {
@@ -881,6 +1096,28 @@ export function createPageRequestExecutor(options) {
881
1096
  headers: { allow: "GET", "content-type": "text/plain; charset=utf-8" },
882
1097
  });
883
1098
  }
1099
+ const isDataRequest = c.req.headers.get(DATA_HEADER) !== null;
1100
+ // Before anything runs: a form posts cross-site without a preflight and carries the visitor's
1101
+ // cookies, so an action is a CSRF target unless its route names the origins it accepts.
1102
+ const origin = c.req.headers.get("origin");
1103
+ const allowedOrigins = mod.allowedOrigins;
1104
+ const allowed = allowedOrigins === undefined
1105
+ ? origin === null ||
1106
+ isSameOriginForm(origin, c.req.headers.get("sec-fetch-site"), c.req)
1107
+ : typeof allowedOrigins === "function"
1108
+ ? allowedOrigins(origin) === true
1109
+ : origin !== null && allowedOrigins.includes(origin);
1110
+ if (!allowed) {
1111
+ return isDataRequest
1112
+ ? Response.json({ ok: false, error: "forbidden_origin" }, { status: 403, headers: { "cache-control": PRIVATE_NO_STORE } })
1113
+ : new Response("Forbidden", {
1114
+ status: 403,
1115
+ headers: {
1116
+ "content-type": "text/plain; charset=utf-8",
1117
+ "cache-control": PRIVATE_NO_STORE,
1118
+ },
1119
+ });
1120
+ }
884
1121
  const requestApi = apiFor(c);
885
1122
  const actionContext = {
886
1123
  params: c.params,
@@ -892,13 +1129,12 @@ export function createPageRequestExecutor(options) {
892
1129
  search: loaderSearch(mod.searchSchema, c.req),
893
1130
  set: controls.scope(ACTION_SCOPE),
894
1131
  };
895
- const isDataRequest = c.req.headers.get(DATA_HEADER) !== null;
896
- let layoutModules;
1132
+ let gated;
897
1133
  let result;
898
1134
  try {
899
1135
  if (route.middlewareIds !== undefined)
900
1136
  await runMiddleware(route, actionContext, controls);
901
- layoutModules = await runLayoutGates(route, actionContext, controls);
1137
+ gated = await runLayoutGates(route, actionContext, controls);
902
1138
  result = await mod.action(actionContext);
903
1139
  }
904
1140
  catch (err) {
@@ -933,23 +1169,109 @@ export function createPageRequestExecutor(options) {
933
1169
  },
934
1170
  });
935
1171
  }
1172
+ if (options.serverComponents?.owns(route.id)) {
1173
+ if (options.rerenderOwnerGET === undefined)
1174
+ throw new Error("component_contract");
1175
+ const actionHeaders = controls.documentHeaders();
1176
+ if (controls.progressiveRedirect(c.req)) {
1177
+ // Relative, so a browser behind a TLS terminator stays on its own scheme. A `//` path
1178
+ // would read as another host, so that one keeps the absolute URL.
1179
+ const { pathname, search } = new URL(c.req.url);
1180
+ const location = pathname.startsWith("//") ? c.req.url : pathname + search;
1181
+ return new Response(null, {
1182
+ status: 303,
1183
+ headers: { ...actionHeaders, location, "cache-control": PRIVATE_NO_STORE },
1184
+ });
1185
+ }
1186
+ const headers = new Headers(c.req.headers);
1187
+ for (const name of [
1188
+ DATA_HEADER,
1189
+ RETAIN_HEADER,
1190
+ NAV_FROM_HEADER,
1191
+ "x-nifra-component",
1192
+ "content-type",
1193
+ "content-length",
1194
+ "transfer-encoding",
1195
+ ])
1196
+ headers.delete(name);
1197
+ const cookie = controls.progressiveCookies(c.req);
1198
+ if (cookie)
1199
+ headers.set("cookie", cookie);
1200
+ else
1201
+ headers.delete("cookie");
1202
+ const request = new Request(c.req.url, {
1203
+ method: "GET",
1204
+ headers,
1205
+ signal: c.signal === undefined ? c.req.signal : AbortSignal.any([c.signal, c.req.signal]),
1206
+ });
1207
+ progressiveActionData.set(request, actionResult);
1208
+ try {
1209
+ const response = await options.rerenderOwnerGET(request, c);
1210
+ const merged = new Headers(response.headers);
1211
+ for (const [name, value] of Object.entries(actionHeaders ?? {}))
1212
+ if (!merged.has(name))
1213
+ merged.set(name, value);
1214
+ merged.set("cache-control", PRIVATE_NO_STORE);
1215
+ return new Response(response.body, {
1216
+ status: response.status,
1217
+ statusText: response.statusText,
1218
+ headers: merged,
1219
+ });
1220
+ }
1221
+ finally {
1222
+ progressiveActionData.delete(request);
1223
+ }
1224
+ }
1225
+ const layoutModules = gated.modules;
936
1226
  const search = searchChainOf(layoutModules, mod, c.req);
937
- const data = mod.loader
938
- ? await mod.loader({
939
- params: c.params,
940
- request: c.req,
941
- req: c.req,
942
- api: requestApi,
943
- env: c.env,
944
- draft,
945
- search,
946
- set: controls.scope(PAGE_SCOPE),
947
- })
948
- : null;
949
- const responseHeaders = controls.documentHeaders();
1227
+ let run;
1228
+ let data;
1229
+ let responseHeaders;
1230
+ // The ordinary layout loaders run now, after the mutation, as a GET of this page would.
1231
+ try {
1232
+ run = await runLayoutChain(route, actionContext, controls, undefined, gated);
1233
+ const pageResult = mod.loader
1234
+ ? mod.loader({
1235
+ params: c.params,
1236
+ request: c.req,
1237
+ req: c.req,
1238
+ api: requestApi,
1239
+ env: c.env,
1240
+ draft,
1241
+ search,
1242
+ set: controls.scope(PAGE_SCOPE),
1243
+ })
1244
+ : null;
1245
+ const [pageData] = run.pending === undefined
1246
+ ? [await pageResult]
1247
+ : await Promise.all([pageResult, run.pending]);
1248
+ data = pageData;
1249
+ responseHeaders = controls.documentHeaders();
1250
+ }
1251
+ catch (err) {
1252
+ if (isStatusSignal(err)) {
1253
+ return ((await renderRouteNotFound(c.req, c.env, route, c.params, err, run, controls.personalized)) ??
1254
+ renderStatusSignal(c.req, c.env, err, controls.personalized, leadingLayouts(run, err)));
1255
+ }
1256
+ return renderLoaderFailure(route, c.req, c.env, c.params, err, controls.personalized, leadingLayouts(run, err));
1257
+ }
1258
+ // The re-render answers a returned redirect or status the way the page's GET does.
1259
+ if (isStatusSignal(data))
1260
+ return ((await renderRouteNotFound(c.req, c.env, route, c.params, data, run, controls.personalized)) ??
1261
+ renderStatusSignal(c.req, c.env, data, controls.personalized, leadingLayouts(run, data)));
1262
+ if (isControlFlow(data))
1263
+ return data;
1264
+ const layoutData = run.layoutData;
1265
+ const layoutProps = {};
1266
+ if (layoutData !== undefined) {
1267
+ layoutProps.layoutData = layoutData;
1268
+ // The page now shows what a layout loaded for this visitor.
1269
+ responseHeaders = { ...responseHeaders, ...PRIVATE_PAGE_HEADERS };
1270
+ }
950
1271
  const nonce = options.nonce === undefined ? undefined : await resolveNonce(c.req, c.env);
951
- const { chain, head } = resolveChainAndHead(layoutModules, mod, {
1272
+ const { chain, head } = resolveChainAndHead(layoutModules, layoutData, mod, {
952
1273
  data,
1274
+ pageData: data,
953
1275
  params: c.params,
954
1276
  origin: originOf(c.req),
955
1277
  ...(nonce === undefined ? {} : { nonce }),
@@ -967,6 +1289,7 @@ export function createPageRequestExecutor(options) {
967
1289
  search,
968
1290
  matchChain: matchChainOf(route.layoutIds, layoutModules, route.id, mod),
969
1291
  hydrate: mod.hydrate !== false,
1292
+ ...layoutProps,
970
1293
  ...preloadOf(route.id),
971
1294
  ...stylesOf(route.id),
972
1295
  ...prerenderedHandover,
@@ -980,7 +1303,7 @@ export function createPageRequestExecutor(options) {
980
1303
  const match = matchNotFoundScope?.(pathOf(c.req)) ?? null;
981
1304
  const target = match === null ? undefined : notFoundTargets[Number(match.routeId)];
982
1305
  if (match === null || target === undefined) {
983
- return renderNotFound(c.req, c.env, pathOf(c.req));
1306
+ return renderNotFound(c, controls);
984
1307
  }
985
1308
  const params = {};
986
1309
  for (let i = 0; i < target.names.length; i++) {
@@ -1010,9 +1333,9 @@ export function createPageRequestExecutor(options) {
1010
1333
  }
1011
1334
  catch (err) {
1012
1335
  if (isStatusSignal(err)) {
1013
- return renderStatusSignal(c.req, c.env, err, controls.personalized);
1336
+ return renderStatusSignal(c.req, c.env, err, controls.personalized, leadingLayouts(run, err));
1014
1337
  }
1015
- return renderLoaderFailure(target.route, c.req, c.env, params, err, controls.personalized);
1338
+ return renderLoaderFailure(target.route, c.req, c.env, params, err, controls.personalized, leadingLayouts(run, err));
1016
1339
  }
1017
1340
  return renderNestedNotFound(c.req, c.env, target.id, target.page, params, run.modules, run.layoutData, controls.personalized);
1018
1341
  }),