@nifrajs/web 5.5.1 → 5.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +154 -0
- package/dist/boundary.d.ts +67 -7
- package/dist/boundary.d.ts.map +1 -1
- package/dist/boundary.js +6 -4
- package/dist/boundary.js.map +1 -1
- package/dist/build-plan.d.ts +3 -0
- package/dist/build-plan.d.ts.map +1 -1
- package/dist/build-plan.js.map +1 -1
- package/dist/build-vite.d.ts.map +1 -1
- package/dist/build-vite.js +2 -0
- package/dist/build-vite.js.map +1 -1
- package/dist/build.d.ts +9 -2
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +9 -0
- package/dist/build.js.map +1 -1
- package/dist/client.d.ts +2 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +140 -12
- package/dist/client.js.map +1 -1
- package/dist/csp.d.ts +8 -0
- package/dist/csp.d.ts.map +1 -1
- package/dist/csp.js +11 -2
- package/dist/csp.js.map +1 -1
- package/dist/deferred.d.ts +6 -4
- package/dist/deferred.d.ts.map +1 -1
- package/dist/deferred.js +10 -6
- package/dist/deferred.js.map +1 -1
- package/dist/dev-feed.d.ts.map +1 -1
- package/dist/dev-feed.js +11 -2
- package/dist/dev-feed.js.map +1 -1
- package/dist/dev-session.d.ts.map +1 -1
- package/dist/dev-session.js +13 -6
- package/dist/dev-session.js.map +1 -1
- package/dist/dev.d.ts +4 -1
- package/dist/dev.d.ts.map +1 -1
- package/dist/dev.js +12 -5
- package/dist/dev.js.map +1 -1
- package/dist/diagnostic-catalog.d.ts +4 -0
- package/dist/diagnostic-catalog.d.ts.map +1 -1
- package/dist/diagnostic-catalog.js +48 -0
- package/dist/diagnostic-catalog.js.map +1 -1
- package/dist/diagnostic.d.ts +1 -1
- package/dist/diagnostic.d.ts.map +1 -1
- package/dist/diagnostic.js +1 -1
- package/dist/diagnostic.js.map +1 -1
- package/dist/fn.d.ts +5 -3
- package/dist/fn.d.ts.map +1 -1
- package/dist/fn.js +20 -5
- package/dist/fn.js.map +1 -1
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +13 -3
- package/dist/fs.js.map +1 -1
- package/dist/identity.d.ts +22 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +60 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.d.ts +7 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/internal/cdn/cloudflare.d.ts +2 -1
- package/dist/internal/cdn/cloudflare.d.ts.map +1 -1
- package/dist/internal/cdn/cloudflare.js +2 -1
- package/dist/internal/cdn/cloudflare.js.map +1 -1
- package/dist/internal/certified-components.d.ts +1 -1
- package/dist/internal/certified-components.js +1 -1
- package/dist/internal/codegen.d.ts +11 -10
- package/dist/internal/codegen.d.ts.map +1 -1
- package/dist/internal/codegen.js +65 -8
- package/dist/internal/codegen.js.map +1 -1
- package/dist/internal/dev-websocket.d.ts +73 -0
- package/dist/internal/dev-websocket.d.ts.map +1 -0
- package/dist/internal/dev-websocket.js +145 -0
- package/dist/internal/dev-websocket.js.map +1 -0
- package/dist/internal/hole-runtime.d.ts +66 -0
- package/dist/internal/hole-runtime.d.ts.map +1 -0
- package/dist/internal/hole-runtime.js +211 -0
- package/dist/internal/hole-runtime.js.map +1 -0
- package/dist/internal/identity-epoch.d.ts +50 -0
- package/dist/internal/identity-epoch.d.ts.map +1 -0
- package/dist/internal/identity-epoch.js +179 -0
- package/dist/internal/identity-epoch.js.map +1 -0
- package/dist/internal/invalidation-contract.d.ts +23 -0
- package/dist/internal/invalidation-contract.d.ts.map +1 -0
- package/dist/internal/invalidation-contract.js +11 -0
- package/dist/internal/invalidation-contract.js.map +1 -0
- package/dist/internal/invalidation.d.ts +62 -0
- package/dist/internal/invalidation.d.ts.map +1 -0
- package/dist/internal/invalidation.js +149 -0
- package/dist/internal/invalidation.js.map +1 -0
- package/dist/internal/island-bindings.d.ts +51 -0
- package/dist/internal/island-bindings.d.ts.map +1 -0
- package/dist/internal/island-bindings.js +147 -0
- package/dist/internal/island-bindings.js.map +1 -0
- package/dist/internal/island-list.d.ts +26 -0
- package/dist/internal/island-list.d.ts.map +1 -0
- package/dist/internal/island-list.js +78 -0
- package/dist/internal/island-list.js.map +1 -0
- package/dist/internal/island-mount.d.ts +4 -0
- package/dist/internal/island-mount.d.ts.map +1 -0
- package/dist/internal/island-mount.js +66 -0
- package/dist/internal/island-mount.js.map +1 -0
- package/dist/internal/island-owner.d.ts +5 -0
- package/dist/internal/island-owner.d.ts.map +1 -0
- package/dist/internal/island-owner.js +20 -0
- package/dist/internal/island-owner.js.map +1 -0
- package/dist/internal/island-resource.d.ts +30 -0
- package/dist/internal/island-resource.d.ts.map +1 -0
- package/dist/internal/island-resource.js +57 -0
- package/dist/internal/island-resource.js.map +1 -0
- package/dist/internal/island-signals.d.ts +51 -0
- package/dist/internal/island-signals.d.ts.map +1 -0
- package/dist/internal/island-signals.js +220 -0
- package/dist/internal/island-signals.js.map +1 -0
- package/dist/internal/keep-alive-table.d.ts +23 -0
- package/dist/internal/keep-alive-table.d.ts.map +1 -0
- package/dist/internal/keep-alive-table.js +53 -0
- package/dist/internal/keep-alive-table.js.map +1 -0
- package/dist/internal/live-contract.d.ts +96 -0
- package/dist/internal/live-contract.d.ts.map +1 -0
- package/dist/internal/live-contract.js +5 -0
- package/dist/internal/live-contract.js.map +1 -0
- package/dist/internal/live-queries.d.ts +22 -0
- package/dist/internal/live-queries.d.ts.map +1 -0
- package/dist/internal/live-queries.js +54 -0
- package/dist/internal/live-queries.js.map +1 -0
- package/dist/internal/page-events.d.ts +10 -0
- package/dist/internal/page-events.d.ts.map +1 -0
- package/dist/internal/page-events.js +26 -0
- package/dist/internal/page-events.js.map +1 -0
- package/dist/internal/page-execution.d.ts +14 -1
- package/dist/internal/page-execution.d.ts.map +1 -1
- package/dist/internal/page-execution.js +415 -31
- package/dist/internal/page-execution.js.map +1 -1
- package/dist/internal/ppr/admission.d.ts +83 -0
- package/dist/internal/ppr/admission.d.ts.map +1 -0
- package/dist/internal/ppr/admission.js +221 -0
- package/dist/internal/ppr/admission.js.map +1 -0
- package/dist/internal/ppr/canary.d.ts +24 -0
- package/dist/internal/ppr/canary.d.ts.map +1 -0
- package/dist/internal/ppr/canary.js +102 -0
- package/dist/internal/ppr/canary.js.map +1 -0
- package/dist/internal/ppr/compile-shell.d.ts +39 -0
- package/dist/internal/ppr/compile-shell.d.ts.map +1 -0
- package/dist/internal/ppr/compile-shell.js +298 -0
- package/dist/internal/ppr/compile-shell.js.map +1 -0
- package/dist/internal/ppr/contract.d.ts +177 -0
- package/dist/internal/ppr/contract.d.ts.map +1 -0
- package/dist/internal/ppr/contract.js +80 -0
- package/dist/internal/ppr/contract.js.map +1 -0
- package/dist/internal/ppr/frames.d.ts +71 -0
- package/dist/internal/ppr/frames.d.ts.map +1 -0
- package/dist/internal/ppr/frames.js +119 -0
- package/dist/internal/ppr/frames.js.map +1 -0
- package/dist/internal/ppr/integration.d.ts +19 -0
- package/dist/internal/ppr/integration.d.ts.map +1 -0
- package/dist/internal/ppr/integration.js +106 -0
- package/dist/internal/ppr/integration.js.map +1 -0
- package/dist/internal/ppr/options.d.ts +96 -0
- package/dist/internal/ppr/options.d.ts.map +1 -0
- package/dist/internal/ppr/options.js +125 -0
- package/dist/internal/ppr/options.js.map +1 -0
- package/dist/internal/ppr/route-rules.d.ts +12 -0
- package/dist/internal/ppr/route-rules.d.ts.map +1 -0
- package/dist/internal/ppr/route-rules.js +53 -0
- package/dist/internal/ppr/route-rules.js.map +1 -0
- package/dist/internal/ppr/runtime.d.ts +29 -0
- package/dist/internal/ppr/runtime.d.ts.map +1 -0
- package/dist/internal/ppr/runtime.js +32 -0
- package/dist/internal/ppr/runtime.js.map +1 -0
- package/dist/internal/ppr/server.d.ts +29 -0
- package/dist/internal/ppr/server.d.ts.map +1 -0
- package/dist/internal/ppr/server.js +1407 -0
- package/dist/internal/ppr/server.js.map +1 -0
- package/dist/internal/ppr/shell-store.d.ts +38 -0
- package/dist/internal/ppr/shell-store.d.ts.map +1 -0
- package/dist/internal/ppr/shell-store.js +90 -0
- package/dist/internal/ppr/shell-store.js.map +1 -0
- package/dist/internal/prerendered-pages.d.ts +1 -1
- package/dist/internal/prerendered-pages.d.ts.map +1 -1
- package/dist/internal/prerendered-pages.js +7 -4
- package/dist/internal/prerendered-pages.js.map +1 -1
- package/dist/internal/query-digest.d.ts +9 -0
- package/dist/internal/query-digest.d.ts.map +1 -0
- package/dist/internal/query-digest.js +23 -0
- package/dist/internal/query-digest.js.map +1 -0
- package/dist/internal/query-runtime.d.ts +6 -1
- package/dist/internal/query-runtime.d.ts.map +1 -1
- package/dist/internal/query-runtime.js +9 -0
- package/dist/internal/query-runtime.js.map +1 -1
- package/dist/internal/query-seam.d.ts +29 -0
- package/dist/internal/query-seam.d.ts.map +1 -0
- package/dist/internal/query-seam.js +23 -0
- package/dist/internal/query-seam.js.map +1 -0
- package/dist/internal/render-document.d.ts +28 -1
- package/dist/internal/render-document.d.ts.map +1 -1
- package/dist/internal/render-document.js +83 -18
- package/dist/internal/render-document.js.map +1 -1
- package/dist/internal/request-abort.d.ts +8 -0
- package/dist/internal/request-abort.d.ts.map +1 -0
- package/dist/internal/request-abort.js +22 -0
- package/dist/internal/request-abort.js.map +1 -0
- package/dist/internal/server-boundary.d.ts +2 -0
- package/dist/internal/server-boundary.d.ts.map +1 -1
- package/dist/internal/server-boundary.js +8 -1
- package/dist/internal/server-boundary.js.map +1 -1
- package/dist/internal/share-data.d.ts +10 -0
- package/dist/internal/share-data.d.ts.map +1 -0
- package/dist/internal/share-data.js +135 -0
- package/dist/internal/share-data.js.map +1 -0
- package/dist/internal/store-signature.d.ts +15 -0
- package/dist/internal/store-signature.d.ts.map +1 -0
- package/dist/internal/store-signature.js +114 -0
- package/dist/internal/store-signature.js.map +1 -0
- package/dist/internal/vite-plugin-compat.d.ts +2 -2
- package/dist/internal/vite-plugin-compat.js +2 -2
- package/dist/internal/web-app.d.ts +25 -0
- package/dist/internal/web-app.d.ts.map +1 -1
- package/dist/internal/web-app.js +78 -3
- package/dist/internal/web-app.js.map +1 -1
- package/dist/islands-swap.d.ts +41 -0
- package/dist/islands-swap.d.ts.map +1 -0
- package/dist/islands-swap.js +270 -0
- package/dist/islands-swap.js.map +1 -0
- package/dist/islands.d.ts +86 -24
- package/dist/islands.d.ts.map +1 -1
- package/dist/islands.js +138 -78
- package/dist/islands.js.map +1 -1
- package/dist/isr.d.ts +67 -13
- package/dist/isr.d.ts.map +1 -1
- package/dist/isr.js +247 -99
- package/dist/isr.js.map +1 -1
- package/dist/keep-alive.d.ts +147 -0
- package/dist/keep-alive.d.ts.map +1 -0
- package/dist/keep-alive.js +1069 -0
- package/dist/keep-alive.js.map +1 -0
- package/dist/live/bus.d.ts +56 -0
- package/dist/live/bus.d.ts.map +1 -0
- package/dist/live/bus.js +32 -0
- package/dist/live/bus.js.map +1 -0
- package/dist/live/core.d.ts +25 -0
- package/dist/live/core.d.ts.map +1 -0
- package/dist/live/core.js +29 -0
- package/dist/live/core.js.map +1 -0
- package/dist/live/history.d.ts +38 -0
- package/dist/live/history.d.ts.map +1 -0
- package/dist/live/history.js +104 -0
- package/dist/live/history.js.map +1 -0
- package/dist/live/hub-protocol.d.ts +46 -0
- package/dist/live/hub-protocol.d.ts.map +1 -0
- package/dist/live/hub-protocol.js +108 -0
- package/dist/live/hub-protocol.js.map +1 -0
- package/dist/live/hub.d.ts +181 -0
- package/dist/live/hub.d.ts.map +1 -0
- package/dist/live/hub.js +539 -0
- package/dist/live/hub.js.map +1 -0
- package/dist/live/keyring.d.ts +87 -0
- package/dist/live/keyring.d.ts.map +1 -0
- package/dist/live/keyring.js +260 -0
- package/dist/live/keyring.js.map +1 -0
- package/dist/live/read-capped.d.ts +6 -0
- package/dist/live/read-capped.d.ts.map +1 -0
- package/dist/live/read-capped.js +42 -0
- package/dist/live/read-capped.js.map +1 -0
- package/dist/live/revocations.d.ts +52 -0
- package/dist/live/revocations.d.ts.map +1 -0
- package/dist/live/revocations.js +116 -0
- package/dist/live/revocations.js.map +1 -0
- package/dist/live/settings.d.ts +14 -0
- package/dist/live/settings.d.ts.map +1 -0
- package/dist/live/settings.js +38 -0
- package/dist/live/settings.js.map +1 -0
- package/dist/live-client.d.ts +34 -0
- package/dist/live-client.d.ts.map +1 -0
- package/dist/live-client.js +840 -0
- package/dist/live-client.js.map +1 -0
- package/dist/live-hub.d.ts +130 -0
- package/dist/live-hub.d.ts.map +1 -0
- package/dist/live-hub.js +507 -0
- package/dist/live-hub.js.map +1 -0
- package/dist/live-redis.d.ts +48 -0
- package/dist/live-redis.d.ts.map +1 -0
- package/dist/live-redis.js +201 -0
- package/dist/live-redis.js.map +1 -0
- package/dist/live.d.ts +94 -0
- package/dist/live.d.ts.map +1 -0
- package/dist/live.js +869 -0
- package/dist/live.js.map +1 -0
- package/dist/manifest.d.ts +113 -2
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +109 -5
- package/dist/manifest.js.map +1 -1
- package/dist/navigation.d.ts +9 -3
- package/dist/navigation.d.ts.map +1 -1
- package/dist/navigation.js +13 -3
- package/dist/navigation.js.map +1 -1
- package/dist/plugins/vite-zone-guard.d.ts +6 -0
- package/dist/plugins/vite-zone-guard.d.ts.map +1 -1
- package/dist/plugins/vite-zone-guard.js +8 -2
- package/dist/plugins/vite-zone-guard.js.map +1 -1
- package/dist/ppr.d.ts +73 -0
- package/dist/ppr.d.ts.map +1 -0
- package/dist/ppr.js +76 -0
- package/dist/ppr.js.map +1 -0
- package/dist/query.d.ts +8 -13
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +112 -18
- package/dist/query.js.map +1 -1
- package/dist/render-seam.d.ts +30 -0
- package/dist/render-seam.d.ts.map +1 -1
- package/dist/render-seam.js +5 -0
- package/dist/render-seam.js.map +1 -1
- package/dist/route-types.d.ts.map +1 -1
- package/dist/route-types.js +2 -2
- package/dist/route-types.js.map +1 -1
- package/dist/router.d.ts +72 -4
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +374 -99
- package/dist/router.js.map +1 -1
- package/dist/server-components/build.d.ts.map +1 -1
- package/dist/server-components/build.js +3 -3
- package/dist/server-components/build.js.map +1 -1
- package/dist/server-components/client.d.ts +23 -1
- package/dist/server-components/client.d.ts.map +1 -1
- package/dist/server-components/client.js +92 -5
- package/dist/server-components/client.js.map +1 -1
- package/dist/server-components/hole.d.ts +8 -0
- package/dist/server-components/hole.d.ts.map +1 -0
- package/dist/server-components/hole.js +20 -0
- package/dist/server-components/hole.js.map +1 -0
- package/dist/server-components/html-policy.d.ts.map +1 -1
- package/dist/server-components/html-policy.js +62 -4
- package/dist/server-components/html-policy.js.map +1 -1
- package/dist/server-components/html-scan.d.ts +17 -0
- package/dist/server-components/html-scan.d.ts.map +1 -0
- package/dist/server-components/html-scan.js +180 -0
- package/dist/server-components/html-scan.js.map +1 -0
- package/dist/server-components/html.d.ts.map +1 -1
- package/dist/server-components/html.js +19 -4
- package/dist/server-components/html.js.map +1 -1
- package/dist/server-components/router-integration.d.ts +4 -1
- package/dist/server-components/router-integration.d.ts.map +1 -1
- package/dist/server-components/router-integration.js +23 -2
- package/dist/server-components/router-integration.js.map +1 -1
- package/dist/server-components/runtime.d.ts +6 -1
- package/dist/server-components/runtime.d.ts.map +1 -1
- package/dist/server-components/runtime.js +86 -28
- package/dist/server-components/runtime.js.map +1 -1
- package/dist/server-components/state.d.ts +3 -1
- package/dist/server-components/state.d.ts.map +1 -1
- package/dist/server-components/state.js +50 -38
- package/dist/server-components/state.js.map +1 -1
- package/dist/server-components/types.d.ts +9 -0
- package/dist/server-components/types.d.ts.map +1 -1
- package/dist/vite.d.ts +5 -1
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +58 -4
- package/dist/vite.js.map +1 -1
- package/package.json +48 -8
- package/src/boundary.ts +77 -5
- package/src/build-plan.ts +3 -0
- package/src/build-vite.ts +2 -0
- package/src/build.ts +19 -1
- package/src/client.ts +147 -10
- package/src/csp.ts +16 -2
- package/src/deferred.ts +11 -5
- package/src/dev-feed.ts +11 -2
- package/src/dev-session.ts +22 -11
- package/src/dev.ts +29 -8
- package/src/diagnostic-catalog.ts +53 -0
- package/src/diagnostic.ts +1 -0
- package/src/fn.ts +24 -7
- package/src/fs.ts +22 -8
- package/src/identity.ts +61 -0
- package/src/index.ts +19 -0
- package/src/internal/cdn/cloudflare.ts +2 -1
- package/src/internal/certified-components.ts +1 -1
- package/src/internal/codegen.ts +87 -8
- package/src/internal/dev-websocket.ts +230 -0
- package/src/internal/hole-runtime.ts +286 -0
- package/src/internal/identity-epoch.ts +235 -0
- package/src/internal/invalidation-contract.ts +35 -0
- package/src/internal/invalidation.ts +213 -0
- package/src/internal/island-bindings.ts +191 -0
- package/src/internal/island-list.ts +92 -0
- package/src/internal/island-mount.ts +68 -0
- package/src/internal/island-owner.ts +20 -0
- package/src/internal/island-resource.ts +76 -0
- package/src/internal/island-signals.ts +249 -0
- package/src/internal/keep-alive-table.ts +72 -0
- package/src/internal/live-contract.ts +106 -0
- package/src/internal/live-queries.ts +60 -0
- package/src/internal/page-events.ts +27 -0
- package/src/internal/page-execution.ts +579 -40
- package/src/internal/ppr/admission.ts +300 -0
- package/src/internal/ppr/canary.ts +122 -0
- package/src/internal/ppr/compile-shell.ts +379 -0
- package/src/internal/ppr/contract.ts +265 -0
- package/src/internal/ppr/frames.ts +170 -0
- package/src/internal/ppr/integration.ts +137 -0
- package/src/internal/ppr/options.ts +221 -0
- package/src/internal/ppr/route-rules.ts +77 -0
- package/src/internal/ppr/runtime.ts +36 -0
- package/src/internal/ppr/server.ts +1764 -0
- package/src/internal/ppr/shell-store.ts +139 -0
- package/src/internal/prerendered-pages.ts +7 -4
- package/src/internal/query-digest.ts +34 -0
- package/src/internal/query-runtime.ts +14 -1
- package/src/internal/query-seam.ts +48 -0
- package/src/internal/render-document.ts +149 -22
- package/src/internal/request-abort.ts +26 -0
- package/src/internal/server-boundary.ts +10 -1
- package/src/internal/share-data.ts +136 -0
- package/src/internal/store-signature.ts +149 -0
- package/src/internal/vite-plugin-compat.ts +2 -2
- package/src/internal/web-app.ts +121 -3
- package/src/islands-swap.ts +283 -0
- package/src/islands.ts +169 -72
- package/src/isr.ts +348 -106
- package/src/keep-alive.ts +1315 -0
- package/src/live/bus.ts +77 -0
- package/src/live/core.ts +52 -0
- package/src/live/history.ts +109 -0
- package/src/live/hub-protocol.ts +133 -0
- package/src/live/hub.ts +615 -0
- package/src/live/keyring.ts +379 -0
- package/src/live/read-capped.ts +36 -0
- package/src/live/revocations.ts +155 -0
- package/src/live/settings.ts +46 -0
- package/src/live-client.ts +973 -0
- package/src/live-hub.ts +658 -0
- package/src/live-redis.ts +259 -0
- package/src/live.ts +1096 -0
- package/src/manifest.ts +274 -6
- package/src/navigation.ts +14 -2
- package/src/plugins/vite-zone-guard.ts +15 -2
- package/src/ppr.ts +153 -0
- package/src/query.ts +147 -20
- package/src/render-seam.ts +35 -0
- package/src/route-types.ts +3 -1
- package/src/router.ts +494 -101
- package/src/server-components/build.ts +9 -3
- package/src/server-components/client.ts +89 -5
- package/src/server-components/hole.ts +23 -0
- package/src/server-components/html-policy.ts +63 -4
- package/src/server-components/html-scan.ts +159 -0
- package/src/server-components/html.ts +18 -4
- package/src/server-components/router-integration.ts +21 -2
- package/src/server-components/runtime.ts +112 -30
- package/src/server-components/state.ts +66 -44
- package/src/server-components/types.ts +16 -0
- package/src/vite.ts +75 -6
- package/dist/nano.d.ts +0 -125
- package/dist/nano.d.ts.map +0 -1
- package/dist/nano.js +0 -190
- package/dist/nano.js.map +0 -1
- package/src/nano.ts +0 -246
|
@@ -10,21 +10,24 @@ import {
|
|
|
10
10
|
status as statusResult,
|
|
11
11
|
} from "@nifrajs/core/server"
|
|
12
12
|
import {
|
|
13
|
+
type BoundaryRegistration,
|
|
13
14
|
type BoundaryRequestCtx,
|
|
14
15
|
type BoundaryStates,
|
|
15
16
|
boundaryDescriptors,
|
|
16
17
|
resolveStaticBoundaries,
|
|
18
|
+
type ShellCtx,
|
|
17
19
|
type StaticBoundaryCache,
|
|
18
20
|
startDynamicBoundaries,
|
|
19
21
|
} from "../boundary.ts"
|
|
20
22
|
import type { CspPolicy } from "../csp.ts"
|
|
21
23
|
import { type CssLoadingMode, DEFAULT_CSS_LOADING, normalizeCssLoading } from "../css-contract.ts"
|
|
22
|
-
import { defer, ndjsonStream, prepareDeferred } from "../deferred.ts"
|
|
24
|
+
import { type Deferred, defer, ndjsonStream, prepareDeferred } from "../deferred.ts"
|
|
23
25
|
import { isDraftEnabled } from "../draft.ts"
|
|
24
26
|
import { routeTags } from "../isr.ts"
|
|
25
27
|
import type {
|
|
26
28
|
LayoutEntry,
|
|
27
29
|
LoaderContext,
|
|
30
|
+
LoaderResponseControls,
|
|
28
31
|
Manifest,
|
|
29
32
|
Meta,
|
|
30
33
|
MetaArgs,
|
|
@@ -35,10 +38,14 @@ import type {
|
|
|
35
38
|
} from "../manifest.ts"
|
|
36
39
|
import type { NonceResolver } from "../nonce.ts"
|
|
37
40
|
import type { MatchChain, PageLocale, PageLocales, RenderAdapter } from "../render-seam.ts"
|
|
41
|
+
import { SWAP_HEADER } from "../render-seam.ts"
|
|
38
42
|
import {
|
|
39
43
|
createMatcher,
|
|
40
44
|
DATA_HEADER,
|
|
45
|
+
HOLE_FILL_HEADER,
|
|
46
|
+
KEEP_HOLES_HEADER,
|
|
41
47
|
NAV_FROM_HEADER,
|
|
48
|
+
PREFETCH_HEADER,
|
|
42
49
|
REDIRECT_HEADER,
|
|
43
50
|
RETAIN_HEADER,
|
|
44
51
|
REVALIDATE_HEADER,
|
|
@@ -46,12 +53,28 @@ import {
|
|
|
46
53
|
} from "../router.ts"
|
|
47
54
|
import { searchOf, searchOfChain } from "../search.ts"
|
|
48
55
|
import { mergeHeads, resolveChainMeta } from "./head-merge.ts"
|
|
56
|
+
import { keepAliveTable, slotDepths } from "./keep-alive-table.ts"
|
|
57
|
+
import {
|
|
58
|
+
holeOwner,
|
|
59
|
+
type LiveEnvelope,
|
|
60
|
+
type LiveFinish,
|
|
61
|
+
type LiveLoad,
|
|
62
|
+
type LiveRouteMatch,
|
|
63
|
+
} from "./live-contract.ts"
|
|
49
64
|
import { prepareModules } from "./module-preparation.ts"
|
|
65
|
+
import {
|
|
66
|
+
type HoleLive,
|
|
67
|
+
isHole,
|
|
68
|
+
type PprIntegration,
|
|
69
|
+
type PprRequest,
|
|
70
|
+
serverHoleComponent,
|
|
71
|
+
} from "./ppr/contract.ts"
|
|
50
72
|
import {
|
|
51
73
|
EMPTY_RETAIN,
|
|
52
74
|
type HeadersLike,
|
|
53
75
|
isControlFlow,
|
|
54
76
|
isResponseResult,
|
|
77
|
+
isRevalidateResult,
|
|
55
78
|
isStatusSignal,
|
|
56
79
|
type LoadedLayoutModules,
|
|
57
80
|
layoutErrorId,
|
|
@@ -101,13 +124,145 @@ export interface PageRouteContext<Env = unknown> {
|
|
|
101
124
|
}
|
|
102
125
|
|
|
103
126
|
/** Whether `c` is a client navigation's data request. On Node, `req.headers` builds every header. */
|
|
127
|
+
// The slot name is echoed in a response header, so only a plain identifier is honored.
|
|
128
|
+
const SLOT_NAME = /^[A-Za-z][\w-]{0,63}$/
|
|
129
|
+
|
|
130
|
+
/** The slot an `x-nifra-swap` request names, when it is a valid slot name. */
|
|
131
|
+
function swapSlotOf(req: Request): string | undefined {
|
|
132
|
+
const name = req.headers.get(SWAP_HEADER)
|
|
133
|
+
return name !== null && SLOT_NAME.test(name) ? name : undefined
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** The `swapSlot` render option for `req`, empty when it is not a swap request. */
|
|
137
|
+
function swapOption(req: Request): { readonly swapSlot?: string } {
|
|
138
|
+
const swapSlot = swapSlotOf(req)
|
|
139
|
+
return swapSlot === undefined ? {} : { swapSlot }
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const headerOf = (c: PageRouteContext<unknown>, name: string): string | null =>
|
|
143
|
+
c.header !== undefined ? c.header(name) : c.req.headers.get(name)
|
|
144
|
+
|
|
104
145
|
function isDataRequest(c: PageRouteContext<unknown>): boolean {
|
|
105
|
-
return (c
|
|
146
|
+
return headerOf(c, DATA_HEADER) !== null
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** The live owners of one response's holes, settled once the last hole that ran does. */
|
|
150
|
+
interface HoleOwners {
|
|
151
|
+
readonly kept: readonly string[]
|
|
152
|
+
readonly loaded: Promise<readonly string[]>
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* A data request's hole refresh: the hosts the client holds, which reload as fills, the holes it
|
|
157
|
+
* keeps, and in a live load each hole's owner. A name the route does not declare is a 400.
|
|
158
|
+
*/
|
|
159
|
+
function holeRefresh(
|
|
160
|
+
c: PageRouteContext<unknown>,
|
|
161
|
+
request: PprRequest,
|
|
162
|
+
live: LiveLoad | undefined,
|
|
163
|
+
): { readonly request: PprRequest; readonly owners?: HoleOwners } | Response {
|
|
164
|
+
if (!isDataRequest(c)) return { request }
|
|
165
|
+
const declared = new Set(request.holes.map((hole) => hole.name))
|
|
166
|
+
const namesOf = (header: string): Set<string> | undefined => {
|
|
167
|
+
const value = headerOf(c, header)
|
|
168
|
+
const names = new Set<string>()
|
|
169
|
+
if (value === null) return names
|
|
170
|
+
for (const name of value.split(",")) {
|
|
171
|
+
const trimmed = name.trim()
|
|
172
|
+
if (declared.has(trimmed)) names.add(trimmed)
|
|
173
|
+
// A live load's names come from a grant this server sealed and the page's own client: one
|
|
174
|
+
// naming a hole an older build declared is skipped.
|
|
175
|
+
else if (live === undefined) return undefined
|
|
176
|
+
}
|
|
177
|
+
return names
|
|
178
|
+
}
|
|
179
|
+
const kept = namesOf(KEEP_HOLES_HEADER)
|
|
180
|
+
const fill = namesOf(HOLE_FILL_HEADER)
|
|
181
|
+
if (kept === undefined || fill === undefined) {
|
|
182
|
+
return new Response("Bad Request: the route has no such hole", {
|
|
183
|
+
status: 400,
|
|
184
|
+
headers: { "content-type": "text/plain; charset=utf-8", "cache-control": "no-store" },
|
|
185
|
+
})
|
|
186
|
+
}
|
|
187
|
+
const refreshed: PprRequest = {
|
|
188
|
+
...request,
|
|
189
|
+
...(fill.size === 0 ? {} : { fill }),
|
|
190
|
+
...(kept.size === 0 ? {} : { kept }),
|
|
191
|
+
}
|
|
192
|
+
if (live === undefined) return { request: refreshed }
|
|
193
|
+
const routeId = request.routeId
|
|
194
|
+
let finish: (loaded: readonly string[]) => void = () => {}
|
|
195
|
+
const loaded = new Promise<readonly string[]>((resolve) => {
|
|
196
|
+
finish = resolve
|
|
197
|
+
})
|
|
198
|
+
const holeLive: HoleLive = {
|
|
199
|
+
collector: (hole) => live.collector(holeOwner(hole, routeId)),
|
|
200
|
+
seal: (hole) => live.seal(holeOwner(hole, routeId)),
|
|
201
|
+
done: (names) => finish(names.map((hole) => holeOwner(hole, routeId))),
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
request: { ...refreshed, live: holeLive },
|
|
205
|
+
owners: { kept: [...kept].map((hole) => holeOwner(hole, routeId)), loaded },
|
|
206
|
+
}
|
|
106
207
|
}
|
|
107
208
|
|
|
108
209
|
/** The page slot of an `ssr = false` route with no `HydrateFallback`: a component that renders nothing. */
|
|
109
210
|
const EMPTY_LEAF = (): null => null
|
|
110
211
|
|
|
212
|
+
/** `ctx.live` outside a live load: keys are declared for nobody. */
|
|
213
|
+
const NO_LIVE = (): void => {}
|
|
214
|
+
|
|
215
|
+
const headersSent = (): never => {
|
|
216
|
+
throw new TypeError(
|
|
217
|
+
"[nifra/web] ppr: a hole's server component cannot set a cookie: the shell already sent the headers",
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** A hole's response headers went out with the shell, so its server component sets none. */
|
|
222
|
+
const SENT_CONTROLS: LoaderResponseControls = Object.freeze({
|
|
223
|
+
headers: Object.freeze({}),
|
|
224
|
+
cookie: headersSent,
|
|
225
|
+
deleteCookie: headersSent,
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* The envelope's `live` member. With holes running it trails them: one grant, issued once the last hole
|
|
230
|
+
* settled, holding each hole that loaded and each the client kept.
|
|
231
|
+
*/
|
|
232
|
+
const liveEnvelopeOf = (
|
|
233
|
+
live: LiveLoad,
|
|
234
|
+
owners: LiveFinish,
|
|
235
|
+
holes: HoleOwners | undefined,
|
|
236
|
+
): Promise<LiveEnvelope> | Deferred<LiveEnvelope> =>
|
|
237
|
+
holes === undefined
|
|
238
|
+
? live.finish(owners)
|
|
239
|
+
: defer(
|
|
240
|
+
holes.loaded.then((loaded) =>
|
|
241
|
+
live.finish({
|
|
242
|
+
...owners,
|
|
243
|
+
fresh: [...owners.fresh, ...loaded],
|
|
244
|
+
retained: [...owners.retained, ...holes.kept],
|
|
245
|
+
}),
|
|
246
|
+
),
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
/** Run one owner's loader with its `ctx.live`, closing the collector once the loader settles. */
|
|
250
|
+
const runLiveLoader = (
|
|
251
|
+
live: LiveLoad,
|
|
252
|
+
owner: string,
|
|
253
|
+
loader: (ctx: LoaderContext) => unknown,
|
|
254
|
+
ctx: LoaderContext,
|
|
255
|
+
): Promise<unknown> => {
|
|
256
|
+
let value: unknown
|
|
257
|
+
try {
|
|
258
|
+
value = loader({ ...ctx, live: live.collector(owner) })
|
|
259
|
+
} catch (err) {
|
|
260
|
+
live.seal(owner)
|
|
261
|
+
return Promise.reject(err)
|
|
262
|
+
}
|
|
263
|
+
return Promise.resolve(value).finally(() => live.seal(owner))
|
|
264
|
+
}
|
|
265
|
+
|
|
111
266
|
/** The request-scoping seam of an in-process `api` (see `inProcessClient`), if it carries one. */
|
|
112
267
|
function platformBinderOf(api: unknown): BackendPlatformBinder | undefined {
|
|
113
268
|
if ((typeof api !== "object" && typeof api !== "function") || api === null) return undefined
|
|
@@ -244,6 +399,16 @@ export interface PageExecutionOptions<Env = unknown> {
|
|
|
244
399
|
readonly csp?: CspPolicy
|
|
245
400
|
/** Set once a cache wrapper attaches; until then a route's freshness and tags stay off the response. */
|
|
246
401
|
readonly cacheChannel?: { readonly enabled: boolean }
|
|
402
|
+
/**
|
|
403
|
+
* Run, or hand to the cache wrapper, what an action's `revalidate()` made stale. A promise is awaited
|
|
404
|
+
* before the action answers; `undefined` means nothing runs it. Never throws, never changes the answer.
|
|
405
|
+
*/
|
|
406
|
+
readonly invalidate?: (
|
|
407
|
+
revalidation: RevalidateResult<unknown>,
|
|
408
|
+
request: Request,
|
|
409
|
+
) => Promise<void> | undefined
|
|
410
|
+
/** The live load a request belongs to; only set when the app configures `live`. */
|
|
411
|
+
readonly liveLoadOf?: ((request: Request) => LiveLoad | undefined) | undefined
|
|
247
412
|
readonly onLoaderError?: (
|
|
248
413
|
error: unknown,
|
|
249
414
|
ctx: {
|
|
@@ -255,6 +420,8 @@ export interface PageExecutionOptions<Env = unknown> {
|
|
|
255
420
|
/** Logs a failure an `_error` boundary answered. Without a boundary the error propagates and the
|
|
256
421
|
* server logs it, so this runs only where the server never sees the throw. */
|
|
257
422
|
readonly logBoundaryError?: (error: unknown, request: Request) => void
|
|
423
|
+
/** Experimental partial prerendering: routes with a `shellLoader` stream a shared shell, then holes. */
|
|
424
|
+
readonly ppr?: PprIntegration
|
|
258
425
|
}
|
|
259
426
|
|
|
260
427
|
type PageResponse = Response | ResponseResult | RenderedPage
|
|
@@ -264,6 +431,8 @@ export interface PageRequestExecutor<Env = unknown> {
|
|
|
264
431
|
post(route: RouteEntry): (ctx: PageRouteContext<Env>) => Promise<PageResponse>
|
|
265
432
|
/** Answer a URL no route matched: the nearest nested `_404` for it, else the root one. */
|
|
266
433
|
fallback(ctx: PageRouteContext<Env>): Promise<PageResponse>
|
|
434
|
+
/** The route a `pathname + search` renders, with its layout chain. */
|
|
435
|
+
matchRoute(path: string): LiveRouteMatch | null
|
|
267
436
|
}
|
|
268
437
|
|
|
269
438
|
/** One URL pattern a nested `_404` answers, as the executor indexes it. */
|
|
@@ -355,11 +524,13 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
355
524
|
const { i18n } = options
|
|
356
525
|
const pageLocales = new Map(i18n?.locales.locales.map((locale) => [locale.key, locale]))
|
|
357
526
|
/** The request's locale for a document, with the served locales the client names locales from. */
|
|
358
|
-
const
|
|
527
|
+
const localeFor = (key: string): { locale?: PageLocale; locales?: PageLocales } => {
|
|
359
528
|
if (i18n === undefined) return {}
|
|
360
|
-
const locale = pageLocales.get(
|
|
529
|
+
const locale = pageLocales.get(key)
|
|
361
530
|
return locale === undefined ? { locales: i18n.locales } : { locale, locales: i18n.locales }
|
|
362
531
|
}
|
|
532
|
+
const localeOption = (req: Request): { locale?: PageLocale; locales?: PageLocales } =>
|
|
533
|
+
i18n === undefined ? {} : localeFor(i18n.localeOf(pathOf(req)))
|
|
363
534
|
/** Whether a route sits under a `[lang]` or `[[lang]]` directory, which the locale routing checks. */
|
|
364
535
|
const localeGuarded = (route: RouteEntry): boolean => {
|
|
365
536
|
if (i18n === undefined) return false
|
|
@@ -372,6 +543,51 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
372
543
|
manifest.routes.map((route) => ({ routeId: route.id, pattern: route.pattern })),
|
|
373
544
|
)
|
|
374
545
|
const routeById = new Map(manifest.routes.map((route) => [route.id, route]))
|
|
546
|
+
const liveLoadOf = options.liveLoadOf
|
|
547
|
+
|
|
548
|
+
/** A live load holds one admission slot from before its middleware until its body is complete. */
|
|
549
|
+
const admitLive =
|
|
550
|
+
(run: (c: PageRouteContext<Env>, controls: PageResponseControls) => Promise<PageResponse>) =>
|
|
551
|
+
async (c: PageRouteContext<Env>, controls: PageResponseControls): Promise<PageResponse> => {
|
|
552
|
+
const live = liveLoadOf?.(c.req)
|
|
553
|
+
if (live === undefined) return run(c, controls)
|
|
554
|
+
const refused = live.admit()
|
|
555
|
+
if (refused !== undefined) return refused
|
|
556
|
+
let outcome: PageResponse
|
|
557
|
+
try {
|
|
558
|
+
outcome = await run(c, controls)
|
|
559
|
+
} catch (err) {
|
|
560
|
+
live.release()
|
|
561
|
+
throw err
|
|
562
|
+
}
|
|
563
|
+
if (outcome instanceof Response) return live.settle(outcome)
|
|
564
|
+
live.release()
|
|
565
|
+
return outcome
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/** Which owners a successful live load ran and which it kept, by their wire ids. */
|
|
569
|
+
const liveOwners = (
|
|
570
|
+
route: RouteEntry,
|
|
571
|
+
modules: LoadedLayoutModules | undefined,
|
|
572
|
+
layoutData: readonly unknown[] | undefined,
|
|
573
|
+
retained: readonly number[],
|
|
574
|
+
mod: RouteModule,
|
|
575
|
+
skipPage: boolean,
|
|
576
|
+
ctx: LoaderContext,
|
|
577
|
+
): LiveFinish => {
|
|
578
|
+
const fresh: string[] = []
|
|
579
|
+
const kept: string[] = []
|
|
580
|
+
for (let i = 0; i < (modules?.length ?? 0); i++) {
|
|
581
|
+
if (modules?.[i]?.loader === undefined) continue
|
|
582
|
+
const owner = `L:${route.layoutIds[i]}`
|
|
583
|
+
if (retained.includes(i)) kept.push(owner)
|
|
584
|
+
// A layout that answered with a status declared keys for data nobody is shown.
|
|
585
|
+
else if (!isStatusSignal(layoutData?.[i])) fresh.push(owner)
|
|
586
|
+
}
|
|
587
|
+
if (skipPage) kept.push(`P:${route.id}`)
|
|
588
|
+
else if (mod.loader !== undefined) fresh.push(`P:${route.id}`)
|
|
589
|
+
return { fresh, retained: kept, ctx }
|
|
590
|
+
}
|
|
375
591
|
|
|
376
592
|
// A nested `_404` answers an unmatched URL from inside the catch-all, never as a route of its own:
|
|
377
593
|
// a registered `/admin/*` would take URLs from a route the router only reaches by backtracking.
|
|
@@ -419,6 +635,19 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
419
635
|
? Promise.resolve(false)
|
|
420
636
|
: isDraftEnabled(req, options.draftSecret)
|
|
421
637
|
|
|
638
|
+
// Where the browser's keep-alive view puts slot wrappers, so the document it hydrates has them:
|
|
639
|
+
// nothing outside an app with a `keepAlive` route.
|
|
640
|
+
const keepTable = keepAliveTable(manifest)
|
|
641
|
+
const slotsFor = (
|
|
642
|
+
layoutIds: readonly string[],
|
|
643
|
+
routeId: string,
|
|
644
|
+
): { slots?: readonly number[] } => {
|
|
645
|
+
// Every page of a keep-alive app renders through the adapter's keep-alive root, wrappers or none,
|
|
646
|
+
// as the browser hydrates it.
|
|
647
|
+
if (keepTable === undefined) return {}
|
|
648
|
+
return { slots: slotDepths(keepTable, layoutIds, routeId) }
|
|
649
|
+
}
|
|
650
|
+
|
|
422
651
|
const preloadOf = (id: string): { preload?: readonly string[] } => {
|
|
423
652
|
const chunks = options.routePreload?.[id]
|
|
424
653
|
return chunks ? { preload: chunks } : {}
|
|
@@ -521,6 +750,7 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
521
750
|
controls: PageResponseControls,
|
|
522
751
|
retain?: RetainContext,
|
|
523
752
|
gated?: GatedLayouts,
|
|
753
|
+
live?: LiveLoad,
|
|
524
754
|
): Promise<LayoutRun> => {
|
|
525
755
|
if (route.layoutIds.length === 0) return emptyLayoutRun
|
|
526
756
|
const modules = gated?.modules ?? (await loadLayoutModules(route))
|
|
@@ -552,14 +782,25 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
552
782
|
params: scopeParams(ctx.params, route.layoutParams?.[i]),
|
|
553
783
|
set: controls.scope(i),
|
|
554
784
|
}
|
|
785
|
+
const layoutId = route.layoutIds[i] as string
|
|
555
786
|
if (modules[i]?.gate === true) {
|
|
556
|
-
results[i] =
|
|
787
|
+
results[i] =
|
|
788
|
+
gated !== undefined
|
|
789
|
+
? gated.gates[i]
|
|
790
|
+
: await runGate(
|
|
791
|
+
route,
|
|
792
|
+
i,
|
|
793
|
+
live === undefined
|
|
794
|
+
? loader
|
|
795
|
+
: (gateCtx) => runLiveLoader(live, `L:${layoutId}`, loader, gateCtx),
|
|
796
|
+
scoped,
|
|
797
|
+
)
|
|
557
798
|
continue
|
|
558
799
|
}
|
|
559
|
-
const layoutId = route.layoutIds[i] as string
|
|
560
800
|
const index = i
|
|
561
801
|
try {
|
|
562
|
-
const value =
|
|
802
|
+
const value =
|
|
803
|
+
live === undefined ? loader(scoped) : runLiveLoader(live, `L:${layoutId}`, loader, scoped)
|
|
563
804
|
// Scalars settle synchronously. Objects keep await's intrinsic promise/thenable
|
|
564
805
|
// assimilation: property-presence and prototype probes are unsafe for proxies.
|
|
565
806
|
if (value !== null && (typeof value === "object" || typeof value === "function")) {
|
|
@@ -792,6 +1033,12 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
792
1033
|
const dirOfId = (id: string, suffix: string): string =>
|
|
793
1034
|
id === suffix ? "" : id.slice(0, id.length - suffix.length - 1)
|
|
794
1035
|
|
|
1036
|
+
/** The server's error log for one request, where a rejected `defer()` value is written. */
|
|
1037
|
+
const deferredLogOf = (req: Request): ((error: unknown) => void) | undefined => {
|
|
1038
|
+
const log = options.logBoundaryError
|
|
1039
|
+
return log === undefined ? undefined : (error) => log(error, req)
|
|
1040
|
+
}
|
|
1041
|
+
|
|
795
1042
|
const boundaryFor = (route: RouteEntry, err: unknown): string | undefined => {
|
|
796
1043
|
const ids = route.errorIds ?? []
|
|
797
1044
|
const failing = layoutErrorId(err)
|
|
@@ -859,6 +1106,8 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
859
1106
|
routeId: errorId,
|
|
860
1107
|
status: 500,
|
|
861
1108
|
hydrate: false,
|
|
1109
|
+
signal: req.signal,
|
|
1110
|
+
logError: deferredLogOf(req),
|
|
862
1111
|
...(layoutData === undefined ? {} : { layoutData }),
|
|
863
1112
|
...(personalized || layoutData !== undefined ? { headers: PRIVATE_PAGE_HEADERS } : {}),
|
|
864
1113
|
...(nonce === undefined ? {} : { nonce }),
|
|
@@ -952,6 +1201,7 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
952
1201
|
draft,
|
|
953
1202
|
search: loaderSearch(page.searchSchema, c.req),
|
|
954
1203
|
set: controls.scope(PAGE_SCOPE),
|
|
1204
|
+
live: NO_LIVE,
|
|
955
1205
|
}
|
|
956
1206
|
if (route.middlewareIds !== undefined) await runMiddleware(route, ctx, controls)
|
|
957
1207
|
const run = await runLayoutChain(route, ctx, controls)
|
|
@@ -1007,6 +1257,8 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1007
1257
|
search: searchChainOf(modules, loaded, req),
|
|
1008
1258
|
path: pathOf(req),
|
|
1009
1259
|
status,
|
|
1260
|
+
signal: req.signal,
|
|
1261
|
+
logError: deferredLogOf(req),
|
|
1010
1262
|
// The browser renders this page inside the root layout, so a page that had to render alone
|
|
1011
1263
|
// stays server-only, as a nested `_404` does.
|
|
1012
1264
|
...(wraps && chain === undefined ? { hydrate: false } : {}),
|
|
@@ -1021,6 +1273,7 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1021
1273
|
...copyOption,
|
|
1022
1274
|
...localeOption(req),
|
|
1023
1275
|
...titleOption,
|
|
1276
|
+
...slotsFor(layoutIds, routeId),
|
|
1024
1277
|
}
|
|
1025
1278
|
if (chain === undefined) return renderPageResult(statusPage)
|
|
1026
1279
|
try {
|
|
@@ -1091,6 +1344,7 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1091
1344
|
responseHeaders: Record<string, string> | undefined,
|
|
1092
1345
|
personalized: boolean,
|
|
1093
1346
|
serverComponents?: import("../server-components/types.ts").ComponentState,
|
|
1347
|
+
live?: LiveEnvelope | Deferred<LiveEnvelope>,
|
|
1094
1348
|
): Promise<PageResponse> => {
|
|
1095
1349
|
// Every layout loader settled before the page rendered, so a status or error page can reuse them.
|
|
1096
1350
|
const settled: LayoutChainSource = async (_page, count) =>
|
|
@@ -1113,8 +1367,14 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1113
1367
|
...deferred,
|
|
1114
1368
|
...(boundarySplit === undefined ? [] : boundarySplit.deferred),
|
|
1115
1369
|
]
|
|
1370
|
+
// Deferred when holes run: their owners are known only once the last one settles.
|
|
1371
|
+
const liveSplit = live === undefined ? undefined : prepareDeferred(live, allDeferred.length)
|
|
1372
|
+
if (liveSplit !== undefined) allDeferred.push(...liveSplit.deferred)
|
|
1116
1373
|
const payload =
|
|
1117
|
-
layoutData === undefined &&
|
|
1374
|
+
layoutData === undefined &&
|
|
1375
|
+
boundarySplit === undefined &&
|
|
1376
|
+
serverComponents === undefined &&
|
|
1377
|
+
live === undefined
|
|
1118
1378
|
? pageSplit.forClient
|
|
1119
1379
|
: {
|
|
1120
1380
|
v: 1 as const,
|
|
@@ -1123,16 +1383,20 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1123
1383
|
retained: layoutRetained,
|
|
1124
1384
|
...(boundarySplit === undefined ? {} : { boundaries: boundarySplit.forClient }),
|
|
1125
1385
|
...(serverComponents === undefined ? {} : { serverComponents }),
|
|
1386
|
+
...(liveSplit === undefined ? {} : { live: liveSplit.forClient }),
|
|
1126
1387
|
}
|
|
1127
1388
|
if (allDeferred.length === 0) {
|
|
1128
1389
|
return Response.json(payload ?? null, { headers: DATA_RESPONSE_HEADERS })
|
|
1129
1390
|
}
|
|
1130
|
-
return new Response(
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1391
|
+
return new Response(
|
|
1392
|
+
ndjsonStream(payload, allDeferred, undefined, req.signal, deferredLogOf(req)),
|
|
1393
|
+
{
|
|
1394
|
+
headers: {
|
|
1395
|
+
"content-type": "application/x-ndjson; charset=utf-8",
|
|
1396
|
+
...DATA_RESPONSE_HEADERS,
|
|
1397
|
+
},
|
|
1134
1398
|
},
|
|
1135
|
-
|
|
1399
|
+
)
|
|
1136
1400
|
}
|
|
1137
1401
|
|
|
1138
1402
|
const nonce = options.nonce === undefined ? undefined : await resolveNonce(req, env)
|
|
@@ -1164,6 +1428,8 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1164
1428
|
search: searchChainOf(layoutModules, mod, req),
|
|
1165
1429
|
matchChain: matchChainOf(route.layoutIds, layoutModules, route.id, mod),
|
|
1166
1430
|
hydrate: mod.hydrate !== false,
|
|
1431
|
+
signal: req.signal,
|
|
1432
|
+
logError: deferredLogOf(req),
|
|
1167
1433
|
...preloadOf(route.id),
|
|
1168
1434
|
...stylesOf(route.id),
|
|
1169
1435
|
...appDocumentOptions,
|
|
@@ -1171,6 +1437,9 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1171
1437
|
}
|
|
1172
1438
|
if (serverComponents !== undefined) page.serverComponents = serverComponents
|
|
1173
1439
|
if (progressiveActionData.has(req)) page.actionData = progressiveActionData.get(req)
|
|
1440
|
+
const swapSlot = swapSlotOf(req)
|
|
1441
|
+
if (swapSlot !== undefined) page.swapSlot = swapSlot
|
|
1442
|
+
if (keepTable !== undefined) Object.assign(page, slotsFor(route.layoutIds, route.id))
|
|
1174
1443
|
if (layoutData !== undefined) page.layoutData = layoutData
|
|
1175
1444
|
if (boundaryStates !== undefined) page.boundaries = boundaryStates
|
|
1176
1445
|
if (responseHeaders !== undefined) page.headers = responseHeaders
|
|
@@ -1293,6 +1562,8 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1293
1562
|
matchChain: matchChainOf(page.layoutIds, layoutModules, id, mod),
|
|
1294
1563
|
status: 404,
|
|
1295
1564
|
hydrate: false,
|
|
1565
|
+
signal: req.signal,
|
|
1566
|
+
logError: deferredLogOf(req),
|
|
1296
1567
|
...localeOption(req),
|
|
1297
1568
|
...(layoutData !== undefined ? { layoutData } : {}),
|
|
1298
1569
|
...stylesOf(id),
|
|
@@ -1382,6 +1653,113 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1382
1653
|
}
|
|
1383
1654
|
}
|
|
1384
1655
|
|
|
1656
|
+
const pprRequestFor = (
|
|
1657
|
+
c: PageRouteContext<Env>,
|
|
1658
|
+
route: RouteEntry,
|
|
1659
|
+
mod: RouteModule,
|
|
1660
|
+
layouts: LoadedLayoutModules,
|
|
1661
|
+
boundaries: readonly BoundaryRegistration[],
|
|
1662
|
+
shellLoader: NonNullable<RouteModule["shellLoader"]>,
|
|
1663
|
+
requestApi: unknown,
|
|
1664
|
+
draft: boolean,
|
|
1665
|
+
search: Record<string, unknown>,
|
|
1666
|
+
): PprRequest => {
|
|
1667
|
+
const path = urlPartsFor(c.req).pathname
|
|
1668
|
+
const locale = i18n?.localeOf(path)
|
|
1669
|
+
const params = c.params
|
|
1670
|
+
const shellOrigin = options.ppr?.origin
|
|
1671
|
+
const variations =
|
|
1672
|
+
options.ppr?.variationsOf(mod.shellVariations, urlPartsFor(c.req).search) ?? {}
|
|
1673
|
+
const shellSearch = options.ppr?.variationQuery(variations) ?? ""
|
|
1674
|
+
// What a shared entry's tags read: its key's parts, never the Host or an undeclared query, which
|
|
1675
|
+
// the first visitor of a key could set so a later purge misses the entry.
|
|
1676
|
+
const shellUrl = (): URL => {
|
|
1677
|
+
const url = new URL(shellOrigin ?? "http://shell.invalid")
|
|
1678
|
+
url.pathname = path
|
|
1679
|
+
url.search = shellSearch
|
|
1680
|
+
return url
|
|
1681
|
+
}
|
|
1682
|
+
// The unbound `api`: a shell is built for every visitor, so no visitor's platform reaches it.
|
|
1683
|
+
const shellContext: Omit<ShellCtx, "signal"> =
|
|
1684
|
+
shellOrigin === undefined
|
|
1685
|
+
? { phase: "shell", params, locale, variations, api }
|
|
1686
|
+
: { phase: "shell", params, locale, variations, api, origin: shellOrigin }
|
|
1687
|
+
return {
|
|
1688
|
+
routeId: route.id,
|
|
1689
|
+
path,
|
|
1690
|
+
locale,
|
|
1691
|
+
variations,
|
|
1692
|
+
request: c.req,
|
|
1693
|
+
holes: boundaries.filter(isHole),
|
|
1694
|
+
holeContext: { request: c.req, params, api: requestApi, env: c.env, draft, search },
|
|
1695
|
+
shellContext,
|
|
1696
|
+
revalidate: mod.revalidate,
|
|
1697
|
+
waitUntil: c.waitUntil,
|
|
1698
|
+
tags: () =>
|
|
1699
|
+
mod.revalidateTags === undefined
|
|
1700
|
+
? []
|
|
1701
|
+
: routeTags(mod.revalidateTags, { params, url: shellUrl() }, route.id),
|
|
1702
|
+
holeTags: (hole) =>
|
|
1703
|
+
hole.revalidateTags === undefined
|
|
1704
|
+
? []
|
|
1705
|
+
: routeTags(hole.revalidateTags, { params, url: shellUrl() }, `${route.id}#${hole.name}`),
|
|
1706
|
+
loadShell: async (signal) => shellLoader({ ...shellContext, signal }),
|
|
1707
|
+
renderShellDocument: async (shellData, states) => {
|
|
1708
|
+
const statics = boundaries.some((boundary) => boundary.mode === "static")
|
|
1709
|
+
? await resolveStaticBoundaries(
|
|
1710
|
+
boundaries,
|
|
1711
|
+
{ phase: "build" },
|
|
1712
|
+
options.staticBoundaryCache,
|
|
1713
|
+
)
|
|
1714
|
+
: undefined
|
|
1715
|
+
const { chain, head } = resolveChainAndHead(
|
|
1716
|
+
layouts,
|
|
1717
|
+
undefined,
|
|
1718
|
+
mod,
|
|
1719
|
+
// A Host header is the client's to choose, so a shared shell's meta gets the configured one.
|
|
1720
|
+
{ data: shellData, pageData: shellData, params, origin: shellOrigin ?? "" },
|
|
1721
|
+
serverLeafOf(route, mod),
|
|
1722
|
+
)
|
|
1723
|
+
// Nothing here reads the request beyond its pathname: no origin, search, nonce or headers.
|
|
1724
|
+
const page: DocumentInput = {
|
|
1725
|
+
adapter,
|
|
1726
|
+
chain,
|
|
1727
|
+
data: shellData,
|
|
1728
|
+
head,
|
|
1729
|
+
clientEntry,
|
|
1730
|
+
routeId: route.id,
|
|
1731
|
+
params,
|
|
1732
|
+
path,
|
|
1733
|
+
search: searchOfChain(
|
|
1734
|
+
[...layouts.map((m) => m.searchSchema), mod.searchSchema],
|
|
1735
|
+
shellSearch === "" ? "" : `?${shellSearch}`,
|
|
1736
|
+
),
|
|
1737
|
+
matchChain: matchChainOf(route.layoutIds, layouts, route.id, mod),
|
|
1738
|
+
hydrate: mod.hydrate !== false,
|
|
1739
|
+
boundaries: { ...statics, ...states },
|
|
1740
|
+
...preloadOf(route.id),
|
|
1741
|
+
...stylesOf(route.id),
|
|
1742
|
+
...appDocumentOptions,
|
|
1743
|
+
...(locale === undefined ? {} : localeFor(locale)),
|
|
1744
|
+
...slotsFor(route.layoutIds, route.id),
|
|
1745
|
+
}
|
|
1746
|
+
if (mod.islandScripts !== undefined) page.islandScripts = mod.islandScripts
|
|
1747
|
+
const result = await renderPageResult(page)
|
|
1748
|
+
// A buffered page hands over its string: a Response round trip to read it back cost more.
|
|
1749
|
+
const rendered = result.toNodeBody?.() ?? result.toResponse()
|
|
1750
|
+
if (rendered.status !== 200) {
|
|
1751
|
+
throw new Error(
|
|
1752
|
+
`[nifra/web] ppr: the shell of "${route.id}" rendered status ${rendered.status}`,
|
|
1753
|
+
)
|
|
1754
|
+
}
|
|
1755
|
+
if (rendered instanceof Response) return rendered.text()
|
|
1756
|
+
return typeof rendered.body === "string"
|
|
1757
|
+
? rendered.body
|
|
1758
|
+
: new TextDecoder().decode(rendered.body)
|
|
1759
|
+
},
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1385
1763
|
return {
|
|
1386
1764
|
get: (route) => {
|
|
1387
1765
|
const is404Fallback = options.staticFallbacks?.[route.pattern] === "404"
|
|
@@ -1414,7 +1792,11 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1414
1792
|
return slot
|
|
1415
1793
|
}
|
|
1416
1794
|
|
|
1417
|
-
|
|
1795
|
+
const page = async (
|
|
1796
|
+
c: PageRouteContext<Env>,
|
|
1797
|
+
controls: PageResponseControls,
|
|
1798
|
+
): Promise<PageResponse> => {
|
|
1799
|
+
const live = liveLoadOf?.(c.req)
|
|
1418
1800
|
const componentRuntime = options.serverComponents
|
|
1419
1801
|
const componentOwner = componentRuntime?.owns(route.id) === true
|
|
1420
1802
|
let componentRequest:
|
|
@@ -1443,6 +1825,9 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1443
1825
|
let componentState: import("../server-components/types.ts").ComponentState | undefined
|
|
1444
1826
|
const requestApi = apiFor(c)
|
|
1445
1827
|
let run: LayoutRun | undefined
|
|
1828
|
+
let skipPage = false
|
|
1829
|
+
let liveCtx: LoaderContext
|
|
1830
|
+
let holeOwners: HoleOwners | undefined
|
|
1446
1831
|
try {
|
|
1447
1832
|
const ctx: LoaderContext = {
|
|
1448
1833
|
params: c.params,
|
|
@@ -1453,15 +1838,26 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1453
1838
|
draft,
|
|
1454
1839
|
search: loaderSearch(mod.searchSchema, c.req),
|
|
1455
1840
|
set: controls.scope(PAGE_SCOPE),
|
|
1841
|
+
live: NO_LIVE,
|
|
1456
1842
|
}
|
|
1457
1843
|
if (route.middlewareIds !== undefined || localeGuarded(route)) {
|
|
1458
1844
|
await runMiddleware(route, ctx, controls)
|
|
1459
1845
|
}
|
|
1846
|
+
// After middleware, before any loader: a grant issued to another viewer keeps nothing.
|
|
1847
|
+
const retains = live === undefined || (await live.bind(ctx))
|
|
1848
|
+
skipPage =
|
|
1849
|
+
live !== undefined &&
|
|
1850
|
+
retains &&
|
|
1851
|
+
!componentOwner &&
|
|
1852
|
+
live.retainPage &&
|
|
1853
|
+
live.routeId === route.id
|
|
1460
1854
|
run = await runLayoutChain(
|
|
1461
1855
|
route,
|
|
1462
1856
|
ctx,
|
|
1463
1857
|
controls,
|
|
1464
|
-
componentOwner ? undefined : retainContextOf(c),
|
|
1858
|
+
componentOwner || !retains ? undefined : retainContextOf(c),
|
|
1859
|
+
undefined,
|
|
1860
|
+
live,
|
|
1465
1861
|
)
|
|
1466
1862
|
layoutModules = run.modules
|
|
1467
1863
|
layoutRetained = run.retained
|
|
@@ -1474,8 +1870,89 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1474
1870
|
: []
|
|
1475
1871
|
if (boundaryDefinitions.length > 0) boundaryDescriptors(boundaryDefinitions)
|
|
1476
1872
|
const effectiveSearch = searchChainOf(run.modules, mod, c.req)
|
|
1873
|
+
const ppr = options.ppr
|
|
1874
|
+
const shellLoader =
|
|
1875
|
+
componentRequest === undefined && !componentOwner ? mod.shellLoader : undefined
|
|
1876
|
+
let pprRequest: PprRequest | undefined
|
|
1877
|
+
// How a document that renders whole gets its holes: complete, unless no shell exists yet.
|
|
1878
|
+
let wholeHoles: "settled" | "deferred" = "settled"
|
|
1879
|
+
if (ppr !== undefined && shellLoader !== undefined) {
|
|
1880
|
+
ppr.assertRoute(route, run.modules, boundaryDefinitions)
|
|
1881
|
+
pprRequest = pprRequestFor(
|
|
1882
|
+
c,
|
|
1883
|
+
route,
|
|
1884
|
+
mod,
|
|
1885
|
+
run.modules,
|
|
1886
|
+
boundaryDefinitions,
|
|
1887
|
+
shellLoader,
|
|
1888
|
+
requestApi,
|
|
1889
|
+
draft,
|
|
1890
|
+
effectiveSearch,
|
|
1891
|
+
)
|
|
1892
|
+
const refresh = holeRefresh(c, pprRequest, live)
|
|
1893
|
+
if (refresh instanceof Response) return refresh
|
|
1894
|
+
pprRequest = refresh.request
|
|
1895
|
+
holeOwners = refresh.owners
|
|
1896
|
+
const components = options.serverComponents
|
|
1897
|
+
const renderHole = components?.hosts?.(route.id) === true ? components.hole : undefined
|
|
1898
|
+
if (
|
|
1899
|
+
renderHole === undefined &&
|
|
1900
|
+
pprRequest.holes.some((hole) => serverHoleComponent(hole.render) !== undefined)
|
|
1901
|
+
) {
|
|
1902
|
+
throw new Error(
|
|
1903
|
+
`[nifra/web] ppr: route "${route.id}" has a hole that shows a server component; list the component under createServerComponents({ holes }) for this route`,
|
|
1904
|
+
)
|
|
1905
|
+
}
|
|
1906
|
+
if (renderHole !== undefined) {
|
|
1907
|
+
const loaderCtx = ctx
|
|
1908
|
+
pprRequest = {
|
|
1909
|
+
...pprRequest,
|
|
1910
|
+
// The owner path's context, but with the hole's signal and no response to change.
|
|
1911
|
+
component: (name, component, input, signal) =>
|
|
1912
|
+
renderHole.call(
|
|
1913
|
+
components,
|
|
1914
|
+
route.id,
|
|
1915
|
+
component,
|
|
1916
|
+
name,
|
|
1917
|
+
input,
|
|
1918
|
+
Object.create(
|
|
1919
|
+
c,
|
|
1920
|
+
Object.getOwnPropertyDescriptors({
|
|
1921
|
+
...loaderCtx,
|
|
1922
|
+
search: effectiveSearch,
|
|
1923
|
+
set: SENT_CONTROLS,
|
|
1924
|
+
signal,
|
|
1925
|
+
data: undefined,
|
|
1926
|
+
layoutData: [],
|
|
1927
|
+
}),
|
|
1928
|
+
),
|
|
1929
|
+
),
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
// A draft is the editor's alone, so it never reads or fills the shared shell.
|
|
1933
|
+
if (!draft && !isDataRequest(c)) {
|
|
1934
|
+
const streamed = await ppr.stream(pprRequest, () => controls.documentHeaders())
|
|
1935
|
+
if (streamed instanceof Response) return streamed
|
|
1936
|
+
wholeHoles = streamed
|
|
1937
|
+
}
|
|
1938
|
+
// Its holes are one visitor's, so no cache may store the page they render into.
|
|
1939
|
+
controls.personalize()
|
|
1940
|
+
}
|
|
1941
|
+
// Every PPR hole runs through the PPR server, so each has admission and its own deadline.
|
|
1942
|
+
const holeStates =
|
|
1943
|
+
pprRequest === undefined || ppr === undefined
|
|
1944
|
+
? undefined
|
|
1945
|
+
: ppr.server.holeStates(
|
|
1946
|
+
pprRequest,
|
|
1947
|
+
!isDataRequest(c)
|
|
1948
|
+
? wholeHoles
|
|
1949
|
+
: c.req.headers.get(PREFETCH_HEADER) === null
|
|
1950
|
+
? "deferred"
|
|
1951
|
+
: "omitted",
|
|
1952
|
+
c.req.signal,
|
|
1953
|
+
)
|
|
1477
1954
|
const boundaryBatch =
|
|
1478
|
-
boundaryDefinitions.length === 0
|
|
1955
|
+
boundaryDefinitions.length === 0 || holeStates !== undefined
|
|
1479
1956
|
? undefined
|
|
1480
1957
|
: startDynamicBoundaries(boundaryDefinitions, {
|
|
1481
1958
|
request: c.req,
|
|
@@ -1485,6 +1962,7 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1485
1962
|
draft,
|
|
1486
1963
|
search: effectiveSearch,
|
|
1487
1964
|
signal: c.req.signal,
|
|
1965
|
+
live: NO_LIVE,
|
|
1488
1966
|
} satisfies BoundaryRequestCtx)
|
|
1489
1967
|
const staticBoundaryPromise =
|
|
1490
1968
|
boundaryDefinitions.length === 0
|
|
@@ -1494,7 +1972,20 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1494
1972
|
{ phase: "build" },
|
|
1495
1973
|
options.staticBoundaryCache,
|
|
1496
1974
|
)
|
|
1497
|
-
const pageResult =
|
|
1975
|
+
const pageResult =
|
|
1976
|
+
pprRequest !== undefined && ppr !== undefined
|
|
1977
|
+
? // A key with no shell yet must not build one through its data either.
|
|
1978
|
+
draft || wholeHoles === "deferred"
|
|
1979
|
+
? pprRequest.loadShell(c.req.signal)
|
|
1980
|
+
: ppr.shellData(pprRequest, c.req.signal)
|
|
1981
|
+
: mod.loader === undefined || skipPage
|
|
1982
|
+
? null
|
|
1983
|
+
: live === undefined
|
|
1984
|
+
? mod.loader({ ...ctx, search: effectiveSearch })
|
|
1985
|
+
: runLiveLoader(live, `P:${route.id}`, mod.loader, {
|
|
1986
|
+
...ctx,
|
|
1987
|
+
search: effectiveSearch,
|
|
1988
|
+
})
|
|
1498
1989
|
const [pageData, , staticBoundaries] =
|
|
1499
1990
|
run.pending === undefined && staticBoundaryPromise === undefined
|
|
1500
1991
|
? ([await pageResult, undefined, undefined] as const)
|
|
@@ -1502,7 +1993,9 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1502
1993
|
? await Promise.all([pageResult, run.pending])
|
|
1503
1994
|
: await Promise.all([pageResult, run.pending, staticBoundaryPromise])
|
|
1504
1995
|
data = pageData
|
|
1505
|
-
|
|
1996
|
+
// A gate on a PPR route runs for its redirect or refusal only. Its data reaches no response,
|
|
1997
|
+
// so a soft navigation renders the layouts exactly as the shared shell did.
|
|
1998
|
+
layoutData = pprRequest === undefined ? run.layoutData : undefined
|
|
1506
1999
|
if (componentOwner) {
|
|
1507
2000
|
for (let i = 0; i < (layoutData?.length ?? 0); i++) {
|
|
1508
2001
|
const outcome = layoutData?.[i]
|
|
@@ -1531,8 +2024,15 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1531
2024
|
options.nonce === undefined ? undefined : await resolveNonce(c.req, c.env),
|
|
1532
2025
|
)
|
|
1533
2026
|
}
|
|
1534
|
-
if (
|
|
1535
|
-
|
|
2027
|
+
if (holeStates !== undefined) {
|
|
2028
|
+
// The static resolver names every boundary, leaving the dynamic ones to the holes.
|
|
2029
|
+
boundaryStates = { ...staticBoundaries, ...(await holeStates) }
|
|
2030
|
+
} else if (boundaryBatch !== undefined) {
|
|
2031
|
+
// Each resolver returns a state for every boundary and marks the other's modes
|
|
2032
|
+
// `unresolved`, so a slot takes its state from the resolver of its own mode.
|
|
2033
|
+
const streamed = { ...boundaryBatch.initial }
|
|
2034
|
+
for (const [name, state] of Object.entries(staticBoundaries ?? {}))
|
|
2035
|
+
if (state.mode === "static") streamed[name] = state
|
|
1536
2036
|
for (const entry of boundaryBatch.pending) {
|
|
1537
2037
|
const initial = streamed[entry.name]
|
|
1538
2038
|
if (initial !== undefined)
|
|
@@ -1545,6 +2045,7 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1545
2045
|
// Inside the loader `try`: a header the response cannot carry is a loader error, rendered
|
|
1546
2046
|
// by the same boundary as one.
|
|
1547
2047
|
responseHeaders = controls.documentHeaders()
|
|
2048
|
+
liveCtx = ctx
|
|
1548
2049
|
} catch (err) {
|
|
1549
2050
|
if (componentRequest !== undefined) return componentRuntime!.failure(err)
|
|
1550
2051
|
if (isStatusSignal(err)) {
|
|
@@ -1591,9 +2092,26 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1591
2092
|
)
|
|
1592
2093
|
if (nested !== undefined) return nested
|
|
1593
2094
|
}
|
|
2095
|
+
const isData = isDataRequest(c)
|
|
2096
|
+
const liveEnvelope =
|
|
2097
|
+
live === undefined || !isData || isStatusSignal(data) || isControlFlow(data)
|
|
2098
|
+
? undefined
|
|
2099
|
+
: await liveEnvelopeOf(
|
|
2100
|
+
live,
|
|
2101
|
+
liveOwners(
|
|
2102
|
+
route,
|
|
2103
|
+
layoutModules,
|
|
2104
|
+
layoutData,
|
|
2105
|
+
layoutRetained,
|
|
2106
|
+
mod,
|
|
2107
|
+
skipPage,
|
|
2108
|
+
liveCtx,
|
|
2109
|
+
),
|
|
2110
|
+
holeOwners,
|
|
2111
|
+
)
|
|
1594
2112
|
return handleDataOrDocument(
|
|
1595
2113
|
c.req,
|
|
1596
|
-
|
|
2114
|
+
isData,
|
|
1597
2115
|
c.env,
|
|
1598
2116
|
route,
|
|
1599
2117
|
c.params,
|
|
@@ -1607,8 +2125,10 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1607
2125
|
responseHeaders,
|
|
1608
2126
|
controls.personalized,
|
|
1609
2127
|
componentState,
|
|
2128
|
+
liveEnvelope,
|
|
1610
2129
|
)
|
|
1611
|
-
}
|
|
2130
|
+
}
|
|
2131
|
+
return withResponseControls(liveLoadOf === undefined ? page : admitLive(page))
|
|
1612
2132
|
},
|
|
1613
2133
|
|
|
1614
2134
|
post: (route) =>
|
|
@@ -1659,6 +2179,7 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1659
2179
|
draft,
|
|
1660
2180
|
search: loaderSearch(mod.searchSchema, c.req),
|
|
1661
2181
|
set: controls.scope(ACTION_SCOPE),
|
|
2182
|
+
live: NO_LIVE,
|
|
1662
2183
|
}
|
|
1663
2184
|
let gated: GatedLayouts
|
|
1664
2185
|
let result: unknown
|
|
@@ -1672,16 +2193,17 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1672
2193
|
if (isControlFlow(err)) return err
|
|
1673
2194
|
throw err
|
|
1674
2195
|
}
|
|
1675
|
-
const
|
|
1676
|
-
|
|
1677
|
-
const
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
:
|
|
2196
|
+
const revalidation = isRevalidateResult(result) ? result : undefined
|
|
2197
|
+
const actionResult = revalidation === undefined ? result : revalidation.data
|
|
2198
|
+
const revalidateHeader: Record<string, string> =
|
|
2199
|
+
revalidation === undefined
|
|
2200
|
+
? {}
|
|
2201
|
+
: { [REVALIDATE_HEADER]: revalidation.__nifraRevalidate.join(",") }
|
|
2202
|
+
// The write is done, whatever the action answers with (a redirect included): what it made
|
|
2203
|
+
// stale is invalidated before the answer leaves.
|
|
2204
|
+
const settling =
|
|
2205
|
+
revalidation === undefined ? undefined : options.invalidate?.(revalidation, c.req)
|
|
2206
|
+
if (settling !== undefined) await settling
|
|
1685
2207
|
if (isControlFlow(actionResult)) return actionResult
|
|
1686
2208
|
if (isDataRequest) {
|
|
1687
2209
|
// Headers shape the document only, but a bad one fails here too, so a navigation and a full
|
|
@@ -1693,13 +2215,16 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1693
2215
|
headers: { ...DATA_RESPONSE_HEADERS, ...revalidateHeader },
|
|
1694
2216
|
})
|
|
1695
2217
|
}
|
|
1696
|
-
return new Response(
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
2218
|
+
return new Response(
|
|
2219
|
+
ndjsonStream(forClient, deferred, undefined, c.req.signal, deferredLogOf(c.req)),
|
|
2220
|
+
{
|
|
2221
|
+
headers: {
|
|
2222
|
+
"content-type": "application/x-ndjson; charset=utf-8",
|
|
2223
|
+
...DATA_RESPONSE_HEADERS,
|
|
2224
|
+
...revalidateHeader,
|
|
2225
|
+
},
|
|
1701
2226
|
},
|
|
1702
|
-
|
|
2227
|
+
)
|
|
1703
2228
|
}
|
|
1704
2229
|
if (options.serverComponents?.owns(route.id)) {
|
|
1705
2230
|
if (options.rerenderOwnerGET === undefined) throw new Error("component_contract")
|
|
@@ -1768,6 +2293,7 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1768
2293
|
draft,
|
|
1769
2294
|
search,
|
|
1770
2295
|
set: controls.scope(PAGE_SCOPE),
|
|
2296
|
+
live: NO_LIVE,
|
|
1771
2297
|
})
|
|
1772
2298
|
: null
|
|
1773
2299
|
const [pageData] =
|
|
@@ -1837,7 +2363,7 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1837
2363
|
},
|
|
1838
2364
|
serverLeafOf(route, mod),
|
|
1839
2365
|
)
|
|
1840
|
-
|
|
2366
|
+
const page: DocumentInput = {
|
|
1841
2367
|
adapter,
|
|
1842
2368
|
chain,
|
|
1843
2369
|
data,
|
|
@@ -1850,6 +2376,8 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1850
2376
|
search,
|
|
1851
2377
|
matchChain: matchChainOf(route.layoutIds, layoutModules, route.id, mod),
|
|
1852
2378
|
hydrate: mod.hydrate !== false,
|
|
2379
|
+
signal: c.req.signal,
|
|
2380
|
+
logError: deferredLogOf(c.req),
|
|
1853
2381
|
...layoutProps,
|
|
1854
2382
|
...preloadOf(route.id),
|
|
1855
2383
|
...stylesOf(route.id),
|
|
@@ -1860,9 +2388,19 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1860
2388
|
...(nonce === undefined ? {} : { nonce }),
|
|
1861
2389
|
...cspOption,
|
|
1862
2390
|
...titleOption,
|
|
1863
|
-
|
|
2391
|
+
...swapOption(c.req),
|
|
2392
|
+
...slotsFor(route.layoutIds, route.id),
|
|
2393
|
+
}
|
|
2394
|
+
if (mod.islandScripts !== undefined) page.islandScripts = mod.islandScripts
|
|
2395
|
+
return renderPageResult(page)
|
|
1864
2396
|
}),
|
|
1865
2397
|
|
|
2398
|
+
matchRoute: (path) => {
|
|
2399
|
+
const matched = matchManifestRoute(path)
|
|
2400
|
+
const route = matched === null ? undefined : routeById.get(matched.routeId)
|
|
2401
|
+
return route === undefined ? null : { routeId: route.id, layoutIds: route.layoutIds }
|
|
2402
|
+
},
|
|
2403
|
+
|
|
1866
2404
|
fallback: withResponseControls(async (c, controls) => {
|
|
1867
2405
|
const match = matchNotFoundScope?.(pathOf(c.req)) ?? null
|
|
1868
2406
|
const target = match === null ? undefined : notFoundTargets[Number(match.routeId)]
|
|
@@ -1887,6 +2425,7 @@ export function createPageRequestExecutor<Env = unknown>(
|
|
|
1887
2425
|
draft,
|
|
1888
2426
|
search: loaderSearch(mod.searchSchema, c.req),
|
|
1889
2427
|
set: controls.scope(PAGE_SCOPE),
|
|
2428
|
+
live: NO_LIVE,
|
|
1890
2429
|
}
|
|
1891
2430
|
if (target.route.middlewareIds !== undefined) {
|
|
1892
2431
|
await runMiddleware(target.route, ctx, controls)
|