@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
|
@@ -5,19 +5,124 @@ import { DEFAULT_CSS_LOADING, normalizeCssLoading } from "../css-contract.js";
|
|
|
5
5
|
import { defer, ndjsonStream, prepareDeferred } from "../deferred.js";
|
|
6
6
|
import { isDraftEnabled } from "../draft.js";
|
|
7
7
|
import { routeTags } from "../isr.js";
|
|
8
|
-
import {
|
|
8
|
+
import { SWAP_HEADER } from "../render-seam.js";
|
|
9
|
+
import { createMatcher, DATA_HEADER, HOLE_FILL_HEADER, KEEP_HOLES_HEADER, NAV_FROM_HEADER, PREFETCH_HEADER, REDIRECT_HEADER, RETAIN_HEADER, REVALIDATE_HEADER, STATUS_HEADER, } from "../router.js";
|
|
9
10
|
import { searchOf, searchOfChain } from "../search.js";
|
|
10
11
|
import { mergeHeads, resolveChainMeta } from "./head-merge.js";
|
|
12
|
+
import { keepAliveTable, slotDepths } from "./keep-alive-table.js";
|
|
13
|
+
import { holeOwner, } from "./live-contract.js";
|
|
11
14
|
import { prepareModules } from "./module-preparation.js";
|
|
12
|
-
import {
|
|
15
|
+
import { isHole, serverHoleComponent, } from "./ppr/contract.js";
|
|
16
|
+
import { EMPTY_RETAIN, isControlFlow, isResponseResult, isRevalidateResult, isStatusSignal, layoutErrorId, notFound, redirect, renderPageResult, STATUS_SIGNAL, STATUS_TEXT, scopeParams, tagLayoutError, withDuplicateInstanceHint, } from "./render-document.js";
|
|
13
17
|
import { originFor, urlPartsFor } from "./request-url.js";
|
|
14
18
|
import { ACTION_SCOPE, DATA_RESPONSE_HEADERS, MIDDLEWARE_SCOPE, PAGE_SCOPE, PageResponseControls, PRIVATE_NO_STORE, PRIVATE_PAGE_HEADERS, privateOutcome, varyOnDataHeader, } from "./response-controls.js";
|
|
15
19
|
/** Whether `c` is a client navigation's data request. On Node, `req.headers` builds every header. */
|
|
20
|
+
// The slot name is echoed in a response header, so only a plain identifier is honored.
|
|
21
|
+
const SLOT_NAME = /^[A-Za-z][\w-]{0,63}$/;
|
|
22
|
+
/** The slot an `x-nifra-swap` request names, when it is a valid slot name. */
|
|
23
|
+
function swapSlotOf(req) {
|
|
24
|
+
const name = req.headers.get(SWAP_HEADER);
|
|
25
|
+
return name !== null && SLOT_NAME.test(name) ? name : undefined;
|
|
26
|
+
}
|
|
27
|
+
/** The `swapSlot` render option for `req`, empty when it is not a swap request. */
|
|
28
|
+
function swapOption(req) {
|
|
29
|
+
const swapSlot = swapSlotOf(req);
|
|
30
|
+
return swapSlot === undefined ? {} : { swapSlot };
|
|
31
|
+
}
|
|
32
|
+
const headerOf = (c, name) => c.header !== undefined ? c.header(name) : c.req.headers.get(name);
|
|
16
33
|
function isDataRequest(c) {
|
|
17
|
-
return (c
|
|
34
|
+
return headerOf(c, DATA_HEADER) !== null;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A data request's hole refresh: the hosts the client holds, which reload as fills, the holes it
|
|
38
|
+
* keeps, and in a live load each hole's owner. A name the route does not declare is a 400.
|
|
39
|
+
*/
|
|
40
|
+
function holeRefresh(c, request, live) {
|
|
41
|
+
if (!isDataRequest(c))
|
|
42
|
+
return { request };
|
|
43
|
+
const declared = new Set(request.holes.map((hole) => hole.name));
|
|
44
|
+
const namesOf = (header) => {
|
|
45
|
+
const value = headerOf(c, header);
|
|
46
|
+
const names = new Set();
|
|
47
|
+
if (value === null)
|
|
48
|
+
return names;
|
|
49
|
+
for (const name of value.split(",")) {
|
|
50
|
+
const trimmed = name.trim();
|
|
51
|
+
if (declared.has(trimmed))
|
|
52
|
+
names.add(trimmed);
|
|
53
|
+
// A live load's names come from a grant this server sealed and the page's own client: one
|
|
54
|
+
// naming a hole an older build declared is skipped.
|
|
55
|
+
else if (live === undefined)
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
return names;
|
|
59
|
+
};
|
|
60
|
+
const kept = namesOf(KEEP_HOLES_HEADER);
|
|
61
|
+
const fill = namesOf(HOLE_FILL_HEADER);
|
|
62
|
+
if (kept === undefined || fill === undefined) {
|
|
63
|
+
return new Response("Bad Request: the route has no such hole", {
|
|
64
|
+
status: 400,
|
|
65
|
+
headers: { "content-type": "text/plain; charset=utf-8", "cache-control": "no-store" },
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
const refreshed = {
|
|
69
|
+
...request,
|
|
70
|
+
...(fill.size === 0 ? {} : { fill }),
|
|
71
|
+
...(kept.size === 0 ? {} : { kept }),
|
|
72
|
+
};
|
|
73
|
+
if (live === undefined)
|
|
74
|
+
return { request: refreshed };
|
|
75
|
+
const routeId = request.routeId;
|
|
76
|
+
let finish = () => { };
|
|
77
|
+
const loaded = new Promise((resolve) => {
|
|
78
|
+
finish = resolve;
|
|
79
|
+
});
|
|
80
|
+
const holeLive = {
|
|
81
|
+
collector: (hole) => live.collector(holeOwner(hole, routeId)),
|
|
82
|
+
seal: (hole) => live.seal(holeOwner(hole, routeId)),
|
|
83
|
+
done: (names) => finish(names.map((hole) => holeOwner(hole, routeId))),
|
|
84
|
+
};
|
|
85
|
+
return {
|
|
86
|
+
request: { ...refreshed, live: holeLive },
|
|
87
|
+
owners: { kept: [...kept].map((hole) => holeOwner(hole, routeId)), loaded },
|
|
88
|
+
};
|
|
18
89
|
}
|
|
19
90
|
/** The page slot of an `ssr = false` route with no `HydrateFallback`: a component that renders nothing. */
|
|
20
91
|
const EMPTY_LEAF = () => null;
|
|
92
|
+
/** `ctx.live` outside a live load: keys are declared for nobody. */
|
|
93
|
+
const NO_LIVE = () => { };
|
|
94
|
+
const headersSent = () => {
|
|
95
|
+
throw new TypeError("[nifra/web] ppr: a hole's server component cannot set a cookie: the shell already sent the headers");
|
|
96
|
+
};
|
|
97
|
+
/** A hole's response headers went out with the shell, so its server component sets none. */
|
|
98
|
+
const SENT_CONTROLS = Object.freeze({
|
|
99
|
+
headers: Object.freeze({}),
|
|
100
|
+
cookie: headersSent,
|
|
101
|
+
deleteCookie: headersSent,
|
|
102
|
+
});
|
|
103
|
+
/**
|
|
104
|
+
* The envelope's `live` member. With holes running it trails them: one grant, issued once the last hole
|
|
105
|
+
* settled, holding each hole that loaded and each the client kept.
|
|
106
|
+
*/
|
|
107
|
+
const liveEnvelopeOf = (live, owners, holes) => holes === undefined
|
|
108
|
+
? live.finish(owners)
|
|
109
|
+
: defer(holes.loaded.then((loaded) => live.finish({
|
|
110
|
+
...owners,
|
|
111
|
+
fresh: [...owners.fresh, ...loaded],
|
|
112
|
+
retained: [...owners.retained, ...holes.kept],
|
|
113
|
+
})));
|
|
114
|
+
/** Run one owner's loader with its `ctx.live`, closing the collector once the loader settles. */
|
|
115
|
+
const runLiveLoader = (live, owner, loader, ctx) => {
|
|
116
|
+
let value;
|
|
117
|
+
try {
|
|
118
|
+
value = loader({ ...ctx, live: live.collector(owner) });
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
live.seal(owner);
|
|
122
|
+
return Promise.reject(err);
|
|
123
|
+
}
|
|
124
|
+
return Promise.resolve(value).finally(() => live.seal(owner));
|
|
125
|
+
};
|
|
21
126
|
/** The request-scoping seam of an in-process `api` (see `inProcessClient`), if it carries one. */
|
|
22
127
|
function platformBinderOf(api) {
|
|
23
128
|
if ((typeof api !== "object" && typeof api !== "function") || api === null)
|
|
@@ -140,12 +245,13 @@ export function createPageRequestExecutor(options) {
|
|
|
140
245
|
const { i18n } = options;
|
|
141
246
|
const pageLocales = new Map(i18n?.locales.locales.map((locale) => [locale.key, locale]));
|
|
142
247
|
/** The request's locale for a document, with the served locales the client names locales from. */
|
|
143
|
-
const
|
|
248
|
+
const localeFor = (key) => {
|
|
144
249
|
if (i18n === undefined)
|
|
145
250
|
return {};
|
|
146
|
-
const locale = pageLocales.get(
|
|
251
|
+
const locale = pageLocales.get(key);
|
|
147
252
|
return locale === undefined ? { locales: i18n.locales } : { locale, locales: i18n.locales };
|
|
148
253
|
};
|
|
254
|
+
const localeOption = (req) => i18n === undefined ? {} : localeFor(i18n.localeOf(pathOf(req)));
|
|
149
255
|
/** Whether a route sits under a `[lang]` or `[[lang]]` directory, which the locale routing checks. */
|
|
150
256
|
const localeGuarded = (route) => {
|
|
151
257
|
if (i18n === undefined)
|
|
@@ -158,6 +264,48 @@ export function createPageRequestExecutor(options) {
|
|
|
158
264
|
};
|
|
159
265
|
const matchManifestRoute = createMatcher(manifest.routes.map((route) => ({ routeId: route.id, pattern: route.pattern })));
|
|
160
266
|
const routeById = new Map(manifest.routes.map((route) => [route.id, route]));
|
|
267
|
+
const liveLoadOf = options.liveLoadOf;
|
|
268
|
+
/** A live load holds one admission slot from before its middleware until its body is complete. */
|
|
269
|
+
const admitLive = (run) => async (c, controls) => {
|
|
270
|
+
const live = liveLoadOf?.(c.req);
|
|
271
|
+
if (live === undefined)
|
|
272
|
+
return run(c, controls);
|
|
273
|
+
const refused = live.admit();
|
|
274
|
+
if (refused !== undefined)
|
|
275
|
+
return refused;
|
|
276
|
+
let outcome;
|
|
277
|
+
try {
|
|
278
|
+
outcome = await run(c, controls);
|
|
279
|
+
}
|
|
280
|
+
catch (err) {
|
|
281
|
+
live.release();
|
|
282
|
+
throw err;
|
|
283
|
+
}
|
|
284
|
+
if (outcome instanceof Response)
|
|
285
|
+
return live.settle(outcome);
|
|
286
|
+
live.release();
|
|
287
|
+
return outcome;
|
|
288
|
+
};
|
|
289
|
+
/** Which owners a successful live load ran and which it kept, by their wire ids. */
|
|
290
|
+
const liveOwners = (route, modules, layoutData, retained, mod, skipPage, ctx) => {
|
|
291
|
+
const fresh = [];
|
|
292
|
+
const kept = [];
|
|
293
|
+
for (let i = 0; i < (modules?.length ?? 0); i++) {
|
|
294
|
+
if (modules?.[i]?.loader === undefined)
|
|
295
|
+
continue;
|
|
296
|
+
const owner = `L:${route.layoutIds[i]}`;
|
|
297
|
+
if (retained.includes(i))
|
|
298
|
+
kept.push(owner);
|
|
299
|
+
// A layout that answered with a status declared keys for data nobody is shown.
|
|
300
|
+
else if (!isStatusSignal(layoutData?.[i]))
|
|
301
|
+
fresh.push(owner);
|
|
302
|
+
}
|
|
303
|
+
if (skipPage)
|
|
304
|
+
kept.push(`P:${route.id}`);
|
|
305
|
+
else if (mod.loader !== undefined)
|
|
306
|
+
fresh.push(`P:${route.id}`);
|
|
307
|
+
return { fresh, retained: kept, ctx };
|
|
308
|
+
};
|
|
161
309
|
// A nested `_404` answers an unmatched URL from inside the catch-all, never as a route of its own:
|
|
162
310
|
// a registered `/admin/*` would take URLs from a route the router only reaches by backtracking.
|
|
163
311
|
// Param names are indexed by position, so two directories that name one segment differently share
|
|
@@ -197,6 +345,16 @@ export function createPageRequestExecutor(options) {
|
|
|
197
345
|
const draftFlag = (req) => options.draftSecret === undefined
|
|
198
346
|
? Promise.resolve(false)
|
|
199
347
|
: isDraftEnabled(req, options.draftSecret);
|
|
348
|
+
// Where the browser's keep-alive view puts slot wrappers, so the document it hydrates has them:
|
|
349
|
+
// nothing outside an app with a `keepAlive` route.
|
|
350
|
+
const keepTable = keepAliveTable(manifest);
|
|
351
|
+
const slotsFor = (layoutIds, routeId) => {
|
|
352
|
+
// Every page of a keep-alive app renders through the adapter's keep-alive root, wrappers or none,
|
|
353
|
+
// as the browser hydrates it.
|
|
354
|
+
if (keepTable === undefined)
|
|
355
|
+
return {};
|
|
356
|
+
return { slots: slotDepths(keepTable, layoutIds, routeId) };
|
|
357
|
+
};
|
|
200
358
|
const preloadOf = (id) => {
|
|
201
359
|
const chunks = options.routePreload?.[id];
|
|
202
360
|
return chunks ? { preload: chunks } : {};
|
|
@@ -274,7 +432,7 @@ export function createPageRequestExecutor(options) {
|
|
|
274
432
|
* Run layout loaders, awaiting gates before starting the page loader and retaining only safe data.
|
|
275
433
|
* `gated` carries gates that already ran for this request, so none runs twice.
|
|
276
434
|
*/
|
|
277
|
-
const runLayoutChain = async (route, ctx, controls, retain, gated) => {
|
|
435
|
+
const runLayoutChain = async (route, ctx, controls, retain, gated, live) => {
|
|
278
436
|
if (route.layoutIds.length === 0)
|
|
279
437
|
return emptyLayoutRun;
|
|
280
438
|
const modules = gated?.modules ?? (await loadLayoutModules(route));
|
|
@@ -308,14 +466,19 @@ export function createPageRequestExecutor(options) {
|
|
|
308
466
|
params: scopeParams(ctx.params, route.layoutParams?.[i]),
|
|
309
467
|
set: controls.scope(i),
|
|
310
468
|
};
|
|
469
|
+
const layoutId = route.layoutIds[i];
|
|
311
470
|
if (modules[i]?.gate === true) {
|
|
312
|
-
results[i] =
|
|
471
|
+
results[i] =
|
|
472
|
+
gated !== undefined
|
|
473
|
+
? gated.gates[i]
|
|
474
|
+
: await runGate(route, i, live === undefined
|
|
475
|
+
? loader
|
|
476
|
+
: (gateCtx) => runLiveLoader(live, `L:${layoutId}`, loader, gateCtx), scoped);
|
|
313
477
|
continue;
|
|
314
478
|
}
|
|
315
|
-
const layoutId = route.layoutIds[i];
|
|
316
479
|
const index = i;
|
|
317
480
|
try {
|
|
318
|
-
const value = loader(scoped);
|
|
481
|
+
const value = live === undefined ? loader(scoped) : runLiveLoader(live, `L:${layoutId}`, loader, scoped);
|
|
319
482
|
// Scalars settle synchronously. Objects keep await's intrinsic promise/thenable
|
|
320
483
|
// assimilation: property-presence and prototype probes are unsafe for proxies.
|
|
321
484
|
if (value !== null && (typeof value === "object" || typeof value === "function")) {
|
|
@@ -512,6 +675,11 @@ export function createPageRequestExecutor(options) {
|
|
|
512
675
|
handles: [...layoutModules.map((layout) => layout.handle), page.handle],
|
|
513
676
|
});
|
|
514
677
|
const dirOfId = (id, suffix) => id === suffix ? "" : id.slice(0, id.length - suffix.length - 1);
|
|
678
|
+
/** The server's error log for one request, where a rejected `defer()` value is written. */
|
|
679
|
+
const deferredLogOf = (req) => {
|
|
680
|
+
const log = options.logBoundaryError;
|
|
681
|
+
return log === undefined ? undefined : (error) => log(error, req);
|
|
682
|
+
};
|
|
515
683
|
const boundaryFor = (route, err) => {
|
|
516
684
|
const ids = route.errorIds ?? [];
|
|
517
685
|
const failing = layoutErrorId(err);
|
|
@@ -568,6 +736,8 @@ export function createPageRequestExecutor(options) {
|
|
|
568
736
|
routeId: errorId,
|
|
569
737
|
status: 500,
|
|
570
738
|
hydrate: false,
|
|
739
|
+
signal: req.signal,
|
|
740
|
+
logError: deferredLogOf(req),
|
|
571
741
|
...(layoutData === undefined ? {} : { layoutData }),
|
|
572
742
|
...(personalized || layoutData !== undefined ? { headers: PRIVATE_PAGE_HEADERS } : {}),
|
|
573
743
|
...(nonce === undefined ? {} : { nonce }),
|
|
@@ -645,6 +815,7 @@ export function createPageRequestExecutor(options) {
|
|
|
645
815
|
draft,
|
|
646
816
|
search: loaderSearch(page.searchSchema, c.req),
|
|
647
817
|
set: controls.scope(PAGE_SCOPE),
|
|
818
|
+
live: NO_LIVE,
|
|
648
819
|
};
|
|
649
820
|
if (route.middlewareIds !== undefined)
|
|
650
821
|
await runMiddleware(route, ctx, controls);
|
|
@@ -689,6 +860,8 @@ export function createPageRequestExecutor(options) {
|
|
|
689
860
|
search: searchChainOf(modules, loaded, req),
|
|
690
861
|
path: pathOf(req),
|
|
691
862
|
status,
|
|
863
|
+
signal: req.signal,
|
|
864
|
+
logError: deferredLogOf(req),
|
|
692
865
|
// The browser renders this page inside the root layout, so a page that had to render alone
|
|
693
866
|
// stays server-only, as a nested `_404` does.
|
|
694
867
|
...(wraps && chain === undefined ? { hydrate: false } : {}),
|
|
@@ -703,6 +876,7 @@ export function createPageRequestExecutor(options) {
|
|
|
703
876
|
...copyOption,
|
|
704
877
|
...localeOption(req),
|
|
705
878
|
...titleOption,
|
|
879
|
+
...slotsFor(layoutIds, routeId),
|
|
706
880
|
};
|
|
707
881
|
if (chain === undefined)
|
|
708
882
|
return renderPageResult(statusPage);
|
|
@@ -748,7 +922,7 @@ export function createPageRequestExecutor(options) {
|
|
|
748
922
|
// A faulty reporter must never break error rendering.
|
|
749
923
|
}
|
|
750
924
|
};
|
|
751
|
-
const handleDataOrDocument = async (req, isData, env, route, params, mod, data, layoutData, layoutModules, layoutRetained, boundaryStates, assemblyCache, responseHeaders, personalized, serverComponents) => {
|
|
925
|
+
const handleDataOrDocument = async (req, isData, env, route, params, mod, data, layoutData, layoutModules, layoutRetained, boundaryStates, assemblyCache, responseHeaders, personalized, serverComponents, live) => {
|
|
752
926
|
// Every layout loader settled before the page rendered, so a status or error page can reuse them.
|
|
753
927
|
const settled = async (_page, count) => settledLayouts(layoutModules, layoutData, count);
|
|
754
928
|
if (isStatusSignal(data))
|
|
@@ -770,7 +944,14 @@ export function createPageRequestExecutor(options) {
|
|
|
770
944
|
...deferred,
|
|
771
945
|
...(boundarySplit === undefined ? [] : boundarySplit.deferred),
|
|
772
946
|
];
|
|
773
|
-
|
|
947
|
+
// Deferred when holes run: their owners are known only once the last one settles.
|
|
948
|
+
const liveSplit = live === undefined ? undefined : prepareDeferred(live, allDeferred.length);
|
|
949
|
+
if (liveSplit !== undefined)
|
|
950
|
+
allDeferred.push(...liveSplit.deferred);
|
|
951
|
+
const payload = layoutData === undefined &&
|
|
952
|
+
boundarySplit === undefined &&
|
|
953
|
+
serverComponents === undefined &&
|
|
954
|
+
live === undefined
|
|
774
955
|
? pageSplit.forClient
|
|
775
956
|
: {
|
|
776
957
|
v: 1,
|
|
@@ -779,11 +960,12 @@ export function createPageRequestExecutor(options) {
|
|
|
779
960
|
retained: layoutRetained,
|
|
780
961
|
...(boundarySplit === undefined ? {} : { boundaries: boundarySplit.forClient }),
|
|
781
962
|
...(serverComponents === undefined ? {} : { serverComponents }),
|
|
963
|
+
...(liveSplit === undefined ? {} : { live: liveSplit.forClient }),
|
|
782
964
|
};
|
|
783
965
|
if (allDeferred.length === 0) {
|
|
784
966
|
return Response.json(payload ?? null, { headers: DATA_RESPONSE_HEADERS });
|
|
785
967
|
}
|
|
786
|
-
return new Response(ndjsonStream(payload, allDeferred), {
|
|
968
|
+
return new Response(ndjsonStream(payload, allDeferred, undefined, req.signal, deferredLogOf(req)), {
|
|
787
969
|
headers: {
|
|
788
970
|
"content-type": "application/x-ndjson; charset=utf-8",
|
|
789
971
|
...DATA_RESPONSE_HEADERS,
|
|
@@ -813,6 +995,8 @@ export function createPageRequestExecutor(options) {
|
|
|
813
995
|
search: searchChainOf(layoutModules, mod, req),
|
|
814
996
|
matchChain: matchChainOf(route.layoutIds, layoutModules, route.id, mod),
|
|
815
997
|
hydrate: mod.hydrate !== false,
|
|
998
|
+
signal: req.signal,
|
|
999
|
+
logError: deferredLogOf(req),
|
|
816
1000
|
...preloadOf(route.id),
|
|
817
1001
|
...stylesOf(route.id),
|
|
818
1002
|
...appDocumentOptions,
|
|
@@ -822,6 +1006,11 @@ export function createPageRequestExecutor(options) {
|
|
|
822
1006
|
page.serverComponents = serverComponents;
|
|
823
1007
|
if (progressiveActionData.has(req))
|
|
824
1008
|
page.actionData = progressiveActionData.get(req);
|
|
1009
|
+
const swapSlot = swapSlotOf(req);
|
|
1010
|
+
if (swapSlot !== undefined)
|
|
1011
|
+
page.swapSlot = swapSlot;
|
|
1012
|
+
if (keepTable !== undefined)
|
|
1013
|
+
Object.assign(page, slotsFor(route.layoutIds, route.id));
|
|
825
1014
|
if (layoutData !== undefined)
|
|
826
1015
|
page.layoutData = layoutData;
|
|
827
1016
|
if (boundaryStates !== undefined)
|
|
@@ -919,6 +1108,8 @@ export function createPageRequestExecutor(options) {
|
|
|
919
1108
|
matchChain: matchChainOf(page.layoutIds, layoutModules, id, mod),
|
|
920
1109
|
status: 404,
|
|
921
1110
|
hydrate: false,
|
|
1111
|
+
signal: req.signal,
|
|
1112
|
+
logError: deferredLogOf(req),
|
|
922
1113
|
...localeOption(req),
|
|
923
1114
|
...(layoutData !== undefined ? { layoutData } : {}),
|
|
924
1115
|
...stylesOf(id),
|
|
@@ -984,6 +1175,86 @@ export function createPageRequestExecutor(options) {
|
|
|
984
1175
|
controls.seal();
|
|
985
1176
|
}
|
|
986
1177
|
};
|
|
1178
|
+
const pprRequestFor = (c, route, mod, layouts, boundaries, shellLoader, requestApi, draft, search) => {
|
|
1179
|
+
const path = urlPartsFor(c.req).pathname;
|
|
1180
|
+
const locale = i18n?.localeOf(path);
|
|
1181
|
+
const params = c.params;
|
|
1182
|
+
const shellOrigin = options.ppr?.origin;
|
|
1183
|
+
const variations = options.ppr?.variationsOf(mod.shellVariations, urlPartsFor(c.req).search) ?? {};
|
|
1184
|
+
const shellSearch = options.ppr?.variationQuery(variations) ?? "";
|
|
1185
|
+
// What a shared entry's tags read: its key's parts, never the Host or an undeclared query, which
|
|
1186
|
+
// the first visitor of a key could set so a later purge misses the entry.
|
|
1187
|
+
const shellUrl = () => {
|
|
1188
|
+
const url = new URL(shellOrigin ?? "http://shell.invalid");
|
|
1189
|
+
url.pathname = path;
|
|
1190
|
+
url.search = shellSearch;
|
|
1191
|
+
return url;
|
|
1192
|
+
};
|
|
1193
|
+
// The unbound `api`: a shell is built for every visitor, so no visitor's platform reaches it.
|
|
1194
|
+
const shellContext = shellOrigin === undefined
|
|
1195
|
+
? { phase: "shell", params, locale, variations, api }
|
|
1196
|
+
: { phase: "shell", params, locale, variations, api, origin: shellOrigin };
|
|
1197
|
+
return {
|
|
1198
|
+
routeId: route.id,
|
|
1199
|
+
path,
|
|
1200
|
+
locale,
|
|
1201
|
+
variations,
|
|
1202
|
+
request: c.req,
|
|
1203
|
+
holes: boundaries.filter(isHole),
|
|
1204
|
+
holeContext: { request: c.req, params, api: requestApi, env: c.env, draft, search },
|
|
1205
|
+
shellContext,
|
|
1206
|
+
revalidate: mod.revalidate,
|
|
1207
|
+
waitUntil: c.waitUntil,
|
|
1208
|
+
tags: () => mod.revalidateTags === undefined
|
|
1209
|
+
? []
|
|
1210
|
+
: routeTags(mod.revalidateTags, { params, url: shellUrl() }, route.id),
|
|
1211
|
+
holeTags: (hole) => hole.revalidateTags === undefined
|
|
1212
|
+
? []
|
|
1213
|
+
: routeTags(hole.revalidateTags, { params, url: shellUrl() }, `${route.id}#${hole.name}`),
|
|
1214
|
+
loadShell: async (signal) => shellLoader({ ...shellContext, signal }),
|
|
1215
|
+
renderShellDocument: async (shellData, states) => {
|
|
1216
|
+
const statics = boundaries.some((boundary) => boundary.mode === "static")
|
|
1217
|
+
? await resolveStaticBoundaries(boundaries, { phase: "build" }, options.staticBoundaryCache)
|
|
1218
|
+
: undefined;
|
|
1219
|
+
const { chain, head } = resolveChainAndHead(layouts, undefined, mod,
|
|
1220
|
+
// A Host header is the client's to choose, so a shared shell's meta gets the configured one.
|
|
1221
|
+
{ data: shellData, pageData: shellData, params, origin: shellOrigin ?? "" }, serverLeafOf(route, mod));
|
|
1222
|
+
// Nothing here reads the request beyond its pathname: no origin, search, nonce or headers.
|
|
1223
|
+
const page = {
|
|
1224
|
+
adapter,
|
|
1225
|
+
chain,
|
|
1226
|
+
data: shellData,
|
|
1227
|
+
head,
|
|
1228
|
+
clientEntry,
|
|
1229
|
+
routeId: route.id,
|
|
1230
|
+
params,
|
|
1231
|
+
path,
|
|
1232
|
+
search: searchOfChain([...layouts.map((m) => m.searchSchema), mod.searchSchema], shellSearch === "" ? "" : `?${shellSearch}`),
|
|
1233
|
+
matchChain: matchChainOf(route.layoutIds, layouts, route.id, mod),
|
|
1234
|
+
hydrate: mod.hydrate !== false,
|
|
1235
|
+
boundaries: { ...statics, ...states },
|
|
1236
|
+
...preloadOf(route.id),
|
|
1237
|
+
...stylesOf(route.id),
|
|
1238
|
+
...appDocumentOptions,
|
|
1239
|
+
...(locale === undefined ? {} : localeFor(locale)),
|
|
1240
|
+
...slotsFor(route.layoutIds, route.id),
|
|
1241
|
+
};
|
|
1242
|
+
if (mod.islandScripts !== undefined)
|
|
1243
|
+
page.islandScripts = mod.islandScripts;
|
|
1244
|
+
const result = await renderPageResult(page);
|
|
1245
|
+
// A buffered page hands over its string: a Response round trip to read it back cost more.
|
|
1246
|
+
const rendered = result.toNodeBody?.() ?? result.toResponse();
|
|
1247
|
+
if (rendered.status !== 200) {
|
|
1248
|
+
throw new Error(`[nifra/web] ppr: the shell of "${route.id}" rendered status ${rendered.status}`);
|
|
1249
|
+
}
|
|
1250
|
+
if (rendered instanceof Response)
|
|
1251
|
+
return rendered.text();
|
|
1252
|
+
return typeof rendered.body === "string"
|
|
1253
|
+
? rendered.body
|
|
1254
|
+
: new TextDecoder().decode(rendered.body);
|
|
1255
|
+
},
|
|
1256
|
+
};
|
|
1257
|
+
};
|
|
987
1258
|
return {
|
|
988
1259
|
get: (route) => {
|
|
989
1260
|
const is404Fallback = options.staticFallbacks?.[route.pattern] === "404";
|
|
@@ -1013,7 +1284,8 @@ export function createPageRequestExecutor(options) {
|
|
|
1013
1284
|
}
|
|
1014
1285
|
return slot;
|
|
1015
1286
|
};
|
|
1016
|
-
|
|
1287
|
+
const page = async (c, controls) => {
|
|
1288
|
+
const live = liveLoadOf?.(c.req);
|
|
1017
1289
|
const componentRuntime = options.serverComponents;
|
|
1018
1290
|
const componentOwner = componentRuntime?.owns(route.id) === true;
|
|
1019
1291
|
let componentRequest;
|
|
@@ -1043,6 +1315,9 @@ export function createPageRequestExecutor(options) {
|
|
|
1043
1315
|
let componentState;
|
|
1044
1316
|
const requestApi = apiFor(c);
|
|
1045
1317
|
let run;
|
|
1318
|
+
let skipPage = false;
|
|
1319
|
+
let liveCtx;
|
|
1320
|
+
let holeOwners;
|
|
1046
1321
|
try {
|
|
1047
1322
|
const ctx = {
|
|
1048
1323
|
params: c.params,
|
|
@@ -1053,11 +1328,20 @@ export function createPageRequestExecutor(options) {
|
|
|
1053
1328
|
draft,
|
|
1054
1329
|
search: loaderSearch(mod.searchSchema, c.req),
|
|
1055
1330
|
set: controls.scope(PAGE_SCOPE),
|
|
1331
|
+
live: NO_LIVE,
|
|
1056
1332
|
};
|
|
1057
1333
|
if (route.middlewareIds !== undefined || localeGuarded(route)) {
|
|
1058
1334
|
await runMiddleware(route, ctx, controls);
|
|
1059
1335
|
}
|
|
1060
|
-
|
|
1336
|
+
// After middleware, before any loader: a grant issued to another viewer keeps nothing.
|
|
1337
|
+
const retains = live === undefined || (await live.bind(ctx));
|
|
1338
|
+
skipPage =
|
|
1339
|
+
live !== undefined &&
|
|
1340
|
+
retains &&
|
|
1341
|
+
!componentOwner &&
|
|
1342
|
+
live.retainPage &&
|
|
1343
|
+
live.routeId === route.id;
|
|
1344
|
+
run = await runLayoutChain(route, ctx, controls, componentOwner || !retains ? undefined : retainContextOf(c), undefined, live);
|
|
1061
1345
|
layoutModules = run.modules;
|
|
1062
1346
|
layoutRetained = run.retained;
|
|
1063
1347
|
const boundaryDefinitions = componentRequest === undefined
|
|
@@ -1069,7 +1353,59 @@ export function createPageRequestExecutor(options) {
|
|
|
1069
1353
|
if (boundaryDefinitions.length > 0)
|
|
1070
1354
|
boundaryDescriptors(boundaryDefinitions);
|
|
1071
1355
|
const effectiveSearch = searchChainOf(run.modules, mod, c.req);
|
|
1072
|
-
const
|
|
1356
|
+
const ppr = options.ppr;
|
|
1357
|
+
const shellLoader = componentRequest === undefined && !componentOwner ? mod.shellLoader : undefined;
|
|
1358
|
+
let pprRequest;
|
|
1359
|
+
// How a document that renders whole gets its holes: complete, unless no shell exists yet.
|
|
1360
|
+
let wholeHoles = "settled";
|
|
1361
|
+
if (ppr !== undefined && shellLoader !== undefined) {
|
|
1362
|
+
ppr.assertRoute(route, run.modules, boundaryDefinitions);
|
|
1363
|
+
pprRequest = pprRequestFor(c, route, mod, run.modules, boundaryDefinitions, shellLoader, requestApi, draft, effectiveSearch);
|
|
1364
|
+
const refresh = holeRefresh(c, pprRequest, live);
|
|
1365
|
+
if (refresh instanceof Response)
|
|
1366
|
+
return refresh;
|
|
1367
|
+
pprRequest = refresh.request;
|
|
1368
|
+
holeOwners = refresh.owners;
|
|
1369
|
+
const components = options.serverComponents;
|
|
1370
|
+
const renderHole = components?.hosts?.(route.id) === true ? components.hole : undefined;
|
|
1371
|
+
if (renderHole === undefined &&
|
|
1372
|
+
pprRequest.holes.some((hole) => serverHoleComponent(hole.render) !== undefined)) {
|
|
1373
|
+
throw new Error(`[nifra/web] ppr: route "${route.id}" has a hole that shows a server component; list the component under createServerComponents({ holes }) for this route`);
|
|
1374
|
+
}
|
|
1375
|
+
if (renderHole !== undefined) {
|
|
1376
|
+
const loaderCtx = ctx;
|
|
1377
|
+
pprRequest = {
|
|
1378
|
+
...pprRequest,
|
|
1379
|
+
// The owner path's context, but with the hole's signal and no response to change.
|
|
1380
|
+
component: (name, component, input, signal) => renderHole.call(components, route.id, component, name, input, Object.create(c, Object.getOwnPropertyDescriptors({
|
|
1381
|
+
...loaderCtx,
|
|
1382
|
+
search: effectiveSearch,
|
|
1383
|
+
set: SENT_CONTROLS,
|
|
1384
|
+
signal,
|
|
1385
|
+
data: undefined,
|
|
1386
|
+
layoutData: [],
|
|
1387
|
+
}))),
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
// A draft is the editor's alone, so it never reads or fills the shared shell.
|
|
1391
|
+
if (!draft && !isDataRequest(c)) {
|
|
1392
|
+
const streamed = await ppr.stream(pprRequest, () => controls.documentHeaders());
|
|
1393
|
+
if (streamed instanceof Response)
|
|
1394
|
+
return streamed;
|
|
1395
|
+
wholeHoles = streamed;
|
|
1396
|
+
}
|
|
1397
|
+
// Its holes are one visitor's, so no cache may store the page they render into.
|
|
1398
|
+
controls.personalize();
|
|
1399
|
+
}
|
|
1400
|
+
// Every PPR hole runs through the PPR server, so each has admission and its own deadline.
|
|
1401
|
+
const holeStates = pprRequest === undefined || ppr === undefined
|
|
1402
|
+
? undefined
|
|
1403
|
+
: ppr.server.holeStates(pprRequest, !isDataRequest(c)
|
|
1404
|
+
? wholeHoles
|
|
1405
|
+
: c.req.headers.get(PREFETCH_HEADER) === null
|
|
1406
|
+
? "deferred"
|
|
1407
|
+
: "omitted", c.req.signal);
|
|
1408
|
+
const boundaryBatch = boundaryDefinitions.length === 0 || holeStates !== undefined
|
|
1073
1409
|
? undefined
|
|
1074
1410
|
: startDynamicBoundaries(boundaryDefinitions, {
|
|
1075
1411
|
request: c.req,
|
|
@@ -1079,18 +1415,33 @@ export function createPageRequestExecutor(options) {
|
|
|
1079
1415
|
draft,
|
|
1080
1416
|
search: effectiveSearch,
|
|
1081
1417
|
signal: c.req.signal,
|
|
1418
|
+
live: NO_LIVE,
|
|
1082
1419
|
});
|
|
1083
1420
|
const staticBoundaryPromise = boundaryDefinitions.length === 0
|
|
1084
1421
|
? undefined
|
|
1085
1422
|
: resolveStaticBoundaries(boundaryDefinitions, { phase: "build" }, options.staticBoundaryCache);
|
|
1086
|
-
const pageResult =
|
|
1423
|
+
const pageResult = pprRequest !== undefined && ppr !== undefined
|
|
1424
|
+
? // A key with no shell yet must not build one through its data either.
|
|
1425
|
+
draft || wholeHoles === "deferred"
|
|
1426
|
+
? pprRequest.loadShell(c.req.signal)
|
|
1427
|
+
: ppr.shellData(pprRequest, c.req.signal)
|
|
1428
|
+
: mod.loader === undefined || skipPage
|
|
1429
|
+
? null
|
|
1430
|
+
: live === undefined
|
|
1431
|
+
? mod.loader({ ...ctx, search: effectiveSearch })
|
|
1432
|
+
: runLiveLoader(live, `P:${route.id}`, mod.loader, {
|
|
1433
|
+
...ctx,
|
|
1434
|
+
search: effectiveSearch,
|
|
1435
|
+
});
|
|
1087
1436
|
const [pageData, , staticBoundaries] = run.pending === undefined && staticBoundaryPromise === undefined
|
|
1088
1437
|
? [await pageResult, undefined, undefined]
|
|
1089
1438
|
: staticBoundaryPromise === undefined
|
|
1090
1439
|
? await Promise.all([pageResult, run.pending])
|
|
1091
1440
|
: await Promise.all([pageResult, run.pending, staticBoundaryPromise]);
|
|
1092
1441
|
data = pageData;
|
|
1093
|
-
|
|
1442
|
+
// A gate on a PPR route runs for its redirect or refusal only. Its data reaches no response,
|
|
1443
|
+
// so a soft navigation renders the layouts exactly as the shared shell did.
|
|
1444
|
+
layoutData = pprRequest === undefined ? run.layoutData : undefined;
|
|
1094
1445
|
if (componentOwner) {
|
|
1095
1446
|
for (let i = 0; i < (layoutData?.length ?? 0); i++) {
|
|
1096
1447
|
const outcome = layoutData?.[i];
|
|
@@ -1110,8 +1461,17 @@ export function createPageRequestExecutor(options) {
|
|
|
1110
1461
|
}));
|
|
1111
1462
|
componentState = await componentRuntime.execute(route.id, componentContext, componentRequest, options.nonce === undefined ? undefined : await resolveNonce(c.req, c.env));
|
|
1112
1463
|
}
|
|
1113
|
-
if (
|
|
1114
|
-
|
|
1464
|
+
if (holeStates !== undefined) {
|
|
1465
|
+
// The static resolver names every boundary, leaving the dynamic ones to the holes.
|
|
1466
|
+
boundaryStates = { ...staticBoundaries, ...(await holeStates) };
|
|
1467
|
+
}
|
|
1468
|
+
else if (boundaryBatch !== undefined) {
|
|
1469
|
+
// Each resolver returns a state for every boundary and marks the other's modes
|
|
1470
|
+
// `unresolved`, so a slot takes its state from the resolver of its own mode.
|
|
1471
|
+
const streamed = { ...boundaryBatch.initial };
|
|
1472
|
+
for (const [name, state] of Object.entries(staticBoundaries ?? {}))
|
|
1473
|
+
if (state.mode === "static")
|
|
1474
|
+
streamed[name] = state;
|
|
1115
1475
|
for (const entry of boundaryBatch.pending) {
|
|
1116
1476
|
const initial = streamed[entry.name];
|
|
1117
1477
|
if (initial !== undefined)
|
|
@@ -1125,6 +1485,7 @@ export function createPageRequestExecutor(options) {
|
|
|
1125
1485
|
// Inside the loader `try`: a header the response cannot carry is a loader error, rendered
|
|
1126
1486
|
// by the same boundary as one.
|
|
1127
1487
|
responseHeaders = controls.documentHeaders();
|
|
1488
|
+
liveCtx = ctx;
|
|
1128
1489
|
}
|
|
1129
1490
|
catch (err) {
|
|
1130
1491
|
if (componentRequest !== undefined)
|
|
@@ -1148,8 +1509,13 @@ export function createPageRequestExecutor(options) {
|
|
|
1148
1509
|
if (nested !== undefined)
|
|
1149
1510
|
return nested;
|
|
1150
1511
|
}
|
|
1151
|
-
|
|
1152
|
-
|
|
1512
|
+
const isData = isDataRequest(c);
|
|
1513
|
+
const liveEnvelope = live === undefined || !isData || isStatusSignal(data) || isControlFlow(data)
|
|
1514
|
+
? undefined
|
|
1515
|
+
: await liveEnvelopeOf(live, liveOwners(route, layoutModules, layoutData, layoutRetained, mod, skipPage, liveCtx), holeOwners);
|
|
1516
|
+
return handleDataOrDocument(c.req, isData, c.env, route, c.params, mod, data, layoutData, layoutModules ?? (await loadLayoutModules(route)), layoutRetained, boundaryStates, layoutModules === undefined ? undefined : assemblyCacheFor(mod, layoutModules, c.req), responseHeaders, controls.personalized, componentState, liveEnvelope);
|
|
1517
|
+
};
|
|
1518
|
+
return withResponseControls(liveLoadOf === undefined ? page : admitLive(page));
|
|
1153
1519
|
},
|
|
1154
1520
|
post: (route) => withResponseControls(async (c, controls) => {
|
|
1155
1521
|
const loaded = loadModule(route);
|
|
@@ -1195,6 +1561,7 @@ export function createPageRequestExecutor(options) {
|
|
|
1195
1561
|
draft,
|
|
1196
1562
|
search: loaderSearch(mod.searchSchema, c.req),
|
|
1197
1563
|
set: controls.scope(ACTION_SCOPE),
|
|
1564
|
+
live: NO_LIVE,
|
|
1198
1565
|
};
|
|
1199
1566
|
let gated;
|
|
1200
1567
|
let result;
|
|
@@ -1210,13 +1577,16 @@ export function createPageRequestExecutor(options) {
|
|
|
1210
1577
|
return err;
|
|
1211
1578
|
throw err;
|
|
1212
1579
|
}
|
|
1213
|
-
const
|
|
1214
|
-
const actionResult =
|
|
1215
|
-
const revalidateHeader =
|
|
1216
|
-
? {
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1580
|
+
const revalidation = isRevalidateResult(result) ? result : undefined;
|
|
1581
|
+
const actionResult = revalidation === undefined ? result : revalidation.data;
|
|
1582
|
+
const revalidateHeader = revalidation === undefined
|
|
1583
|
+
? {}
|
|
1584
|
+
: { [REVALIDATE_HEADER]: revalidation.__nifraRevalidate.join(",") };
|
|
1585
|
+
// The write is done, whatever the action answers with (a redirect included): what it made
|
|
1586
|
+
// stale is invalidated before the answer leaves.
|
|
1587
|
+
const settling = revalidation === undefined ? undefined : options.invalidate?.(revalidation, c.req);
|
|
1588
|
+
if (settling !== undefined)
|
|
1589
|
+
await settling;
|
|
1220
1590
|
if (isControlFlow(actionResult))
|
|
1221
1591
|
return actionResult;
|
|
1222
1592
|
if (isDataRequest) {
|
|
@@ -1229,7 +1599,7 @@ export function createPageRequestExecutor(options) {
|
|
|
1229
1599
|
headers: { ...DATA_RESPONSE_HEADERS, ...revalidateHeader },
|
|
1230
1600
|
});
|
|
1231
1601
|
}
|
|
1232
|
-
return new Response(ndjsonStream(forClient, deferred), {
|
|
1602
|
+
return new Response(ndjsonStream(forClient, deferred, undefined, c.req.signal, deferredLogOf(c.req)), {
|
|
1233
1603
|
headers: {
|
|
1234
1604
|
"content-type": "application/x-ndjson; charset=utf-8",
|
|
1235
1605
|
...DATA_RESPONSE_HEADERS,
|
|
@@ -1308,6 +1678,7 @@ export function createPageRequestExecutor(options) {
|
|
|
1308
1678
|
draft,
|
|
1309
1679
|
search,
|
|
1310
1680
|
set: controls.scope(PAGE_SCOPE),
|
|
1681
|
+
live: NO_LIVE,
|
|
1311
1682
|
})
|
|
1312
1683
|
: null;
|
|
1313
1684
|
const [pageData] = run.pending === undefined
|
|
@@ -1344,7 +1715,7 @@ export function createPageRequestExecutor(options) {
|
|
|
1344
1715
|
origin: originFor(c.req),
|
|
1345
1716
|
...(nonce === undefined ? {} : { nonce }),
|
|
1346
1717
|
}, serverLeafOf(route, mod));
|
|
1347
|
-
|
|
1718
|
+
const page = {
|
|
1348
1719
|
adapter,
|
|
1349
1720
|
chain,
|
|
1350
1721
|
data,
|
|
@@ -1357,6 +1728,8 @@ export function createPageRequestExecutor(options) {
|
|
|
1357
1728
|
search,
|
|
1358
1729
|
matchChain: matchChainOf(route.layoutIds, layoutModules, route.id, mod),
|
|
1359
1730
|
hydrate: mod.hydrate !== false,
|
|
1731
|
+
signal: c.req.signal,
|
|
1732
|
+
logError: deferredLogOf(c.req),
|
|
1360
1733
|
...layoutProps,
|
|
1361
1734
|
...preloadOf(route.id),
|
|
1362
1735
|
...stylesOf(route.id),
|
|
@@ -1367,8 +1740,18 @@ export function createPageRequestExecutor(options) {
|
|
|
1367
1740
|
...(nonce === undefined ? {} : { nonce }),
|
|
1368
1741
|
...cspOption,
|
|
1369
1742
|
...titleOption,
|
|
1370
|
-
|
|
1743
|
+
...swapOption(c.req),
|
|
1744
|
+
...slotsFor(route.layoutIds, route.id),
|
|
1745
|
+
};
|
|
1746
|
+
if (mod.islandScripts !== undefined)
|
|
1747
|
+
page.islandScripts = mod.islandScripts;
|
|
1748
|
+
return renderPageResult(page);
|
|
1371
1749
|
}),
|
|
1750
|
+
matchRoute: (path) => {
|
|
1751
|
+
const matched = matchManifestRoute(path);
|
|
1752
|
+
const route = matched === null ? undefined : routeById.get(matched.routeId);
|
|
1753
|
+
return route === undefined ? null : { routeId: route.id, layoutIds: route.layoutIds };
|
|
1754
|
+
},
|
|
1372
1755
|
fallback: withResponseControls(async (c, controls) => {
|
|
1373
1756
|
const match = matchNotFoundScope?.(pathOf(c.req)) ?? null;
|
|
1374
1757
|
const target = match === null ? undefined : notFoundTargets[Number(match.routeId)];
|
|
@@ -1394,6 +1777,7 @@ export function createPageRequestExecutor(options) {
|
|
|
1394
1777
|
draft,
|
|
1395
1778
|
search: loaderSearch(mod.searchSchema, c.req),
|
|
1396
1779
|
set: controls.scope(PAGE_SCOPE),
|
|
1780
|
+
live: NO_LIVE,
|
|
1397
1781
|
};
|
|
1398
1782
|
if (target.route.middlewareIds !== undefined) {
|
|
1399
1783
|
await runMiddleware(target.route, ctx, controls);
|