@nifrajs/web 5.5.0 → 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 +162 -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
|
@@ -20,9 +20,11 @@ import {
|
|
|
20
20
|
import { BoundedComponentTasks, componentAbort } from "./scheduler.ts"
|
|
21
21
|
import { componentValue } from "./schema.ts"
|
|
22
22
|
import type {
|
|
23
|
+
ComponentHoles,
|
|
23
24
|
ComponentInstance,
|
|
24
25
|
ComponentOwner,
|
|
25
26
|
ComponentRecord,
|
|
27
|
+
ComponentRender,
|
|
26
28
|
ServerComponent,
|
|
27
29
|
ServerComponentContext,
|
|
28
30
|
ServerComponentHandle,
|
|
@@ -42,6 +44,11 @@ export interface ServerComponentsOptions {
|
|
|
42
44
|
readonly build: string
|
|
43
45
|
readonly adapter: ServerComponentRenderAdapter
|
|
44
46
|
readonly owners: Readonly<Record<string, ComponentOwner>>
|
|
47
|
+
/**
|
|
48
|
+
* Experimental: route id to the server components its partially prerendered holes may show, each
|
|
49
|
+
* through `serverHole`. A route is an owner or hosts holes, never both.
|
|
50
|
+
*/
|
|
51
|
+
readonly holes?: Readonly<Record<string, ComponentHoles>>
|
|
45
52
|
readonly capabilities?: readonly CapabilityDefinition[]
|
|
46
53
|
readonly assets?: readonly string[]
|
|
47
54
|
}
|
|
@@ -103,20 +110,19 @@ export function createServerComponents(options: ServerComponentsOptions): Server
|
|
|
103
110
|
)
|
|
104
111
|
if (capabilities.size !== (options.capabilities?.length ?? 0))
|
|
105
112
|
throw new ServerComponentError("component_contract")
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
throw new ServerComponentError("component_contract")
|
|
113
|
+
/** A route's component allowlist, by reference, each within the capabilities the route grants. */
|
|
114
|
+
const allowlist = (
|
|
115
|
+
list: readonly ServerComponentHandle[],
|
|
116
|
+
granted: readonly string[] = [],
|
|
117
|
+
): ReadonlyMap<string, ServerComponentHandle> => {
|
|
112
118
|
const components = new Map<string, ServerComponentHandle>()
|
|
113
|
-
const allowed = new Set(
|
|
119
|
+
const allowed = new Set(granted)
|
|
114
120
|
for (const capability of allowed) {
|
|
115
121
|
const declared = capabilities.get(capability)
|
|
116
122
|
if (declared === undefined || (declared.zone === "domain" && declared.access !== "read"))
|
|
117
123
|
throw new ServerComponentError("component_contract")
|
|
118
124
|
}
|
|
119
|
-
for (const component of
|
|
125
|
+
for (const component of list) {
|
|
120
126
|
if (
|
|
121
127
|
!isServerComponent(component) ||
|
|
122
128
|
component.reference === undefined ||
|
|
@@ -128,11 +134,106 @@ export function createServerComponents(options: ServerComponentsOptions): Server
|
|
|
128
134
|
if (serverComponentCapabilities(component).some((capability) => !allowed.has(capability)))
|
|
129
135
|
throw new ServerComponentError("component_contract")
|
|
130
136
|
}
|
|
131
|
-
|
|
137
|
+
return components
|
|
138
|
+
}
|
|
139
|
+
for (const [routeId, owner] of Object.entries(options.owners)) {
|
|
140
|
+
if (
|
|
141
|
+
typeof owner.instances !== "function" ||
|
|
142
|
+
(owner.scope !== "anonymous" && typeof owner.scope !== "function")
|
|
143
|
+
)
|
|
144
|
+
throw new ServerComponentError("component_contract")
|
|
145
|
+
owners.set(routeId, { owner, components: allowlist(owner.components, owner.capabilities) })
|
|
146
|
+
}
|
|
147
|
+
const holes = new Map<
|
|
148
|
+
string,
|
|
149
|
+
{ capabilities: readonly string[]; components: ReadonlyMap<string, ServerComponentHandle> }
|
|
150
|
+
>()
|
|
151
|
+
for (const [routeId, entry] of Object.entries(options.holes ?? {})) {
|
|
152
|
+
// An owner route renders whole, so its holes never run.
|
|
153
|
+
if (owners.has(routeId) || !Array.isArray(entry.components))
|
|
154
|
+
throw new ServerComponentError("component_contract")
|
|
155
|
+
holes.set(routeId, {
|
|
156
|
+
capabilities: entry.capabilities ?? [],
|
|
157
|
+
components: allowlist(entry.components, entry.capabilities),
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
/** A rendered instance as a record: its range HTML checked, its payload and island props public. */
|
|
161
|
+
const recordOf = (
|
|
162
|
+
instance: {
|
|
163
|
+
readonly id: string
|
|
164
|
+
readonly reference: string
|
|
165
|
+
readonly key: ComponentRecord["key"]
|
|
166
|
+
readonly parent?: string | undefined
|
|
167
|
+
readonly input: ComponentRecord["input"]
|
|
168
|
+
},
|
|
169
|
+
rendered: ComponentRender,
|
|
170
|
+
): ComponentRecord => {
|
|
171
|
+
const record: ComponentRecord = {
|
|
172
|
+
instance: instance.id,
|
|
173
|
+
reference: instance.reference,
|
|
174
|
+
key: instance.key,
|
|
175
|
+
parent: instance.parent ?? null,
|
|
176
|
+
input: instance.input,
|
|
177
|
+
html: componentRangeHTML(rendered.html, assets),
|
|
178
|
+
}
|
|
179
|
+
if (rendered.payload !== undefined)
|
|
180
|
+
Object.assign(record, {
|
|
181
|
+
payload: componentValue(rendered.payload, COMPONENT_LIMITS.outputBytes),
|
|
182
|
+
})
|
|
183
|
+
if (rendered.islands !== undefined)
|
|
184
|
+
Object.assign(record, {
|
|
185
|
+
islands: rendered.islands.map((island) => ({
|
|
186
|
+
...island,
|
|
187
|
+
props: componentValue(island.props),
|
|
188
|
+
})),
|
|
189
|
+
})
|
|
190
|
+
return record
|
|
132
191
|
}
|
|
133
192
|
return {
|
|
134
193
|
owns: (routeId) => owners.has(routeId),
|
|
135
|
-
capabilities: (routeId) =>
|
|
194
|
+
capabilities: (routeId) =>
|
|
195
|
+
owners.get(routeId)?.owner.capabilities ?? holes.get(routeId)?.capabilities ?? [],
|
|
196
|
+
hosts: (routeId) => holes.has(routeId),
|
|
197
|
+
async hole(routeId, component, key, input, context) {
|
|
198
|
+
const reference = isServerComponent(component) ? component.reference : undefined
|
|
199
|
+
const handle =
|
|
200
|
+
reference === undefined ? undefined : holes.get(routeId)?.components.get(reference)
|
|
201
|
+
if (
|
|
202
|
+
reference === undefined ||
|
|
203
|
+
handle === undefined ||
|
|
204
|
+
handle !== component ||
|
|
205
|
+
!componentKey(key)
|
|
206
|
+
)
|
|
207
|
+
throw new ServerComponentError("component_contract")
|
|
208
|
+
const signal = AbortSignal.any([
|
|
209
|
+
context.signal,
|
|
210
|
+
AbortSignal.timeout(COMPONENT_LIMITS.executionMs),
|
|
211
|
+
])
|
|
212
|
+
const ctx: ServerComponentContext = Object.create(context, {
|
|
213
|
+
signal: { value: signal, enumerable: true },
|
|
214
|
+
})
|
|
215
|
+
const value = componentValue(input, COMPONENT_LIMITS.inputBytes)
|
|
216
|
+
const id = await serverComponentIdentity(routeId, handle, key, null)
|
|
217
|
+
return admission.run(signal, async () => {
|
|
218
|
+
// biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: The route's hole allowlist identifies this handle; authorization validates the input before any callback runs.
|
|
219
|
+
const authorized = await authorizeServerComponent(handle as ServerComponent, value, ctx)
|
|
220
|
+
const prepared = await prepareAuthorizedServerComponent(authorized)
|
|
221
|
+
const rendered = await options.adapter.render(prepared.view, prepared.props, {
|
|
222
|
+
reference,
|
|
223
|
+
instance: id,
|
|
224
|
+
signal,
|
|
225
|
+
children: [],
|
|
226
|
+
})
|
|
227
|
+
if (signal.aborted) throw componentAbort(signal)
|
|
228
|
+
if (typeof rendered.html !== "string") throw new ServerComponentError("component_contract")
|
|
229
|
+
if ((rendered.islands?.length ?? 0) > COMPONENT_LIMITS.instances)
|
|
230
|
+
throw new ServerComponentError("component_limit", 413)
|
|
231
|
+
const record = recordOf({ id, reference, key, input: value }, rendered)
|
|
232
|
+
if (encoder.encode(JSON.stringify(record)).length > COMPONENT_LIMITS.outputBytes)
|
|
233
|
+
throw new ServerComponentError("component_limit", 413)
|
|
234
|
+
return record
|
|
235
|
+
})
|
|
236
|
+
},
|
|
136
237
|
classify(request, routeId) {
|
|
137
238
|
const selection = readComponentRequest(request)
|
|
138
239
|
if (selection === undefined) return undefined
|
|
@@ -275,29 +376,10 @@ export function createServerComponents(options: ServerComponentsOptions): Server
|
|
|
275
376
|
if (signal.aborted) throw componentAbort(signal)
|
|
276
377
|
if (typeof rendered.html !== "string")
|
|
277
378
|
throw new ServerComponentError("component_contract")
|
|
278
|
-
const
|
|
379
|
+
const record = recordOf({ ...instance, id }, rendered)
|
|
279
380
|
count += rendered.islands?.length ?? 0
|
|
280
381
|
if (count > COMPONENT_LIMITS.instances)
|
|
281
382
|
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
383
|
const size = encoder.encode(JSON.stringify(record)).length
|
|
302
384
|
bytes += size
|
|
303
385
|
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
|
}
|
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.
|
package/dist/nano.d.ts
DELETED
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `@nifrajs/web/nano` - the smallest state layer that still lets an island drive a keyed list.
|
|
3
|
-
*
|
|
4
|
-
* The islands lane (`@nifrajs/web/islands`) is imperative and correct-by-construction, but it has no
|
|
5
|
-
* state primitive: every DOM update is hand-written, and a list means rebuilding the whole container.
|
|
6
|
-
* nano adds a small, closed set of primitives and stops:
|
|
7
|
-
*
|
|
8
|
-
* - `signal(v)` - a current-value cell you read with `.get()` and write with `.set()`.
|
|
9
|
-
* - `computed(fn, [deps])` - a derived cell. Its dependencies are DECLARED, never auto-tracked.
|
|
10
|
-
* - `resource(fetcher, [deps])` - an async cell with an explicit pending/error/ready value. This is
|
|
11
|
-
* nano's answer to "suspense": no thrown promise, no magic boundary, just a value you can bind.
|
|
12
|
-
* - `bind` / `bindList` / `bindResource` - explicit, one-directional DOM bindings that each return
|
|
13
|
-
* their teardown.
|
|
14
|
-
*
|
|
15
|
-
* The design rule is the whole point: every reactive edge is a call you can see - a `bind(...)` or a
|
|
16
|
-
* `computed(fn, [deps])`. There is no re-render scope, no VDOM, no template compiler, and no effect
|
|
17
|
-
* that silently subscribes to whatever it happened to read. That is what removes the three failure
|
|
18
|
-
* modes an AI (or a human) hits in a framework - stale closures, wrong effect deps, hydration
|
|
19
|
-
* mismatch - and, just as importantly, it is what makes every remaining mistake STATICALLY
|
|
20
|
-
* detectable: a missing dep is `[deps]` that doesn't list a signal the body reads; a leak is a `bind`
|
|
21
|
-
* whose cleanup isn't returned. A framework's reactivity can't be linted like that; nano's can.
|
|
22
|
-
*
|
|
23
|
-
* Where nano stops: no client router and no nested/composable view state (a component tree that mounts
|
|
24
|
-
* and unmounts its own children). When an app needs those, it has outgrown a vanilla page - reach for
|
|
25
|
-
* a framework adapter (`@nifrajs/web-preact`), not more nano.
|
|
26
|
-
*
|
|
27
|
-
* Browser code: it touches the DOM and is meant to run in an island enhancer, never under SSR.
|
|
28
|
-
*/
|
|
29
|
-
/** A value you can read now and be told about later. Both `signal` and `computed` are `Readable`, so
|
|
30
|
-
* `bind`/`bindList` accept either. */
|
|
31
|
-
export interface Readable<T> {
|
|
32
|
-
get(): T;
|
|
33
|
-
subscribe(listener: (value: T) => void): () => void;
|
|
34
|
-
}
|
|
35
|
-
/** A writable cell. `set` notifies subscribers only when the value actually changes (`Object.is`), so
|
|
36
|
-
* a redundant write costs nothing. */
|
|
37
|
-
export interface Signal<T> extends Readable<T> {
|
|
38
|
-
set(value: T): void;
|
|
39
|
-
}
|
|
40
|
-
/** A current-value cell. `signal(0)` -> `.get()` reads, `.set(1)` writes and notifies. */
|
|
41
|
-
export declare function signal<T>(initial: T): Signal<T>;
|
|
42
|
-
/**
|
|
43
|
-
* A derived cell. `computed(() => a.get() + b.get(), [a, b])` recomputes whenever a declared
|
|
44
|
-
* dependency changes and notifies its own subscribers when the derived value changes.
|
|
45
|
-
*
|
|
46
|
-
* The `deps` array is mandatory and explicit ON PURPOSE - it is the contract a linter checks against
|
|
47
|
-
* the signals the body reads (`NF-C023`). Auto-tracking would remove the array but reintroduce the
|
|
48
|
-
* exact "why didn't this update" bug that makes framework reactivity hard to get right.
|
|
49
|
-
*/
|
|
50
|
-
export declare function computed<T>(compute: () => T, deps: readonly Readable<unknown>[]): Readable<T>;
|
|
51
|
-
/** The three states of an async cell. A discriminated union on `status` so a consumer must handle
|
|
52
|
-
* every branch - there is no "value that might secretly be loading", the failure mode of ad-hoc
|
|
53
|
-
* `isLoading` booleans. `value`/`error` are narrowed by `status`. */
|
|
54
|
-
export type ResourceState<T> = {
|
|
55
|
-
readonly status: "pending";
|
|
56
|
-
readonly value: undefined;
|
|
57
|
-
readonly error: undefined;
|
|
58
|
-
} | {
|
|
59
|
-
readonly status: "ready";
|
|
60
|
-
readonly value: T;
|
|
61
|
-
readonly error: undefined;
|
|
62
|
-
} | {
|
|
63
|
-
readonly status: "error";
|
|
64
|
-
readonly value: undefined;
|
|
65
|
-
readonly error: unknown;
|
|
66
|
-
};
|
|
67
|
-
/** An async cell: a `Readable` of `ResourceState<T>` plus `refetch()`. This is nano's answer to
|
|
68
|
-
* "suspense" - an explicit pending/error/ready value, never a thrown promise or a magic boundary. */
|
|
69
|
-
export interface Resource<T> extends Readable<ResourceState<T>> {
|
|
70
|
-
refetch(): void;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* An async derived cell. `resource(fetcher, [deps])` runs `fetcher` immediately and again whenever a
|
|
74
|
-
* declared dependency changes, exposing the result as an explicit `{ status, value, error }`.
|
|
75
|
-
*
|
|
76
|
-
* Two footguns are handled here so they cannot be got wrong by hand:
|
|
77
|
-
* - Races: each run holds a generation; a stale run's resolution is dropped, and its `AbortSignal` is
|
|
78
|
-
* aborted, so an earlier-started-later-finishing fetch never overwrites a newer one.
|
|
79
|
-
* - Deps: like `computed`, dependencies are DECLARED (`NF-C023` checks the fetcher's `.get()` reads
|
|
80
|
-
* against the array). A `resource` that reads a signal its deps omit won't refetch when it changes.
|
|
81
|
-
*/
|
|
82
|
-
export declare function resource<T>(fetcher: (signal: AbortSignal) => Promise<T>, deps?: readonly Readable<unknown>[]): Resource<T>;
|
|
83
|
-
/**
|
|
84
|
-
* Bind one element to a source: `apply(el, value)` runs once immediately and again on every change.
|
|
85
|
-
* Returns the unsubscribe - hand it back as the island's cleanup (or collect several).
|
|
86
|
-
*
|
|
87
|
-
* bind(count, remaining, (el, n) => { el.textContent = String(n) })
|
|
88
|
-
*/
|
|
89
|
-
export declare function bind<T>(el: HTMLElement, source: Readable<T>, apply: (el: HTMLElement, value: T) => void): () => void;
|
|
90
|
-
/** How a `bindList` turns items into keyed DOM. `key` MUST be stable and unique per item (never the
|
|
91
|
-
* array index) - it is what lets add/remove/reorder touch only the changed rows. */
|
|
92
|
-
export interface BindListOptions<T> {
|
|
93
|
-
key(item: T): string | number;
|
|
94
|
-
create(item: T): HTMLElement;
|
|
95
|
-
update?(el: HTMLElement, item: T): void;
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Bind a list signal to a container with keyed reconciliation: new items are `create`d, surviving
|
|
99
|
-
* items are `update`d in place (keeping focus, scroll, and selection), removed items are detached,
|
|
100
|
-
* and the children are ordered to match the array. Returns the unsubscribe.
|
|
101
|
-
*
|
|
102
|
-
* This is the one imperative task too error-prone to hand-write each time - it is why nano exists on
|
|
103
|
-
* top of islands rather than leaving you to rebuild the container on every change.
|
|
104
|
-
*/
|
|
105
|
-
export declare function bindList<T>(source: Readable<readonly T[]>, container: HTMLElement, options: BindListOptions<T>): () => void;
|
|
106
|
-
/** How a `bindResource` maps each async state to the DOM. `ready` is required (there is always a
|
|
107
|
-
* success shape to render); `pending`/`error` are optional. Every branch is explicit - no hidden
|
|
108
|
-
* "still loading" state can slip through as a rendered `undefined`. */
|
|
109
|
-
export interface BindResourceHandlers<T> {
|
|
110
|
-
ready(el: HTMLElement, value: T): void;
|
|
111
|
-
pending?(el: HTMLElement): void;
|
|
112
|
-
error?(el: HTMLElement, error: unknown): void;
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Bind an element to a `resource`, dispatching on `status`. Like `bind`, it applies immediately and
|
|
116
|
-
* on every change and returns the unsubscribe - collect it (a discarded disposer is `NF-C021`).
|
|
117
|
-
*
|
|
118
|
-
* bindResource(el, user, {
|
|
119
|
-
* pending: (n) => { n.textContent = "Loading…" },
|
|
120
|
-
* ready: (n, u) => { n.textContent = u.name },
|
|
121
|
-
* error: (n) => { n.textContent = "Failed to load" },
|
|
122
|
-
* })
|
|
123
|
-
*/
|
|
124
|
-
export declare function bindResource<T>(el: HTMLElement, source: Readable<ResourceState<T>>, handlers: BindResourceHandlers<T>): () => void;
|
|
125
|
-
//# sourceMappingURL=nano.d.ts.map
|
package/dist/nano.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"nano.d.ts","sourceRoot":"","sources":["../src/nano.ts"],"names":[],"mappings":"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"}
|