@nifrajs/web 4.0.2 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -0
- package/dist/build-vite.d.ts +19 -0
- package/dist/build-vite.d.ts.map +1 -1
- package/dist/build-vite.js +31 -10
- package/dist/build-vite.js.map +1 -1
- package/dist/build.d.ts +8 -10
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +90 -31
- package/dist/build.js.map +1 -1
- package/dist/cdn.d.ts +8 -0
- package/dist/cdn.d.ts.map +1 -1
- package/dist/cdn.js +14 -15
- package/dist/cdn.js.map +1 -1
- package/dist/client.d.ts +6 -6
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +46 -18
- package/dist/client.js.map +1 -1
- package/dist/deferred.d.ts +2 -2
- package/dist/deferred.d.ts.map +1 -1
- package/dist/deferred.js +28 -7
- package/dist/deferred.js.map +1 -1
- package/dist/dev-feed.d.ts.map +1 -1
- package/dist/dev-feed.js +24 -5
- package/dist/dev-feed.js.map +1 -1
- package/dist/dev-port.d.ts +3 -2
- package/dist/dev-port.d.ts.map +1 -1
- package/dist/dev-port.js +2 -6
- package/dist/dev-port.js.map +1 -1
- package/dist/dev-session.d.ts.map +1 -1
- package/dist/dev-session.js +4 -14
- package/dist/dev-session.js.map +1 -1
- package/dist/dev.d.ts +7 -17
- package/dist/dev.d.ts.map +1 -1
- package/dist/dev.js +13 -1
- package/dist/dev.js.map +1 -1
- package/dist/draft.d.ts +8 -5
- package/dist/draft.d.ts.map +1 -1
- package/dist/draft.js +30 -13
- package/dist/draft.js.map +1 -1
- package/dist/fonts-google.d.ts.map +1 -1
- package/dist/fonts-google.js +7 -0
- package/dist/fonts-google.js.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/internal/codegen.d.ts +1 -0
- package/dist/internal/codegen.d.ts.map +1 -1
- package/dist/internal/codegen.js +56 -29
- package/dist/internal/codegen.js.map +1 -1
- package/dist/internal/disposable-dir.d.ts +8 -0
- package/dist/internal/disposable-dir.d.ts.map +1 -0
- package/dist/internal/disposable-dir.js +25 -0
- package/dist/internal/disposable-dir.js.map +1 -0
- package/dist/internal/entry-chunks.d.ts +9 -0
- package/dist/internal/entry-chunks.d.ts.map +1 -0
- package/dist/internal/entry-chunks.js +15 -0
- package/dist/internal/entry-chunks.js.map +1 -0
- package/dist/internal/evidence-traversal.d.ts +12 -0
- package/dist/internal/evidence-traversal.d.ts.map +1 -0
- package/dist/internal/evidence-traversal.js +48 -0
- package/dist/internal/evidence-traversal.js.map +1 -0
- package/dist/internal/head-merge.d.ts +6 -0
- package/dist/internal/head-merge.d.ts.map +1 -1
- package/dist/internal/head-merge.js +9 -0
- package/dist/internal/head-merge.js.map +1 -1
- package/dist/internal/js-string.d.ts +2 -0
- package/dist/internal/js-string.d.ts.map +1 -1
- package/dist/internal/js-string.js +5 -1
- package/dist/internal/js-string.js.map +1 -1
- package/dist/internal/mount-shadow.js +1 -1
- package/dist/internal/mount-shadow.js.map +1 -1
- package/dist/internal/output-guard.d.ts +7 -0
- package/dist/internal/output-guard.d.ts.map +1 -1
- package/dist/internal/output-guard.js +80 -8
- package/dist/internal/output-guard.js.map +1 -1
- package/dist/internal/package-entries.d.ts +34 -0
- package/dist/internal/package-entries.d.ts.map +1 -0
- package/dist/internal/package-entries.js +324 -0
- package/dist/internal/package-entries.js.map +1 -0
- package/dist/internal/page-execution.d.ts +6 -0
- package/dist/internal/page-execution.d.ts.map +1 -1
- package/dist/internal/page-execution.js +437 -114
- package/dist/internal/page-execution.js.map +1 -1
- package/dist/internal/parity.d.ts.map +1 -1
- package/dist/internal/parity.js +15 -2
- package/dist/internal/parity.js.map +1 -1
- package/dist/internal/private-env.d.ts +8 -0
- package/dist/internal/private-env.d.ts.map +1 -1
- package/dist/internal/private-env.js +25 -10
- package/dist/internal/private-env.js.map +1 -1
- package/dist/internal/progressive-cookies.d.ts +14 -0
- package/dist/internal/progressive-cookies.d.ts.map +1 -0
- package/dist/internal/progressive-cookies.js +43 -0
- package/dist/internal/progressive-cookies.js.map +1 -0
- package/dist/internal/public-env.d.ts +2 -0
- package/dist/internal/public-env.d.ts.map +1 -0
- package/dist/internal/public-env.js +13 -0
- package/dist/internal/public-env.js.map +1 -0
- package/dist/internal/render-document.d.ts +5 -44
- package/dist/internal/render-document.d.ts.map +1 -1
- package/dist/internal/render-document.js +51 -63
- package/dist/internal/render-document.js.map +1 -1
- package/dist/internal/response-controls.d.ts +2 -0
- package/dist/internal/response-controls.d.ts.map +1 -1
- package/dist/internal/response-controls.js +26 -16
- package/dist/internal/response-controls.js.map +1 -1
- package/dist/internal/revive-dates.d.ts +16 -0
- package/dist/internal/revive-dates.d.ts.map +1 -0
- package/dist/internal/revive-dates.js +32 -0
- package/dist/internal/revive-dates.js.map +1 -0
- package/dist/internal/route-path.d.ts +14 -0
- package/dist/internal/route-path.d.ts.map +1 -0
- package/dist/internal/route-path.js +19 -0
- package/dist/internal/route-path.js.map +1 -0
- package/dist/internal/secret-scan.d.ts.map +1 -1
- package/dist/internal/secret-scan.js +190 -20
- package/dist/internal/secret-scan.js.map +1 -1
- package/dist/internal/server-boundary.d.ts.map +1 -1
- package/dist/internal/server-boundary.js +33 -5
- package/dist/internal/server-boundary.js.map +1 -1
- package/dist/internal/web-app.d.ts +25 -18
- package/dist/internal/web-app.d.ts.map +1 -1
- package/dist/internal/web-app.js +81 -24
- package/dist/internal/web-app.js.map +1 -1
- package/dist/internal/wire-date.d.ts +3 -0
- package/dist/internal/wire-date.d.ts.map +1 -0
- package/dist/internal/wire-date.js +14 -0
- package/dist/internal/wire-date.js.map +1 -0
- package/dist/internal/without-query.d.ts +4 -0
- package/dist/internal/without-query.d.ts.map +1 -0
- package/dist/internal/without-query.js +9 -0
- package/dist/internal/without-query.js.map +1 -0
- package/dist/internal/zone-graph.d.ts +4 -0
- package/dist/internal/zone-graph.d.ts.map +1 -1
- package/dist/internal/zone-graph.js +71 -20
- package/dist/internal/zone-graph.js.map +1 -1
- package/dist/isr.d.ts +59 -3
- package/dist/isr.d.ts.map +1 -1
- package/dist/isr.js +311 -41
- package/dist/isr.js.map +1 -1
- package/dist/llms-txt.d.ts +2 -0
- package/dist/llms-txt.d.ts.map +1 -1
- package/dist/llms-txt.js +29 -3
- package/dist/llms-txt.js.map +1 -1
- package/dist/manifest.d.ts +37 -4
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +4 -0
- package/dist/manifest.js.map +1 -1
- package/dist/open-resolved.d.ts +18 -0
- package/dist/open-resolved.d.ts.map +1 -0
- package/dist/open-resolved.js +62 -0
- package/dist/open-resolved.js.map +1 -0
- package/dist/plugins/kit.d.ts +1 -0
- package/dist/plugins/kit.d.ts.map +1 -1
- package/dist/plugins/kit.js +26 -11
- package/dist/plugins/kit.js.map +1 -1
- package/dist/plugins/vite-leak-guard.d.ts +9 -1
- package/dist/plugins/vite-leak-guard.d.ts.map +1 -1
- package/dist/plugins/vite-leak-guard.js +84 -7
- package/dist/plugins/vite-leak-guard.js.map +1 -1
- package/dist/plugins/vite-zone-guard.d.ts.map +1 -1
- package/dist/plugins/vite-zone-guard.js +17 -21
- package/dist/plugins/vite-zone-guard.js.map +1 -1
- package/dist/plugins/zone-guard.d.ts.map +1 -1
- package/dist/plugins/zone-guard.js +44 -3
- package/dist/plugins/zone-guard.js.map +1 -1
- package/dist/prerender.d.ts +5 -1
- package/dist/prerender.d.ts.map +1 -1
- package/dist/prerender.js +41 -19
- package/dist/prerender.js.map +1 -1
- package/dist/public-dir.d.ts +8 -1
- package/dist/public-dir.d.ts.map +1 -1
- package/dist/public-dir.js +17 -6
- package/dist/public-dir.js.map +1 -1
- package/dist/query.d.ts +13 -4
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +89 -26
- package/dist/query.js.map +1 -1
- package/dist/render-seam.d.ts +25 -2
- package/dist/render-seam.d.ts.map +1 -1
- package/dist/render-seam.js +1 -0
- package/dist/render-seam.js.map +1 -1
- package/dist/route-types.d.ts.map +1 -1
- package/dist/route-types.js +20 -9
- package/dist/route-types.js.map +1 -1
- package/dist/router.d.ts +9 -2
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +47 -50
- package/dist/router.js.map +1 -1
- package/dist/seo.d.ts +49 -0
- package/dist/seo.d.ts.map +1 -0
- package/dist/seo.js +48 -0
- package/dist/seo.js.map +1 -0
- package/dist/server-components/build-info.d.ts +3 -0
- package/dist/server-components/build-info.d.ts.map +1 -0
- package/dist/server-components/build-info.js +7 -0
- package/dist/server-components/build-info.js.map +1 -0
- package/dist/server-components/build-target.d.ts +12 -0
- package/dist/server-components/build-target.d.ts.map +1 -0
- package/dist/server-components/build-target.js +153 -0
- package/dist/server-components/build-target.js.map +1 -0
- package/dist/server-components/build.d.ts +24 -0
- package/dist/server-components/build.d.ts.map +1 -0
- package/dist/server-components/build.js +366 -0
- package/dist/server-components/build.js.map +1 -0
- package/dist/server-components/bundle-proof.d.ts +3 -0
- package/dist/server-components/bundle-proof.d.ts.map +1 -0
- package/dist/server-components/bundle-proof.js +23 -0
- package/dist/server-components/bundle-proof.js.map +1 -0
- package/dist/server-components/client.d.ts +46 -0
- package/dist/server-components/client.d.ts.map +1 -0
- package/dist/server-components/client.js +594 -0
- package/dist/server-components/client.js.map +1 -0
- package/dist/server-components/definition.d.ts +11 -0
- package/dist/server-components/definition.d.ts.map +1 -0
- package/dist/server-components/definition.js +94 -0
- package/dist/server-components/definition.js.map +1 -0
- package/dist/server-components/dev-http.d.ts +5 -0
- package/dist/server-components/dev-http.d.ts.map +1 -0
- package/dist/server-components/dev-http.js +71 -0
- package/dist/server-components/dev-http.js.map +1 -0
- package/dist/server-components/error.d.ts +9 -0
- package/dist/server-components/error.d.ts.map +1 -0
- package/dist/server-components/error.js +24 -0
- package/dist/server-components/error.js.map +1 -0
- package/dist/server-components/frames.d.ts +7 -0
- package/dist/server-components/frames.d.ts.map +1 -0
- package/dist/server-components/frames.js +169 -0
- package/dist/server-components/frames.js.map +1 -0
- package/dist/server-components/html-policy.d.ts +6 -0
- package/dist/server-components/html-policy.d.ts.map +1 -0
- package/dist/server-components/html-policy.js +75 -0
- package/dist/server-components/html-policy.js.map +1 -0
- package/dist/server-components/html.d.ts +4 -0
- package/dist/server-components/html.d.ts.map +1 -0
- package/dist/server-components/html.js +67 -0
- package/dist/server-components/html.js.map +1 -0
- package/dist/server-components/islands.d.ts +44 -0
- package/dist/server-components/islands.d.ts.map +1 -0
- package/dist/server-components/islands.js +42 -0
- package/dist/server-components/islands.js.map +1 -0
- package/dist/server-components/native.d.ts +4 -0
- package/dist/server-components/native.d.ts.map +1 -0
- package/dist/server-components/native.js +81 -0
- package/dist/server-components/native.js.map +1 -0
- package/dist/server-components/portal.d.ts +6 -0
- package/dist/server-components/portal.d.ts.map +1 -0
- package/dist/server-components/portal.js +35 -0
- package/dist/server-components/portal.js.map +1 -0
- package/dist/server-components/props.d.ts +3 -0
- package/dist/server-components/props.d.ts.map +1 -0
- package/dist/server-components/props.js +14 -0
- package/dist/server-components/props.js.map +1 -0
- package/dist/server-components/protocol.d.ts +44 -0
- package/dist/server-components/protocol.d.ts.map +1 -0
- package/dist/server-components/protocol.js +139 -0
- package/dist/server-components/protocol.js.map +1 -0
- package/dist/server-components/range.d.ts +24 -0
- package/dist/server-components/range.d.ts.map +1 -0
- package/dist/server-components/range.js +263 -0
- package/dist/server-components/range.js.map +1 -0
- package/dist/server-components/registry.d.ts +6 -0
- package/dist/server-components/registry.d.ts.map +1 -0
- package/dist/server-components/registry.js +30 -0
- package/dist/server-components/registry.js.map +1 -0
- package/dist/server-components/response.d.ts +4 -0
- package/dist/server-components/response.d.ts.map +1 -0
- package/dist/server-components/response.js +32 -0
- package/dist/server-components/response.js.map +1 -0
- package/dist/server-components/router-integration.d.ts +9 -0
- package/dist/server-components/router-integration.d.ts.map +1 -0
- package/dist/server-components/router-integration.js +29 -0
- package/dist/server-components/router-integration.js.map +1 -0
- package/dist/server-components/runtime.d.ts +14 -0
- package/dist/server-components/runtime.d.ts.map +1 -0
- package/dist/server-components/runtime.js +276 -0
- package/dist/server-components/runtime.js.map +1 -0
- package/dist/server-components/scheduler.d.ts +14 -0
- package/dist/server-components/scheduler.d.ts.map +1 -0
- package/dist/server-components/scheduler.js +74 -0
- package/dist/server-components/scheduler.js.map +1 -0
- package/dist/server-components/schema.d.ts +4 -0
- package/dist/server-components/schema.d.ts.map +1 -0
- package/dist/server-components/schema.js +77 -0
- package/dist/server-components/schema.js.map +1 -0
- package/dist/server-components/server-entry.d.ts +12 -0
- package/dist/server-components/server-entry.d.ts.map +1 -0
- package/dist/server-components/server-entry.js +90 -0
- package/dist/server-components/server-entry.js.map +1 -0
- package/dist/server-components/source.d.ts +15 -0
- package/dist/server-components/source.d.ts.map +1 -0
- package/dist/server-components/source.js +269 -0
- package/dist/server-components/source.js.map +1 -0
- package/dist/server-components/state.d.ts +4 -0
- package/dist/server-components/state.d.ts.map +1 -0
- package/dist/server-components/state.js +123 -0
- package/dist/server-components/state.js.map +1 -0
- package/dist/server-components/stream.d.ts +6 -0
- package/dist/server-components/stream.d.ts.map +1 -0
- package/dist/server-components/stream.js +70 -0
- package/dist/server-components/stream.js.map +1 -0
- package/dist/server-components/trusted-html.d.ts +3 -0
- package/dist/server-components/trusted-html.d.ts.map +1 -0
- package/dist/server-components/trusted-html.js +14 -0
- package/dist/server-components/trusted-html.js.map +1 -0
- package/dist/server-components/types.d.ts +107 -0
- package/dist/server-components/types.d.ts.map +1 -0
- package/dist/server-components/types.js +2 -0
- package/dist/server-components/types.js.map +1 -0
- package/dist/server-components/typescript.d.ts +11 -0
- package/dist/server-components/typescript.d.ts.map +1 -0
- package/dist/server-components/typescript.js +28 -0
- package/dist/server-components/typescript.js.map +1 -0
- package/dist/server-components/value.d.ts +5 -0
- package/dist/server-components/value.d.ts.map +1 -0
- package/dist/server-components/value.js +77 -0
- package/dist/server-components/value.js.map +1 -0
- package/dist/server-components/vite.d.ts +12 -0
- package/dist/server-components/vite.d.ts.map +1 -0
- package/dist/server-components/vite.js +197 -0
- package/dist/server-components/vite.js.map +1 -0
- package/dist/server-components.d.ts +5 -0
- package/dist/server-components.d.ts.map +1 -0
- package/dist/server-components.js +4 -0
- package/dist/server-components.js.map +1 -0
- package/dist/service-worker.d.ts +8 -1
- package/dist/service-worker.d.ts.map +1 -1
- package/dist/service-worker.js +20 -6
- package/dist/service-worker.js.map +1 -1
- package/dist/vite.d.ts +12 -0
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +7 -1
- package/dist/vite.js.map +1 -1
- package/dist/zones.d.ts +64 -2
- package/dist/zones.d.ts.map +1 -1
- package/dist/zones.js +331 -28
- package/dist/zones.js.map +1 -1
- package/package.json +102 -4
- package/src/build-vite.ts +31 -9
- package/src/build.ts +109 -31
- package/src/cdn.ts +18 -12
- package/src/client.ts +48 -17
- package/src/deferred.ts +27 -9
- package/src/dev-feed.ts +24 -5
- package/src/dev-port.ts +4 -10
- package/src/dev-session.ts +4 -13
- package/src/dev.ts +29 -1
- package/src/draft.ts +35 -14
- package/src/fonts-google.ts +10 -0
- package/src/index.ts +5 -6
- package/src/internal/codegen.ts +60 -29
- package/src/internal/disposable-dir.ts +24 -0
- package/src/internal/entry-chunks.ts +18 -0
- package/src/internal/evidence-traversal.ts +57 -0
- package/src/internal/head-merge.ts +16 -0
- package/src/internal/js-string.ts +6 -1
- package/src/internal/mount-shadow.ts +1 -1
- package/src/internal/output-guard.ts +88 -7
- package/src/internal/package-entries.ts +331 -0
- package/src/internal/page-execution.ts +573 -114
- package/src/internal/parity.ts +15 -2
- package/src/internal/private-env.ts +27 -13
- package/src/internal/progressive-cookies.ts +60 -0
- package/src/internal/public-env.ts +14 -0
- package/src/internal/render-document.ts +66 -89
- package/src/internal/response-controls.ts +31 -14
- package/src/internal/revive-dates.ts +35 -0
- package/src/internal/route-path.ts +24 -0
- package/src/internal/secret-scan.ts +190 -18
- package/src/internal/server-boundary.ts +29 -5
- package/src/internal/web-app.ts +126 -42
- package/src/internal/wire-date.ts +16 -0
- package/src/internal/without-query.ts +8 -0
- package/src/internal/zone-graph.ts +78 -25
- package/src/isr.ts +364 -45
- package/src/llms-txt.ts +32 -3
- package/src/manifest.ts +38 -4
- package/src/open-resolved.ts +64 -0
- package/src/plugins/kit.ts +31 -11
- package/src/plugins/vite-leak-guard.ts +104 -8
- package/src/plugins/vite-zone-guard.ts +21 -19
- package/src/plugins/zone-guard.ts +41 -2
- package/src/prerender.ts +39 -18
- package/src/public-dir.ts +27 -8
- package/src/query.ts +107 -26
- package/src/render-seam.ts +30 -2
- package/src/route-types.ts +34 -7
- package/src/router.ts +66 -59
- package/src/seo.ts +68 -0
- package/src/server-components/build-info.ts +11 -0
- package/src/server-components/build-target.ts +194 -0
- package/src/server-components/build.ts +419 -0
- package/src/server-components/bundle-proof.ts +33 -0
- package/src/server-components/client.ts +663 -0
- package/src/server-components/definition.ts +125 -0
- package/src/server-components/dev-http.ts +75 -0
- package/src/server-components/error.ts +24 -0
- package/src/server-components/frames.ts +181 -0
- package/src/server-components/html-policy.ts +94 -0
- package/src/server-components/html.ts +81 -0
- package/src/server-components/islands.ts +111 -0
- package/src/server-components/native.ts +101 -0
- package/src/server-components/portal.ts +42 -0
- package/src/server-components/props.ts +19 -0
- package/src/server-components/protocol.ts +167 -0
- package/src/server-components/range.ts +313 -0
- package/src/server-components/registry.ts +34 -0
- package/src/server-components/response.ts +44 -0
- package/src/server-components/router-integration.ts +40 -0
- package/src/server-components/runtime.ts +376 -0
- package/src/server-components/scheduler.ts +84 -0
- package/src/server-components/schema.ts +69 -0
- package/src/server-components/server-entry.ts +98 -0
- package/src/server-components/source.ts +324 -0
- package/src/server-components/state.ts +143 -0
- package/src/server-components/stream.ts +71 -0
- package/src/server-components/trusted-html.ts +21 -0
- package/src/server-components/types.ts +138 -0
- package/src/server-components/typescript.ts +39 -0
- package/src/server-components/value.ts +82 -0
- package/src/server-components/vite.ts +242 -0
- package/src/server-components.ts +32 -0
- package/src/service-worker.ts +24 -7
- package/src/vite.ts +8 -1
- package/src/zones.ts +418 -32
|
@@ -3,7 +3,12 @@ import {
|
|
|
3
3
|
NIFRA_BACKEND_BIND_PLATFORM,
|
|
4
4
|
NIFRA_PLATFORM_CLIENT_IP_DERIVED,
|
|
5
5
|
} from "@nifrajs/core/mount"
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
isSameOriginForm,
|
|
8
|
+
type Platform,
|
|
9
|
+
type ResponseResult,
|
|
10
|
+
status as statusResult,
|
|
11
|
+
} from "@nifrajs/core/server"
|
|
7
12
|
import {
|
|
8
13
|
type BoundaryRequestCtx,
|
|
9
14
|
type BoundaryStates,
|
|
@@ -40,7 +45,7 @@ import {
|
|
|
40
45
|
STATUS_HEADER,
|
|
41
46
|
} from "../router.ts"
|
|
42
47
|
import { searchOf, searchOfChain } from "../search.ts"
|
|
43
|
-
import { mergeHeads,
|
|
48
|
+
import { mergeHeads, resolveChainMeta } from "./head-merge.ts"
|
|
44
49
|
import {
|
|
45
50
|
EMPTY_RETAIN,
|
|
46
51
|
type HeadersLike,
|
|
@@ -78,6 +83,7 @@ export type { NonceResolver } from "../nonce.ts"
|
|
|
78
83
|
|
|
79
84
|
/** Structural context supplied by the core server to one page route handler. */
|
|
80
85
|
export interface PageRouteContext<Env = unknown> {
|
|
86
|
+
readonly signal?: AbortSignal
|
|
81
87
|
readonly params: Record<string, string>
|
|
82
88
|
readonly req: Request
|
|
83
89
|
readonly env: Env
|
|
@@ -166,12 +172,19 @@ function appTarget(location: string, requestUrl: string): string {
|
|
|
166
172
|
} catch {
|
|
167
173
|
return location // the browser fails on it the same way when it loads it
|
|
168
174
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
175
|
+
if (target.origin !== new URL(requestUrl).origin) return location
|
|
176
|
+
// A path starting `//` (from `/.//host`) is a protocol-relative URL to whoever resolves it next.
|
|
177
|
+
return target.pathname.startsWith("//")
|
|
178
|
+
? target.href
|
|
179
|
+
: target.pathname + target.search + target.hash
|
|
172
180
|
}
|
|
173
181
|
|
|
174
182
|
export interface PageExecutionOptions<Env = unknown> {
|
|
183
|
+
readonly serverComponents?: import("../server-components/types.ts").ServerComponentRuntime
|
|
184
|
+
readonly rerenderOwnerGET?: (
|
|
185
|
+
request: Request,
|
|
186
|
+
context: PageRouteContext<Env>,
|
|
187
|
+
) => Promise<Response>
|
|
175
188
|
readonly adapter: RenderAdapter
|
|
176
189
|
readonly manifest: Manifest
|
|
177
190
|
readonly clientEntry: string
|
|
@@ -201,6 +214,9 @@ export interface PageExecutionOptions<Env = unknown> {
|
|
|
201
214
|
readonly route: string
|
|
202
215
|
},
|
|
203
216
|
) => void
|
|
217
|
+
/** Logs a failure an `_error` boundary answered. Without a boundary the error propagates and the
|
|
218
|
+
* server logs it, so this runs only where the server never sees the throw. */
|
|
219
|
+
readonly logBoundaryError?: (error: unknown, request: Request) => void
|
|
204
220
|
}
|
|
205
221
|
|
|
206
222
|
type PageResponse = Response | ResponseResult | RenderedPage
|
|
@@ -238,6 +254,24 @@ type LayoutRun = {
|
|
|
238
254
|
readonly pending: Promise<unknown> | undefined
|
|
239
255
|
}
|
|
240
256
|
|
|
257
|
+
/** A route's layouts after only their gates ran: `gates[i]` is gate `i`'s value. */
|
|
258
|
+
type GatedLayouts = {
|
|
259
|
+
readonly modules: LoadedLayoutModules
|
|
260
|
+
readonly gates: readonly unknown[]
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/** Layouts whose loaders settled for this request: what a status or error page renders in. */
|
|
264
|
+
type LayoutChain = {
|
|
265
|
+
readonly modules: LoadedLayoutModules
|
|
266
|
+
readonly layoutData: readonly unknown[] | undefined
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Not called for a page that exports `inLayouts = false`, so such a page runs no layout loader.
|
|
271
|
+
* `undefined` renders the page alone.
|
|
272
|
+
*/
|
|
273
|
+
type LayoutChainSource = (page: RouteModule, count: number) => Promise<LayoutChain | undefined>
|
|
274
|
+
|
|
241
275
|
/**
|
|
242
276
|
* Own the page request program behind one private seam.
|
|
243
277
|
*
|
|
@@ -251,6 +285,8 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
251
285
|
options: PageExecutionOptions<Env>,
|
|
252
286
|
): PageRequestExecutor<Env> {
|
|
253
287
|
const { adapter, manifest, clientEntry, api } = options
|
|
288
|
+
// The action result is request-local; a browser cannot manufacture this handoff.
|
|
289
|
+
const progressiveActionData = new WeakMap<Request, unknown>()
|
|
254
290
|
// Bound once per render, so every loader, action and boundary in that render shares one view of the
|
|
255
291
|
// in-process client carrying the visitor's platform. An `api` without the seam passes through as-is.
|
|
256
292
|
const bindApi = platformBinderOf(api)
|
|
@@ -327,9 +363,9 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
327
363
|
): string | undefined | Promise<string | undefined> =>
|
|
328
364
|
options.nonce === undefined ? undefined : options.nonce({ request, env })
|
|
329
365
|
|
|
330
|
-
const
|
|
331
|
-
|
|
332
|
-
|
|
366
|
+
const stylesFrom = (
|
|
367
|
+
styles: readonly string[] | undefined,
|
|
368
|
+
): { styles?: readonly string[]; cssLoading?: CssLoadingMode } => {
|
|
333
369
|
if (styles === undefined || styles.length === 0) return {}
|
|
334
370
|
return {
|
|
335
371
|
styles,
|
|
@@ -337,6 +373,9 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
337
373
|
}
|
|
338
374
|
}
|
|
339
375
|
|
|
376
|
+
const stylesOf = (id: string): { styles?: readonly string[]; cssLoading?: CssLoadingMode } =>
|
|
377
|
+
stylesFrom(options.routeStyles?.[id] ?? options.styles)
|
|
378
|
+
|
|
340
379
|
// Load a route's layout modules once for the current execution. The render path needs each whole
|
|
341
380
|
// module for its component + meta, while the data path also needs loader/gate/boundary declarations.
|
|
342
381
|
const loadLayoutModules = async (route: RouteEntry): Promise<LoadedLayoutModules> => {
|
|
@@ -361,15 +400,60 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
361
400
|
pending: undefined,
|
|
362
401
|
}
|
|
363
402
|
|
|
364
|
-
/**
|
|
403
|
+
/**
|
|
404
|
+
* Await one gate. A returned redirect, status or Response refuses the request exactly as a thrown
|
|
405
|
+
* one does: stored as layout data it would let the page loader and the action run.
|
|
406
|
+
*/
|
|
407
|
+
const runGate = async (
|
|
408
|
+
route: RouteEntry,
|
|
409
|
+
index: number,
|
|
410
|
+
loader: NonNullable<LoadedLayoutModules[number]["loader"]>,
|
|
411
|
+
ctx: LoaderContext,
|
|
412
|
+
): Promise<unknown> => {
|
|
413
|
+
let value: unknown
|
|
414
|
+
try {
|
|
415
|
+
value = await loader(ctx)
|
|
416
|
+
} catch (err) {
|
|
417
|
+
// biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: The loaded layout array is derived from these same manifest IDs in the same order.
|
|
418
|
+
throw tagLayoutError(err, route.layoutIds[index] as string)
|
|
419
|
+
}
|
|
420
|
+
// biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: The loaded layout array is derived from these same manifest IDs in the same order.
|
|
421
|
+
if (isControlFlow(value)) throw tagLayoutError(value, route.layoutIds[index] as string)
|
|
422
|
+
return value
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Development only. A layout loader that refuses access reads as a guard, and is not one: a client
|
|
426
|
+
// navigation can skip it and an action below it runs without it.
|
|
427
|
+
const warnsUngatedRefusal =
|
|
428
|
+
typeof process !== "undefined" && process.env?.NODE_ENV === "development"
|
|
429
|
+
const warnedUngated = new Set<string>()
|
|
430
|
+
const warnUngatedRefusal = (layoutId: string, outcome: unknown): void => {
|
|
431
|
+
if (warnedUngated.has(layoutId)) return
|
|
432
|
+
// notFound() and gone() say the record is missing, which a layout may decide without guarding.
|
|
433
|
+
const refusesAccess = isStatusSignal(outcome)
|
|
434
|
+
? outcome.status === 401 || outcome.status === 403
|
|
435
|
+
: isControlFlow(outcome)
|
|
436
|
+
if (!refusesAccess) return
|
|
437
|
+
warnedUngated.add(layoutId)
|
|
438
|
+
const file = manifest.layouts?.[layoutId]?.file ?? layoutId
|
|
439
|
+
console.warn(
|
|
440
|
+
`[nifra/web] the loader in "${file}" refused a request, but that layout is not a gate. A client navigation that keeps the layout on screen can skip its loader, and an action below it runs without it, so it does not protect the pages under it. Export \`gate = true\` from that file, or move the check into middleware. (Shown in development only.)`,
|
|
441
|
+
)
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Run layout loaders, awaiting gates before starting the page loader and retaining only safe data.
|
|
446
|
+
* `gated` carries gates that already ran for this request, so none runs twice.
|
|
447
|
+
*/
|
|
365
448
|
const runLayoutChain = async (
|
|
366
449
|
route: RouteEntry,
|
|
367
450
|
ctx: LoaderContext,
|
|
368
451
|
controls: PageResponseControls,
|
|
369
452
|
retain?: RetainContext,
|
|
453
|
+
gated?: GatedLayouts,
|
|
370
454
|
): Promise<LayoutRun> => {
|
|
371
455
|
if (route.layoutIds.length === 0) return emptyLayoutRun
|
|
372
|
-
const modules = await loadLayoutModules(route)
|
|
456
|
+
const modules = gated?.modules ?? (await loadLayoutModules(route))
|
|
373
457
|
if (!modules.some((m) => m.loader !== undefined)) {
|
|
374
458
|
return { modules, layoutData: undefined, retained: [], pending: undefined }
|
|
375
459
|
}
|
|
@@ -385,6 +469,7 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
385
469
|
try {
|
|
386
470
|
keep = keepsLayoutData(route, i, layout, ctx.params, retain)
|
|
387
471
|
} catch (err) {
|
|
472
|
+
// biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: The loaded layout array is derived from these same manifest IDs in the same order.
|
|
388
473
|
throw tagLayoutError(err, route.layoutIds[i] as string)
|
|
389
474
|
}
|
|
390
475
|
if (keep) {
|
|
@@ -398,11 +483,7 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
398
483
|
set: controls.scope(i),
|
|
399
484
|
}
|
|
400
485
|
if (modules[i]?.gate === true) {
|
|
401
|
-
|
|
402
|
-
results[i] = await loader(scoped)
|
|
403
|
-
} catch (err) {
|
|
404
|
-
throw tagLayoutError(err, route.layoutIds[i] as string)
|
|
405
|
-
}
|
|
486
|
+
results[i] = gated === undefined ? await runGate(route, i, loader, scoped) : gated.gates[i]
|
|
406
487
|
continue
|
|
407
488
|
}
|
|
408
489
|
const layoutId = route.layoutIds[i] as string
|
|
@@ -414,15 +495,25 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
414
495
|
if (value !== null && (typeof value === "object" || typeof value === "function")) {
|
|
415
496
|
pending.push(
|
|
416
497
|
(async () => {
|
|
498
|
+
let settled: unknown
|
|
417
499
|
try {
|
|
418
|
-
|
|
500
|
+
settled = await value
|
|
419
501
|
} catch (err) {
|
|
502
|
+
if (warnsUngatedRefusal) warnUngatedRefusal(layoutId, err)
|
|
420
503
|
throw tagLayoutError(err, layoutId)
|
|
421
504
|
}
|
|
505
|
+
// A returned redirect or Response refuses exactly as a thrown one: kept as layout data,
|
|
506
|
+
// it would ship beside the page loader's data.
|
|
507
|
+
if (isControlFlow(settled)) {
|
|
508
|
+
if (warnsUngatedRefusal) warnUngatedRefusal(layoutId, settled)
|
|
509
|
+
throw tagLayoutError(settled, layoutId)
|
|
510
|
+
}
|
|
511
|
+
results[index] = settled
|
|
422
512
|
})(),
|
|
423
513
|
)
|
|
424
514
|
} else results[index] = value
|
|
425
515
|
} catch (err) {
|
|
516
|
+
if (warnsUngatedRefusal) warnUngatedRefusal(layoutId, err)
|
|
426
517
|
// Preserve a synchronous throw as a pending layout failure, just like the async lane.
|
|
427
518
|
pending.push(Promise.reject(tagLayoutError(err, layoutId)))
|
|
428
519
|
}
|
|
@@ -441,22 +532,19 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
441
532
|
route: RouteEntry,
|
|
442
533
|
ctx: LoaderContext,
|
|
443
534
|
controls: PageResponseControls,
|
|
444
|
-
): Promise<
|
|
535
|
+
): Promise<GatedLayouts> => {
|
|
445
536
|
const modules = await loadLayoutModules(route)
|
|
537
|
+
const gates: unknown[] = []
|
|
446
538
|
for (let i = 0; i < modules.length; i++) {
|
|
447
539
|
const mod = modules[i]
|
|
448
540
|
if (mod?.gate !== true || mod.loader === undefined) continue
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
})
|
|
455
|
-
} catch (err) {
|
|
456
|
-
throw tagLayoutError(err, route.layoutIds[i] as string)
|
|
457
|
-
}
|
|
541
|
+
gates[i] = await runGate(route, i, mod.loader, {
|
|
542
|
+
...ctx,
|
|
543
|
+
params: scopeParams(ctx.params, route.layoutParams?.[i]),
|
|
544
|
+
set: controls.scope(i),
|
|
545
|
+
})
|
|
458
546
|
}
|
|
459
|
-
return modules
|
|
547
|
+
return { modules, gates }
|
|
460
548
|
}
|
|
461
549
|
|
|
462
550
|
/** Outermost first; a failure carries its directory so the nearest `_error` above it renders. */
|
|
@@ -594,16 +682,14 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
594
682
|
|
|
595
683
|
const resolveChainAndHead = (
|
|
596
684
|
layoutModules: LoadedLayoutModules,
|
|
685
|
+
layoutData: readonly unknown[] | undefined,
|
|
597
686
|
page: RouteModule,
|
|
598
687
|
metaArgs: MetaArgs,
|
|
599
688
|
leaf: unknown = page.default,
|
|
600
689
|
): { chain: unknown[]; head: Meta } => {
|
|
601
690
|
const chain = [...layoutModules.map((m) => m.default), leaf]
|
|
602
|
-
const
|
|
603
|
-
|
|
604
|
-
resolveMeta(page.meta, metaArgs),
|
|
605
|
-
]
|
|
606
|
-
return { chain, head: mergeHeads(heads) }
|
|
691
|
+
const metas = [...layoutModules.map((m) => m.meta), page.meta]
|
|
692
|
+
return { chain, head: mergeHeads(resolveChainMeta(metas, layoutData, metaArgs)) }
|
|
607
693
|
}
|
|
608
694
|
|
|
609
695
|
/** What `useMatches` reports for a chain: the layouts' ids and `handle`s, then the page's. */
|
|
@@ -649,49 +735,160 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
649
735
|
}
|
|
650
736
|
}
|
|
651
737
|
|
|
738
|
+
/** What a status or error page's `meta` receives: no route params, and its own data. */
|
|
739
|
+
const statusMetaArgs = (req: Request, data: unknown, nonce: string | undefined): MetaArgs =>
|
|
740
|
+
nonce === undefined
|
|
741
|
+
? { data, pageData: data, params: {}, origin: originOf(req) }
|
|
742
|
+
: { data, pageData: data, params: {}, origin: originOf(req), nonce }
|
|
743
|
+
|
|
652
744
|
const renderError = async (
|
|
745
|
+
req: Request,
|
|
653
746
|
route: RouteEntry,
|
|
654
747
|
errorId: string,
|
|
655
748
|
err: unknown,
|
|
656
749
|
personalized: boolean,
|
|
657
750
|
nonce?: string,
|
|
751
|
+
layouts?: LayoutChainSource,
|
|
658
752
|
): Promise<PageResponse> => {
|
|
659
753
|
const errDir = dirOfId(errorId, "_error")
|
|
660
754
|
const keptLayoutIds = route.layoutIds.filter((id) => {
|
|
661
755
|
const ld = dirOfId(id, "_layout")
|
|
662
756
|
return ld === "" || ld === errDir || errDir.startsWith(`${ld}/`)
|
|
663
757
|
})
|
|
664
|
-
const
|
|
758
|
+
const errorModule = await (manifest.errors?.[errorId] as LayoutEntry).load()
|
|
759
|
+
const e = err instanceof Error ? err : new Error(String(err))
|
|
760
|
+
// An error's text can carry a connection string or a query, so only a run that says it is
|
|
761
|
+
// development or test shows it; an unset NODE_ENV (an unbuilt `bun app.ts`) counts as production.
|
|
762
|
+
const nodeEnv = typeof process === "undefined" ? undefined : process.env.NODE_ENV
|
|
763
|
+
const data =
|
|
764
|
+
nodeEnv === "development" || nodeEnv === "test"
|
|
765
|
+
? { name: e.name, message: e.message }
|
|
766
|
+
: { name: "Error", message: "Internal Server Error" }
|
|
767
|
+
const inLayouts = errorModule.inLayouts !== false
|
|
768
|
+
const chain =
|
|
769
|
+
inLayouts && layouts !== undefined && keptLayoutIds.length > 0
|
|
770
|
+
? await layouts(errorModule, keptLayoutIds.length)
|
|
771
|
+
: undefined
|
|
772
|
+
const render = (components: unknown[], wrapped?: { head: Meta; chain: LayoutChain }) => {
|
|
773
|
+
const layoutData = wrapped?.chain.layoutData
|
|
774
|
+
return renderPageResult({
|
|
775
|
+
adapter,
|
|
776
|
+
chain: components,
|
|
777
|
+
data,
|
|
778
|
+
...(wrapped === undefined ? {} : { head: wrapped.head }),
|
|
779
|
+
clientEntry,
|
|
780
|
+
routeId: errorId,
|
|
781
|
+
status: 500,
|
|
782
|
+
hydrate: false,
|
|
783
|
+
...(layoutData === undefined ? {} : { layoutData }),
|
|
784
|
+
...(personalized || layoutData !== undefined ? { headers: PRIVATE_PAGE_HEADERS } : {}),
|
|
785
|
+
...(nonce === undefined ? {} : { nonce }),
|
|
786
|
+
...cspOption,
|
|
787
|
+
...titleOption,
|
|
788
|
+
})
|
|
789
|
+
}
|
|
790
|
+
if (chain !== undefined) {
|
|
791
|
+
const resolved = resolveChainAndHead(
|
|
792
|
+
chain.modules,
|
|
793
|
+
chain.layoutData,
|
|
794
|
+
errorModule,
|
|
795
|
+
statusMetaArgs(req, data, nonce),
|
|
796
|
+
)
|
|
797
|
+
try {
|
|
798
|
+
return await render(resolved.chain, { head: resolved.head, chain })
|
|
799
|
+
} catch {
|
|
800
|
+
// A layout that fails to render leaves the error page alone, never a second error.
|
|
801
|
+
return render([errorModule.default])
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
// Layouts whose data did not settle still frame the error page, without their data.
|
|
805
|
+
const layoutComponents = await Promise.all(
|
|
665
806
|
keptLayoutIds.map((id) =>
|
|
666
807
|
(manifest.layouts[id] as LayoutEntry).load().then((m) => m.default),
|
|
667
808
|
),
|
|
668
809
|
)
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
hydrate: false,
|
|
683
|
-
...(personalized ? { headers: PRIVATE_PAGE_HEADERS } : {}),
|
|
684
|
-
...(nonce === undefined ? {} : { nonce }),
|
|
685
|
-
...cspOption,
|
|
686
|
-
...titleOption,
|
|
687
|
-
})
|
|
810
|
+
return render([...layoutComponents, errorModule.default])
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
/** The leading `count` layouts of `modules`, unless one of them answered with a status or redirect. */
|
|
814
|
+
const settledLayouts = (
|
|
815
|
+
modules: LoadedLayoutModules,
|
|
816
|
+
layoutData: readonly unknown[] | undefined,
|
|
817
|
+
count: number,
|
|
818
|
+
): LayoutChain | undefined => {
|
|
819
|
+
if (modules.length < count) return undefined
|
|
820
|
+
const data = layoutData?.slice(0, count)
|
|
821
|
+
if (data?.some((value) => isControlFlow(value) || isStatusSignal(value))) return undefined
|
|
822
|
+
return { modules: modules.slice(0, count), layoutData: data }
|
|
688
823
|
}
|
|
689
824
|
|
|
825
|
+
/** A run's leading layouts once all of its loaders settle - never when a layout or middleware refused. */
|
|
826
|
+
const leadingLayouts =
|
|
827
|
+
(run: LayoutRun | undefined, err?: unknown): LayoutChainSource =>
|
|
828
|
+
async (_page, count) => {
|
|
829
|
+
if (run === undefined || (err !== undefined && layoutErrorId(err) !== undefined)) {
|
|
830
|
+
return undefined
|
|
831
|
+
}
|
|
832
|
+
try {
|
|
833
|
+
await run.pending
|
|
834
|
+
} catch {
|
|
835
|
+
return undefined
|
|
836
|
+
}
|
|
837
|
+
return settledLayouts(run.modules, run.layoutData, count)
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
const rootLayoutIds: readonly string[] = manifest.layouts._layout === undefined ? [] : ["_layout"]
|
|
841
|
+
const rootMiddlewareIds: readonly string[] =
|
|
842
|
+
manifest.middlewares?._middleware === undefined ? [] : ["_middleware"]
|
|
843
|
+
/**
|
|
844
|
+
* Run the root middleware and layouts for a request no route ran them for (an unmatched URL), as a
|
|
845
|
+
* nested `_404` runs its own. Any refusal or failure renders the page alone.
|
|
846
|
+
*/
|
|
847
|
+
const rootLayoutsFor =
|
|
848
|
+
(c: PageRouteContext<Env>, controls: PageResponseControls): LayoutChainSource =>
|
|
849
|
+
async (page) => {
|
|
850
|
+
const entry = manifest.statusPages?.["404"] ?? manifest.notFound
|
|
851
|
+
if (rootLayoutIds.length === 0 || entry === undefined) return undefined
|
|
852
|
+
const layoutsOnly: RouteEntry = {
|
|
853
|
+
id: "_404",
|
|
854
|
+
pattern: "/*",
|
|
855
|
+
layoutIds: rootLayoutIds,
|
|
856
|
+
errorIds: [],
|
|
857
|
+
file: entry.file,
|
|
858
|
+
load: entry.load,
|
|
859
|
+
}
|
|
860
|
+
const route: RouteEntry =
|
|
861
|
+
rootMiddlewareIds.length === 0
|
|
862
|
+
? layoutsOnly
|
|
863
|
+
: { ...layoutsOnly, middlewareIds: rootMiddlewareIds }
|
|
864
|
+
try {
|
|
865
|
+
const draft = await draftFlag(c.req)
|
|
866
|
+
if (draft) controls.personalize()
|
|
867
|
+
const ctx: LoaderContext = {
|
|
868
|
+
params: {},
|
|
869
|
+
request: c.req,
|
|
870
|
+
req: c.req,
|
|
871
|
+
api: apiFor(c),
|
|
872
|
+
env: c.env,
|
|
873
|
+
draft,
|
|
874
|
+
search: loaderSearch(page.searchSchema, c.req),
|
|
875
|
+
set: controls.scope(PAGE_SCOPE),
|
|
876
|
+
}
|
|
877
|
+
if (route.middlewareIds !== undefined) await runMiddleware(route, ctx, controls)
|
|
878
|
+
const run = await runLayoutChain(route, ctx, controls)
|
|
879
|
+
await run.pending
|
|
880
|
+
return settledLayouts(run.modules, run.layoutData, rootLayoutIds.length)
|
|
881
|
+
} catch {
|
|
882
|
+
return undefined
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
|
|
690
886
|
const renderTerminalStatus = async (
|
|
691
887
|
status: number,
|
|
692
|
-
|
|
888
|
+
req: Request,
|
|
693
889
|
extraHeaders?: HeadersLike,
|
|
694
890
|
nonce?: string,
|
|
891
|
+
layouts?: LayoutChainSource,
|
|
695
892
|
): Promise<PageResponse> => {
|
|
696
893
|
const page = manifest.statusPages?.[String(status)] ?? manifest.notFound
|
|
697
894
|
if (page === undefined) {
|
|
@@ -700,36 +897,57 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
700
897
|
return new Response(STATUS_TEXT[status] ?? "Error", { status, headers })
|
|
701
898
|
}
|
|
702
899
|
const routeId = manifest.statusPages?.[String(status)] !== undefined ? `_${status}` : "_404"
|
|
703
|
-
const loaded =
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
900
|
+
const loaded = await page.load()
|
|
901
|
+
const wraps = loaded.inLayouts !== false && rootLayoutIds.length > 0
|
|
902
|
+
const chain =
|
|
903
|
+
wraps && layouts !== undefined ? await layouts(loaded, rootLayoutIds.length) : undefined
|
|
904
|
+
const modules = chain?.modules ?? []
|
|
905
|
+
const layoutIds = chain === undefined ? [] : rootLayoutIds
|
|
906
|
+
const layoutData = chain?.layoutData
|
|
907
|
+
const { chain: components, head } = resolveChainAndHead(
|
|
908
|
+
modules,
|
|
909
|
+
layoutData,
|
|
910
|
+
loaded,
|
|
911
|
+
statusMetaArgs(req, null, nonce),
|
|
912
|
+
)
|
|
913
|
+
let headers = extraHeaders
|
|
914
|
+
if (layoutData !== undefined) {
|
|
915
|
+
// The page now shows what a layout loaded for this visitor.
|
|
916
|
+
const privateHeaders = new Headers(extraHeaders)
|
|
917
|
+
privateHeaders.set("cache-control", PRIVATE_NO_STORE)
|
|
918
|
+
headers = privateHeaders
|
|
707
919
|
}
|
|
708
|
-
|
|
920
|
+
const statusPage = {
|
|
709
921
|
adapter,
|
|
710
|
-
chain:
|
|
922
|
+
chain: components,
|
|
711
923
|
data: null,
|
|
924
|
+
head,
|
|
712
925
|
clientEntry,
|
|
713
926
|
routeId,
|
|
714
|
-
matchChain:
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
search: searchOf(
|
|
718
|
-
loaded.searchSchema,
|
|
719
|
-
path.includes("?") ? path.slice(path.indexOf("?")) : "",
|
|
720
|
-
),
|
|
721
|
-
}
|
|
722
|
-
: {}),
|
|
723
|
-
...(path !== undefined ? { path } : {}),
|
|
927
|
+
matchChain: matchChainOf(layoutIds, modules, routeId, loaded),
|
|
928
|
+
search: searchChainOf(modules, loaded, req),
|
|
929
|
+
path: pathOf(req),
|
|
724
930
|
status,
|
|
725
|
-
|
|
931
|
+
// The browser renders this page inside the root layout, so a page that had to render alone
|
|
932
|
+
// stays server-only, as a nested `_404` does.
|
|
933
|
+
...(wraps && chain === undefined ? { hydrate: false } : {}),
|
|
934
|
+
...(layoutData === undefined ? {} : { layoutData }),
|
|
935
|
+
...(headers !== undefined ? { headers } : {}),
|
|
726
936
|
...(nonce === undefined ? {} : { nonce }),
|
|
727
937
|
...cspOption,
|
|
728
938
|
...preloadOf(routeId),
|
|
729
|
-
|
|
939
|
+
// The build keys stylesheets by route, so a page inside the root layout links the whole-app one.
|
|
940
|
+
...(chain === undefined ? stylesOf(routeId) : stylesFrom(options.styles)),
|
|
730
941
|
...prerenderedHandover,
|
|
731
942
|
...titleOption,
|
|
732
|
-
}
|
|
943
|
+
}
|
|
944
|
+
if (chain === undefined) return renderPageResult(statusPage)
|
|
945
|
+
try {
|
|
946
|
+
return await renderPageResult(statusPage)
|
|
947
|
+
} catch {
|
|
948
|
+
// A layout that fails to render leaves the status page alone, never a second error.
|
|
949
|
+
return renderTerminalStatus(status, req, extraHeaders, nonce)
|
|
950
|
+
}
|
|
733
951
|
}
|
|
734
952
|
|
|
735
953
|
const renderStatusSignal = async (
|
|
@@ -737,6 +955,7 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
737
955
|
env: Env,
|
|
738
956
|
signal: StatusSignal,
|
|
739
957
|
personalized: boolean,
|
|
958
|
+
layouts?: LayoutChainSource,
|
|
740
959
|
): Promise<PageResponse> => {
|
|
741
960
|
const { status, headers } = signal[STATUS_SIGNAL]
|
|
742
961
|
if (req.headers.get(DATA_HEADER) !== null) {
|
|
@@ -750,7 +969,7 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
750
969
|
}
|
|
751
970
|
const nonce = await resolveNonce(req, env)
|
|
752
971
|
if (headers === undefined && !personalized) {
|
|
753
|
-
return renderTerminalStatus(status,
|
|
972
|
+
return renderTerminalStatus(status, req, undefined, nonce, layouts)
|
|
754
973
|
}
|
|
755
974
|
const documentHeaders = new Headers(headers)
|
|
756
975
|
if (headers !== undefined) {
|
|
@@ -758,7 +977,7 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
758
977
|
}
|
|
759
978
|
// A cookie queued before the signal makes the status page specific to this visitor.
|
|
760
979
|
if (personalized) documentHeaders.set("cache-control", PRIVATE_NO_STORE)
|
|
761
|
-
return renderTerminalStatus(status,
|
|
980
|
+
return renderTerminalStatus(status, req, documentHeaders, nonce, layouts)
|
|
762
981
|
}
|
|
763
982
|
|
|
764
983
|
const reportLoaderError = (
|
|
@@ -789,8 +1008,12 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
789
1008
|
assemblyCache: RenderAssemblyCache | undefined,
|
|
790
1009
|
responseHeaders: Record<string, string> | undefined,
|
|
791
1010
|
personalized: boolean,
|
|
1011
|
+
serverComponents?: import("../server-components/types.ts").ComponentState,
|
|
792
1012
|
): Promise<PageResponse> => {
|
|
793
|
-
|
|
1013
|
+
// Every layout loader settled before the page rendered, so a status or error page can reuse them.
|
|
1014
|
+
const settled: LayoutChainSource = async (_page, count) =>
|
|
1015
|
+
settledLayouts(layoutModules, layoutData, count)
|
|
1016
|
+
if (isStatusSignal(data)) return renderStatusSignal(req, env, data, personalized, settled)
|
|
794
1017
|
if (isControlFlow(data)) return data
|
|
795
1018
|
if (req.headers.get(DATA_HEADER) !== null) {
|
|
796
1019
|
const pageSplit = prepareDeferred(data)
|
|
@@ -809,7 +1032,7 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
809
1032
|
...(boundarySplit === undefined ? [] : boundarySplit.deferred),
|
|
810
1033
|
]
|
|
811
1034
|
const payload =
|
|
812
|
-
layoutData === undefined && boundarySplit === undefined
|
|
1035
|
+
layoutData === undefined && boundarySplit === undefined && serverComponents === undefined
|
|
813
1036
|
? pageSplit.forClient
|
|
814
1037
|
: {
|
|
815
1038
|
v: 1 as const,
|
|
@@ -817,6 +1040,7 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
817
1040
|
layoutData: layoutSplits.map((split) => split.forClient),
|
|
818
1041
|
retained: layoutRetained,
|
|
819
1042
|
...(boundarySplit === undefined ? {} : { boundaries: boundarySplit.forClient }),
|
|
1043
|
+
...(serverComponents === undefined ? {} : { serverComponents }),
|
|
820
1044
|
}
|
|
821
1045
|
if (allDeferred.length === 0) {
|
|
822
1046
|
return Response.json(payload ?? null, { headers: DATA_RESPONSE_HEADERS })
|
|
@@ -832,9 +1056,11 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
832
1056
|
const nonce = options.nonce === undefined ? undefined : await resolveNonce(req, env)
|
|
833
1057
|
const { chain, head } = resolveChainAndHead(
|
|
834
1058
|
layoutModules,
|
|
1059
|
+
layoutData,
|
|
835
1060
|
mod,
|
|
836
1061
|
{
|
|
837
1062
|
data,
|
|
1063
|
+
pageData: data,
|
|
838
1064
|
params,
|
|
839
1065
|
origin: originOf(req),
|
|
840
1066
|
...(nonce === undefined ? {} : { nonce }),
|
|
@@ -853,6 +1079,8 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
853
1079
|
path: pathOf(req),
|
|
854
1080
|
search: searchChainOf(layoutModules, mod, req),
|
|
855
1081
|
matchChain: matchChainOf(route.layoutIds, layoutModules, route.id, mod),
|
|
1082
|
+
...(serverComponents === undefined ? {} : { serverComponents }),
|
|
1083
|
+
...(progressiveActionData.has(req) ? { actionData: progressiveActionData.get(req) } : {}),
|
|
856
1084
|
hydrate: mod.hydrate !== false,
|
|
857
1085
|
...(layoutData !== undefined ? { layoutData } : {}),
|
|
858
1086
|
...(boundaryStates !== undefined ? { boundaries: boundaryStates } : {}),
|
|
@@ -882,12 +1110,21 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
882
1110
|
...(assemblyCache === undefined ? {} : { assemblyCache }),
|
|
883
1111
|
})
|
|
884
1112
|
} catch (err) {
|
|
885
|
-
if (isStatusSignal(err)) return renderStatusSignal(req, env, err, personalized)
|
|
1113
|
+
if (isStatusSignal(err)) return renderStatusSignal(req, env, err, personalized, settled)
|
|
886
1114
|
if (isControlFlow(err)) throw err
|
|
887
1115
|
reportLoaderError(route, req, { ...params }, err)
|
|
888
1116
|
const errorId = boundaryFor(route, err)
|
|
889
1117
|
if (errorId === undefined) throw err
|
|
890
|
-
|
|
1118
|
+
options.logBoundaryError?.(err, req)
|
|
1119
|
+
return renderError(
|
|
1120
|
+
req,
|
|
1121
|
+
route,
|
|
1122
|
+
errorId,
|
|
1123
|
+
withDuplicateInstanceHint(err),
|
|
1124
|
+
personalized,
|
|
1125
|
+
nonce,
|
|
1126
|
+
settled,
|
|
1127
|
+
)
|
|
891
1128
|
}
|
|
892
1129
|
}
|
|
893
1130
|
|
|
@@ -899,11 +1136,13 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
899
1136
|
params: Record<string, string>,
|
|
900
1137
|
err: unknown,
|
|
901
1138
|
personalized: boolean,
|
|
1139
|
+
layouts?: LayoutChainSource,
|
|
902
1140
|
): Promise<PageResponse> => {
|
|
903
1141
|
if (isControlFlow(err)) throw err
|
|
904
1142
|
reportLoaderError(route, req, params, err)
|
|
905
1143
|
const errorId = boundaryFor(route, err)
|
|
906
1144
|
if (errorId === undefined) throw err
|
|
1145
|
+
options.logBoundaryError?.(err, req)
|
|
907
1146
|
if (req.headers.get(DATA_HEADER) !== null) {
|
|
908
1147
|
return new Response("Internal Server Error", {
|
|
909
1148
|
status: 500,
|
|
@@ -911,11 +1150,13 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
911
1150
|
})
|
|
912
1151
|
}
|
|
913
1152
|
return renderError(
|
|
1153
|
+
req,
|
|
914
1154
|
route,
|
|
915
1155
|
errorId,
|
|
916
1156
|
withDuplicateInstanceHint(err),
|
|
917
1157
|
personalized,
|
|
918
1158
|
options.nonce === undefined ? undefined : await resolveNonce(req, env),
|
|
1159
|
+
layouts,
|
|
919
1160
|
)
|
|
920
1161
|
}
|
|
921
1162
|
|
|
@@ -946,8 +1187,9 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
946
1187
|
}
|
|
947
1188
|
const mod = await page.load()
|
|
948
1189
|
const nonce = options.nonce === undefined ? undefined : await resolveNonce(req, env)
|
|
949
|
-
const { chain, head } = resolveChainAndHead(layoutModules, mod, {
|
|
1190
|
+
const { chain, head } = resolveChainAndHead(layoutModules, layoutData, mod, {
|
|
950
1191
|
data: null,
|
|
1192
|
+
pageData: null,
|
|
951
1193
|
params,
|
|
952
1194
|
origin: originOf(req),
|
|
953
1195
|
...(nonce === undefined ? {} : { nonce }),
|
|
@@ -1018,17 +1260,17 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1018
1260
|
}
|
|
1019
1261
|
|
|
1020
1262
|
const renderNotFound = async (
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
path?: string,
|
|
1263
|
+
c: PageRouteContext<Env>,
|
|
1264
|
+
controls: PageResponseControls,
|
|
1024
1265
|
): Promise<PageResponse> => {
|
|
1025
1266
|
const page = manifest.statusPages?.["404"] ?? manifest.notFound
|
|
1026
|
-
if (page === undefined) return renderTerminalStatus(404,
|
|
1267
|
+
if (page === undefined) return renderTerminalStatus(404, c.req)
|
|
1027
1268
|
return renderTerminalStatus(
|
|
1028
1269
|
404,
|
|
1029
|
-
|
|
1270
|
+
c.req,
|
|
1030
1271
|
undefined,
|
|
1031
|
-
options.nonce === undefined ? undefined : await resolveNonce(
|
|
1272
|
+
options.nonce === undefined ? undefined : await resolveNonce(c.req, c.env),
|
|
1273
|
+
rootLayoutsFor(c, controls),
|
|
1032
1274
|
)
|
|
1033
1275
|
}
|
|
1034
1276
|
|
|
@@ -1084,8 +1326,19 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1084
1326
|
}
|
|
1085
1327
|
|
|
1086
1328
|
return withResponseControls(async (c, controls) => {
|
|
1087
|
-
|
|
1088
|
-
|
|
1329
|
+
const componentRuntime = options.serverComponents
|
|
1330
|
+
const componentOwner = componentRuntime?.owns(route.id) === true
|
|
1331
|
+
let componentRequest:
|
|
1332
|
+
| import("../server-components/protocol.ts").ComponentRequest
|
|
1333
|
+
| undefined
|
|
1334
|
+
try {
|
|
1335
|
+
componentRequest = componentRuntime?.classify(c.req, route.id)
|
|
1336
|
+
} catch (error) {
|
|
1337
|
+
return componentRuntime!.failure(error)
|
|
1338
|
+
}
|
|
1339
|
+
if (componentOwner || componentRequest !== undefined) controls.personalize()
|
|
1340
|
+
if (!componentOwner && is404Fallback && !prerenderedSet.has(urlPartsFor(c.req).pathname)) {
|
|
1341
|
+
return renderNotFound(c, controls)
|
|
1089
1342
|
}
|
|
1090
1343
|
const mod = await route.load()
|
|
1091
1344
|
const draft = await draftFlag(c.req)
|
|
@@ -1097,6 +1350,7 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1097
1350
|
let layoutModules: LoadedLayoutModules | undefined
|
|
1098
1351
|
let layoutRetained: readonly number[] = []
|
|
1099
1352
|
let responseHeaders: Record<string, string> | undefined
|
|
1353
|
+
let componentState: import("../server-components/types.ts").ComponentState | undefined
|
|
1100
1354
|
const requestApi = apiFor(c)
|
|
1101
1355
|
let run: LayoutRun | undefined
|
|
1102
1356
|
try {
|
|
@@ -1111,13 +1365,21 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1111
1365
|
set: controls.scope(PAGE_SCOPE),
|
|
1112
1366
|
}
|
|
1113
1367
|
if (route.middlewareIds !== undefined) await runMiddleware(route, ctx, controls)
|
|
1114
|
-
run = await runLayoutChain(
|
|
1368
|
+
run = await runLayoutChain(
|
|
1369
|
+
route,
|
|
1370
|
+
ctx,
|
|
1371
|
+
controls,
|
|
1372
|
+
componentOwner ? undefined : retainContextOf(c.req),
|
|
1373
|
+
)
|
|
1115
1374
|
layoutModules = run.modules
|
|
1116
1375
|
layoutRetained = run.retained
|
|
1117
|
-
const boundaryDefinitions =
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1376
|
+
const boundaryDefinitions =
|
|
1377
|
+
componentRequest === undefined
|
|
1378
|
+
? [
|
|
1379
|
+
...run.modules.flatMap((layout) => layout.boundaries ?? []),
|
|
1380
|
+
...(mod.boundaries ?? []),
|
|
1381
|
+
]
|
|
1382
|
+
: []
|
|
1121
1383
|
if (boundaryDefinitions.length > 0) boundaryDescriptors(boundaryDefinitions)
|
|
1122
1384
|
const effectiveSearch = searchChainOf(run.modules, mod, c.req)
|
|
1123
1385
|
const boundaryBatch =
|
|
@@ -1149,6 +1411,34 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1149
1411
|
: await Promise.all([pageResult, run.pending, staticBoundaryPromise])
|
|
1150
1412
|
data = pageData
|
|
1151
1413
|
layoutData = run.layoutData
|
|
1414
|
+
if (componentOwner) {
|
|
1415
|
+
for (let i = 0; i < (layoutData?.length ?? 0); i++) {
|
|
1416
|
+
const outcome = layoutData?.[i]
|
|
1417
|
+
if (isControlFlow(outcome) || isStatusSignal(outcome))
|
|
1418
|
+
throw tagLayoutError(outcome, route.layoutIds[i] as string)
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
if (componentRequest !== undefined && (isControlFlow(data) || isStatusSignal(data)))
|
|
1422
|
+
return componentRuntime!.failure(data)
|
|
1423
|
+
if (componentOwner && !isControlFlow(data) && !isStatusSignal(data)) {
|
|
1424
|
+
const componentContext = Object.create(
|
|
1425
|
+
c,
|
|
1426
|
+
Object.getOwnPropertyDescriptors({
|
|
1427
|
+
...ctx,
|
|
1428
|
+
search: effectiveSearch,
|
|
1429
|
+
signal:
|
|
1430
|
+
c.signal === undefined ? c.req.signal : AbortSignal.any([c.signal, c.req.signal]),
|
|
1431
|
+
data,
|
|
1432
|
+
layoutData: layoutData ?? [],
|
|
1433
|
+
}),
|
|
1434
|
+
)
|
|
1435
|
+
componentState = await componentRuntime!.execute(
|
|
1436
|
+
route.id,
|
|
1437
|
+
componentContext,
|
|
1438
|
+
componentRequest,
|
|
1439
|
+
options.nonce === undefined ? undefined : await resolveNonce(c.req, c.env),
|
|
1440
|
+
)
|
|
1441
|
+
}
|
|
1152
1442
|
if (boundaryBatch !== undefined) {
|
|
1153
1443
|
const streamed = { ...boundaryBatch.initial, ...(staticBoundaries ?? {}) }
|
|
1154
1444
|
for (const entry of boundaryBatch.pending) {
|
|
@@ -1164,6 +1454,7 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1164
1454
|
// by the same boundary as one.
|
|
1165
1455
|
responseHeaders = controls.documentHeaders()
|
|
1166
1456
|
} catch (err) {
|
|
1457
|
+
if (componentRequest !== undefined) return componentRuntime!.failure(err)
|
|
1167
1458
|
if (isStatusSignal(err)) {
|
|
1168
1459
|
return (
|
|
1169
1460
|
(await renderRouteNotFound(
|
|
@@ -1174,10 +1465,27 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1174
1465
|
err,
|
|
1175
1466
|
run,
|
|
1176
1467
|
controls.personalized,
|
|
1177
|
-
)) ??
|
|
1468
|
+
)) ??
|
|
1469
|
+
renderStatusSignal(c.req, c.env, err, controls.personalized, leadingLayouts(run, err))
|
|
1178
1470
|
)
|
|
1179
1471
|
}
|
|
1180
|
-
return renderLoaderFailure(
|
|
1472
|
+
return renderLoaderFailure(
|
|
1473
|
+
route,
|
|
1474
|
+
c.req,
|
|
1475
|
+
c.env,
|
|
1476
|
+
c.params,
|
|
1477
|
+
err,
|
|
1478
|
+
controls.personalized,
|
|
1479
|
+
leadingLayouts(run, err),
|
|
1480
|
+
)
|
|
1481
|
+
}
|
|
1482
|
+
if (componentRequest !== undefined && componentState !== undefined) {
|
|
1483
|
+
const answer = componentRuntime!.response(componentState, componentRequest)
|
|
1484
|
+
const headers = new Headers(answer.headers)
|
|
1485
|
+
for (const [name, value] of Object.entries(responseHeaders ?? {}))
|
|
1486
|
+
if (name !== "cache-control" && name !== "content-type" && name !== "vary")
|
|
1487
|
+
headers.set(name, value)
|
|
1488
|
+
return new Response(answer.body, { status: answer.status, headers })
|
|
1181
1489
|
}
|
|
1182
1490
|
if (isStatusSignal(data)) {
|
|
1183
1491
|
const nested = await renderRouteNotFound(
|
|
@@ -1205,6 +1513,7 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1205
1513
|
layoutModules === undefined ? undefined : assemblyCacheFor(mod, layoutModules),
|
|
1206
1514
|
responseHeaders,
|
|
1207
1515
|
controls.personalized,
|
|
1516
|
+
componentState,
|
|
1208
1517
|
)
|
|
1209
1518
|
})
|
|
1210
1519
|
},
|
|
@@ -1220,6 +1529,32 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1220
1529
|
headers: { allow: "GET", "content-type": "text/plain; charset=utf-8" },
|
|
1221
1530
|
})
|
|
1222
1531
|
}
|
|
1532
|
+
const isDataRequest = c.req.headers.get(DATA_HEADER) !== null
|
|
1533
|
+
// Before anything runs: a form posts cross-site without a preflight and carries the visitor's
|
|
1534
|
+
// cookies, so an action is a CSRF target unless its route names the origins it accepts.
|
|
1535
|
+
const origin = c.req.headers.get("origin")
|
|
1536
|
+
const allowedOrigins = mod.allowedOrigins
|
|
1537
|
+
const allowed =
|
|
1538
|
+
allowedOrigins === undefined
|
|
1539
|
+
? origin === null ||
|
|
1540
|
+
isSameOriginForm(origin, c.req.headers.get("sec-fetch-site"), c.req)
|
|
1541
|
+
: typeof allowedOrigins === "function"
|
|
1542
|
+
? allowedOrigins(origin) === true
|
|
1543
|
+
: origin !== null && allowedOrigins.includes(origin)
|
|
1544
|
+
if (!allowed) {
|
|
1545
|
+
return isDataRequest
|
|
1546
|
+
? Response.json(
|
|
1547
|
+
{ ok: false, error: "forbidden_origin" },
|
|
1548
|
+
{ status: 403, headers: { "cache-control": PRIVATE_NO_STORE } },
|
|
1549
|
+
)
|
|
1550
|
+
: new Response("Forbidden", {
|
|
1551
|
+
status: 403,
|
|
1552
|
+
headers: {
|
|
1553
|
+
"content-type": "text/plain; charset=utf-8",
|
|
1554
|
+
"cache-control": PRIVATE_NO_STORE,
|
|
1555
|
+
},
|
|
1556
|
+
})
|
|
1557
|
+
}
|
|
1223
1558
|
const requestApi = apiFor(c)
|
|
1224
1559
|
const actionContext: LoaderContext = {
|
|
1225
1560
|
params: c.params,
|
|
@@ -1231,12 +1566,11 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1231
1566
|
search: loaderSearch(mod.searchSchema, c.req),
|
|
1232
1567
|
set: controls.scope(ACTION_SCOPE),
|
|
1233
1568
|
}
|
|
1234
|
-
|
|
1235
|
-
let layoutModules: LoadedLayoutModules
|
|
1569
|
+
let gated: GatedLayouts
|
|
1236
1570
|
let result: unknown
|
|
1237
1571
|
try {
|
|
1238
1572
|
if (route.middlewareIds !== undefined) await runMiddleware(route, actionContext, controls)
|
|
1239
|
-
|
|
1573
|
+
gated = await runLayoutGates(route, actionContext, controls)
|
|
1240
1574
|
result = await mod.action(actionContext)
|
|
1241
1575
|
} catch (err) {
|
|
1242
1576
|
if (isControlFlow(err)) return err
|
|
@@ -1271,26 +1605,136 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1271
1605
|
},
|
|
1272
1606
|
})
|
|
1273
1607
|
}
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1608
|
+
if (options.serverComponents?.owns(route.id)) {
|
|
1609
|
+
if (options.rerenderOwnerGET === undefined) throw new Error("component_contract")
|
|
1610
|
+
const actionHeaders = controls.documentHeaders()
|
|
1611
|
+
if (controls.progressiveRedirect(c.req)) {
|
|
1612
|
+
// Relative, so a browser behind a TLS terminator stays on its own scheme. A `//` path
|
|
1613
|
+
// would read as another host, so that one keeps the absolute URL.
|
|
1614
|
+
const { pathname, search } = new URL(c.req.url)
|
|
1615
|
+
const location = pathname.startsWith("//") ? c.req.url : pathname + search
|
|
1616
|
+
return new Response(null, {
|
|
1617
|
+
status: 303,
|
|
1618
|
+
headers: { ...actionHeaders, location, "cache-control": PRIVATE_NO_STORE },
|
|
1619
|
+
})
|
|
1620
|
+
}
|
|
1621
|
+
const headers = new Headers(c.req.headers)
|
|
1622
|
+
for (const name of [
|
|
1623
|
+
DATA_HEADER,
|
|
1624
|
+
RETAIN_HEADER,
|
|
1625
|
+
NAV_FROM_HEADER,
|
|
1626
|
+
"x-nifra-component",
|
|
1627
|
+
"content-type",
|
|
1628
|
+
"content-length",
|
|
1629
|
+
"transfer-encoding",
|
|
1630
|
+
])
|
|
1631
|
+
headers.delete(name)
|
|
1632
|
+
const cookie = controls.progressiveCookies(c.req)
|
|
1633
|
+
if (cookie) headers.set("cookie", cookie)
|
|
1634
|
+
else headers.delete("cookie")
|
|
1635
|
+
const request = new Request(c.req.url, {
|
|
1636
|
+
method: "GET",
|
|
1637
|
+
headers,
|
|
1638
|
+
signal:
|
|
1639
|
+
c.signal === undefined ? c.req.signal : AbortSignal.any([c.signal, c.req.signal]),
|
|
1640
|
+
})
|
|
1641
|
+
progressiveActionData.set(request, actionResult)
|
|
1642
|
+
try {
|
|
1643
|
+
const response = await options.rerenderOwnerGET(request, c)
|
|
1644
|
+
const merged = new Headers(response.headers)
|
|
1645
|
+
for (const [name, value] of Object.entries(actionHeaders ?? {}))
|
|
1646
|
+
if (!merged.has(name)) merged.set(name, value)
|
|
1647
|
+
merged.set("cache-control", PRIVATE_NO_STORE)
|
|
1648
|
+
return new Response(response.body, {
|
|
1649
|
+
status: response.status,
|
|
1650
|
+
statusText: response.statusText,
|
|
1651
|
+
headers: merged,
|
|
1285
1652
|
})
|
|
1286
|
-
|
|
1287
|
-
|
|
1653
|
+
} finally {
|
|
1654
|
+
progressiveActionData.delete(request)
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
const layoutModules = gated.modules
|
|
1658
|
+
const search = searchChainOf(layoutModules, mod, c.req)
|
|
1659
|
+
let run: LayoutRun | undefined
|
|
1660
|
+
let data: unknown
|
|
1661
|
+
let responseHeaders: Record<string, string> | undefined
|
|
1662
|
+
// The ordinary layout loaders run now, after the mutation, as a GET of this page would.
|
|
1663
|
+
try {
|
|
1664
|
+
run = await runLayoutChain(route, actionContext, controls, undefined, gated)
|
|
1665
|
+
const pageResult = mod.loader
|
|
1666
|
+
? mod.loader({
|
|
1667
|
+
params: c.params,
|
|
1668
|
+
request: c.req,
|
|
1669
|
+
req: c.req,
|
|
1670
|
+
api: requestApi,
|
|
1671
|
+
env: c.env,
|
|
1672
|
+
draft,
|
|
1673
|
+
search,
|
|
1674
|
+
set: controls.scope(PAGE_SCOPE),
|
|
1675
|
+
})
|
|
1676
|
+
: null
|
|
1677
|
+
const [pageData] =
|
|
1678
|
+
run.pending === undefined
|
|
1679
|
+
? ([await pageResult] as const)
|
|
1680
|
+
: await Promise.all([pageResult, run.pending])
|
|
1681
|
+
data = pageData
|
|
1682
|
+
responseHeaders = controls.documentHeaders()
|
|
1683
|
+
} catch (err) {
|
|
1684
|
+
if (isStatusSignal(err)) {
|
|
1685
|
+
return (
|
|
1686
|
+
(await renderRouteNotFound(
|
|
1687
|
+
c.req,
|
|
1688
|
+
c.env,
|
|
1689
|
+
route,
|
|
1690
|
+
c.params,
|
|
1691
|
+
err,
|
|
1692
|
+
run,
|
|
1693
|
+
controls.personalized,
|
|
1694
|
+
)) ??
|
|
1695
|
+
renderStatusSignal(c.req, c.env, err, controls.personalized, leadingLayouts(run, err))
|
|
1696
|
+
)
|
|
1697
|
+
}
|
|
1698
|
+
return renderLoaderFailure(
|
|
1699
|
+
route,
|
|
1700
|
+
c.req,
|
|
1701
|
+
c.env,
|
|
1702
|
+
c.params,
|
|
1703
|
+
err,
|
|
1704
|
+
controls.personalized,
|
|
1705
|
+
leadingLayouts(run, err),
|
|
1706
|
+
)
|
|
1707
|
+
}
|
|
1708
|
+
// The re-render answers a returned redirect or status the way the page's GET does.
|
|
1709
|
+
if (isStatusSignal(data))
|
|
1710
|
+
return (
|
|
1711
|
+
(await renderRouteNotFound(
|
|
1712
|
+
c.req,
|
|
1713
|
+
c.env,
|
|
1714
|
+
route,
|
|
1715
|
+
c.params,
|
|
1716
|
+
data,
|
|
1717
|
+
run,
|
|
1718
|
+
controls.personalized,
|
|
1719
|
+
)) ??
|
|
1720
|
+
renderStatusSignal(c.req, c.env, data, controls.personalized, leadingLayouts(run, data))
|
|
1721
|
+
)
|
|
1722
|
+
if (isControlFlow(data)) return data
|
|
1723
|
+
const layoutData = run.layoutData
|
|
1724
|
+
const layoutProps: { layoutData?: Exclude<typeof layoutData, undefined> } = {}
|
|
1725
|
+
if (layoutData !== undefined) {
|
|
1726
|
+
layoutProps.layoutData = layoutData
|
|
1727
|
+
// The page now shows what a layout loaded for this visitor.
|
|
1728
|
+
responseHeaders = { ...responseHeaders, ...PRIVATE_PAGE_HEADERS }
|
|
1729
|
+
}
|
|
1288
1730
|
const nonce = options.nonce === undefined ? undefined : await resolveNonce(c.req, c.env)
|
|
1289
1731
|
const { chain, head } = resolveChainAndHead(
|
|
1290
1732
|
layoutModules,
|
|
1733
|
+
layoutData,
|
|
1291
1734
|
mod,
|
|
1292
1735
|
{
|
|
1293
1736
|
data,
|
|
1737
|
+
pageData: data,
|
|
1294
1738
|
params: c.params,
|
|
1295
1739
|
origin: originOf(c.req),
|
|
1296
1740
|
...(nonce === undefined ? {} : { nonce }),
|
|
@@ -1310,6 +1754,7 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1310
1754
|
search,
|
|
1311
1755
|
matchChain: matchChainOf(route.layoutIds, layoutModules, route.id, mod),
|
|
1312
1756
|
hydrate: mod.hydrate !== false,
|
|
1757
|
+
...layoutProps,
|
|
1313
1758
|
...preloadOf(route.id),
|
|
1314
1759
|
...stylesOf(route.id),
|
|
1315
1760
|
...prerenderedHandover,
|
|
@@ -1324,7 +1769,7 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1324
1769
|
const match = matchNotFoundScope?.(pathOf(c.req)) ?? null
|
|
1325
1770
|
const target = match === null ? undefined : notFoundTargets[Number(match.routeId)]
|
|
1326
1771
|
if (match === null || target === undefined) {
|
|
1327
|
-
return renderNotFound(c
|
|
1772
|
+
return renderNotFound(c, controls)
|
|
1328
1773
|
}
|
|
1329
1774
|
const params: Record<string, string> = {}
|
|
1330
1775
|
for (let i = 0; i < target.names.length; i++) {
|
|
@@ -1333,7 +1778,7 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1333
1778
|
const mod = await target.page.load()
|
|
1334
1779
|
const draft = await draftFlag(c.req)
|
|
1335
1780
|
if (draft) controls.personalize()
|
|
1336
|
-
let run: LayoutRun
|
|
1781
|
+
let run: LayoutRun | undefined
|
|
1337
1782
|
try {
|
|
1338
1783
|
const ctx: LoaderContext = {
|
|
1339
1784
|
params,
|
|
@@ -1352,9 +1797,23 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1352
1797
|
await run.pending
|
|
1353
1798
|
} catch (err) {
|
|
1354
1799
|
if (isStatusSignal(err)) {
|
|
1355
|
-
return renderStatusSignal(
|
|
1800
|
+
return renderStatusSignal(
|
|
1801
|
+
c.req,
|
|
1802
|
+
c.env,
|
|
1803
|
+
err,
|
|
1804
|
+
controls.personalized,
|
|
1805
|
+
leadingLayouts(run, err),
|
|
1806
|
+
)
|
|
1356
1807
|
}
|
|
1357
|
-
return renderLoaderFailure(
|
|
1808
|
+
return renderLoaderFailure(
|
|
1809
|
+
target.route,
|
|
1810
|
+
c.req,
|
|
1811
|
+
c.env,
|
|
1812
|
+
params,
|
|
1813
|
+
err,
|
|
1814
|
+
controls.personalized,
|
|
1815
|
+
leadingLayouts(run, err),
|
|
1816
|
+
)
|
|
1358
1817
|
}
|
|
1359
1818
|
return renderNestedNotFound(
|
|
1360
1819
|
c.req,
|