@nifrajs/web 4.0.2 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (384) hide show
  1. package/README.md +11 -0
  2. package/dist/build-vite.d.ts +19 -0
  3. package/dist/build-vite.d.ts.map +1 -1
  4. package/dist/build-vite.js +31 -10
  5. package/dist/build-vite.js.map +1 -1
  6. package/dist/build.d.ts +8 -10
  7. package/dist/build.d.ts.map +1 -1
  8. package/dist/build.js +90 -31
  9. package/dist/build.js.map +1 -1
  10. package/dist/cdn.d.ts +8 -0
  11. package/dist/cdn.d.ts.map +1 -1
  12. package/dist/cdn.js +14 -15
  13. package/dist/cdn.js.map +1 -1
  14. package/dist/client.d.ts +1 -1
  15. package/dist/client.d.ts.map +1 -1
  16. package/dist/client.js +25 -7
  17. package/dist/client.js.map +1 -1
  18. package/dist/deferred.d.ts +2 -2
  19. package/dist/deferred.d.ts.map +1 -1
  20. package/dist/deferred.js +28 -7
  21. package/dist/deferred.js.map +1 -1
  22. package/dist/dev-feed.d.ts.map +1 -1
  23. package/dist/dev-feed.js +24 -5
  24. package/dist/dev-feed.js.map +1 -1
  25. package/dist/dev-session.d.ts.map +1 -1
  26. package/dist/dev-session.js +4 -14
  27. package/dist/dev-session.js.map +1 -1
  28. package/dist/dev.d.ts +7 -17
  29. package/dist/dev.d.ts.map +1 -1
  30. package/dist/dev.js +13 -1
  31. package/dist/dev.js.map +1 -1
  32. package/dist/draft.d.ts +8 -5
  33. package/dist/draft.d.ts.map +1 -1
  34. package/dist/draft.js +30 -13
  35. package/dist/draft.js.map +1 -1
  36. package/dist/fonts-google.d.ts.map +1 -1
  37. package/dist/fonts-google.js +7 -0
  38. package/dist/fonts-google.js.map +1 -1
  39. package/dist/index.d.ts +2 -2
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +2 -2
  42. package/dist/index.js.map +1 -1
  43. package/dist/internal/codegen.d.ts +1 -0
  44. package/dist/internal/codegen.d.ts.map +1 -1
  45. package/dist/internal/codegen.js +56 -29
  46. package/dist/internal/codegen.js.map +1 -1
  47. package/dist/internal/disposable-dir.d.ts +8 -0
  48. package/dist/internal/disposable-dir.d.ts.map +1 -0
  49. package/dist/internal/disposable-dir.js +25 -0
  50. package/dist/internal/disposable-dir.js.map +1 -0
  51. package/dist/internal/entry-chunks.d.ts +9 -0
  52. package/dist/internal/entry-chunks.d.ts.map +1 -0
  53. package/dist/internal/entry-chunks.js +15 -0
  54. package/dist/internal/entry-chunks.js.map +1 -0
  55. package/dist/internal/evidence-traversal.d.ts +12 -0
  56. package/dist/internal/evidence-traversal.d.ts.map +1 -0
  57. package/dist/internal/evidence-traversal.js +48 -0
  58. package/dist/internal/evidence-traversal.js.map +1 -0
  59. package/dist/internal/head-merge.d.ts +6 -0
  60. package/dist/internal/head-merge.d.ts.map +1 -1
  61. package/dist/internal/head-merge.js +9 -0
  62. package/dist/internal/head-merge.js.map +1 -1
  63. package/dist/internal/js-string.d.ts +2 -0
  64. package/dist/internal/js-string.d.ts.map +1 -1
  65. package/dist/internal/js-string.js +5 -1
  66. package/dist/internal/js-string.js.map +1 -1
  67. package/dist/internal/mount-shadow.js +1 -1
  68. package/dist/internal/mount-shadow.js.map +1 -1
  69. package/dist/internal/output-guard.d.ts +7 -0
  70. package/dist/internal/output-guard.d.ts.map +1 -1
  71. package/dist/internal/output-guard.js +80 -8
  72. package/dist/internal/output-guard.js.map +1 -1
  73. package/dist/internal/page-execution.d.ts +6 -0
  74. package/dist/internal/page-execution.d.ts.map +1 -1
  75. package/dist/internal/page-execution.js +377 -108
  76. package/dist/internal/page-execution.js.map +1 -1
  77. package/dist/internal/progressive-cookies.d.ts +14 -0
  78. package/dist/internal/progressive-cookies.d.ts.map +1 -0
  79. package/dist/internal/progressive-cookies.js +43 -0
  80. package/dist/internal/progressive-cookies.js.map +1 -0
  81. package/dist/internal/public-env.d.ts +2 -0
  82. package/dist/internal/public-env.d.ts.map +1 -0
  83. package/dist/internal/public-env.js +13 -0
  84. package/dist/internal/public-env.js.map +1 -0
  85. package/dist/internal/render-document.d.ts +1 -0
  86. package/dist/internal/render-document.d.ts.map +1 -1
  87. package/dist/internal/render-document.js +28 -5
  88. package/dist/internal/render-document.js.map +1 -1
  89. package/dist/internal/response-controls.d.ts +2 -0
  90. package/dist/internal/response-controls.d.ts.map +1 -1
  91. package/dist/internal/response-controls.js +26 -16
  92. package/dist/internal/response-controls.js.map +1 -1
  93. package/dist/internal/revive-dates.d.ts +16 -0
  94. package/dist/internal/revive-dates.d.ts.map +1 -0
  95. package/dist/internal/revive-dates.js +32 -0
  96. package/dist/internal/revive-dates.js.map +1 -0
  97. package/dist/internal/route-path.d.ts +8 -0
  98. package/dist/internal/route-path.d.ts.map +1 -0
  99. package/dist/internal/route-path.js +8 -0
  100. package/dist/internal/route-path.js.map +1 -0
  101. package/dist/internal/secret-scan.d.ts.map +1 -1
  102. package/dist/internal/secret-scan.js +88 -20
  103. package/dist/internal/secret-scan.js.map +1 -1
  104. package/dist/internal/server-boundary.d.ts.map +1 -1
  105. package/dist/internal/server-boundary.js +33 -5
  106. package/dist/internal/server-boundary.js.map +1 -1
  107. package/dist/internal/web-app.d.ts +21 -16
  108. package/dist/internal/web-app.d.ts.map +1 -1
  109. package/dist/internal/web-app.js +73 -21
  110. package/dist/internal/web-app.js.map +1 -1
  111. package/dist/internal/wire-date.d.ts +3 -0
  112. package/dist/internal/wire-date.d.ts.map +1 -0
  113. package/dist/internal/wire-date.js +14 -0
  114. package/dist/internal/wire-date.js.map +1 -0
  115. package/dist/internal/zone-graph.d.ts +4 -0
  116. package/dist/internal/zone-graph.d.ts.map +1 -1
  117. package/dist/internal/zone-graph.js +54 -14
  118. package/dist/internal/zone-graph.js.map +1 -1
  119. package/dist/isr.d.ts +58 -2
  120. package/dist/isr.d.ts.map +1 -1
  121. package/dist/isr.js +296 -39
  122. package/dist/isr.js.map +1 -1
  123. package/dist/llms-txt.d.ts +2 -0
  124. package/dist/llms-txt.d.ts.map +1 -1
  125. package/dist/llms-txt.js +29 -3
  126. package/dist/llms-txt.js.map +1 -1
  127. package/dist/manifest.d.ts +28 -4
  128. package/dist/manifest.d.ts.map +1 -1
  129. package/dist/manifest.js +3 -0
  130. package/dist/manifest.js.map +1 -1
  131. package/dist/open-resolved.d.ts +18 -0
  132. package/dist/open-resolved.d.ts.map +1 -0
  133. package/dist/open-resolved.js +62 -0
  134. package/dist/open-resolved.js.map +1 -0
  135. package/dist/plugins/kit.d.ts +1 -0
  136. package/dist/plugins/kit.d.ts.map +1 -1
  137. package/dist/plugins/kit.js +26 -11
  138. package/dist/plugins/kit.js.map +1 -1
  139. package/dist/plugins/vite-leak-guard.d.ts +3 -1
  140. package/dist/plugins/vite-leak-guard.d.ts.map +1 -1
  141. package/dist/plugins/vite-leak-guard.js +4 -0
  142. package/dist/plugins/vite-leak-guard.js.map +1 -1
  143. package/dist/public-dir.d.ts +8 -1
  144. package/dist/public-dir.d.ts.map +1 -1
  145. package/dist/public-dir.js +17 -6
  146. package/dist/public-dir.js.map +1 -1
  147. package/dist/query.d.ts +13 -4
  148. package/dist/query.d.ts.map +1 -1
  149. package/dist/query.js +89 -26
  150. package/dist/query.js.map +1 -1
  151. package/dist/render-seam.d.ts +2 -0
  152. package/dist/render-seam.d.ts.map +1 -1
  153. package/dist/render-seam.js +1 -0
  154. package/dist/render-seam.js.map +1 -1
  155. package/dist/route-types.d.ts.map +1 -1
  156. package/dist/route-types.js +20 -9
  157. package/dist/route-types.js.map +1 -1
  158. package/dist/router.d.ts +9 -2
  159. package/dist/router.d.ts.map +1 -1
  160. package/dist/router.js +39 -46
  161. package/dist/router.js.map +1 -1
  162. package/dist/server-components/build-info.d.ts +3 -0
  163. package/dist/server-components/build-info.d.ts.map +1 -0
  164. package/dist/server-components/build-info.js +7 -0
  165. package/dist/server-components/build-info.js.map +1 -0
  166. package/dist/server-components/build-target.d.ts +12 -0
  167. package/dist/server-components/build-target.d.ts.map +1 -0
  168. package/dist/server-components/build-target.js +153 -0
  169. package/dist/server-components/build-target.js.map +1 -0
  170. package/dist/server-components/build.d.ts +24 -0
  171. package/dist/server-components/build.d.ts.map +1 -0
  172. package/dist/server-components/build.js +366 -0
  173. package/dist/server-components/build.js.map +1 -0
  174. package/dist/server-components/bundle-proof.d.ts +3 -0
  175. package/dist/server-components/bundle-proof.d.ts.map +1 -0
  176. package/dist/server-components/bundle-proof.js +23 -0
  177. package/dist/server-components/bundle-proof.js.map +1 -0
  178. package/dist/server-components/client.d.ts +46 -0
  179. package/dist/server-components/client.d.ts.map +1 -0
  180. package/dist/server-components/client.js +594 -0
  181. package/dist/server-components/client.js.map +1 -0
  182. package/dist/server-components/definition.d.ts +11 -0
  183. package/dist/server-components/definition.d.ts.map +1 -0
  184. package/dist/server-components/definition.js +94 -0
  185. package/dist/server-components/definition.js.map +1 -0
  186. package/dist/server-components/dev-http.d.ts +5 -0
  187. package/dist/server-components/dev-http.d.ts.map +1 -0
  188. package/dist/server-components/dev-http.js +71 -0
  189. package/dist/server-components/dev-http.js.map +1 -0
  190. package/dist/server-components/error.d.ts +9 -0
  191. package/dist/server-components/error.d.ts.map +1 -0
  192. package/dist/server-components/error.js +24 -0
  193. package/dist/server-components/error.js.map +1 -0
  194. package/dist/server-components/frames.d.ts +7 -0
  195. package/dist/server-components/frames.d.ts.map +1 -0
  196. package/dist/server-components/frames.js +169 -0
  197. package/dist/server-components/frames.js.map +1 -0
  198. package/dist/server-components/html-policy.d.ts +6 -0
  199. package/dist/server-components/html-policy.d.ts.map +1 -0
  200. package/dist/server-components/html-policy.js +75 -0
  201. package/dist/server-components/html-policy.js.map +1 -0
  202. package/dist/server-components/html.d.ts +4 -0
  203. package/dist/server-components/html.d.ts.map +1 -0
  204. package/dist/server-components/html.js +67 -0
  205. package/dist/server-components/html.js.map +1 -0
  206. package/dist/server-components/islands.d.ts +44 -0
  207. package/dist/server-components/islands.d.ts.map +1 -0
  208. package/dist/server-components/islands.js +42 -0
  209. package/dist/server-components/islands.js.map +1 -0
  210. package/dist/server-components/native.d.ts +4 -0
  211. package/dist/server-components/native.d.ts.map +1 -0
  212. package/dist/server-components/native.js +81 -0
  213. package/dist/server-components/native.js.map +1 -0
  214. package/dist/server-components/portal.d.ts +6 -0
  215. package/dist/server-components/portal.d.ts.map +1 -0
  216. package/dist/server-components/portal.js +35 -0
  217. package/dist/server-components/portal.js.map +1 -0
  218. package/dist/server-components/props.d.ts +3 -0
  219. package/dist/server-components/props.d.ts.map +1 -0
  220. package/dist/server-components/props.js +14 -0
  221. package/dist/server-components/props.js.map +1 -0
  222. package/dist/server-components/protocol.d.ts +44 -0
  223. package/dist/server-components/protocol.d.ts.map +1 -0
  224. package/dist/server-components/protocol.js +139 -0
  225. package/dist/server-components/protocol.js.map +1 -0
  226. package/dist/server-components/range.d.ts +24 -0
  227. package/dist/server-components/range.d.ts.map +1 -0
  228. package/dist/server-components/range.js +263 -0
  229. package/dist/server-components/range.js.map +1 -0
  230. package/dist/server-components/registry.d.ts +6 -0
  231. package/dist/server-components/registry.d.ts.map +1 -0
  232. package/dist/server-components/registry.js +30 -0
  233. package/dist/server-components/registry.js.map +1 -0
  234. package/dist/server-components/response.d.ts +4 -0
  235. package/dist/server-components/response.d.ts.map +1 -0
  236. package/dist/server-components/response.js +32 -0
  237. package/dist/server-components/response.js.map +1 -0
  238. package/dist/server-components/router-integration.d.ts +9 -0
  239. package/dist/server-components/router-integration.d.ts.map +1 -0
  240. package/dist/server-components/router-integration.js +29 -0
  241. package/dist/server-components/router-integration.js.map +1 -0
  242. package/dist/server-components/runtime.d.ts +14 -0
  243. package/dist/server-components/runtime.d.ts.map +1 -0
  244. package/dist/server-components/runtime.js +276 -0
  245. package/dist/server-components/runtime.js.map +1 -0
  246. package/dist/server-components/scheduler.d.ts +14 -0
  247. package/dist/server-components/scheduler.d.ts.map +1 -0
  248. package/dist/server-components/scheduler.js +74 -0
  249. package/dist/server-components/scheduler.js.map +1 -0
  250. package/dist/server-components/schema.d.ts +4 -0
  251. package/dist/server-components/schema.d.ts.map +1 -0
  252. package/dist/server-components/schema.js +77 -0
  253. package/dist/server-components/schema.js.map +1 -0
  254. package/dist/server-components/server-entry.d.ts +12 -0
  255. package/dist/server-components/server-entry.d.ts.map +1 -0
  256. package/dist/server-components/server-entry.js +90 -0
  257. package/dist/server-components/server-entry.js.map +1 -0
  258. package/dist/server-components/source.d.ts +15 -0
  259. package/dist/server-components/source.d.ts.map +1 -0
  260. package/dist/server-components/source.js +269 -0
  261. package/dist/server-components/source.js.map +1 -0
  262. package/dist/server-components/state.d.ts +4 -0
  263. package/dist/server-components/state.d.ts.map +1 -0
  264. package/dist/server-components/state.js +123 -0
  265. package/dist/server-components/state.js.map +1 -0
  266. package/dist/server-components/stream.d.ts +6 -0
  267. package/dist/server-components/stream.d.ts.map +1 -0
  268. package/dist/server-components/stream.js +70 -0
  269. package/dist/server-components/stream.js.map +1 -0
  270. package/dist/server-components/trusted-html.d.ts +3 -0
  271. package/dist/server-components/trusted-html.d.ts.map +1 -0
  272. package/dist/server-components/trusted-html.js +14 -0
  273. package/dist/server-components/trusted-html.js.map +1 -0
  274. package/dist/server-components/types.d.ts +107 -0
  275. package/dist/server-components/types.d.ts.map +1 -0
  276. package/dist/server-components/types.js +2 -0
  277. package/dist/server-components/types.js.map +1 -0
  278. package/dist/server-components/typescript.d.ts +11 -0
  279. package/dist/server-components/typescript.d.ts.map +1 -0
  280. package/dist/server-components/typescript.js +28 -0
  281. package/dist/server-components/typescript.js.map +1 -0
  282. package/dist/server-components/value.d.ts +5 -0
  283. package/dist/server-components/value.d.ts.map +1 -0
  284. package/dist/server-components/value.js +77 -0
  285. package/dist/server-components/value.js.map +1 -0
  286. package/dist/server-components/vite.d.ts +12 -0
  287. package/dist/server-components/vite.d.ts.map +1 -0
  288. package/dist/server-components/vite.js +197 -0
  289. package/dist/server-components/vite.js.map +1 -0
  290. package/dist/server-components.d.ts +5 -0
  291. package/dist/server-components.d.ts.map +1 -0
  292. package/dist/server-components.js +4 -0
  293. package/dist/server-components.js.map +1 -0
  294. package/dist/service-worker.d.ts +8 -1
  295. package/dist/service-worker.d.ts.map +1 -1
  296. package/dist/service-worker.js +20 -6
  297. package/dist/service-worker.js.map +1 -1
  298. package/dist/vite.d.ts +12 -0
  299. package/dist/vite.d.ts.map +1 -1
  300. package/dist/vite.js +7 -1
  301. package/dist/vite.js.map +1 -1
  302. package/dist/zones.d.ts +40 -0
  303. package/dist/zones.d.ts.map +1 -1
  304. package/dist/zones.js +170 -6
  305. package/dist/zones.js.map +1 -1
  306. package/package.json +97 -4
  307. package/src/build-vite.ts +31 -9
  308. package/src/build.ts +109 -31
  309. package/src/cdn.ts +18 -12
  310. package/src/client.ts +25 -6
  311. package/src/deferred.ts +27 -9
  312. package/src/dev-feed.ts +24 -5
  313. package/src/dev-session.ts +4 -13
  314. package/src/dev.ts +29 -1
  315. package/src/draft.ts +35 -14
  316. package/src/fonts-google.ts +10 -0
  317. package/src/index.ts +3 -2
  318. package/src/internal/codegen.ts +60 -29
  319. package/src/internal/disposable-dir.ts +24 -0
  320. package/src/internal/entry-chunks.ts +18 -0
  321. package/src/internal/evidence-traversal.ts +57 -0
  322. package/src/internal/head-merge.ts +16 -0
  323. package/src/internal/js-string.ts +6 -1
  324. package/src/internal/mount-shadow.ts +1 -1
  325. package/src/internal/output-guard.ts +88 -7
  326. package/src/internal/page-execution.ts +506 -108
  327. package/src/internal/progressive-cookies.ts +60 -0
  328. package/src/internal/public-env.ts +14 -0
  329. package/src/internal/render-document.ts +26 -11
  330. package/src/internal/response-controls.ts +31 -14
  331. package/src/internal/revive-dates.ts +35 -0
  332. package/src/internal/route-path.ts +8 -0
  333. package/src/internal/secret-scan.ts +90 -18
  334. package/src/internal/server-boundary.ts +29 -5
  335. package/src/internal/web-app.ts +116 -37
  336. package/src/internal/wire-date.ts +16 -0
  337. package/src/internal/zone-graph.ts +59 -19
  338. package/src/isr.ts +351 -43
  339. package/src/llms-txt.ts +32 -3
  340. package/src/manifest.ts +28 -4
  341. package/src/open-resolved.ts +64 -0
  342. package/src/plugins/kit.ts +31 -11
  343. package/src/plugins/vite-leak-guard.ts +5 -1
  344. package/src/public-dir.ts +27 -8
  345. package/src/query.ts +107 -26
  346. package/src/render-seam.ts +2 -0
  347. package/src/route-types.ts +34 -7
  348. package/src/router.ts +58 -55
  349. package/src/server-components/build-info.ts +11 -0
  350. package/src/server-components/build-target.ts +194 -0
  351. package/src/server-components/build.ts +419 -0
  352. package/src/server-components/bundle-proof.ts +33 -0
  353. package/src/server-components/client.ts +663 -0
  354. package/src/server-components/definition.ts +125 -0
  355. package/src/server-components/dev-http.ts +75 -0
  356. package/src/server-components/error.ts +24 -0
  357. package/src/server-components/frames.ts +181 -0
  358. package/src/server-components/html-policy.ts +94 -0
  359. package/src/server-components/html.ts +81 -0
  360. package/src/server-components/islands.ts +111 -0
  361. package/src/server-components/native.ts +101 -0
  362. package/src/server-components/portal.ts +42 -0
  363. package/src/server-components/props.ts +19 -0
  364. package/src/server-components/protocol.ts +167 -0
  365. package/src/server-components/range.ts +313 -0
  366. package/src/server-components/registry.ts +34 -0
  367. package/src/server-components/response.ts +44 -0
  368. package/src/server-components/router-integration.ts +40 -0
  369. package/src/server-components/runtime.ts +376 -0
  370. package/src/server-components/scheduler.ts +84 -0
  371. package/src/server-components/schema.ts +69 -0
  372. package/src/server-components/server-entry.ts +98 -0
  373. package/src/server-components/source.ts +324 -0
  374. package/src/server-components/state.ts +143 -0
  375. package/src/server-components/stream.ts +71 -0
  376. package/src/server-components/trusted-html.ts +21 -0
  377. package/src/server-components/types.ts +138 -0
  378. package/src/server-components/typescript.ts +39 -0
  379. package/src/server-components/value.ts +82 -0
  380. package/src/server-components/vite.ts +242 -0
  381. package/src/server-components.ts +32 -0
  382. package/src/service-worker.ts +24 -7
  383. package/src/vite.ts +8 -1
  384. package/src/zones.ts +208 -7
@@ -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";
@@ -107,6 +107,8 @@ function appTarget(location, requestUrl) {
107
107
  */
108
108
  export function createPageRequestExecutor(options) {
109
109
  const { adapter, manifest, clientEntry, api } = options;
110
+ // The action result is request-local; a browser cannot manufacture this handoff.
111
+ const progressiveActionData = new WeakMap();
110
112
  // Bound once per render, so every loader, action and boundary in that render shares one view of the
111
113
  // in-process client carrying the visitor's platform. An `api` without the seam passes through as-is.
112
114
  const bindApi = platformBinderOf(api);
@@ -165,9 +167,7 @@ export function createPageRequestExecutor(options) {
165
167
  return chunks ? { preload: chunks } : {};
166
168
  };
167
169
  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;
170
+ const stylesFrom = (styles) => {
171
171
  if (styles === undefined || styles.length === 0)
172
172
  return {};
173
173
  return {
@@ -175,6 +175,7 @@ export function createPageRequestExecutor(options) {
175
175
  ...(cssLoading === "deferred" ? { cssLoading } : {}),
176
176
  };
177
177
  };
178
+ const stylesOf = (id) => stylesFrom(options.routeStyles?.[id] ?? options.styles);
178
179
  // Load a route's layout modules once for the current execution. The render path needs each whole
179
180
  // module for its component + meta, while the data path also needs loader/gate/boundary declarations.
180
181
  const loadLayoutModules = async (route) => {
@@ -194,11 +195,32 @@ export function createPageRequestExecutor(options) {
194
195
  retained: [],
195
196
  pending: undefined,
196
197
  };
197
- /** Run layout loaders, awaiting gates before starting the page loader and retaining only safe data. */
198
- const runLayoutChain = async (route, ctx, controls, retain) => {
198
+ /**
199
+ * Await one gate. A returned redirect, status or Response refuses the request exactly as a thrown
200
+ * one does: stored as layout data it would let the page loader and the action run.
201
+ */
202
+ const runGate = async (route, index, loader, ctx) => {
203
+ let value;
204
+ try {
205
+ value = await loader(ctx);
206
+ }
207
+ catch (err) {
208
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: The loaded layout array is derived from these same manifest IDs in the same order.
209
+ throw tagLayoutError(err, route.layoutIds[index]);
210
+ }
211
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: The loaded layout array is derived from these same manifest IDs in the same order.
212
+ if (isControlFlow(value))
213
+ throw tagLayoutError(value, route.layoutIds[index]);
214
+ return value;
215
+ };
216
+ /**
217
+ * Run layout loaders, awaiting gates before starting the page loader and retaining only safe data.
218
+ * `gated` carries gates that already ran for this request, so none runs twice.
219
+ */
220
+ const runLayoutChain = async (route, ctx, controls, retain, gated) => {
199
221
  if (route.layoutIds.length === 0)
200
222
  return emptyLayoutRun;
201
- const modules = await loadLayoutModules(route);
223
+ const modules = gated?.modules ?? (await loadLayoutModules(route));
202
224
  if (!modules.some((m) => m.loader !== undefined)) {
203
225
  return { modules, layoutData: undefined, retained: [], pending: undefined };
204
226
  }
@@ -216,6 +238,7 @@ export function createPageRequestExecutor(options) {
216
238
  keep = keepsLayoutData(route, i, layout, ctx.params, retain);
217
239
  }
218
240
  catch (err) {
241
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: The loaded layout array is derived from these same manifest IDs in the same order.
219
242
  throw tagLayoutError(err, route.layoutIds[i]);
220
243
  }
221
244
  if (keep) {
@@ -229,12 +252,7 @@ export function createPageRequestExecutor(options) {
229
252
  set: controls.scope(i),
230
253
  };
231
254
  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
- }
255
+ results[i] = gated === undefined ? await runGate(route, i, loader, scoped) : gated.gates[i];
238
256
  continue;
239
257
  }
240
258
  const layoutId = route.layoutIds[i];
@@ -271,22 +289,18 @@ export function createPageRequestExecutor(options) {
271
289
  /** Run only authorization gates before an action; ordinary layout data runs after a native action. */
272
290
  const runLayoutGates = async (route, ctx, controls) => {
273
291
  const modules = await loadLayoutModules(route);
292
+ const gates = [];
274
293
  for (let i = 0; i < modules.length; i++) {
275
294
  const mod = modules[i];
276
295
  if (mod?.gate !== true || mod.loader === undefined)
277
296
  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
- }
297
+ gates[i] = await runGate(route, i, mod.loader, {
298
+ ...ctx,
299
+ params: scopeParams(ctx.params, route.layoutParams?.[i]),
300
+ set: controls.scope(i),
301
+ });
288
302
  }
289
- return modules;
303
+ return { modules, gates };
290
304
  };
291
305
  /** Outermost first; a failure carries its directory so the nearest `_error` above it renders. */
292
306
  const runMiddleware = async (route, ctx, controls) => {
@@ -400,13 +414,10 @@ export function createPageRequestExecutor(options) {
400
414
  }
401
415
  return mod.HydrateFallback ?? EMPTY_LEAF;
402
416
  };
403
- const resolveChainAndHead = (layoutModules, page, metaArgs, leaf = page.default) => {
417
+ const resolveChainAndHead = (layoutModules, layoutData, page, metaArgs, leaf = page.default) => {
404
418
  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) };
419
+ const metas = [...layoutModules.map((m) => m.meta), page.meta];
420
+ return { chain, head: mergeHeads(resolveChainMeta(metas, layoutData, metaArgs)) };
410
421
  };
411
422
  /** What `useMatches` reports for a chain: the layouts' ids and `handle`s, then the page's. */
412
423
  const matchChainOf = (layoutIds, layoutModules, pageId, page) => ({
@@ -444,34 +455,128 @@ export function createPageRequestExecutor(options) {
444
455
  return "/";
445
456
  }
446
457
  };
447
- const renderError = async (route, errorId, err, personalized, nonce) => {
458
+ /** What a status or error page's `meta` receives: no route params, and its own data. */
459
+ const statusMetaArgs = (req, data, nonce) => nonce === undefined
460
+ ? { data, pageData: data, params: {}, origin: originOf(req) }
461
+ : { data, pageData: data, params: {}, origin: originOf(req), nonce };
462
+ const renderError = async (req, route, errorId, err, personalized, nonce, layouts) => {
448
463
  const errDir = dirOfId(errorId, "_error");
449
464
  const keptLayoutIds = route.layoutIds.filter((id) => {
450
465
  const ld = dirOfId(id, "_layout");
451
466
  return ld === "" || ld === errDir || errDir.startsWith(`${ld}/`);
452
467
  });
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();
468
+ const errorModule = await (manifest.errors?.[errorId]).load();
455
469
  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
- });
470
+ // An error's text can carry a connection string or a query, so only a run that says it is
471
+ // development or test shows it; an unset NODE_ENV (an unbuilt `bun app.ts`) counts as production.
472
+ const nodeEnv = typeof process === "undefined" ? undefined : process.env.NODE_ENV;
473
+ const data = nodeEnv === "development" || nodeEnv === "test"
474
+ ? { name: e.name, message: e.message }
475
+ : { name: "Error", message: "Internal Server Error" };
476
+ const inLayouts = errorModule.inLayouts !== false;
477
+ const chain = inLayouts && layouts !== undefined && keptLayoutIds.length > 0
478
+ ? await layouts(errorModule, keptLayoutIds.length)
479
+ : undefined;
480
+ const render = (components, wrapped) => {
481
+ const layoutData = wrapped?.chain.layoutData;
482
+ return renderPageResult({
483
+ adapter,
484
+ chain: components,
485
+ data,
486
+ ...(wrapped === undefined ? {} : { head: wrapped.head }),
487
+ clientEntry,
488
+ routeId: errorId,
489
+ status: 500,
490
+ hydrate: false,
491
+ ...(layoutData === undefined ? {} : { layoutData }),
492
+ ...(personalized || layoutData !== undefined ? { headers: PRIVATE_PAGE_HEADERS } : {}),
493
+ ...(nonce === undefined ? {} : { nonce }),
494
+ ...cspOption,
495
+ ...titleOption,
496
+ });
497
+ };
498
+ if (chain !== undefined) {
499
+ const resolved = resolveChainAndHead(chain.modules, chain.layoutData, errorModule, statusMetaArgs(req, data, nonce));
500
+ try {
501
+ return await render(resolved.chain, { head: resolved.head, chain });
502
+ }
503
+ catch {
504
+ // A layout that fails to render leaves the error page alone, never a second error.
505
+ return render([errorModule.default]);
506
+ }
507
+ }
508
+ // Layouts whose data did not settle still frame the error page, without their data.
509
+ const layoutComponents = await Promise.all(keptLayoutIds.map((id) => manifest.layouts[id].load().then((m) => m.default)));
510
+ return render([...layoutComponents, errorModule.default]);
511
+ };
512
+ /** The leading `count` layouts of `modules`, unless one of them answered with a status or redirect. */
513
+ const settledLayouts = (modules, layoutData, count) => {
514
+ if (modules.length < count)
515
+ return undefined;
516
+ const data = layoutData?.slice(0, count);
517
+ if (data?.some((value) => isControlFlow(value) || isStatusSignal(value)))
518
+ return undefined;
519
+ return { modules: modules.slice(0, count), layoutData: data };
520
+ };
521
+ /** A run's leading layouts once all of its loaders settle - never when a layout or middleware refused. */
522
+ const leadingLayouts = (run, err) => async (_page, count) => {
523
+ if (run === undefined || (err !== undefined && layoutErrorId(err) !== undefined)) {
524
+ return undefined;
525
+ }
526
+ try {
527
+ await run.pending;
528
+ }
529
+ catch {
530
+ return undefined;
531
+ }
532
+ return settledLayouts(run.modules, run.layoutData, count);
473
533
  };
474
- const renderTerminalStatus = async (status, path, extraHeaders, nonce) => {
534
+ const rootLayoutIds = manifest.layouts._layout === undefined ? [] : ["_layout"];
535
+ const rootMiddlewareIds = manifest.middlewares?._middleware === undefined ? [] : ["_middleware"];
536
+ /**
537
+ * Run the root middleware and layouts for a request no route ran them for (an unmatched URL), as a
538
+ * nested `_404` runs its own. Any refusal or failure renders the page alone.
539
+ */
540
+ const rootLayoutsFor = (c, controls) => async (page) => {
541
+ const entry = manifest.statusPages?.["404"] ?? manifest.notFound;
542
+ if (rootLayoutIds.length === 0 || entry === undefined)
543
+ return undefined;
544
+ const layoutsOnly = {
545
+ id: "_404",
546
+ pattern: "/*",
547
+ layoutIds: rootLayoutIds,
548
+ errorIds: [],
549
+ file: entry.file,
550
+ load: entry.load,
551
+ };
552
+ const route = rootMiddlewareIds.length === 0
553
+ ? layoutsOnly
554
+ : { ...layoutsOnly, middlewareIds: rootMiddlewareIds };
555
+ try {
556
+ const draft = await draftFlag(c.req);
557
+ if (draft)
558
+ controls.personalize();
559
+ const ctx = {
560
+ params: {},
561
+ request: c.req,
562
+ req: c.req,
563
+ api: apiFor(c),
564
+ env: c.env,
565
+ draft,
566
+ search: loaderSearch(page.searchSchema, c.req),
567
+ set: controls.scope(PAGE_SCOPE),
568
+ };
569
+ if (route.middlewareIds !== undefined)
570
+ await runMiddleware(route, ctx, controls);
571
+ const run = await runLayoutChain(route, ctx, controls);
572
+ await run.pending;
573
+ return settledLayouts(run.modules, run.layoutData, rootLayoutIds.length);
574
+ }
575
+ catch {
576
+ return undefined;
577
+ }
578
+ };
579
+ const renderTerminalStatus = async (status, req, extraHeaders, nonce, layouts) => {
475
580
  const page = manifest.statusPages?.[String(status)] ?? manifest.notFound;
476
581
  if (page === undefined) {
477
582
  const headers = new Headers(extraHeaders);
@@ -479,31 +584,55 @@ export function createPageRequestExecutor(options) {
479
584
  return new Response(STATUS_TEXT[status] ?? "Error", { status, headers });
480
585
  }
481
586
  const routeId = manifest.statusPages?.[String(status)] !== undefined ? `_${status}` : "_404";
482
- const loaded = (await page.load());
483
- return renderPageResult({
587
+ const loaded = await page.load();
588
+ const wraps = loaded.inLayouts !== false && rootLayoutIds.length > 0;
589
+ const chain = wraps && layouts !== undefined ? await layouts(loaded, rootLayoutIds.length) : undefined;
590
+ const modules = chain?.modules ?? [];
591
+ const layoutIds = chain === undefined ? [] : rootLayoutIds;
592
+ const layoutData = chain?.layoutData;
593
+ const { chain: components, head } = resolveChainAndHead(modules, layoutData, loaded, statusMetaArgs(req, null, nonce));
594
+ let headers = extraHeaders;
595
+ if (layoutData !== undefined) {
596
+ // The page now shows what a layout loaded for this visitor.
597
+ const privateHeaders = new Headers(extraHeaders);
598
+ privateHeaders.set("cache-control", PRIVATE_NO_STORE);
599
+ headers = privateHeaders;
600
+ }
601
+ const statusPage = {
484
602
  adapter,
485
- chain: [loaded.default],
603
+ chain: components,
486
604
  data: null,
605
+ head,
487
606
  clientEntry,
488
607
  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 } : {}),
608
+ matchChain: matchChainOf(layoutIds, modules, routeId, loaded),
609
+ search: searchChainOf(modules, loaded, req),
610
+ path: pathOf(req),
496
611
  status,
497
- ...(extraHeaders !== undefined ? { headers: extraHeaders } : {}),
612
+ // The browser renders this page inside the root layout, so a page that had to render alone
613
+ // stays server-only, as a nested `_404` does.
614
+ ...(wraps && chain === undefined ? { hydrate: false } : {}),
615
+ ...(layoutData === undefined ? {} : { layoutData }),
616
+ ...(headers !== undefined ? { headers } : {}),
498
617
  ...(nonce === undefined ? {} : { nonce }),
499
618
  ...cspOption,
500
619
  ...preloadOf(routeId),
501
- ...stylesOf(routeId),
620
+ // The build keys stylesheets by route, so a page inside the root layout links the whole-app one.
621
+ ...(chain === undefined ? stylesOf(routeId) : stylesFrom(options.styles)),
502
622
  ...prerenderedHandover,
503
623
  ...titleOption,
504
- });
624
+ };
625
+ if (chain === undefined)
626
+ return renderPageResult(statusPage);
627
+ try {
628
+ return await renderPageResult(statusPage);
629
+ }
630
+ catch {
631
+ // A layout that fails to render leaves the status page alone, never a second error.
632
+ return renderTerminalStatus(status, req, extraHeaders, nonce);
633
+ }
505
634
  };
506
- const renderStatusSignal = async (req, env, signal, personalized) => {
635
+ const renderStatusSignal = async (req, env, signal, personalized, layouts) => {
507
636
  const { status, headers } = signal[STATUS_SIGNAL];
508
637
  if (req.headers.get(DATA_HEADER) !== null) {
509
638
  // Same URL as the status page, so it is never storable: a cache keyed on the URL alone would
@@ -516,7 +645,7 @@ export function createPageRequestExecutor(options) {
516
645
  }
517
646
  const nonce = await resolveNonce(req, env);
518
647
  if (headers === undefined && !personalized) {
519
- return renderTerminalStatus(status, pathOf(req), undefined, nonce);
648
+ return renderTerminalStatus(status, req, undefined, nonce, layouts);
520
649
  }
521
650
  const documentHeaders = new Headers(headers);
522
651
  if (headers !== undefined) {
@@ -525,7 +654,7 @@ export function createPageRequestExecutor(options) {
525
654
  // A cookie queued before the signal makes the status page specific to this visitor.
526
655
  if (personalized)
527
656
  documentHeaders.set("cache-control", PRIVATE_NO_STORE);
528
- return renderTerminalStatus(status, pathOf(req), documentHeaders, nonce);
657
+ return renderTerminalStatus(status, req, documentHeaders, nonce, layouts);
529
658
  };
530
659
  const reportLoaderError = (route, req, params, err) => {
531
660
  if (options.onLoaderError === undefined)
@@ -537,9 +666,11 @@ export function createPageRequestExecutor(options) {
537
666
  // A faulty reporter must never break error rendering.
538
667
  }
539
668
  };
540
- const handleDataOrDocument = async (req, env, route, params, mod, data, layoutData, layoutModules, layoutRetained, boundaryStates, assemblyCache, responseHeaders, personalized) => {
669
+ const handleDataOrDocument = async (req, env, route, params, mod, data, layoutData, layoutModules, layoutRetained, boundaryStates, assemblyCache, responseHeaders, personalized, serverComponents) => {
670
+ // Every layout loader settled before the page rendered, so a status or error page can reuse them.
671
+ const settled = async (_page, count) => settledLayouts(layoutModules, layoutData, count);
541
672
  if (isStatusSignal(data))
542
- return renderStatusSignal(req, env, data, personalized);
673
+ return renderStatusSignal(req, env, data, personalized, settled);
543
674
  if (isControlFlow(data))
544
675
  return data;
545
676
  if (req.headers.get(DATA_HEADER) !== null) {
@@ -557,7 +688,7 @@ export function createPageRequestExecutor(options) {
557
688
  ...deferred,
558
689
  ...(boundarySplit === undefined ? [] : boundarySplit.deferred),
559
690
  ];
560
- const payload = layoutData === undefined && boundarySplit === undefined
691
+ const payload = layoutData === undefined && boundarySplit === undefined && serverComponents === undefined
561
692
  ? pageSplit.forClient
562
693
  : {
563
694
  v: 1,
@@ -565,6 +696,7 @@ export function createPageRequestExecutor(options) {
565
696
  layoutData: layoutSplits.map((split) => split.forClient),
566
697
  retained: layoutRetained,
567
698
  ...(boundarySplit === undefined ? {} : { boundaries: boundarySplit.forClient }),
699
+ ...(serverComponents === undefined ? {} : { serverComponents }),
568
700
  };
569
701
  if (allDeferred.length === 0) {
570
702
  return Response.json(payload ?? null, { headers: DATA_RESPONSE_HEADERS });
@@ -577,8 +709,9 @@ export function createPageRequestExecutor(options) {
577
709
  });
578
710
  }
579
711
  const nonce = options.nonce === undefined ? undefined : await resolveNonce(req, env);
580
- const { chain, head } = resolveChainAndHead(layoutModules, mod, {
712
+ const { chain, head } = resolveChainAndHead(layoutModules, layoutData, mod, {
581
713
  data,
714
+ pageData: data,
582
715
  params,
583
716
  origin: originOf(req),
584
717
  ...(nonce === undefined ? {} : { nonce }),
@@ -595,6 +728,8 @@ export function createPageRequestExecutor(options) {
595
728
  path: pathOf(req),
596
729
  search: searchChainOf(layoutModules, mod, req),
597
730
  matchChain: matchChainOf(route.layoutIds, layoutModules, route.id, mod),
731
+ ...(serverComponents === undefined ? {} : { serverComponents }),
732
+ ...(progressiveActionData.has(req) ? { actionData: progressiveActionData.get(req) } : {}),
598
733
  hydrate: mod.hydrate !== false,
599
734
  ...(layoutData !== undefined ? { layoutData } : {}),
600
735
  ...(boundaryStates !== undefined ? { boundaries: boundaryStates } : {}),
@@ -622,31 +757,33 @@ export function createPageRequestExecutor(options) {
622
757
  }
623
758
  catch (err) {
624
759
  if (isStatusSignal(err))
625
- return renderStatusSignal(req, env, err, personalized);
760
+ return renderStatusSignal(req, env, err, personalized, settled);
626
761
  if (isControlFlow(err))
627
762
  throw err;
628
763
  reportLoaderError(route, req, { ...params }, err);
629
764
  const errorId = boundaryFor(route, err);
630
765
  if (errorId === undefined)
631
766
  throw err;
632
- return renderError(route, errorId, withDuplicateInstanceHint(err), personalized, nonce);
767
+ options.logBoundaryError?.(err, req);
768
+ return renderError(req, route, errorId, withDuplicateInstanceHint(err), personalized, nonce, settled);
633
769
  }
634
770
  };
635
771
  /** What a failed loader answers with, once a status signal has been ruled out. */
636
- const renderLoaderFailure = async (route, req, env, params, err, personalized) => {
772
+ const renderLoaderFailure = async (route, req, env, params, err, personalized, layouts) => {
637
773
  if (isControlFlow(err))
638
774
  throw err;
639
775
  reportLoaderError(route, req, params, err);
640
776
  const errorId = boundaryFor(route, err);
641
777
  if (errorId === undefined)
642
778
  throw err;
779
+ options.logBoundaryError?.(err, req);
643
780
  if (req.headers.get(DATA_HEADER) !== null) {
644
781
  return new Response("Internal Server Error", {
645
782
  status: 500,
646
783
  headers: { "content-type": "text/plain; charset=utf-8" },
647
784
  });
648
785
  }
649
- return renderError(route, errorId, withDuplicateInstanceHint(err), personalized, options.nonce === undefined ? undefined : await resolveNonce(req, env));
786
+ return renderError(req, route, errorId, withDuplicateInstanceHint(err), personalized, options.nonce === undefined ? undefined : await resolveNonce(req, env), layouts);
650
787
  };
651
788
  /**
652
789
  * Render a nested `_404` inside its layouts. `layoutModules` and `layoutData` are exactly the
@@ -665,8 +802,9 @@ export function createPageRequestExecutor(options) {
665
802
  }
666
803
  const mod = await page.load();
667
804
  const nonce = options.nonce === undefined ? undefined : await resolveNonce(req, env);
668
- const { chain, head } = resolveChainAndHead(layoutModules, mod, {
805
+ const { chain, head } = resolveChainAndHead(layoutModules, layoutData, mod, {
669
806
  data: null,
807
+ pageData: null,
670
808
  params,
671
809
  origin: originOf(req),
672
810
  ...(nonce === undefined ? {} : { nonce }),
@@ -721,11 +859,11 @@ export function createPageRequestExecutor(options) {
721
859
  const kept = page.layoutIds.length;
722
860
  return renderNestedNotFound(req, env, id, page, params, run.modules.slice(0, kept), run.layoutData?.slice(0, kept), personalized, signal[STATUS_SIGNAL].headers);
723
861
  };
724
- const renderNotFound = async (request, env, path) => {
862
+ const renderNotFound = async (c, controls) => {
725
863
  const page = manifest.statusPages?.["404"] ?? manifest.notFound;
726
864
  if (page === undefined)
727
- return renderTerminalStatus(404, path);
728
- return renderTerminalStatus(404, path, undefined, options.nonce === undefined ? undefined : await resolveNonce(request, env));
865
+ return renderTerminalStatus(404, c.req);
866
+ return renderTerminalStatus(404, c.req, undefined, options.nonce === undefined ? undefined : await resolveNonce(c.req, c.env), rootLayoutsFor(c, controls));
729
867
  };
730
868
  /**
731
869
  * Run a page handler with the request's `ctx.set`, sealed once the handler settles - whatever it
@@ -779,8 +917,19 @@ export function createPageRequestExecutor(options) {
779
917
  return assemblySlot;
780
918
  };
781
919
  return withResponseControls(async (c, controls) => {
782
- if (is404Fallback && !prerenderedSet.has(urlPartsFor(c.req).pathname)) {
783
- return renderNotFound(c.req, c.env, pathOf(c.req));
920
+ const componentRuntime = options.serverComponents;
921
+ const componentOwner = componentRuntime?.owns(route.id) === true;
922
+ let componentRequest;
923
+ try {
924
+ componentRequest = componentRuntime?.classify(c.req, route.id);
925
+ }
926
+ catch (error) {
927
+ return componentRuntime.failure(error);
928
+ }
929
+ if (componentOwner || componentRequest !== undefined)
930
+ controls.personalize();
931
+ if (!componentOwner && is404Fallback && !prerenderedSet.has(urlPartsFor(c.req).pathname)) {
932
+ return renderNotFound(c, controls);
784
933
  }
785
934
  const mod = await route.load();
786
935
  const draft = await draftFlag(c.req);
@@ -793,6 +942,7 @@ export function createPageRequestExecutor(options) {
793
942
  let layoutModules;
794
943
  let layoutRetained = [];
795
944
  let responseHeaders;
945
+ let componentState;
796
946
  const requestApi = apiFor(c);
797
947
  let run;
798
948
  try {
@@ -808,13 +958,15 @@ export function createPageRequestExecutor(options) {
808
958
  };
809
959
  if (route.middlewareIds !== undefined)
810
960
  await runMiddleware(route, ctx, controls);
811
- run = await runLayoutChain(route, ctx, controls, retainContextOf(c.req));
961
+ run = await runLayoutChain(route, ctx, controls, componentOwner ? undefined : retainContextOf(c.req));
812
962
  layoutModules = run.modules;
813
963
  layoutRetained = run.retained;
814
- const boundaryDefinitions = [
815
- ...run.modules.flatMap((layout) => layout.boundaries ?? []),
816
- ...(mod.boundaries ?? []),
817
- ];
964
+ const boundaryDefinitions = componentRequest === undefined
965
+ ? [
966
+ ...run.modules.flatMap((layout) => layout.boundaries ?? []),
967
+ ...(mod.boundaries ?? []),
968
+ ]
969
+ : [];
818
970
  if (boundaryDefinitions.length > 0)
819
971
  boundaryDescriptors(boundaryDefinitions);
820
972
  const effectiveSearch = searchChainOf(run.modules, mod, c.req);
@@ -840,6 +992,25 @@ export function createPageRequestExecutor(options) {
840
992
  : await Promise.all([pageResult, run.pending, staticBoundaryPromise]);
841
993
  data = pageData;
842
994
  layoutData = run.layoutData;
995
+ if (componentOwner) {
996
+ for (let i = 0; i < (layoutData?.length ?? 0); i++) {
997
+ const outcome = layoutData?.[i];
998
+ if (isControlFlow(outcome) || isStatusSignal(outcome))
999
+ throw tagLayoutError(outcome, route.layoutIds[i]);
1000
+ }
1001
+ }
1002
+ if (componentRequest !== undefined && (isControlFlow(data) || isStatusSignal(data)))
1003
+ return componentRuntime.failure(data);
1004
+ if (componentOwner && !isControlFlow(data) && !isStatusSignal(data)) {
1005
+ const componentContext = Object.create(c, Object.getOwnPropertyDescriptors({
1006
+ ...ctx,
1007
+ search: effectiveSearch,
1008
+ signal: c.signal === undefined ? c.req.signal : AbortSignal.any([c.signal, c.req.signal]),
1009
+ data,
1010
+ layoutData: layoutData ?? [],
1011
+ }));
1012
+ componentState = await componentRuntime.execute(route.id, componentContext, componentRequest, options.nonce === undefined ? undefined : await resolveNonce(c.req, c.env));
1013
+ }
843
1014
  if (boundaryBatch !== undefined) {
844
1015
  const streamed = { ...boundaryBatch.initial, ...(staticBoundaries ?? {}) };
845
1016
  for (const entry of boundaryBatch.pending) {
@@ -857,17 +1028,28 @@ export function createPageRequestExecutor(options) {
857
1028
  responseHeaders = controls.documentHeaders();
858
1029
  }
859
1030
  catch (err) {
1031
+ if (componentRequest !== undefined)
1032
+ return componentRuntime.failure(err);
860
1033
  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));
1034
+ return ((await renderRouteNotFound(c.req, c.env, route, c.params, err, run, controls.personalized)) ??
1035
+ renderStatusSignal(c.req, c.env, err, controls.personalized, leadingLayouts(run, err)));
862
1036
  }
863
- return renderLoaderFailure(route, c.req, c.env, c.params, err, controls.personalized);
1037
+ return renderLoaderFailure(route, c.req, c.env, c.params, err, controls.personalized, leadingLayouts(run, err));
1038
+ }
1039
+ if (componentRequest !== undefined && componentState !== undefined) {
1040
+ const answer = componentRuntime.response(componentState, componentRequest);
1041
+ const headers = new Headers(answer.headers);
1042
+ for (const [name, value] of Object.entries(responseHeaders ?? {}))
1043
+ if (name !== "cache-control" && name !== "content-type" && name !== "vary")
1044
+ headers.set(name, value);
1045
+ return new Response(answer.body, { status: answer.status, headers });
864
1046
  }
865
1047
  if (isStatusSignal(data)) {
866
1048
  const nested = await renderRouteNotFound(c.req, c.env, route, c.params, data, run, controls.personalized);
867
1049
  if (nested !== undefined)
868
1050
  return nested;
869
1051
  }
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);
1052
+ 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
1053
  });
872
1054
  },
873
1055
  post: (route) => withResponseControls(async (c, controls) => {
@@ -893,12 +1075,12 @@ export function createPageRequestExecutor(options) {
893
1075
  set: controls.scope(ACTION_SCOPE),
894
1076
  };
895
1077
  const isDataRequest = c.req.headers.get(DATA_HEADER) !== null;
896
- let layoutModules;
1078
+ let gated;
897
1079
  let result;
898
1080
  try {
899
1081
  if (route.middlewareIds !== undefined)
900
1082
  await runMiddleware(route, actionContext, controls);
901
- layoutModules = await runLayoutGates(route, actionContext, controls);
1083
+ gated = await runLayoutGates(route, actionContext, controls);
902
1084
  result = await mod.action(actionContext);
903
1085
  }
904
1086
  catch (err) {
@@ -933,23 +1115,109 @@ export function createPageRequestExecutor(options) {
933
1115
  },
934
1116
  });
935
1117
  }
1118
+ if (options.serverComponents?.owns(route.id)) {
1119
+ if (options.rerenderOwnerGET === undefined)
1120
+ throw new Error("component_contract");
1121
+ const actionHeaders = controls.documentHeaders();
1122
+ if (controls.progressiveRedirect(c.req)) {
1123
+ // Relative, so a browser behind a TLS terminator stays on its own scheme. A `//` path
1124
+ // would read as another host, so that one keeps the absolute URL.
1125
+ const { pathname, search } = new URL(c.req.url);
1126
+ const location = pathname.startsWith("//") ? c.req.url : pathname + search;
1127
+ return new Response(null, {
1128
+ status: 303,
1129
+ headers: { ...actionHeaders, location, "cache-control": PRIVATE_NO_STORE },
1130
+ });
1131
+ }
1132
+ const headers = new Headers(c.req.headers);
1133
+ for (const name of [
1134
+ DATA_HEADER,
1135
+ RETAIN_HEADER,
1136
+ NAV_FROM_HEADER,
1137
+ "x-nifra-component",
1138
+ "content-type",
1139
+ "content-length",
1140
+ "transfer-encoding",
1141
+ ])
1142
+ headers.delete(name);
1143
+ const cookie = controls.progressiveCookies(c.req);
1144
+ if (cookie)
1145
+ headers.set("cookie", cookie);
1146
+ else
1147
+ headers.delete("cookie");
1148
+ const request = new Request(c.req.url, {
1149
+ method: "GET",
1150
+ headers,
1151
+ signal: c.signal === undefined ? c.req.signal : AbortSignal.any([c.signal, c.req.signal]),
1152
+ });
1153
+ progressiveActionData.set(request, actionResult);
1154
+ try {
1155
+ const response = await options.rerenderOwnerGET(request, c);
1156
+ const merged = new Headers(response.headers);
1157
+ for (const [name, value] of Object.entries(actionHeaders ?? {}))
1158
+ if (!merged.has(name))
1159
+ merged.set(name, value);
1160
+ merged.set("cache-control", PRIVATE_NO_STORE);
1161
+ return new Response(response.body, {
1162
+ status: response.status,
1163
+ statusText: response.statusText,
1164
+ headers: merged,
1165
+ });
1166
+ }
1167
+ finally {
1168
+ progressiveActionData.delete(request);
1169
+ }
1170
+ }
1171
+ const layoutModules = gated.modules;
936
1172
  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();
1173
+ let run;
1174
+ let data;
1175
+ let responseHeaders;
1176
+ // The ordinary layout loaders run now, after the mutation, as a GET of this page would.
1177
+ try {
1178
+ run = await runLayoutChain(route, actionContext, controls, undefined, gated);
1179
+ const pageResult = mod.loader
1180
+ ? mod.loader({
1181
+ params: c.params,
1182
+ request: c.req,
1183
+ req: c.req,
1184
+ api: requestApi,
1185
+ env: c.env,
1186
+ draft,
1187
+ search,
1188
+ set: controls.scope(PAGE_SCOPE),
1189
+ })
1190
+ : null;
1191
+ const [pageData] = run.pending === undefined
1192
+ ? [await pageResult]
1193
+ : await Promise.all([pageResult, run.pending]);
1194
+ data = pageData;
1195
+ responseHeaders = controls.documentHeaders();
1196
+ }
1197
+ catch (err) {
1198
+ if (isStatusSignal(err)) {
1199
+ return ((await renderRouteNotFound(c.req, c.env, route, c.params, err, run, controls.personalized)) ??
1200
+ renderStatusSignal(c.req, c.env, err, controls.personalized, leadingLayouts(run, err)));
1201
+ }
1202
+ return renderLoaderFailure(route, c.req, c.env, c.params, err, controls.personalized, leadingLayouts(run, err));
1203
+ }
1204
+ // The re-render answers a returned redirect or status the way the page's GET does.
1205
+ if (isStatusSignal(data))
1206
+ return ((await renderRouteNotFound(c.req, c.env, route, c.params, data, run, controls.personalized)) ??
1207
+ renderStatusSignal(c.req, c.env, data, controls.personalized, leadingLayouts(run, data)));
1208
+ if (isControlFlow(data))
1209
+ return data;
1210
+ const layoutData = run.layoutData;
1211
+ const layoutProps = {};
1212
+ if (layoutData !== undefined) {
1213
+ layoutProps.layoutData = layoutData;
1214
+ // The page now shows what a layout loaded for this visitor.
1215
+ responseHeaders = { ...responseHeaders, ...PRIVATE_PAGE_HEADERS };
1216
+ }
950
1217
  const nonce = options.nonce === undefined ? undefined : await resolveNonce(c.req, c.env);
951
- const { chain, head } = resolveChainAndHead(layoutModules, mod, {
1218
+ const { chain, head } = resolveChainAndHead(layoutModules, layoutData, mod, {
952
1219
  data,
1220
+ pageData: data,
953
1221
  params: c.params,
954
1222
  origin: originOf(c.req),
955
1223
  ...(nonce === undefined ? {} : { nonce }),
@@ -967,6 +1235,7 @@ export function createPageRequestExecutor(options) {
967
1235
  search,
968
1236
  matchChain: matchChainOf(route.layoutIds, layoutModules, route.id, mod),
969
1237
  hydrate: mod.hydrate !== false,
1238
+ ...layoutProps,
970
1239
  ...preloadOf(route.id),
971
1240
  ...stylesOf(route.id),
972
1241
  ...prerenderedHandover,
@@ -980,7 +1249,7 @@ export function createPageRequestExecutor(options) {
980
1249
  const match = matchNotFoundScope?.(pathOf(c.req)) ?? null;
981
1250
  const target = match === null ? undefined : notFoundTargets[Number(match.routeId)];
982
1251
  if (match === null || target === undefined) {
983
- return renderNotFound(c.req, c.env, pathOf(c.req));
1252
+ return renderNotFound(c, controls);
984
1253
  }
985
1254
  const params = {};
986
1255
  for (let i = 0; i < target.names.length; i++) {
@@ -1010,9 +1279,9 @@ export function createPageRequestExecutor(options) {
1010
1279
  }
1011
1280
  catch (err) {
1012
1281
  if (isStatusSignal(err)) {
1013
- return renderStatusSignal(c.req, c.env, err, controls.personalized);
1282
+ return renderStatusSignal(c.req, c.env, err, controls.personalized, leadingLayouts(run, err));
1014
1283
  }
1015
- return renderLoaderFailure(target.route, c.req, c.env, params, err, controls.personalized);
1284
+ return renderLoaderFailure(target.route, c.req, c.env, params, err, controls.personalized, leadingLayouts(run, err));
1016
1285
  }
1017
1286
  return renderNestedNotFound(c.req, c.env, target.id, target.page, params, run.modules, run.layoutData, controls.personalized);
1018
1287
  }),