@nifrajs/web 5.5.1 → 5.7.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 +243 -0
- package/README.md +21 -11
- 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 +10 -0
- package/dist/build.js.map +1 -1
- package/dist/client.d.ts +3 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +174 -23
- 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 +8 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- 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 +73 -10
- 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 +180 -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 +101 -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 +16 -1
- package/dist/internal/page-execution.d.ts.map +1 -1
- package/dist/internal/page-execution.js +432 -31
- package/dist/internal/page-execution.js.map +1 -1
- package/dist/internal/parity.d.ts +3 -0
- package/dist/internal/parity.d.ts.map +1 -1
- package/dist/internal/parity.js +28 -5
- package/dist/internal/parity.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 +35 -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 +1409 -0
- package/dist/internal/ppr/server.js.map +1 -0
- package/dist/internal/ppr/shell-store.d.ts +39 -0
- package/dist/internal/ppr/shell-store.d.ts.map +1 -0
- package/dist/internal/ppr/shell-store.js +106 -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/present.d.ts +11 -0
- package/dist/internal/present.d.ts.map +1 -0
- package/dist/internal/present.js +13 -0
- package/dist/internal/present.js.map +1 -0
- 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 +34 -64
- package/dist/internal/render-document.d.ts.map +1 -1
- package/dist/internal/render-document.js +82 -64
- 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/status-page.d.ts +68 -0
- package/dist/internal/status-page.d.ts.map +1 -0
- package/dist/internal/status-page.js +66 -0
- package/dist/internal/status-page.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 +26 -0
- package/dist/internal/web-app.d.ts.map +1 -1
- package/dist/internal/web-app.js +79 -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 +1102 -0
- package/dist/keep-alive.js.map +1 -0
- package/dist/live/bus.d.ts +70 -0
- package/dist/live/bus.d.ts.map +1 -0
- package/dist/live/bus.js +55 -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 +185 -0
- package/dist/live/hub.d.ts.map +1 -0
- package/dist/live/hub.js +544 -0
- package/dist/live/hub.js.map +1 -0
- package/dist/live/keyring.d.ts +92 -0
- package/dist/live/keyring.d.ts.map +1 -0
- package/dist/live/keyring.js +265 -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 +878 -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 +60 -0
- package/dist/live-redis.d.ts.map +1 -0
- package/dist/live-redis.js +212 -0
- package/dist/live-redis.js.map +1 -0
- package/dist/live.d.ts +150 -0
- package/dist/live.d.ts.map +1 -0
- package/dist/live.js +972 -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 +32 -0
- package/dist/render-seam.d.ts.map +1 -1
- package/dist/render-seam.js +7 -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 +416 -129
- 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/definition.d.ts.map +1 -1
- package/dist/server-components/definition.js +2 -1
- package/dist/server-components/definition.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 +88 -29
- 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/server-components/vite.d.ts.map +1 -1
- package/dist/server-components/vite.js +26 -9
- package/dist/server-components/vite.js.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 +20 -1
- package/src/client.ts +183 -23
- 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 +20 -4
- package/src/internal/cdn/cloudflare.ts +2 -1
- package/src/internal/certified-components.ts +1 -1
- package/src/internal/codegen.ts +95 -9
- package/src/internal/dev-websocket.ts +230 -0
- package/src/internal/hole-runtime.ts +286 -0
- package/src/internal/identity-epoch.ts +239 -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 +111 -0
- package/src/internal/live-queries.ts +60 -0
- package/src/internal/page-events.ts +27 -0
- package/src/internal/page-execution.ts +605 -45
- package/src/internal/parity.ts +35 -6
- 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 +39 -0
- package/src/internal/ppr/server.ts +1766 -0
- package/src/internal/ppr/shell-store.ts +152 -0
- package/src/internal/prerendered-pages.ts +7 -4
- package/src/internal/present.ts +20 -0
- 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 +158 -113
- 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/status-page.ts +95 -0
- package/src/internal/store-signature.ts +149 -0
- package/src/internal/vite-plugin-compat.ts +2 -2
- package/src/internal/web-app.ts +123 -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 +1354 -0
- package/src/live/bus.ts +104 -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 +620 -0
- package/src/live/keyring.ts +384 -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 +1011 -0
- package/src/live-hub.ts +658 -0
- package/src/live-redis.ts +284 -0
- package/src/live.ts +1257 -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 +37 -0
- package/src/route-types.ts +3 -1
- package/src/router.ts +534 -132
- package/src/server-components/build.ts +9 -3
- package/src/server-components/client.ts +89 -5
- package/src/server-components/definition.ts +2 -1
- 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 +114 -31
- package/src/server-components/state.ts +66 -44
- package/src/server-components/types.ts +16 -0
- package/src/server-components/vite.ts +33 -14
- 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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CapabilityDefinition } from "@nifrajs/core/capabilities"
|
|
2
|
-
import { isResponseResult
|
|
2
|
+
import { isResponseResult } from "../internal/render-document.ts"
|
|
3
|
+
import { isStatusSignal, STATUS_SIGNAL } from "../internal/status-page.ts"
|
|
3
4
|
import {
|
|
4
5
|
authorizeServerComponent,
|
|
5
6
|
isServerComponent,
|
|
@@ -20,9 +21,11 @@ import {
|
|
|
20
21
|
import { BoundedComponentTasks, componentAbort } from "./scheduler.ts"
|
|
21
22
|
import { componentValue } from "./schema.ts"
|
|
22
23
|
import type {
|
|
24
|
+
ComponentHoles,
|
|
23
25
|
ComponentInstance,
|
|
24
26
|
ComponentOwner,
|
|
25
27
|
ComponentRecord,
|
|
28
|
+
ComponentRender,
|
|
26
29
|
ServerComponent,
|
|
27
30
|
ServerComponentContext,
|
|
28
31
|
ServerComponentHandle,
|
|
@@ -42,6 +45,11 @@ export interface ServerComponentsOptions {
|
|
|
42
45
|
readonly build: string
|
|
43
46
|
readonly adapter: ServerComponentRenderAdapter
|
|
44
47
|
readonly owners: Readonly<Record<string, ComponentOwner>>
|
|
48
|
+
/**
|
|
49
|
+
* Experimental: route id to the server components its partially prerendered holes may show, each
|
|
50
|
+
* through `serverHole`. A route is an owner or hosts holes, never both.
|
|
51
|
+
*/
|
|
52
|
+
readonly holes?: Readonly<Record<string, ComponentHoles>>
|
|
45
53
|
readonly capabilities?: readonly CapabilityDefinition[]
|
|
46
54
|
readonly assets?: readonly string[]
|
|
47
55
|
}
|
|
@@ -103,20 +111,19 @@ export function createServerComponents(options: ServerComponentsOptions): Server
|
|
|
103
111
|
)
|
|
104
112
|
if (capabilities.size !== (options.capabilities?.length ?? 0))
|
|
105
113
|
throw new ServerComponentError("component_contract")
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
throw new ServerComponentError("component_contract")
|
|
114
|
+
/** A route's component allowlist, by reference, each within the capabilities the route grants. */
|
|
115
|
+
const allowlist = (
|
|
116
|
+
list: readonly ServerComponentHandle[],
|
|
117
|
+
granted: readonly string[] = [],
|
|
118
|
+
): ReadonlyMap<string, ServerComponentHandle> => {
|
|
112
119
|
const components = new Map<string, ServerComponentHandle>()
|
|
113
|
-
const allowed = new Set(
|
|
120
|
+
const allowed = new Set(granted)
|
|
114
121
|
for (const capability of allowed) {
|
|
115
122
|
const declared = capabilities.get(capability)
|
|
116
123
|
if (declared === undefined || (declared.zone === "domain" && declared.access !== "read"))
|
|
117
124
|
throw new ServerComponentError("component_contract")
|
|
118
125
|
}
|
|
119
|
-
for (const component of
|
|
126
|
+
for (const component of list) {
|
|
120
127
|
if (
|
|
121
128
|
!isServerComponent(component) ||
|
|
122
129
|
component.reference === undefined ||
|
|
@@ -128,11 +135,106 @@ export function createServerComponents(options: ServerComponentsOptions): Server
|
|
|
128
135
|
if (serverComponentCapabilities(component).some((capability) => !allowed.has(capability)))
|
|
129
136
|
throw new ServerComponentError("component_contract")
|
|
130
137
|
}
|
|
131
|
-
|
|
138
|
+
return components
|
|
139
|
+
}
|
|
140
|
+
for (const [routeId, owner] of Object.entries(options.owners)) {
|
|
141
|
+
if (
|
|
142
|
+
typeof owner.instances !== "function" ||
|
|
143
|
+
(owner.scope !== "anonymous" && typeof owner.scope !== "function")
|
|
144
|
+
)
|
|
145
|
+
throw new ServerComponentError("component_contract")
|
|
146
|
+
owners.set(routeId, { owner, components: allowlist(owner.components, owner.capabilities) })
|
|
147
|
+
}
|
|
148
|
+
const holes = new Map<
|
|
149
|
+
string,
|
|
150
|
+
{ capabilities: readonly string[]; components: ReadonlyMap<string, ServerComponentHandle> }
|
|
151
|
+
>()
|
|
152
|
+
for (const [routeId, entry] of Object.entries(options.holes ?? {})) {
|
|
153
|
+
// An owner route renders whole, so its holes never run.
|
|
154
|
+
if (owners.has(routeId) || !Array.isArray(entry.components))
|
|
155
|
+
throw new ServerComponentError("component_contract")
|
|
156
|
+
holes.set(routeId, {
|
|
157
|
+
capabilities: entry.capabilities ?? [],
|
|
158
|
+
components: allowlist(entry.components, entry.capabilities),
|
|
159
|
+
})
|
|
160
|
+
}
|
|
161
|
+
/** A rendered instance as a record: its range HTML checked, its payload and island props public. */
|
|
162
|
+
const recordOf = (
|
|
163
|
+
instance: {
|
|
164
|
+
readonly id: string
|
|
165
|
+
readonly reference: string
|
|
166
|
+
readonly key: ComponentRecord["key"]
|
|
167
|
+
readonly parent?: string | undefined
|
|
168
|
+
readonly input: ComponentRecord["input"]
|
|
169
|
+
},
|
|
170
|
+
rendered: ComponentRender,
|
|
171
|
+
): ComponentRecord => {
|
|
172
|
+
const record: ComponentRecord = {
|
|
173
|
+
instance: instance.id,
|
|
174
|
+
reference: instance.reference,
|
|
175
|
+
key: instance.key,
|
|
176
|
+
parent: instance.parent ?? null,
|
|
177
|
+
input: instance.input,
|
|
178
|
+
html: componentRangeHTML(rendered.html, assets),
|
|
179
|
+
}
|
|
180
|
+
if (rendered.payload !== undefined)
|
|
181
|
+
Object.assign(record, {
|
|
182
|
+
payload: componentValue(rendered.payload, COMPONENT_LIMITS.outputBytes),
|
|
183
|
+
})
|
|
184
|
+
if (rendered.islands !== undefined)
|
|
185
|
+
Object.assign(record, {
|
|
186
|
+
islands: rendered.islands.map((island) => ({
|
|
187
|
+
...island,
|
|
188
|
+
props: componentValue(island.props),
|
|
189
|
+
})),
|
|
190
|
+
})
|
|
191
|
+
return record
|
|
132
192
|
}
|
|
133
193
|
return {
|
|
134
194
|
owns: (routeId) => owners.has(routeId),
|
|
135
|
-
capabilities: (routeId) =>
|
|
195
|
+
capabilities: (routeId) =>
|
|
196
|
+
owners.get(routeId)?.owner.capabilities ?? holes.get(routeId)?.capabilities ?? [],
|
|
197
|
+
hosts: (routeId) => holes.has(routeId),
|
|
198
|
+
async hole(routeId, component, key, input, context) {
|
|
199
|
+
const reference = isServerComponent(component) ? component.reference : undefined
|
|
200
|
+
const handle =
|
|
201
|
+
reference === undefined ? undefined : holes.get(routeId)?.components.get(reference)
|
|
202
|
+
if (
|
|
203
|
+
reference === undefined ||
|
|
204
|
+
handle === undefined ||
|
|
205
|
+
handle !== component ||
|
|
206
|
+
!componentKey(key)
|
|
207
|
+
)
|
|
208
|
+
throw new ServerComponentError("component_contract")
|
|
209
|
+
const signal = AbortSignal.any([
|
|
210
|
+
context.signal,
|
|
211
|
+
AbortSignal.timeout(COMPONENT_LIMITS.executionMs),
|
|
212
|
+
])
|
|
213
|
+
const ctx: ServerComponentContext = Object.create(context, {
|
|
214
|
+
signal: { value: signal, enumerable: true },
|
|
215
|
+
})
|
|
216
|
+
const value = componentValue(input, COMPONENT_LIMITS.inputBytes)
|
|
217
|
+
const id = await serverComponentIdentity(routeId, handle, key, null)
|
|
218
|
+
return admission.run(signal, async () => {
|
|
219
|
+
// biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: The route's hole allowlist identifies this handle; authorization validates the input before any callback runs.
|
|
220
|
+
const authorized = await authorizeServerComponent(handle as ServerComponent, value, ctx)
|
|
221
|
+
const prepared = await prepareAuthorizedServerComponent(authorized)
|
|
222
|
+
const rendered = await options.adapter.render(prepared.view, prepared.props, {
|
|
223
|
+
reference,
|
|
224
|
+
instance: id,
|
|
225
|
+
signal,
|
|
226
|
+
children: [],
|
|
227
|
+
})
|
|
228
|
+
if (signal.aborted) throw componentAbort(signal)
|
|
229
|
+
if (typeof rendered.html !== "string") throw new ServerComponentError("component_contract")
|
|
230
|
+
if ((rendered.islands?.length ?? 0) > COMPONENT_LIMITS.instances)
|
|
231
|
+
throw new ServerComponentError("component_limit", 413)
|
|
232
|
+
const record = recordOf({ id, reference, key, input: value }, rendered)
|
|
233
|
+
if (encoder.encode(JSON.stringify(record)).length > COMPONENT_LIMITS.outputBytes)
|
|
234
|
+
throw new ServerComponentError("component_limit", 413)
|
|
235
|
+
return record
|
|
236
|
+
})
|
|
237
|
+
},
|
|
136
238
|
classify(request, routeId) {
|
|
137
239
|
const selection = readComponentRequest(request)
|
|
138
240
|
if (selection === undefined) return undefined
|
|
@@ -275,29 +377,10 @@ export function createServerComponents(options: ServerComponentsOptions): Server
|
|
|
275
377
|
if (signal.aborted) throw componentAbort(signal)
|
|
276
378
|
if (typeof rendered.html !== "string")
|
|
277
379
|
throw new ServerComponentError("component_contract")
|
|
278
|
-
const
|
|
380
|
+
const record = recordOf({ ...instance, id }, rendered)
|
|
279
381
|
count += rendered.islands?.length ?? 0
|
|
280
382
|
if (count > COMPONENT_LIMITS.instances)
|
|
281
383
|
throw new ServerComponentError("component_limit", 413)
|
|
282
|
-
const record: ComponentRecord = {
|
|
283
|
-
instance: id,
|
|
284
|
-
reference: instance.reference,
|
|
285
|
-
key: instance.key,
|
|
286
|
-
parent: instance.parent ?? null,
|
|
287
|
-
input: instance.input,
|
|
288
|
-
html,
|
|
289
|
-
}
|
|
290
|
-
if (rendered.payload !== undefined)
|
|
291
|
-
Object.assign(record, {
|
|
292
|
-
payload: componentValue(rendered.payload, COMPONENT_LIMITS.outputBytes),
|
|
293
|
-
})
|
|
294
|
-
if (rendered.islands !== undefined)
|
|
295
|
-
Object.assign(record, {
|
|
296
|
-
islands: rendered.islands.map((island) => ({
|
|
297
|
-
...island,
|
|
298
|
-
props: componentValue(island.props),
|
|
299
|
-
})),
|
|
300
|
-
})
|
|
301
384
|
const size = encoder.encode(JSON.stringify(record)).length
|
|
302
385
|
bytes += size
|
|
303
386
|
if (
|
|
@@ -50,6 +50,69 @@ function island(value: unknown): ComponentIsland {
|
|
|
50
50
|
if (item.slots !== undefined) Object.assign(result, { slots: item.slots })
|
|
51
51
|
return result
|
|
52
52
|
}
|
|
53
|
+
/** One record, checked as each of a state's is; `ids` and `islandIds` collect what must be unique. */
|
|
54
|
+
function componentRecord(
|
|
55
|
+
value: unknown,
|
|
56
|
+
ids: Set<string>,
|
|
57
|
+
islandIds: Set<string>,
|
|
58
|
+
): ComponentRecord {
|
|
59
|
+
const record = object(
|
|
60
|
+
value,
|
|
61
|
+
["instance", "reference", "key", "parent", "input", "html"],
|
|
62
|
+
["payload", "islands"],
|
|
63
|
+
)
|
|
64
|
+
if (
|
|
65
|
+
!componentToken(record.instance) ||
|
|
66
|
+
ids.has(record.instance) ||
|
|
67
|
+
!componentToken(record.reference) ||
|
|
68
|
+
!componentKey(record.key) ||
|
|
69
|
+
(record.parent !== null && !componentToken(record.parent)) ||
|
|
70
|
+
typeof record.html !== "string"
|
|
71
|
+
)
|
|
72
|
+
throw new ServerComponentError("component_protocol", 400)
|
|
73
|
+
ids.add(record.instance)
|
|
74
|
+
validateComponentHTML(record.html)
|
|
75
|
+
if (
|
|
76
|
+
record.islands !== undefined &&
|
|
77
|
+
(!Array.isArray(record.islands) || record.islands.length > COMPONENT_LIMITS.instances)
|
|
78
|
+
)
|
|
79
|
+
throw new ServerComponentError("component_protocol", 400)
|
|
80
|
+
const islands =
|
|
81
|
+
// biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: Object shape is checked before field inspection; each typed field follows its closed vocabulary check.
|
|
82
|
+
record.islands === undefined ? undefined : (record.islands as unknown[]).map(island)
|
|
83
|
+
for (const island of islands ?? []) {
|
|
84
|
+
if (islandIds.has(island.renderId)) throw new ServerComponentError("component_protocol", 400)
|
|
85
|
+
islandIds.add(island.renderId)
|
|
86
|
+
}
|
|
87
|
+
const result: ComponentRecord = {
|
|
88
|
+
instance: record.instance,
|
|
89
|
+
reference: record.reference,
|
|
90
|
+
key: record.key,
|
|
91
|
+
// biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: Object shape is checked before field inspection; each typed field follows its closed vocabulary check.
|
|
92
|
+
parent: record.parent as string | null,
|
|
93
|
+
html: record.html,
|
|
94
|
+
input: componentValue(record.input, COMPONENT_LIMITS.inputBytes),
|
|
95
|
+
}
|
|
96
|
+
if (record.payload !== undefined)
|
|
97
|
+
Object.assign(result, {
|
|
98
|
+
payload: componentValue(record.payload, COMPONENT_LIMITS.outputBytes),
|
|
99
|
+
})
|
|
100
|
+
if (islands !== undefined) Object.assign(result, { islands })
|
|
101
|
+
return result
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** A record that arrives alone, as a partially prerendered hole's does, checked as a state's are. */
|
|
105
|
+
export function parseComponentRecord(value: unknown): ComponentRecord {
|
|
106
|
+
const record = componentRecord(
|
|
107
|
+
componentValue(value, COMPONENT_LIMITS.documentBytes),
|
|
108
|
+
new Set(),
|
|
109
|
+
new Set(),
|
|
110
|
+
)
|
|
111
|
+
if ((record.islands?.length ?? 0) >= COMPONENT_LIMITS.instances)
|
|
112
|
+
throw new ServerComponentError("component_limit", 413)
|
|
113
|
+
return record
|
|
114
|
+
}
|
|
115
|
+
|
|
53
116
|
export function parseComponentState(
|
|
54
117
|
value: unknown,
|
|
55
118
|
maxBytes: number = COMPONENT_LIMITS.documentBytes,
|
|
@@ -72,52 +135,11 @@ export function parseComponentState(
|
|
|
72
135
|
const islandIds = new Set<string>()
|
|
73
136
|
let instances = state.records.length
|
|
74
137
|
const records: ComponentRecord[] = state.records.map((value: unknown) => {
|
|
75
|
-
const record =
|
|
76
|
-
|
|
77
|
-
["instance", "reference", "key", "parent", "input", "html"],
|
|
78
|
-
["payload", "islands"],
|
|
79
|
-
)
|
|
80
|
-
if (
|
|
81
|
-
!componentToken(record.instance) ||
|
|
82
|
-
ids.has(record.instance) ||
|
|
83
|
-
!componentToken(record.reference) ||
|
|
84
|
-
!componentKey(record.key) ||
|
|
85
|
-
(record.parent !== null && !componentToken(record.parent)) ||
|
|
86
|
-
typeof record.html !== "string"
|
|
87
|
-
)
|
|
88
|
-
throw new ServerComponentError("component_protocol", 400)
|
|
89
|
-
ids.add(record.instance)
|
|
90
|
-
validateComponentHTML(record.html)
|
|
91
|
-
if (
|
|
92
|
-
record.islands !== undefined &&
|
|
93
|
-
(!Array.isArray(record.islands) || record.islands.length > COMPONENT_LIMITS.instances)
|
|
94
|
-
)
|
|
95
|
-
throw new ServerComponentError("component_protocol", 400)
|
|
96
|
-
const islands =
|
|
97
|
-
// biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: Object shape is checked before field inspection; each typed field follows its closed vocabulary check.
|
|
98
|
-
record.islands === undefined ? undefined : (record.islands as unknown[]).map(island)
|
|
99
|
-
for (const island of islands ?? []) {
|
|
100
|
-
if (islandIds.has(island.renderId)) throw new ServerComponentError("component_protocol", 400)
|
|
101
|
-
islandIds.add(island.renderId)
|
|
102
|
-
}
|
|
103
|
-
instances += islands?.length ?? 0
|
|
138
|
+
const record = componentRecord(value, ids, islandIds)
|
|
139
|
+
instances += record.islands?.length ?? 0
|
|
104
140
|
if (instances > COMPONENT_LIMITS.instances)
|
|
105
141
|
throw new ServerComponentError("component_limit", 413)
|
|
106
|
-
|
|
107
|
-
instance: record.instance,
|
|
108
|
-
reference: record.reference,
|
|
109
|
-
key: record.key,
|
|
110
|
-
// biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: Object shape is checked before field inspection; each typed field follows its closed vocabulary check.
|
|
111
|
-
parent: record.parent as string | null,
|
|
112
|
-
html: record.html,
|
|
113
|
-
input: componentValue(record.input, COMPONENT_LIMITS.inputBytes),
|
|
114
|
-
}
|
|
115
|
-
if (record.payload !== undefined)
|
|
116
|
-
Object.assign(result, {
|
|
117
|
-
payload: componentValue(record.payload, COMPONENT_LIMITS.outputBytes),
|
|
118
|
-
})
|
|
119
|
-
if (islands !== undefined) Object.assign(result, { islands })
|
|
120
|
-
return result
|
|
142
|
+
return record
|
|
121
143
|
})
|
|
122
144
|
// Refreshes contain a single child and may name its already mounted parent. Documents carry the full forest.
|
|
123
145
|
for (const record of records) {
|
|
@@ -98,6 +98,12 @@ export interface ComponentOwner {
|
|
|
98
98
|
readonly scope: "anonymous" | ((ctx: ServerComponentContext) => string | Promise<string>)
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
/** Experimental: the server components a partially prerendered route's holes may show. */
|
|
102
|
+
export interface ComponentHoles {
|
|
103
|
+
readonly components: readonly ServerComponentHandle[]
|
|
104
|
+
readonly capabilities?: readonly string[]
|
|
105
|
+
}
|
|
106
|
+
|
|
101
107
|
export interface ServerComponentHandle {
|
|
102
108
|
readonly reference?: string
|
|
103
109
|
readonly input: StandardSchemaV1
|
|
@@ -135,4 +141,14 @@ export interface ServerComponentRuntime {
|
|
|
135
141
|
): Promise<ComponentState>
|
|
136
142
|
response(state: ComponentState, selection: import("./protocol.ts").ComponentRequest): Response
|
|
137
143
|
failure(error: unknown): Response
|
|
144
|
+
/** Whether the route's partially prerendered holes may show server components. */
|
|
145
|
+
hosts?(routeId: string): boolean
|
|
146
|
+
/** Render `component` as the route's hole `key`, with the hole loader's output as its input. */
|
|
147
|
+
hole?(
|
|
148
|
+
routeId: string,
|
|
149
|
+
component: object,
|
|
150
|
+
key: string,
|
|
151
|
+
input: unknown,
|
|
152
|
+
context: ServerComponentContext,
|
|
153
|
+
): Promise<ComponentRecord>
|
|
138
154
|
}
|
|
@@ -25,6 +25,17 @@ const reactRuntime = "\0nifra-component-react-runtime"
|
|
|
25
25
|
// Vite uses forward slashes for module IDs even when the filesystem uses backslashes.
|
|
26
26
|
const bare = (id: string): string => (id.split("?")[0] ?? id).replaceAll("\\", "/")
|
|
27
27
|
|
|
28
|
+
// Blanked with spaces rather than cut, so later offsets still point at the source.
|
|
29
|
+
function withoutUseClient(source: string, path: string): string {
|
|
30
|
+
for (const statement of sourceFile(source, path).statements) {
|
|
31
|
+
if (!ts.isExpressionStatement(statement) || !ts.isStringLiteral(statement.expression)) break
|
|
32
|
+
if (statement.expression.text !== "use client") continue
|
|
33
|
+
const start = statement.getStart()
|
|
34
|
+
return `${source.slice(0, start)}${" ".repeat(statement.end - start)}${source.slice(statement.end)}`
|
|
35
|
+
}
|
|
36
|
+
return source
|
|
37
|
+
}
|
|
38
|
+
|
|
28
39
|
export interface ServerComponentsPluginOptions {
|
|
29
40
|
readonly root: string
|
|
30
41
|
readonly adapter: "react" | "vue" | "svelte" | "solid" | "preact"
|
|
@@ -224,23 +235,31 @@ export function serverComponentsPlugin(
|
|
|
224
235
|
return null
|
|
225
236
|
},
|
|
226
237
|
transform(source, id) {
|
|
227
|
-
if (id.includes(nativeQuery) || internal.has(bare(id))) return null
|
|
228
238
|
const path = bare(id)
|
|
229
239
|
let code = source
|
|
230
|
-
if (
|
|
231
|
-
|
|
232
|
-
|
|
240
|
+
if (!id.includes(nativeQuery) && !internal.has(path)) {
|
|
241
|
+
if (componentFile.test(path)) {
|
|
242
|
+
code = compileComponentModule(source, path, options.root, this.environment.name).code
|
|
243
|
+
componentModules.add(path)
|
|
244
|
+
}
|
|
245
|
+
if (islandFile.test(path))
|
|
246
|
+
code = compileComponentModule(
|
|
247
|
+
source,
|
|
248
|
+
path,
|
|
249
|
+
options.root,
|
|
250
|
+
this.environment.name,
|
|
251
|
+
"island",
|
|
252
|
+
).code
|
|
253
|
+
if (this.environment.name === "rsc" && options.adapter === "react")
|
|
254
|
+
code = guardCreateElementCalls(code, path)
|
|
233
255
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
).code
|
|
242
|
-
if (this.environment.name === "rsc" && options.adapter === "react")
|
|
243
|
-
code = guardCreateElementCalls(code, path)
|
|
256
|
+
// Only the rsc build acts on "use client"; the client and ssr bundles would warn about it.
|
|
257
|
+
if (
|
|
258
|
+
this.environment.name !== "rsc" &&
|
|
259
|
+
options.adapter === "react" &&
|
|
260
|
+
code.includes("use client")
|
|
261
|
+
)
|
|
262
|
+
code = withoutUseClient(code, path)
|
|
244
263
|
return code === source ? null : { code, map: null }
|
|
245
264
|
},
|
|
246
265
|
}
|
package/src/vite.ts
CHANGED
|
@@ -23,10 +23,21 @@ import { isHiddenPath } from "@nifrajs/core/static-file"
|
|
|
23
23
|
import { CLIENT_BATCH_MAX_BYTES } from "./dev-client.ts"
|
|
24
24
|
import { DEV_REQUEST_ID_HEADER, DEV_SERVER_FILES } from "./dev-feed.ts"
|
|
25
25
|
import { listenOrExplain } from "./dev-port.ts"
|
|
26
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
createDevSession,
|
|
28
|
+
type DevAppHooks,
|
|
29
|
+
isDevAgentPath,
|
|
30
|
+
isLoopbackHost,
|
|
31
|
+
} from "./dev-session.ts"
|
|
27
32
|
import { discoverRoutes } from "./fs.ts"
|
|
28
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
DEFAULT_DEV_PORT,
|
|
35
|
+
generateClientEntry,
|
|
36
|
+
type LiveClientEntry,
|
|
37
|
+
setSsrModuleLoader,
|
|
38
|
+
} from "./index.ts"
|
|
29
39
|
import { admitViteTags } from "./internal/dev-csp.ts"
|
|
40
|
+
import { type UpgradableApp, upgradeOnNode } from "./internal/dev-websocket.ts"
|
|
30
41
|
import { viteDedupePackages } from "./internal/identity-policy.ts"
|
|
31
42
|
import {
|
|
32
43
|
assertIdentityParity,
|
|
@@ -49,7 +60,7 @@ import { viteServerFnStub } from "./plugins/vite-server-fn.ts"
|
|
|
49
60
|
import { viteZoneGuard } from "./plugins/vite-zone-guard.ts"
|
|
50
61
|
|
|
51
62
|
/** Minimal app surface - `createWebApp(...)` satisfies it. */
|
|
52
|
-
interface FetchApp {
|
|
63
|
+
interface FetchApp extends UpgradableApp {
|
|
53
64
|
fetch(request: Request): Response | Promise<Response>
|
|
54
65
|
}
|
|
55
66
|
|
|
@@ -79,7 +90,10 @@ interface ViteLike {
|
|
|
79
90
|
/** The module graph, used to invalidate a changed file AND its transitive importers before reload. */
|
|
80
91
|
readonly moduleGraph?: ViteModuleGraph
|
|
81
92
|
/** The resolved config: `publicDir` is absolute, or `""` when the public directory is off. */
|
|
82
|
-
readonly config?: {
|
|
93
|
+
readonly config?: {
|
|
94
|
+
readonly publicDir?: string
|
|
95
|
+
readonly server?: { readonly allowedHosts?: true | readonly string[] }
|
|
96
|
+
}
|
|
83
97
|
readonly watcher: {
|
|
84
98
|
on(event: "change" | "add" | "unlink", cb: (path: string) => void): void
|
|
85
99
|
on(event: "ready", cb: () => void): void
|
|
@@ -163,6 +177,8 @@ export interface ViteDevServerOptions {
|
|
|
163
177
|
* fix (default `true`). Off, the errors still reach the feed.
|
|
164
178
|
*/
|
|
165
179
|
readonly indicator?: boolean
|
|
180
|
+
/** The app runs `createWebApp({ live })`: the same option as the build's (`BuildClientOptions.live`). */
|
|
181
|
+
readonly live?: LiveClientEntry | undefined
|
|
166
182
|
}
|
|
167
183
|
|
|
168
184
|
export interface ViteDevServer {
|
|
@@ -236,7 +252,7 @@ const readAgentBody = async (req: IncomingMessage, max: number): Promise<Buffer
|
|
|
236
252
|
function toWebRequest(req: IncomingMessage, body: Buffer | undefined): Request {
|
|
237
253
|
const headers = new Headers()
|
|
238
254
|
for (const [key, value] of Object.entries(req.headers)) {
|
|
239
|
-
if (typeof value === "string") headers
|
|
255
|
+
if (typeof value === "string") appendJoined(headers, key, value)
|
|
240
256
|
else if (Array.isArray(value)) for (const v of value) headers.append(key, v)
|
|
241
257
|
}
|
|
242
258
|
const url = `http://${req.headers.host ?? "localhost"}${req.url ?? "/"}`
|
|
@@ -252,6 +268,23 @@ function toWebRequest(req: IncomingMessage, body: Buffer | undefined): Request {
|
|
|
252
268
|
return new Request(url, init as unknown as ConstructorParameters<typeof Request>[1])
|
|
253
269
|
}
|
|
254
270
|
|
|
271
|
+
/**
|
|
272
|
+
* Node joins a repeated field into one string, and a trailing space there means the last member was
|
|
273
|
+
* empty (each field line is trimmed). `Headers` strips the whitespace around each value it is given,
|
|
274
|
+
* so the empty members are appended one by one to keep the "a, " production shows.
|
|
275
|
+
*/
|
|
276
|
+
function appendJoined(headers: Headers, name: string, joined: string): void {
|
|
277
|
+
const delimiter = name === "cookie" ? "; " : ", "
|
|
278
|
+
let base = joined
|
|
279
|
+
let empty = 0
|
|
280
|
+
while (base.endsWith(delimiter)) {
|
|
281
|
+
base = base.slice(0, -delimiter.length)
|
|
282
|
+
empty++
|
|
283
|
+
}
|
|
284
|
+
headers.append(name, base)
|
|
285
|
+
for (; empty > 0; empty--) headers.append(name, "")
|
|
286
|
+
}
|
|
287
|
+
|
|
255
288
|
/** The bits of a Node ServerResponse `pipeWebBodyToNode` touches - structural, to avoid a node:http dep here. */
|
|
256
289
|
interface NodeResLike {
|
|
257
290
|
/** Set once the socket is gone: `close` has fired, and a write returns `false` for good. */
|
|
@@ -395,7 +428,11 @@ export async function createViteDevServer(options: ViteDevServerOptions): Promis
|
|
|
395
428
|
const manifest = discoverRoutes(routesDir)
|
|
396
429
|
writeFileSync(
|
|
397
430
|
resolvePath(root, DEV_ENTRY),
|
|
398
|
-
generateClientEntry(manifest, {
|
|
431
|
+
generateClientEntry(manifest, {
|
|
432
|
+
clientModule: options.clientModule,
|
|
433
|
+
resolve: toUrl,
|
|
434
|
+
live: options.live,
|
|
435
|
+
}),
|
|
399
436
|
)
|
|
400
437
|
}
|
|
401
438
|
const usePolling = options.poll ?? process.env.CHOKIDAR_USEPOLLING === "1"
|
|
@@ -457,6 +494,38 @@ export async function createViteDevServer(options: ViteDevServerOptions): Promis
|
|
|
457
494
|
handleWithVite(req, res)
|
|
458
495
|
})
|
|
459
496
|
|
|
497
|
+
// The app's own WebSocket routes. Vite's HMR socket rides this server with a listener of its own,
|
|
498
|
+
// so its handshakes, and any no app route takes, are left to the other listeners.
|
|
499
|
+
server.on("upgrade", (req, socket, head) => {
|
|
500
|
+
const protocol = req.headers["sec-websocket-protocol"] ?? ""
|
|
501
|
+
if (protocol.includes("vite-hmr") || protocol.includes("vite-ping")) return
|
|
502
|
+
if (!allowsHost(req.headers.host)) {
|
|
503
|
+
socket.on("error", () => {})
|
|
504
|
+
socket.end("HTTP/1.1 403 Forbidden\r\ncontent-length: 0\r\nconnection: close\r\n\r\n")
|
|
505
|
+
return
|
|
506
|
+
}
|
|
507
|
+
void upgradeOnNode(app, toWebRequest(req, undefined), req, socket, head)
|
|
508
|
+
})
|
|
509
|
+
|
|
510
|
+
/** Vite's own rule for a handshake's Host: a loopback name, or one `server.allowedHosts` lists. */
|
|
511
|
+
function allowsHost(host: string | undefined): boolean {
|
|
512
|
+
if (isLoopbackHost(host)) return true
|
|
513
|
+
const allowed = vite.config?.server?.allowedHosts
|
|
514
|
+
if (allowed === true) return true
|
|
515
|
+
if (allowed === undefined || host === undefined) return false
|
|
516
|
+
let name: string
|
|
517
|
+
try {
|
|
518
|
+
name = new URL(`http://${host}`).hostname
|
|
519
|
+
} catch {
|
|
520
|
+
return false
|
|
521
|
+
}
|
|
522
|
+
return allowed.some(
|
|
523
|
+
(entry) =>
|
|
524
|
+
entry === name ||
|
|
525
|
+
(entry.startsWith(".") && (name === entry.slice(1) || name.endsWith(entry))),
|
|
526
|
+
)
|
|
527
|
+
}
|
|
528
|
+
|
|
460
529
|
// Vite's public middleware serves every file under the public directory, dotfiles included. A
|
|
461
530
|
// build ships no hidden file and the Bun pipeline's `servePublicDir` refuses one, so a request for
|
|
462
531
|
// `/.env` or `/.git/config` goes to the app here too, as it does in production.
|