@nifrajs/web 3.3.0 → 4.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 (407) hide show
  1. package/dist/backend-only.d.ts +15 -0
  2. package/dist/backend-only.d.ts.map +1 -0
  3. package/dist/backend-only.js +16 -0
  4. package/dist/backend-only.js.map +1 -0
  5. package/dist/boundary.d.ts +5 -0
  6. package/dist/boundary.d.ts.map +1 -1
  7. package/dist/boundary.js +11 -2
  8. package/dist/boundary.js.map +1 -1
  9. package/dist/build-plan.d.ts +35 -6
  10. package/dist/build-plan.d.ts.map +1 -1
  11. package/dist/build-plan.js +89 -35
  12. package/dist/build-plan.js.map +1 -1
  13. package/dist/build-vite.d.ts +18 -0
  14. package/dist/build-vite.d.ts.map +1 -1
  15. package/dist/build-vite.js +97 -17
  16. package/dist/build-vite.js.map +1 -1
  17. package/dist/build.d.ts +72 -23
  18. package/dist/build.d.ts.map +1 -1
  19. package/dist/build.js +411 -98
  20. package/dist/build.js.map +1 -1
  21. package/dist/bun-dev-entry.d.ts +1 -1
  22. package/dist/bun-dev-entry.js +1 -1
  23. package/dist/cdn.d.ts +74 -0
  24. package/dist/cdn.d.ts.map +1 -0
  25. package/dist/cdn.js +132 -0
  26. package/dist/cdn.js.map +1 -0
  27. package/dist/client.d.ts +21 -1
  28. package/dist/client.d.ts.map +1 -1
  29. package/dist/client.js +226 -15
  30. package/dist/client.js.map +1 -1
  31. package/dist/conformance.d.ts +4 -3
  32. package/dist/conformance.d.ts.map +1 -1
  33. package/dist/conformance.js +12 -3
  34. package/dist/conformance.js.map +1 -1
  35. package/dist/csp.d.ts +93 -0
  36. package/dist/csp.d.ts.map +1 -0
  37. package/dist/csp.js +122 -0
  38. package/dist/csp.js.map +1 -0
  39. package/dist/css-contract.d.ts +21 -0
  40. package/dist/css-contract.d.ts.map +1 -0
  41. package/dist/css-contract.js +28 -0
  42. package/dist/css-contract.js.map +1 -0
  43. package/dist/dev-client.d.ts +76 -0
  44. package/dist/dev-client.d.ts.map +1 -0
  45. package/dist/dev-client.js +280 -0
  46. package/dist/dev-client.js.map +1 -0
  47. package/dist/dev-diagnostics.d.ts +26 -2
  48. package/dist/dev-diagnostics.d.ts.map +1 -1
  49. package/dist/dev-diagnostics.js +38 -7
  50. package/dist/dev-diagnostics.js.map +1 -1
  51. package/dist/dev-error.d.ts +4 -13
  52. package/dist/dev-error.d.ts.map +1 -1
  53. package/dist/dev-error.js +46 -4
  54. package/dist/dev-error.js.map +1 -1
  55. package/dist/dev-feed.d.ts +298 -0
  56. package/dist/dev-feed.d.ts.map +1 -0
  57. package/dist/dev-feed.js +797 -0
  58. package/dist/dev-feed.js.map +1 -0
  59. package/dist/dev-indicator.d.ts +38 -0
  60. package/dist/dev-indicator.d.ts.map +1 -0
  61. package/dist/dev-indicator.js +93 -0
  62. package/dist/dev-indicator.js.map +1 -0
  63. package/dist/dev-session.d.ts +77 -0
  64. package/dist/dev-session.d.ts.map +1 -0
  65. package/dist/dev-session.js +580 -0
  66. package/dist/dev-session.js.map +1 -0
  67. package/dist/dev-sourcemap.d.ts +27 -0
  68. package/dist/dev-sourcemap.d.ts.map +1 -0
  69. package/dist/dev-sourcemap.js +235 -0
  70. package/dist/dev-sourcemap.js.map +1 -0
  71. package/dist/dev-ssr-graph.d.ts.map +1 -1
  72. package/dist/dev-ssr-graph.js +13 -5
  73. package/dist/dev-ssr-graph.js.map +1 -1
  74. package/dist/dev.d.ts +15 -2
  75. package/dist/dev.d.ts.map +1 -1
  76. package/dist/dev.js +69 -44
  77. package/dist/dev.js.map +1 -1
  78. package/dist/diagnostic-catalog.d.ts +35 -0
  79. package/dist/diagnostic-catalog.d.ts.map +1 -0
  80. package/dist/diagnostic-catalog.js +203 -0
  81. package/dist/diagnostic-catalog.js.map +1 -0
  82. package/dist/diagnostic-prompt.d.ts +49 -0
  83. package/dist/diagnostic-prompt.d.ts.map +1 -0
  84. package/dist/diagnostic-prompt.js +303 -0
  85. package/dist/diagnostic-prompt.js.map +1 -0
  86. package/dist/diagnostic.d.ts +17 -23
  87. package/dist/diagnostic.d.ts.map +1 -1
  88. package/dist/diagnostic.js +31 -39
  89. package/dist/diagnostic.js.map +1 -1
  90. package/dist/draft.d.ts.map +1 -1
  91. package/dist/draft.js +4 -1
  92. package/dist/draft.js.map +1 -1
  93. package/dist/fn.d.ts +5 -0
  94. package/dist/fn.d.ts.map +1 -1
  95. package/dist/fn.js +13 -3
  96. package/dist/fn.js.map +1 -1
  97. package/dist/fs.d.ts.map +1 -1
  98. package/dist/fs.js +4 -2
  99. package/dist/fs.js.map +1 -1
  100. package/dist/index.d.ts +15 -15
  101. package/dist/index.d.ts.map +1 -1
  102. package/dist/index.js +13 -12
  103. package/dist/index.js.map +1 -1
  104. package/dist/internal/cdn/cloudflare.d.ts +48 -0
  105. package/dist/internal/cdn/cloudflare.d.ts.map +1 -0
  106. package/dist/internal/cdn/cloudflare.js +94 -0
  107. package/dist/internal/cdn/cloudflare.js.map +1 -0
  108. package/dist/internal/cdn/core.d.ts +91 -0
  109. package/dist/internal/cdn/core.d.ts.map +1 -0
  110. package/dist/internal/cdn/core.js +220 -0
  111. package/dist/internal/cdn/core.js.map +1 -0
  112. package/dist/internal/cdn/fastly.d.ts +17 -0
  113. package/dist/internal/cdn/fastly.d.ts.map +1 -0
  114. package/dist/internal/cdn/fastly.js +43 -0
  115. package/dist/internal/cdn/fastly.js.map +1 -0
  116. package/dist/internal/cdn/vercel.d.ts +31 -0
  117. package/dist/internal/cdn/vercel.d.ts.map +1 -0
  118. package/dist/internal/cdn/vercel.js +56 -0
  119. package/dist/internal/cdn/vercel.js.map +1 -0
  120. package/dist/internal/code-unit-order.d.ts +4 -0
  121. package/dist/internal/code-unit-order.d.ts.map +1 -0
  122. package/dist/internal/code-unit-order.js +6 -0
  123. package/dist/internal/code-unit-order.js.map +1 -0
  124. package/dist/internal/codegen.d.ts +3 -0
  125. package/dist/internal/codegen.d.ts.map +1 -1
  126. package/dist/internal/codegen.js +151 -30
  127. package/dist/internal/codegen.js.map +1 -1
  128. package/dist/internal/dev-csp.d.ts +24 -0
  129. package/dist/internal/dev-csp.d.ts.map +1 -0
  130. package/dist/internal/dev-csp.js +160 -0
  131. package/dist/internal/dev-csp.js.map +1 -0
  132. package/dist/internal/dev-reserved.d.ts +25 -0
  133. package/dist/internal/dev-reserved.d.ts.map +1 -0
  134. package/dist/internal/dev-reserved.js +39 -0
  135. package/dist/internal/dev-reserved.js.map +1 -0
  136. package/dist/internal/html-spans.d.ts +10 -0
  137. package/dist/internal/html-spans.d.ts.map +1 -0
  138. package/dist/internal/html-spans.js +46 -0
  139. package/dist/internal/html-spans.js.map +1 -0
  140. package/dist/internal/loading-runtime.d.ts +44 -0
  141. package/dist/internal/loading-runtime.d.ts.map +1 -0
  142. package/dist/internal/loading-runtime.js +206 -0
  143. package/dist/internal/loading-runtime.js.map +1 -0
  144. package/dist/internal/matches-runtime.d.ts +8 -0
  145. package/dist/internal/matches-runtime.d.ts.map +1 -0
  146. package/dist/internal/matches-runtime.js +61 -0
  147. package/dist/internal/matches-runtime.js.map +1 -0
  148. package/dist/internal/mount-shadow.d.ts +36 -0
  149. package/dist/internal/mount-shadow.d.ts.map +1 -0
  150. package/dist/internal/mount-shadow.js +65 -0
  151. package/dist/internal/mount-shadow.js.map +1 -0
  152. package/dist/internal/node-builtins.d.ts +3 -0
  153. package/dist/internal/node-builtins.d.ts.map +1 -0
  154. package/dist/internal/node-builtins.js +79 -0
  155. package/dist/internal/node-builtins.js.map +1 -0
  156. package/dist/internal/output-guard.d.ts +65 -0
  157. package/dist/internal/output-guard.d.ts.map +1 -0
  158. package/dist/internal/output-guard.js +630 -0
  159. package/dist/internal/output-guard.js.map +1 -0
  160. package/dist/internal/page-execution.d.ts +69 -0
  161. package/dist/internal/page-execution.d.ts.map +1 -0
  162. package/dist/internal/page-execution.js +1021 -0
  163. package/dist/internal/page-execution.js.map +1 -0
  164. package/dist/internal/parity.d.ts +21 -0
  165. package/dist/internal/parity.d.ts.map +1 -1
  166. package/dist/internal/parity.js +104 -22
  167. package/dist/internal/parity.js.map +1 -1
  168. package/dist/internal/private-env.d.ts +12 -0
  169. package/dist/internal/private-env.d.ts.map +1 -0
  170. package/dist/internal/private-env.js +335 -0
  171. package/dist/internal/private-env.js.map +1 -0
  172. package/dist/internal/public-url.d.ts +3 -0
  173. package/dist/internal/public-url.d.ts.map +1 -0
  174. package/dist/internal/public-url.js +18 -0
  175. package/dist/internal/public-url.js.map +1 -0
  176. package/dist/internal/render-document.d.ts +37 -21
  177. package/dist/internal/render-document.d.ts.map +1 -1
  178. package/dist/internal/render-document.js +104 -65
  179. package/dist/internal/render-document.js.map +1 -1
  180. package/dist/internal/request-url.d.ts +3 -0
  181. package/dist/internal/request-url.d.ts.map +1 -0
  182. package/dist/internal/request-url.js +14 -0
  183. package/dist/internal/request-url.js.map +1 -0
  184. package/dist/internal/response-controls.d.ts +75 -0
  185. package/dist/internal/response-controls.d.ts.map +1 -0
  186. package/dist/internal/response-controls.js +256 -0
  187. package/dist/internal/response-controls.js.map +1 -0
  188. package/dist/internal/runtime-contract.d.ts +11 -16
  189. package/dist/internal/runtime-contract.d.ts.map +1 -1
  190. package/dist/internal/runtime-contract.js.map +1 -1
  191. package/dist/internal/secret-scan.d.ts +104 -0
  192. package/dist/internal/secret-scan.d.ts.map +1 -0
  193. package/dist/internal/secret-scan.js +485 -0
  194. package/dist/internal/secret-scan.js.map +1 -0
  195. package/dist/internal/server-boundary.d.ts +6 -29
  196. package/dist/internal/server-boundary.d.ts.map +1 -1
  197. package/dist/internal/server-boundary.js +6 -66
  198. package/dist/internal/server-boundary.js.map +1 -1
  199. package/dist/internal/source-map.d.ts +47 -0
  200. package/dist/internal/source-map.d.ts.map +1 -0
  201. package/dist/internal/source-map.js +225 -0
  202. package/dist/internal/source-map.js.map +1 -0
  203. package/dist/internal/target-compat.d.ts +27 -0
  204. package/dist/internal/target-compat.d.ts.map +1 -0
  205. package/dist/internal/target-compat.js +77 -0
  206. package/dist/internal/target-compat.js.map +1 -0
  207. package/dist/internal/timing-safe-equal.d.ts +6 -0
  208. package/dist/internal/timing-safe-equal.d.ts.map +1 -1
  209. package/dist/internal/timing-safe-equal.js +10 -0
  210. package/dist/internal/timing-safe-equal.js.map +1 -1
  211. package/dist/internal/vitals-runtime.d.ts +18 -0
  212. package/dist/internal/vitals-runtime.d.ts.map +1 -0
  213. package/dist/internal/vitals-runtime.js +38 -0
  214. package/dist/internal/vitals-runtime.js.map +1 -0
  215. package/dist/internal/web-app.d.ts +56 -8
  216. package/dist/internal/web-app.d.ts.map +1 -1
  217. package/dist/internal/web-app.js +215 -772
  218. package/dist/internal/web-app.js.map +1 -1
  219. package/dist/internal/zone-graph.d.ts +89 -0
  220. package/dist/internal/zone-graph.d.ts.map +1 -0
  221. package/dist/internal/zone-graph.js +492 -0
  222. package/dist/internal/zone-graph.js.map +1 -0
  223. package/dist/isr.d.ts +83 -6
  224. package/dist/isr.d.ts.map +1 -1
  225. package/dist/isr.js +301 -39
  226. package/dist/isr.js.map +1 -1
  227. package/dist/llms-txt.d.ts +3 -0
  228. package/dist/llms-txt.d.ts.map +1 -1
  229. package/dist/llms-txt.js +37 -9
  230. package/dist/llms-txt.js.map +1 -1
  231. package/dist/manifest.d.ts +221 -14
  232. package/dist/manifest.d.ts.map +1 -1
  233. package/dist/manifest.js +386 -9
  234. package/dist/manifest.js.map +1 -1
  235. package/dist/module-graph.d.ts +26 -5
  236. package/dist/module-graph.d.ts.map +1 -1
  237. package/dist/module-graph.js +17 -7
  238. package/dist/module-graph.js.map +1 -1
  239. package/dist/navigation.d.ts +17 -3
  240. package/dist/navigation.d.ts.map +1 -1
  241. package/dist/navigation.js +11 -5
  242. package/dist/navigation.js.map +1 -1
  243. package/dist/nonce.d.ts +47 -0
  244. package/dist/nonce.d.ts.map +1 -0
  245. package/dist/nonce.js +63 -0
  246. package/dist/nonce.js.map +1 -0
  247. package/dist/plugins/css-modules.d.ts.map +1 -1
  248. package/dist/plugins/css-modules.js +4 -3
  249. package/dist/plugins/css-modules.js.map +1 -1
  250. package/dist/plugins/kit.d.ts +9 -0
  251. package/dist/plugins/kit.d.ts.map +1 -1
  252. package/dist/plugins/kit.js +18 -0
  253. package/dist/plugins/kit.js.map +1 -1
  254. package/dist/plugins/svg.d.ts +22 -3
  255. package/dist/plugins/svg.d.ts.map +1 -1
  256. package/dist/plugins/svg.js +112 -1
  257. package/dist/plugins/svg.js.map +1 -1
  258. package/dist/plugins/vite-leak-guard.d.ts +107 -3
  259. package/dist/plugins/vite-leak-guard.d.ts.map +1 -1
  260. package/dist/plugins/vite-leak-guard.js +378 -17
  261. package/dist/plugins/vite-leak-guard.js.map +1 -1
  262. package/dist/plugins/vite-zone-guard.d.ts +61 -0
  263. package/dist/plugins/vite-zone-guard.d.ts.map +1 -0
  264. package/dist/plugins/vite-zone-guard.js +208 -0
  265. package/dist/plugins/vite-zone-guard.js.map +1 -0
  266. package/dist/plugins/zone-guard.d.ts +27 -0
  267. package/dist/plugins/zone-guard.d.ts.map +1 -0
  268. package/dist/plugins/zone-guard.js +72 -0
  269. package/dist/plugins/zone-guard.js.map +1 -0
  270. package/dist/prerender.d.ts +10 -0
  271. package/dist/prerender.d.ts.map +1 -1
  272. package/dist/prerender.js +35 -3
  273. package/dist/prerender.js.map +1 -1
  274. package/dist/public-dir.d.ts +9 -0
  275. package/dist/public-dir.d.ts.map +1 -1
  276. package/dist/public-dir.js +131 -48
  277. package/dist/public-dir.js.map +1 -1
  278. package/dist/pwa-manifest.d.ts +68 -0
  279. package/dist/pwa-manifest.d.ts.map +1 -0
  280. package/dist/pwa-manifest.js +178 -0
  281. package/dist/pwa-manifest.js.map +1 -0
  282. package/dist/query.js +4 -4
  283. package/dist/query.js.map +1 -1
  284. package/dist/render-seam.d.ts +42 -2
  285. package/dist/render-seam.d.ts.map +1 -1
  286. package/dist/render-seam.js +6 -0
  287. package/dist/render-seam.js.map +1 -1
  288. package/dist/route-manifest.d.ts +3 -0
  289. package/dist/route-manifest.d.ts.map +1 -1
  290. package/dist/route-manifest.js +26 -3
  291. package/dist/route-manifest.js.map +1 -1
  292. package/dist/route-types.d.ts +24 -0
  293. package/dist/route-types.d.ts.map +1 -0
  294. package/dist/route-types.js +195 -0
  295. package/dist/route-types.js.map +1 -0
  296. package/dist/router.d.ts +28 -6
  297. package/dist/router.d.ts.map +1 -1
  298. package/dist/router.js +129 -40
  299. package/dist/router.js.map +1 -1
  300. package/dist/trusted-html.d.ts +34 -0
  301. package/dist/trusted-html.d.ts.map +1 -0
  302. package/dist/trusted-html.js +35 -0
  303. package/dist/trusted-html.js.map +1 -0
  304. package/dist/vitals.d.ts +33 -0
  305. package/dist/vitals.d.ts.map +1 -0
  306. package/dist/vitals.js +16 -0
  307. package/dist/vitals.js.map +1 -0
  308. package/dist/vite.d.ts +16 -1
  309. package/dist/vite.d.ts.map +1 -1
  310. package/dist/vite.js +187 -36
  311. package/dist/vite.js.map +1 -1
  312. package/dist/zones.d.ts +70 -0
  313. package/dist/zones.d.ts.map +1 -0
  314. package/dist/zones.js +365 -0
  315. package/dist/zones.js.map +1 -0
  316. package/package.json +67 -14
  317. package/src/backend-only.ts +16 -0
  318. package/src/boundary.ts +18 -5
  319. package/src/build-plan.ts +113 -36
  320. package/src/build-vite.ts +124 -15
  321. package/src/build.ts +535 -102
  322. package/src/bun-dev-entry.ts +1 -1
  323. package/src/cdn.ts +216 -0
  324. package/src/client.ts +251 -18
  325. package/src/conformance.ts +16 -3
  326. package/src/csp.ts +194 -0
  327. package/src/css-contract.ts +44 -0
  328. package/src/dev-client.ts +349 -0
  329. package/src/dev-diagnostics.ts +60 -7
  330. package/src/dev-error.ts +49 -5
  331. package/src/dev-feed.ts +1140 -0
  332. package/src/dev-indicator.ts +113 -0
  333. package/src/dev-session.ts +729 -0
  334. package/src/dev-sourcemap.ts +281 -0
  335. package/src/dev-ssr-graph.ts +16 -5
  336. package/src/dev.ts +86 -43
  337. package/src/diagnostic-catalog.ts +243 -0
  338. package/src/diagnostic-prompt.ts +355 -0
  339. package/src/diagnostic.ts +63 -66
  340. package/src/draft.ts +4 -1
  341. package/src/fn.ts +19 -3
  342. package/src/fs.ts +13 -2
  343. package/src/index.ts +57 -10
  344. package/src/internal/cdn/cloudflare.ts +146 -0
  345. package/src/internal/cdn/core.ts +316 -0
  346. package/src/internal/cdn/fastly.ts +62 -0
  347. package/src/internal/cdn/vercel.ts +95 -0
  348. package/src/internal/code-unit-order.ts +5 -0
  349. package/src/internal/codegen.ts +180 -29
  350. package/src/internal/dev-csp.ts +176 -0
  351. package/src/internal/dev-reserved.ts +52 -0
  352. package/src/internal/html-spans.ts +50 -0
  353. package/src/internal/loading-runtime.ts +244 -0
  354. package/src/internal/matches-runtime.ts +62 -0
  355. package/src/internal/mount-shadow.ts +88 -0
  356. package/src/internal/node-builtins.ts +79 -0
  357. package/src/internal/output-guard.ts +701 -0
  358. package/src/internal/page-execution.ts +1371 -0
  359. package/src/internal/parity.ts +150 -36
  360. package/src/internal/private-env.ts +334 -0
  361. package/src/internal/public-url.ts +22 -0
  362. package/src/internal/render-document.ts +153 -73
  363. package/src/internal/request-url.ts +14 -0
  364. package/src/internal/response-controls.ts +284 -0
  365. package/src/internal/runtime-contract.ts +10 -15
  366. package/src/internal/secret-scan.ts +623 -0
  367. package/src/internal/server-boundary.ts +6 -69
  368. package/src/internal/source-map.ts +256 -0
  369. package/src/internal/target-compat.ts +92 -0
  370. package/src/internal/timing-safe-equal.ts +13 -0
  371. package/src/internal/vitals-runtime.ts +53 -0
  372. package/src/internal/web-app.ts +290 -879
  373. package/src/internal/zone-graph.ts +593 -0
  374. package/src/isr.ts +366 -39
  375. package/src/llms-txt.ts +44 -8
  376. package/src/manifest.ts +671 -17
  377. package/src/module-graph.ts +53 -9
  378. package/src/navigation.ts +29 -6
  379. package/src/nonce.ts +104 -0
  380. package/src/plugins/css-modules.ts +5 -3
  381. package/src/plugins/kit.ts +24 -0
  382. package/src/plugins/svg.ts +135 -4
  383. package/src/plugins/vite-leak-guard.ts +529 -16
  384. package/src/plugins/vite-zone-guard.ts +263 -0
  385. package/src/plugins/zone-guard.ts +110 -0
  386. package/src/prerender.ts +49 -3
  387. package/src/public-dir.ts +135 -49
  388. package/src/pwa-manifest.ts +226 -0
  389. package/src/query.ts +4 -4
  390. package/src/render-seam.ts +46 -1
  391. package/src/route-manifest.ts +15 -3
  392. package/src/route-types.ts +264 -0
  393. package/src/router.ts +163 -63
  394. package/src/trusted-html.ts +44 -0
  395. package/src/vitals.ts +44 -0
  396. package/src/vite.ts +207 -36
  397. package/src/zones.ts +430 -0
  398. package/dist/plugins/vite-server-only.d.ts +0 -49
  399. package/dist/plugins/vite-server-only.d.ts.map +0 -1
  400. package/dist/plugins/vite-server-only.js +0 -57
  401. package/dist/plugins/vite-server-only.js.map +0 -1
  402. package/dist/server-only.d.ts +0 -21
  403. package/dist/server-only.d.ts.map +0 -1
  404. package/dist/server-only.js +0 -25
  405. package/dist/server-only.js.map +0 -1
  406. package/src/plugins/vite-server-only.ts +0 -75
  407. package/src/server-only.ts +0 -25
@@ -0,0 +1,1021 @@
1
+ import { NIFRA_BACKEND_BIND_PLATFORM, NIFRA_PLATFORM_CLIENT_IP_DERIVED, } from "@nifrajs/core/mount";
2
+ import { status as statusResult } from "@nifrajs/core/server";
3
+ import { boundaryDescriptors, resolveStaticBoundaries, startDynamicBoundaries, } from "../boundary.js";
4
+ import { DEFAULT_CSS_LOADING, normalizeCssLoading } from "../css-contract.js";
5
+ import { defer, ndjsonStream, prepareDeferred } from "../deferred.js";
6
+ import { isDraftEnabled } from "../draft.js";
7
+ import { routeTags } from "../isr.js";
8
+ import { createMatcher, DATA_HEADER, NAV_FROM_HEADER, REDIRECT_HEADER, RETAIN_HEADER, REVALIDATE_HEADER, STATUS_HEADER, } from "../router.js";
9
+ import { searchOf, searchOfChain } from "../search.js";
10
+ import { mergeHeads, resolveMeta } from "./head-merge.js";
11
+ import { EMPTY_RETAIN, isControlFlow, isResponseResult, isStatusSignal, layoutErrorId, renderPageResult, STATUS_SIGNAL, STATUS_TEXT, scopeParams, tagLayoutError, withDuplicateInstanceHint, } from "./render-document.js";
12
+ import { urlPartsFor } from "./request-url.js";
13
+ import { ACTION_SCOPE, DATA_RESPONSE_HEADERS, MIDDLEWARE_SCOPE, PAGE_SCOPE, PageResponseControls, PRIVATE_NO_STORE, PRIVATE_PAGE_HEADERS, privateOutcome, varyOnDataHeader, } from "./response-controls.js";
14
+ /** The page slot of an `ssr = false` route with no `HydrateFallback`: a component that renders nothing. */
15
+ const EMPTY_LEAF = () => null;
16
+ /** The request-scoping seam of an in-process `api` (see `inProcessClient`), if it carries one. */
17
+ function platformBinderOf(api) {
18
+ if ((typeof api !== "object" && typeof api !== "function") || api === null)
19
+ return undefined;
20
+ const bind = api[NIFRA_BACKEND_BIND_PLATFORM];
21
+ return typeof bind === "function" ? bind : undefined;
22
+ }
23
+ /**
24
+ * The page request's platform identity for loader calls: `clientIp` (read lazily, so a backend that
25
+ * never reads `c.clientIp` never pays the socket lookup), `env` and `waitUntil` - and nothing else. The
26
+ * page request's headers (cookie, authorization) never travel; a loader passes them explicitly if it
27
+ * wants an authenticated call. The prototype marker tells the backend its `clientIp` is already derived,
28
+ * so its own trust declaration does not re-read forwarding headers the synthesized request does not
29
+ * carry; a spread copy drops both the getter and the marker, so it falls back to re-deriving.
30
+ */
31
+ class LoaderPlatform {
32
+ env;
33
+ waitUntil;
34
+ #c;
35
+ constructor(c) {
36
+ this.#c = c;
37
+ this.env = c.env;
38
+ this.waitUntil = c.waitUntil;
39
+ }
40
+ get clientIp() {
41
+ return this.#c.clientIp;
42
+ }
43
+ }
44
+ ;
45
+ LoaderPlatform.prototype[NIFRA_PLATFORM_CLIENT_IP_DERIVED] = true;
46
+ /**
47
+ * A redirect answering a data request (a client navigation or form submit), as the client router reads
48
+ * one: fetch would follow a 3xx and hand the router another page's data, so the target rides
49
+ * `x-nifra-redirect` on a 204 that keeps the redirect's own headers. `undefined` for any other outcome
50
+ * or request.
51
+ */
52
+ function dataRedirect(req, outcome) {
53
+ if (req.headers.get(DATA_HEADER) === null)
54
+ return undefined;
55
+ if (isResponseResult(outcome)) {
56
+ const plain = outcome.plain;
57
+ if (plain === undefined)
58
+ return dataRedirect(req, outcome.toResponse());
59
+ if (plain.status < 300 || plain.status >= 400)
60
+ return undefined;
61
+ const { location, ...own } = plain.headers ?? {};
62
+ if (location === undefined)
63
+ return undefined;
64
+ return statusResult(204, undefined, {
65
+ headers: {
66
+ ...own,
67
+ [REDIRECT_HEADER]: appTarget(location, req.url),
68
+ ...DATA_RESPONSE_HEADERS,
69
+ },
70
+ });
71
+ }
72
+ if (!(outcome instanceof Response) || outcome.status < 300 || outcome.status >= 400) {
73
+ return undefined;
74
+ }
75
+ const location = outcome.headers.get("location");
76
+ if (location === null)
77
+ return undefined;
78
+ const headers = new Headers(outcome.headers);
79
+ headers.delete("location");
80
+ headers.delete("content-length");
81
+ headers.set(REDIRECT_HEADER, appTarget(location, req.url));
82
+ for (const [name, value] of Object.entries(DATA_RESPONSE_HEADERS))
83
+ headers.set(name, value);
84
+ return new Response(null, { status: 204, headers });
85
+ }
86
+ /** A same-origin target as a path, which the client router can follow without loading a document. */
87
+ function appTarget(location, requestUrl) {
88
+ let target;
89
+ try {
90
+ target = new URL(location, requestUrl);
91
+ }
92
+ catch {
93
+ return location; // the browser fails on it the same way when it loads it
94
+ }
95
+ return target.origin === new URL(requestUrl).origin
96
+ ? target.pathname + target.search + target.hash
97
+ : location;
98
+ }
99
+ /**
100
+ * Own the page request program behind one private seam.
101
+ *
102
+ * `createWebApp` remains responsible for app construction, mount ordering, and route registration.
103
+ * This module owns the page-specific contract: context, layout execution, control-flow precedence,
104
+ * data/document assembly, and error/status rendering. GET and POST retain their distinct ordering
105
+ * (ordinary layout data may run after an action; a gate must run before it) while sharing the same
106
+ * outcome and render decisions.
107
+ */
108
+ export function createPageRequestExecutor(options) {
109
+ const { adapter, manifest, clientEntry, api } = options;
110
+ // Bound once per render, so every loader, action and boundary in that render shares one view of the
111
+ // in-process client carrying the visitor's platform. An `api` without the seam passes through as-is.
112
+ const bindApi = platformBinderOf(api);
113
+ // One view per render, shared by every loader, action and boundary it runs.
114
+ const apiFor = (c) => bindApi === undefined ? api : bindApi.call(api, new LoaderPlatform(c));
115
+ const cssLoading = normalizeCssLoading(options.cssLoading ?? DEFAULT_CSS_LOADING);
116
+ const titleOption = options.title === undefined ? {} : { title: options.title };
117
+ const cspOption = options.csp === undefined ? {} : { csp: options.csp };
118
+ const prerenderedSet = new Set(options.prerenderedPaths ?? []);
119
+ const prerenderedHandover = options.prerenderedListUrl === undefined
120
+ ? { prerenderedPaths: options.prerenderedPaths ?? [] }
121
+ : { prerenderedListUrl: options.prerenderedListUrl };
122
+ const matchManifestRoute = createMatcher(manifest.routes.map((route) => ({ routeId: route.id, pattern: route.pattern })));
123
+ const routeById = new Map(manifest.routes.map((route) => [route.id, route]));
124
+ // A nested `_404` answers an unmatched URL from inside the catch-all, never as a route of its own:
125
+ // a registered `/admin/*` would take URLs from a route the router only reaches by backtracking.
126
+ // Param names are indexed by position, so two directories that name one segment differently share
127
+ // the index; each target keeps its own names to read a match back.
128
+ const notFoundTargets = [];
129
+ const notFoundPatterns = [];
130
+ for (const [id, page] of Object.entries(manifest.notFounds ?? {})) {
131
+ for (const scope of page.scopes) {
132
+ const names = [];
133
+ const pattern = scope.pattern.replace(/([:*])([A-Za-z_][A-Za-z0-9_]*)/g, (_match, sigil, name) => `${sigil}p${names.push(name) - 1}`);
134
+ notFoundPatterns.push({ routeId: String(notFoundTargets.length), pattern });
135
+ notFoundTargets.push({
136
+ id,
137
+ page,
138
+ names,
139
+ route: {
140
+ id,
141
+ pattern: scope.pattern,
142
+ layoutIds: page.layoutIds,
143
+ layoutParams: scope.layoutParams,
144
+ ...(page.middlewareIds === undefined
145
+ ? {}
146
+ : {
147
+ middlewareIds: page.middlewareIds,
148
+ ...(scope.middlewareParams === undefined
149
+ ? {}
150
+ : { middlewareParams: scope.middlewareParams }),
151
+ }),
152
+ errorIds: page.errorIds,
153
+ file: page.file,
154
+ load: page.load,
155
+ },
156
+ });
157
+ }
158
+ }
159
+ const matchNotFoundScope = notFoundPatterns.length === 0 ? undefined : createMatcher(notFoundPatterns);
160
+ const draftFlag = (req) => options.draftSecret === undefined
161
+ ? Promise.resolve(false)
162
+ : isDraftEnabled(req, options.draftSecret);
163
+ const preloadOf = (id) => {
164
+ const chunks = options.routePreload?.[id];
165
+ return chunks ? { preload: chunks } : {};
166
+ };
167
+ 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;
171
+ if (styles === undefined || styles.length === 0)
172
+ return {};
173
+ return {
174
+ styles,
175
+ ...(cssLoading === "deferred" ? { cssLoading } : {}),
176
+ };
177
+ };
178
+ // Load a route's layout modules once for the current execution. The render path needs each whole
179
+ // module for its component + meta, while the data path also needs loader/gate/boundary declarations.
180
+ const loadLayoutModules = async (route) => {
181
+ const modules = await Promise.all(route.layoutIds.map((id) => manifest.layouts[id].load()));
182
+ for (let i = 0; i < modules.length; i++) {
183
+ if (modules[i].action === undefined)
184
+ continue;
185
+ const id = route.layoutIds[i];
186
+ throw new Error(`[nifra/web] "${manifest.layouts[id].file}" exports an \`action\`, but layouts do not run one - only route files do. Move the mutation into the route that submits to it.`);
187
+ }
188
+ return modules;
189
+ };
190
+ // No modules, data, or per-request state: reuse the settled result for page-only routes.
191
+ const emptyLayoutRun = {
192
+ modules: [],
193
+ layoutData: undefined,
194
+ retained: [],
195
+ pending: undefined,
196
+ };
197
+ /** Run layout loaders, awaiting gates before starting the page loader and retaining only safe data. */
198
+ const runLayoutChain = async (route, ctx, controls, retain) => {
199
+ if (route.layoutIds.length === 0)
200
+ return emptyLayoutRun;
201
+ const modules = await loadLayoutModules(route);
202
+ if (!modules.some((m) => m.loader !== undefined)) {
203
+ return { modules, layoutData: undefined, retained: [], pending: undefined };
204
+ }
205
+ const results = new Array(modules.length).fill(null);
206
+ const retained = [];
207
+ const pending = [];
208
+ for (let i = 0; i < modules.length; i++) {
209
+ const layout = modules[i];
210
+ const loader = layout.loader;
211
+ if (loader === undefined)
212
+ continue;
213
+ if (retain !== undefined && layout.gate !== true) {
214
+ let keep;
215
+ try {
216
+ keep = keepsLayoutData(route, i, layout, ctx.params, retain);
217
+ }
218
+ catch (err) {
219
+ throw tagLayoutError(err, route.layoutIds[i]);
220
+ }
221
+ if (keep) {
222
+ retained.push(i);
223
+ continue;
224
+ }
225
+ }
226
+ const scoped = {
227
+ ...ctx,
228
+ params: scopeParams(ctx.params, route.layoutParams?.[i]),
229
+ set: controls.scope(i),
230
+ };
231
+ 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
+ }
238
+ continue;
239
+ }
240
+ const layoutId = route.layoutIds[i];
241
+ const index = i;
242
+ try {
243
+ const value = loader(scoped);
244
+ // Scalars settle synchronously. Objects keep await's intrinsic promise/thenable
245
+ // assimilation: property-presence and prototype probes are unsafe for proxies.
246
+ if (value !== null && (typeof value === "object" || typeof value === "function")) {
247
+ pending.push((async () => {
248
+ try {
249
+ results[index] = await value;
250
+ }
251
+ catch (err) {
252
+ throw tagLayoutError(err, layoutId);
253
+ }
254
+ })());
255
+ }
256
+ else
257
+ results[index] = value;
258
+ }
259
+ catch (err) {
260
+ // Preserve a synchronous throw as a pending layout failure, just like the async lane.
261
+ pending.push(Promise.reject(tagLayoutError(err, layoutId)));
262
+ }
263
+ }
264
+ return {
265
+ modules,
266
+ layoutData: results,
267
+ retained,
268
+ pending: pending.length === 0 ? undefined : pending.length === 1 ? pending[0] : Promise.all(pending),
269
+ };
270
+ };
271
+ /** Run only authorization gates before an action; ordinary layout data runs after a native action. */
272
+ const runLayoutGates = async (route, ctx, controls) => {
273
+ const modules = await loadLayoutModules(route);
274
+ for (let i = 0; i < modules.length; i++) {
275
+ const mod = modules[i];
276
+ if (mod?.gate !== true || mod.loader === undefined)
277
+ 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
+ }
288
+ }
289
+ return modules;
290
+ };
291
+ /** Outermost first; a failure carries its directory so the nearest `_error` above it renders. */
292
+ const runMiddleware = async (route, ctx, controls) => {
293
+ const ids = route.middlewareIds ?? [];
294
+ for (let i = 0; i < ids.length; i++) {
295
+ const id = ids[i];
296
+ const entry = manifest.middlewares?.[id];
297
+ const dirTag = `${id.slice(0, id.length - "_middleware".length)}_layout`;
298
+ let outcome;
299
+ try {
300
+ const middleware = (await entry.load()).default;
301
+ // A `_layout.backend.ts` that exports no middleware is a layout data module, nothing more.
302
+ if (middleware === undefined)
303
+ continue;
304
+ if (typeof middleware !== "function") {
305
+ throw new Error(`[nifra/web] "${entry.file}" exports a middleware that is not a function.`);
306
+ }
307
+ outcome = await middleware({
308
+ ...ctx,
309
+ params: scopeParams(ctx.params, route.middlewareParams?.[i]),
310
+ set: controls.scope(MIDDLEWARE_SCOPE + i),
311
+ });
312
+ }
313
+ catch (err) {
314
+ throw tagLayoutError(err, dirTag);
315
+ }
316
+ if (outcome === undefined)
317
+ continue;
318
+ throw tagLayoutError(isControlFlow(outcome)
319
+ ? outcome
320
+ : new Error(`[nifra/web] the middleware in "${entry.file}" returned a value. Middleware returns nothing to let the request through, or a redirect(), a status such as notFound(), or a Response to answer it.`), dirTag);
321
+ }
322
+ };
323
+ const retainedIndices = (header) => {
324
+ if (header === null || header === "")
325
+ return EMPTY_RETAIN;
326
+ const out = new Set();
327
+ for (const part of header.split(",")) {
328
+ if (!/^\d+$/.test(part))
329
+ continue;
330
+ out.add(Number(part));
331
+ }
332
+ return out;
333
+ };
334
+ /**
335
+ * The browser's side of a client navigation: the page it is leaving and the layout slots it asked
336
+ * to keep. Undefined unless this is a data request whose `from` is same-origin and routable.
337
+ */
338
+ const retainContextOf = (req) => {
339
+ if (req.headers.get(DATA_HEADER) === null)
340
+ return undefined;
341
+ const requested = retainedIndices(req.headers.get(RETAIN_HEADER));
342
+ const from = req.headers.get(NAV_FROM_HEADER);
343
+ if (requested.size === 0 || from === null)
344
+ return undefined;
345
+ let toUrl;
346
+ let fromUrl;
347
+ try {
348
+ toUrl = new URL(req.url);
349
+ fromUrl = new URL(from, toUrl);
350
+ }
351
+ catch {
352
+ return undefined;
353
+ }
354
+ if (fromUrl.origin !== toUrl.origin)
355
+ return undefined;
356
+ const previousMatch = matchManifestRoute(fromUrl.pathname + fromUrl.search);
357
+ if (previousMatch === null)
358
+ return undefined;
359
+ const previous = routeById.get(previousMatch.routeId);
360
+ if (previous === undefined)
361
+ return undefined;
362
+ return { requested, from: previous, fromParams: previousMatch.params, fromUrl, toUrl };
363
+ };
364
+ /** A query change re-runs layout loaders by default: any can read it, and none declares keys. */
365
+ const keepsLayoutData = (route, index, layout, params, retain) => {
366
+ if (!retain.requested.has(index))
367
+ return false;
368
+ const previous = retain.from;
369
+ if (route.layoutIds[index] !== previous.layoutIds[index])
370
+ return false;
371
+ const owned = route.layoutParams?.[index] ?? [];
372
+ const previouslyOwned = previous.layoutParams?.[index] ?? [];
373
+ const defaultShouldRevalidate = retain.fromUrl.search !== retain.toUrl.search ||
374
+ owned.length !== previouslyOwned.length ||
375
+ owned.some((name, i) => name !== previouslyOwned[i]) ||
376
+ owned.some((name) => params[name] !== retain.fromParams[name]);
377
+ const decide = layout.shouldRevalidate;
378
+ if (typeof decide !== "function")
379
+ return !defaultShouldRevalidate;
380
+ // Copies, so a hook that mutates its arguments cannot reach the request's params or the next
381
+ // layout's view of the navigation.
382
+ const answer = decide({
383
+ currentUrl: new URL(retain.fromUrl),
384
+ nextUrl: new URL(retain.toUrl),
385
+ currentParams: { ...retain.fromParams },
386
+ nextParams: { ...params },
387
+ defaultShouldRevalidate,
388
+ });
389
+ return answer === false;
390
+ };
391
+ const loaderSearch = (searchSchema, request) => searchOf(searchSchema, urlPartsFor(request).search);
392
+ const searchChainOf = (layoutMods, mod, request) => searchOfChain([...layoutMods.map((m) => m.searchSchema), mod.searchSchema], urlPartsFor(request).search);
393
+ // `ssr = false`: the slot holds `HydrateFallback`, which the browser hydrates before the page.
394
+ const serverLeafOf = (route, mod) => {
395
+ if (mod.ssr !== false)
396
+ return mod.default;
397
+ if (mod.hydrate === false) {
398
+ throw new Error(`[nifra/web] route "${route.id}" sets both \`ssr = false\` and \`hydrate = false\`: the ` +
399
+ "server skips its component and no client would ever render it. Remove one of the two.");
400
+ }
401
+ return mod.HydrateFallback ?? EMPTY_LEAF;
402
+ };
403
+ const resolveChainAndHead = (layoutModules, page, metaArgs, leaf = page.default) => {
404
+ 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) };
410
+ };
411
+ /** What `useMatches` reports for a chain: the layouts' ids and `handle`s, then the page's. */
412
+ const matchChainOf = (layoutIds, layoutModules, pageId, page) => ({
413
+ ids: [...layoutIds, pageId],
414
+ handles: [...layoutModules.map((layout) => layout.handle), page.handle],
415
+ });
416
+ const dirOfId = (id, suffix) => id === suffix ? "" : id.slice(0, id.length - suffix.length - 1);
417
+ const boundaryFor = (route, err) => {
418
+ const ids = route.errorIds ?? [];
419
+ const failing = layoutErrorId(err);
420
+ if (failing === undefined)
421
+ return ids.at(-1);
422
+ const layoutDir = dirOfId(failing, "_layout");
423
+ for (let i = ids.length - 1; i >= 0; i--) {
424
+ const errDir = dirOfId(ids[i], "_error");
425
+ if (errDir === "" || errDir === layoutDir || layoutDir.startsWith(`${errDir}/`))
426
+ return ids[i];
427
+ }
428
+ return undefined;
429
+ };
430
+ const originOf = (req) => {
431
+ try {
432
+ return new URL(req.url).origin;
433
+ }
434
+ catch {
435
+ return "";
436
+ }
437
+ };
438
+ const pathOf = (req) => {
439
+ try {
440
+ const parts = urlPartsFor(req);
441
+ return parts.pathname + parts.search;
442
+ }
443
+ catch {
444
+ return "/";
445
+ }
446
+ };
447
+ const renderError = async (route, errorId, err, personalized, nonce) => {
448
+ const errDir = dirOfId(errorId, "_error");
449
+ const keptLayoutIds = route.layoutIds.filter((id) => {
450
+ const ld = dirOfId(id, "_layout");
451
+ return ld === "" || ld === errDir || errDir.startsWith(`${ld}/`);
452
+ });
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();
455
+ 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
+ });
473
+ };
474
+ const renderTerminalStatus = async (status, path, extraHeaders, nonce) => {
475
+ const page = manifest.statusPages?.[String(status)] ?? manifest.notFound;
476
+ if (page === undefined) {
477
+ const headers = new Headers(extraHeaders);
478
+ headers.set("content-type", "text/plain; charset=utf-8");
479
+ return new Response(STATUS_TEXT[status] ?? "Error", { status, headers });
480
+ }
481
+ const routeId = manifest.statusPages?.[String(status)] !== undefined ? `_${status}` : "_404";
482
+ const loaded = (await page.load());
483
+ return renderPageResult({
484
+ adapter,
485
+ chain: [loaded.default],
486
+ data: null,
487
+ clientEntry,
488
+ 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 } : {}),
496
+ status,
497
+ ...(extraHeaders !== undefined ? { headers: extraHeaders } : {}),
498
+ ...(nonce === undefined ? {} : { nonce }),
499
+ ...cspOption,
500
+ ...preloadOf(routeId),
501
+ ...stylesOf(routeId),
502
+ ...prerenderedHandover,
503
+ ...titleOption,
504
+ });
505
+ };
506
+ const renderStatusSignal = async (req, env, signal, personalized) => {
507
+ const { status, headers } = signal[STATUS_SIGNAL];
508
+ if (req.headers.get(DATA_HEADER) !== null) {
509
+ // Same URL as the status page, so it is never storable: a cache keyed on the URL alone would
510
+ // otherwise answer a document request with this empty body.
511
+ const responseHeaders = new Headers(headers);
512
+ responseHeaders.set(STATUS_HEADER, String(status));
513
+ responseHeaders.set("cache-control", PRIVATE_NO_STORE);
514
+ responseHeaders.set("vary", varyOnDataHeader(responseHeaders.get("vary")));
515
+ return new Response(null, { status, headers: responseHeaders });
516
+ }
517
+ const nonce = await resolveNonce(req, env);
518
+ if (headers === undefined && !personalized) {
519
+ return renderTerminalStatus(status, pathOf(req), undefined, nonce);
520
+ }
521
+ const documentHeaders = new Headers(headers);
522
+ if (headers !== undefined) {
523
+ documentHeaders.set("vary", varyOnDataHeader(documentHeaders.get("vary")));
524
+ }
525
+ // A cookie queued before the signal makes the status page specific to this visitor.
526
+ if (personalized)
527
+ documentHeaders.set("cache-control", PRIVATE_NO_STORE);
528
+ return renderTerminalStatus(status, pathOf(req), documentHeaders, nonce);
529
+ };
530
+ const reportLoaderError = (route, req, params, err) => {
531
+ if (options.onLoaderError === undefined)
532
+ return;
533
+ try {
534
+ options.onLoaderError(err, { request: req, params, route: route.pattern });
535
+ }
536
+ catch {
537
+ // A faulty reporter must never break error rendering.
538
+ }
539
+ };
540
+ const handleDataOrDocument = async (req, env, route, params, mod, data, layoutData, layoutModules, layoutRetained, boundaryStates, assemblyCache, responseHeaders, personalized) => {
541
+ if (isStatusSignal(data))
542
+ return renderStatusSignal(req, env, data, personalized);
543
+ if (isControlFlow(data))
544
+ return data;
545
+ if (req.headers.get(DATA_HEADER) !== null) {
546
+ const pageSplit = prepareDeferred(data);
547
+ const layoutSplits = [];
548
+ let offset = pageSplit.deferred.length;
549
+ for (const entry of layoutData ?? []) {
550
+ const split = prepareDeferred(entry, offset);
551
+ layoutSplits.push(split);
552
+ offset += split.deferred.length;
553
+ }
554
+ const deferred = [...pageSplit.deferred, ...layoutSplits.flatMap((split) => split.deferred)];
555
+ const boundarySplit = boundaryStates === undefined ? undefined : prepareDeferred(boundaryStates, deferred.length);
556
+ const allDeferred = [
557
+ ...deferred,
558
+ ...(boundarySplit === undefined ? [] : boundarySplit.deferred),
559
+ ];
560
+ const payload = layoutData === undefined && boundarySplit === undefined
561
+ ? pageSplit.forClient
562
+ : {
563
+ v: 1,
564
+ data: pageSplit.forClient,
565
+ layoutData: layoutSplits.map((split) => split.forClient),
566
+ retained: layoutRetained,
567
+ ...(boundarySplit === undefined ? {} : { boundaries: boundarySplit.forClient }),
568
+ };
569
+ if (allDeferred.length === 0) {
570
+ return Response.json(payload ?? null, { headers: DATA_RESPONSE_HEADERS });
571
+ }
572
+ return new Response(ndjsonStream(payload, allDeferred), {
573
+ headers: {
574
+ "content-type": "application/x-ndjson; charset=utf-8",
575
+ ...DATA_RESPONSE_HEADERS,
576
+ },
577
+ });
578
+ }
579
+ const nonce = options.nonce === undefined ? undefined : await resolveNonce(req, env);
580
+ const { chain, head } = resolveChainAndHead(layoutModules, mod, {
581
+ data,
582
+ params,
583
+ origin: originOf(req),
584
+ ...(nonce === undefined ? {} : { nonce }),
585
+ }, serverLeafOf(route, mod));
586
+ try {
587
+ return await renderPageResult({
588
+ adapter,
589
+ chain,
590
+ data,
591
+ head,
592
+ clientEntry,
593
+ routeId: route.id,
594
+ params,
595
+ path: pathOf(req),
596
+ search: searchChainOf(layoutModules, mod, req),
597
+ matchChain: matchChainOf(route.layoutIds, layoutModules, route.id, mod),
598
+ hydrate: mod.hydrate !== false,
599
+ ...(layoutData !== undefined ? { layoutData } : {}),
600
+ ...(boundaryStates !== undefined ? { boundaries: boundaryStates } : {}),
601
+ ...preloadOf(route.id),
602
+ ...stylesOf(route.id),
603
+ ...prerenderedHandover,
604
+ ...(responseHeaders === undefined ? {} : { headers: responseHeaders }),
605
+ // A response that sets a cookie is one visitor's: it never advertises ISR freshness.
606
+ ...(mod.revalidate !== undefined && !personalized && options.cacheChannel?.enabled === true
607
+ ? { revalidate: mod.revalidate }
608
+ : {}),
609
+ ...(mod.revalidateTags !== undefined &&
610
+ !personalized &&
611
+ options.cacheChannel?.enabled === true
612
+ ? {
613
+ revalidateTags: routeTags(mod.revalidateTags, { params, url: new URL(req.url) }, route.id),
614
+ }
615
+ : {}),
616
+ ...(mod.islandScripts !== undefined ? { islandScripts: mod.islandScripts } : {}),
617
+ ...(nonce === undefined ? {} : { nonce }),
618
+ ...cspOption,
619
+ ...titleOption,
620
+ ...(assemblyCache === undefined ? {} : { assemblyCache }),
621
+ });
622
+ }
623
+ catch (err) {
624
+ if (isStatusSignal(err))
625
+ return renderStatusSignal(req, env, err, personalized);
626
+ if (isControlFlow(err))
627
+ throw err;
628
+ reportLoaderError(route, req, { ...params }, err);
629
+ const errorId = boundaryFor(route, err);
630
+ if (errorId === undefined)
631
+ throw err;
632
+ return renderError(route, errorId, withDuplicateInstanceHint(err), personalized, nonce);
633
+ }
634
+ };
635
+ /** What a failed loader answers with, once a status signal has been ruled out. */
636
+ const renderLoaderFailure = async (route, req, env, params, err, personalized) => {
637
+ if (isControlFlow(err))
638
+ throw err;
639
+ reportLoaderError(route, req, params, err);
640
+ const errorId = boundaryFor(route, err);
641
+ if (errorId === undefined)
642
+ throw err;
643
+ if (req.headers.get(DATA_HEADER) !== null) {
644
+ return new Response("Internal Server Error", {
645
+ status: 500,
646
+ headers: { "content-type": "text/plain; charset=utf-8" },
647
+ });
648
+ }
649
+ return renderError(route, errorId, withDuplicateInstanceHint(err), personalized, options.nonce === undefined ? undefined : await resolveNonce(req, env));
650
+ };
651
+ /**
652
+ * Render a nested `_404` inside its layouts. `layoutModules` and `layoutData` are exactly the
653
+ * layouts at or above the page, their gates already passed.
654
+ */
655
+ const renderNestedNotFound = async (req, env, id, page, params, layoutModules, layoutData, personalized, signalHeaders) => {
656
+ // The page can show what a layout loaded for this visitor, and a 404 is storable by default.
657
+ const isPrivate = personalized || layoutData !== undefined;
658
+ if (req.headers.get(DATA_HEADER) !== null) {
659
+ // No status header: the page has no client chunk, so the navigation falls back to loading the
660
+ // URL as a document, which renders it here.
661
+ const headers = new Headers(signalHeaders);
662
+ headers.set("cache-control", PRIVATE_NO_STORE);
663
+ headers.set("vary", varyOnDataHeader(headers.get("vary")));
664
+ return new Response(null, { status: 404, headers });
665
+ }
666
+ const mod = await page.load();
667
+ const nonce = options.nonce === undefined ? undefined : await resolveNonce(req, env);
668
+ const { chain, head } = resolveChainAndHead(layoutModules, mod, {
669
+ data: null,
670
+ params,
671
+ origin: originOf(req),
672
+ ...(nonce === undefined ? {} : { nonce }),
673
+ });
674
+ let headers;
675
+ if (signalHeaders !== undefined || isPrivate) {
676
+ headers = new Headers(signalHeaders);
677
+ if (signalHeaders !== undefined)
678
+ headers.set("vary", varyOnDataHeader(headers.get("vary")));
679
+ if (isPrivate)
680
+ headers.set("cache-control", PRIVATE_NO_STORE);
681
+ }
682
+ return renderPageResult({
683
+ adapter,
684
+ chain,
685
+ data: null,
686
+ head,
687
+ clientEntry,
688
+ routeId: id,
689
+ params,
690
+ path: pathOf(req),
691
+ search: searchChainOf(layoutModules, mod, req),
692
+ matchChain: matchChainOf(page.layoutIds, layoutModules, id, mod),
693
+ status: 404,
694
+ hydrate: false,
695
+ ...(layoutData !== undefined ? { layoutData } : {}),
696
+ ...stylesOf(id),
697
+ ...(headers === undefined ? {} : { headers }),
698
+ ...(nonce === undefined ? {} : { nonce }),
699
+ ...cspOption,
700
+ ...titleOption,
701
+ });
702
+ };
703
+ /** The nearest nested `_404` for the route's own `notFound()`, else `undefined` (root page). */
704
+ const renderRouteNotFound = async (req, env, route, params, signal, run, personalized) => {
705
+ const id = route.notFoundIds?.at(-1);
706
+ if (id === undefined || run === undefined)
707
+ return undefined;
708
+ if (signal[STATUS_SIGNAL].status !== 404 || layoutErrorId(signal) !== undefined) {
709
+ return undefined;
710
+ }
711
+ const page = manifest.notFounds?.[id];
712
+ if (page === undefined)
713
+ return undefined;
714
+ try {
715
+ await run.pending;
716
+ }
717
+ catch {
718
+ return undefined;
719
+ }
720
+ // The page's layouts are the leading ones of the route's: its directory contains the route.
721
+ const kept = page.layoutIds.length;
722
+ return renderNestedNotFound(req, env, id, page, params, run.modules.slice(0, kept), run.layoutData?.slice(0, kept), personalized, signal[STATUS_SIGNAL].headers);
723
+ };
724
+ const renderNotFound = async (request, env, path) => {
725
+ const page = manifest.statusPages?.["404"] ?? manifest.notFound;
726
+ if (page === undefined)
727
+ return renderTerminalStatus(404, path);
728
+ return renderTerminalStatus(404, path, undefined, options.nonce === undefined ? undefined : await resolveNonce(request, env));
729
+ };
730
+ /**
731
+ * Run a page handler with the request's `ctx.set`, sealed once the handler settles - whatever it
732
+ * answered with - so a write that arrives after the response was decided throws. A request that
733
+ * queued a cookie answers with a response no shared cache may store, returned or thrown. A redirect
734
+ * answering a data request, returned or thrown, reaches the client router intact.
735
+ */
736
+ const withResponseControls = (run) => async (c) => {
737
+ const controls = new PageResponseControls(c);
738
+ try {
739
+ const outcome = await run(c, controls);
740
+ const answer = dataRedirect(c.req, outcome) ?? outcome;
741
+ return controls.personalized ? privateOutcome(answer) : answer;
742
+ }
743
+ catch (err) {
744
+ if (!isControlFlow(err))
745
+ throw err;
746
+ const answer = dataRedirect(c.req, err);
747
+ // Returned rather than rethrown: the serving app sends a thrown `Response` exactly as it is,
748
+ // without the cookies the request queued.
749
+ if (controls.personalized)
750
+ return privateOutcome(answer ?? err);
751
+ if (answer !== undefined)
752
+ return answer;
753
+ throw err;
754
+ }
755
+ finally {
756
+ controls.seal();
757
+ }
758
+ };
759
+ return {
760
+ get: (route) => {
761
+ const is404Fallback = options.staticFallbacks?.[route.pattern] === "404";
762
+ let assemblySlot;
763
+ let assemblySlotMod;
764
+ let assemblySlotLayouts;
765
+ const assemblyCacheFor = (mod, layouts) => {
766
+ if (typeof mod.meta === "function")
767
+ return undefined;
768
+ for (const layout of layouts)
769
+ if (typeof layout.meta === "function")
770
+ return undefined;
771
+ const layoutsUnchanged = assemblySlotLayouts !== undefined &&
772
+ assemblySlotLayouts.length === layouts.length &&
773
+ assemblySlotLayouts.every((layout, index) => layout === layouts[index]);
774
+ if (assemblySlotMod !== mod || !layoutsUnchanged) {
775
+ assemblySlot = {};
776
+ assemblySlotMod = mod;
777
+ assemblySlotLayouts = layouts;
778
+ }
779
+ return assemblySlot;
780
+ };
781
+ return withResponseControls(async (c, controls) => {
782
+ if (is404Fallback && !prerenderedSet.has(urlPartsFor(c.req).pathname)) {
783
+ return renderNotFound(c.req, c.env, pathOf(c.req));
784
+ }
785
+ const mod = await route.load();
786
+ const draft = await draftFlag(c.req);
787
+ // Unpublished content is the editor's alone: whatever cache-control a loader sets, no cache keeps it.
788
+ if (draft)
789
+ controls.personalize();
790
+ let data;
791
+ let layoutData;
792
+ let boundaryStates;
793
+ let layoutModules;
794
+ let layoutRetained = [];
795
+ let responseHeaders;
796
+ const requestApi = apiFor(c);
797
+ let run;
798
+ try {
799
+ const ctx = {
800
+ params: c.params,
801
+ request: c.req,
802
+ req: c.req,
803
+ api: requestApi,
804
+ env: c.env,
805
+ draft,
806
+ search: loaderSearch(mod.searchSchema, c.req),
807
+ set: controls.scope(PAGE_SCOPE),
808
+ };
809
+ if (route.middlewareIds !== undefined)
810
+ await runMiddleware(route, ctx, controls);
811
+ run = await runLayoutChain(route, ctx, controls, retainContextOf(c.req));
812
+ layoutModules = run.modules;
813
+ layoutRetained = run.retained;
814
+ const boundaryDefinitions = [
815
+ ...run.modules.flatMap((layout) => layout.boundaries ?? []),
816
+ ...(mod.boundaries ?? []),
817
+ ];
818
+ if (boundaryDefinitions.length > 0)
819
+ boundaryDescriptors(boundaryDefinitions);
820
+ const effectiveSearch = searchChainOf(run.modules, mod, c.req);
821
+ const boundaryBatch = boundaryDefinitions.length === 0
822
+ ? undefined
823
+ : startDynamicBoundaries(boundaryDefinitions, {
824
+ request: c.req,
825
+ params: c.params,
826
+ api: requestApi,
827
+ env: c.env,
828
+ draft,
829
+ search: effectiveSearch,
830
+ signal: c.req.signal,
831
+ });
832
+ const staticBoundaryPromise = boundaryDefinitions.length === 0
833
+ ? undefined
834
+ : resolveStaticBoundaries(boundaryDefinitions, { phase: "build" }, options.staticBoundaryCache);
835
+ const pageResult = mod.loader ? mod.loader({ ...ctx, search: effectiveSearch }) : null;
836
+ const [pageData, , staticBoundaries] = run.pending === undefined && staticBoundaryPromise === undefined
837
+ ? [await pageResult, undefined, undefined]
838
+ : staticBoundaryPromise === undefined
839
+ ? await Promise.all([pageResult, run.pending])
840
+ : await Promise.all([pageResult, run.pending, staticBoundaryPromise]);
841
+ data = pageData;
842
+ layoutData = run.layoutData;
843
+ if (boundaryBatch !== undefined) {
844
+ const streamed = { ...boundaryBatch.initial, ...(staticBoundaries ?? {}) };
845
+ for (const entry of boundaryBatch.pending) {
846
+ const initial = streamed[entry.name];
847
+ if (initial !== undefined)
848
+ streamed[entry.name] = { ...initial, data: defer(entry.promise) };
849
+ }
850
+ boundaryStates = streamed;
851
+ }
852
+ else if (staticBoundaries !== undefined) {
853
+ boundaryStates = staticBoundaries;
854
+ }
855
+ // Inside the loader `try`: a header the response cannot carry is a loader error, rendered
856
+ // by the same boundary as one.
857
+ responseHeaders = controls.documentHeaders();
858
+ }
859
+ catch (err) {
860
+ 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));
862
+ }
863
+ return renderLoaderFailure(route, c.req, c.env, c.params, err, controls.personalized);
864
+ }
865
+ if (isStatusSignal(data)) {
866
+ const nested = await renderRouteNotFound(c.req, c.env, route, c.params, data, run, controls.personalized);
867
+ if (nested !== undefined)
868
+ return nested;
869
+ }
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);
871
+ });
872
+ },
873
+ post: (route) => withResponseControls(async (c, controls) => {
874
+ const mod = await route.load();
875
+ const draft = await draftFlag(c.req);
876
+ if (draft)
877
+ controls.personalize();
878
+ if (mod.action === undefined) {
879
+ return new Response("Method Not Allowed", {
880
+ status: 405,
881
+ headers: { allow: "GET", "content-type": "text/plain; charset=utf-8" },
882
+ });
883
+ }
884
+ const requestApi = apiFor(c);
885
+ const actionContext = {
886
+ params: c.params,
887
+ request: c.req,
888
+ req: c.req,
889
+ api: requestApi,
890
+ env: c.env,
891
+ draft,
892
+ search: loaderSearch(mod.searchSchema, c.req),
893
+ set: controls.scope(ACTION_SCOPE),
894
+ };
895
+ const isDataRequest = c.req.headers.get(DATA_HEADER) !== null;
896
+ let layoutModules;
897
+ let result;
898
+ try {
899
+ if (route.middlewareIds !== undefined)
900
+ await runMiddleware(route, actionContext, controls);
901
+ layoutModules = await runLayoutGates(route, actionContext, controls);
902
+ result = await mod.action(actionContext);
903
+ }
904
+ catch (err) {
905
+ if (isControlFlow(err))
906
+ return err;
907
+ throw err;
908
+ }
909
+ const isRevalidate = result !== null && typeof result === "object" && "__nifraRevalidate" in result;
910
+ const actionResult = isRevalidate ? result.data : result;
911
+ const revalidateHeader = isRevalidate
912
+ ? {
913
+ [REVALIDATE_HEADER]: result.__nifraRevalidate.join(","),
914
+ }
915
+ : {};
916
+ if (isControlFlow(actionResult))
917
+ return actionResult;
918
+ if (isDataRequest) {
919
+ // Headers shape the document only, but a bad one fails here too, so a navigation and a full
920
+ // page load agree on whether the action's response is valid.
921
+ controls.commit();
922
+ const { forClient, deferred } = prepareDeferred(actionResult);
923
+ if (deferred.length === 0) {
924
+ return Response.json(actionResult ?? null, {
925
+ headers: { ...DATA_RESPONSE_HEADERS, ...revalidateHeader },
926
+ });
927
+ }
928
+ return new Response(ndjsonStream(forClient, deferred), {
929
+ headers: {
930
+ "content-type": "application/x-ndjson; charset=utf-8",
931
+ ...DATA_RESPONSE_HEADERS,
932
+ ...revalidateHeader,
933
+ },
934
+ });
935
+ }
936
+ 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();
950
+ const nonce = options.nonce === undefined ? undefined : await resolveNonce(c.req, c.env);
951
+ const { chain, head } = resolveChainAndHead(layoutModules, mod, {
952
+ data,
953
+ params: c.params,
954
+ origin: originOf(c.req),
955
+ ...(nonce === undefined ? {} : { nonce }),
956
+ }, serverLeafOf(route, mod));
957
+ return renderPageResult({
958
+ adapter,
959
+ chain,
960
+ data,
961
+ actionData: actionResult,
962
+ head,
963
+ clientEntry,
964
+ routeId: route.id,
965
+ params: c.params,
966
+ path: pathOf(c.req),
967
+ search,
968
+ matchChain: matchChainOf(route.layoutIds, layoutModules, route.id, mod),
969
+ hydrate: mod.hydrate !== false,
970
+ ...preloadOf(route.id),
971
+ ...stylesOf(route.id),
972
+ ...prerenderedHandover,
973
+ ...(responseHeaders === undefined ? {} : { headers: responseHeaders }),
974
+ ...(nonce === undefined ? {} : { nonce }),
975
+ ...cspOption,
976
+ ...titleOption,
977
+ });
978
+ }),
979
+ fallback: withResponseControls(async (c, controls) => {
980
+ const match = matchNotFoundScope?.(pathOf(c.req)) ?? null;
981
+ const target = match === null ? undefined : notFoundTargets[Number(match.routeId)];
982
+ if (match === null || target === undefined) {
983
+ return renderNotFound(c.req, c.env, pathOf(c.req));
984
+ }
985
+ const params = {};
986
+ for (let i = 0; i < target.names.length; i++) {
987
+ params[target.names[i]] = match.params[`p${i}`];
988
+ }
989
+ const mod = await target.page.load();
990
+ const draft = await draftFlag(c.req);
991
+ if (draft)
992
+ controls.personalize();
993
+ let run;
994
+ try {
995
+ const ctx = {
996
+ params,
997
+ request: c.req,
998
+ req: c.req,
999
+ api: apiFor(c),
1000
+ env: c.env,
1001
+ draft,
1002
+ search: loaderSearch(mod.searchSchema, c.req),
1003
+ set: controls.scope(PAGE_SCOPE),
1004
+ };
1005
+ if (target.route.middlewareIds !== undefined) {
1006
+ await runMiddleware(target.route, ctx, controls);
1007
+ }
1008
+ run = await runLayoutChain(target.route, ctx, controls);
1009
+ await run.pending;
1010
+ }
1011
+ catch (err) {
1012
+ if (isStatusSignal(err)) {
1013
+ return renderStatusSignal(c.req, c.env, err, controls.personalized);
1014
+ }
1015
+ return renderLoaderFailure(target.route, c.req, c.env, params, err, controls.personalized);
1016
+ }
1017
+ return renderNestedNotFound(c.req, c.env, target.id, target.page, params, run.modules, run.layoutData, controls.personalized);
1018
+ }),
1019
+ };
1020
+ }
1021
+ //# sourceMappingURL=page-execution.js.map