@nifrajs/web 5.5.1 → 5.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +154 -0
- package/dist/boundary.d.ts +67 -7
- package/dist/boundary.d.ts.map +1 -1
- package/dist/boundary.js +6 -4
- package/dist/boundary.js.map +1 -1
- package/dist/build-plan.d.ts +3 -0
- package/dist/build-plan.d.ts.map +1 -1
- package/dist/build-plan.js.map +1 -1
- package/dist/build-vite.d.ts.map +1 -1
- package/dist/build-vite.js +2 -0
- package/dist/build-vite.js.map +1 -1
- package/dist/build.d.ts +9 -2
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +9 -0
- package/dist/build.js.map +1 -1
- package/dist/client.d.ts +2 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +140 -12
- package/dist/client.js.map +1 -1
- package/dist/csp.d.ts +8 -0
- package/dist/csp.d.ts.map +1 -1
- package/dist/csp.js +11 -2
- package/dist/csp.js.map +1 -1
- package/dist/deferred.d.ts +6 -4
- package/dist/deferred.d.ts.map +1 -1
- package/dist/deferred.js +10 -6
- package/dist/deferred.js.map +1 -1
- package/dist/dev-feed.d.ts.map +1 -1
- package/dist/dev-feed.js +11 -2
- package/dist/dev-feed.js.map +1 -1
- package/dist/dev-session.d.ts.map +1 -1
- package/dist/dev-session.js +13 -6
- package/dist/dev-session.js.map +1 -1
- package/dist/dev.d.ts +4 -1
- package/dist/dev.d.ts.map +1 -1
- package/dist/dev.js +12 -5
- package/dist/dev.js.map +1 -1
- package/dist/diagnostic-catalog.d.ts +4 -0
- package/dist/diagnostic-catalog.d.ts.map +1 -1
- package/dist/diagnostic-catalog.js +48 -0
- package/dist/diagnostic-catalog.js.map +1 -1
- package/dist/diagnostic.d.ts +1 -1
- package/dist/diagnostic.d.ts.map +1 -1
- package/dist/diagnostic.js +1 -1
- package/dist/diagnostic.js.map +1 -1
- package/dist/fn.d.ts +5 -3
- package/dist/fn.d.ts.map +1 -1
- package/dist/fn.js +20 -5
- package/dist/fn.js.map +1 -1
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +13 -3
- package/dist/fs.js.map +1 -1
- package/dist/identity.d.ts +22 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +60 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.d.ts +7 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/internal/cdn/cloudflare.d.ts +2 -1
- package/dist/internal/cdn/cloudflare.d.ts.map +1 -1
- package/dist/internal/cdn/cloudflare.js +2 -1
- package/dist/internal/cdn/cloudflare.js.map +1 -1
- package/dist/internal/certified-components.d.ts +1 -1
- package/dist/internal/certified-components.js +1 -1
- package/dist/internal/codegen.d.ts +11 -10
- package/dist/internal/codegen.d.ts.map +1 -1
- package/dist/internal/codegen.js +65 -8
- package/dist/internal/codegen.js.map +1 -1
- package/dist/internal/dev-websocket.d.ts +73 -0
- package/dist/internal/dev-websocket.d.ts.map +1 -0
- package/dist/internal/dev-websocket.js +145 -0
- package/dist/internal/dev-websocket.js.map +1 -0
- package/dist/internal/hole-runtime.d.ts +66 -0
- package/dist/internal/hole-runtime.d.ts.map +1 -0
- package/dist/internal/hole-runtime.js +211 -0
- package/dist/internal/hole-runtime.js.map +1 -0
- package/dist/internal/identity-epoch.d.ts +50 -0
- package/dist/internal/identity-epoch.d.ts.map +1 -0
- package/dist/internal/identity-epoch.js +179 -0
- package/dist/internal/identity-epoch.js.map +1 -0
- package/dist/internal/invalidation-contract.d.ts +23 -0
- package/dist/internal/invalidation-contract.d.ts.map +1 -0
- package/dist/internal/invalidation-contract.js +11 -0
- package/dist/internal/invalidation-contract.js.map +1 -0
- package/dist/internal/invalidation.d.ts +62 -0
- package/dist/internal/invalidation.d.ts.map +1 -0
- package/dist/internal/invalidation.js +149 -0
- package/dist/internal/invalidation.js.map +1 -0
- package/dist/internal/island-bindings.d.ts +51 -0
- package/dist/internal/island-bindings.d.ts.map +1 -0
- package/dist/internal/island-bindings.js +147 -0
- package/dist/internal/island-bindings.js.map +1 -0
- package/dist/internal/island-list.d.ts +26 -0
- package/dist/internal/island-list.d.ts.map +1 -0
- package/dist/internal/island-list.js +78 -0
- package/dist/internal/island-list.js.map +1 -0
- package/dist/internal/island-mount.d.ts +4 -0
- package/dist/internal/island-mount.d.ts.map +1 -0
- package/dist/internal/island-mount.js +66 -0
- package/dist/internal/island-mount.js.map +1 -0
- package/dist/internal/island-owner.d.ts +5 -0
- package/dist/internal/island-owner.d.ts.map +1 -0
- package/dist/internal/island-owner.js +20 -0
- package/dist/internal/island-owner.js.map +1 -0
- package/dist/internal/island-resource.d.ts +30 -0
- package/dist/internal/island-resource.d.ts.map +1 -0
- package/dist/internal/island-resource.js +57 -0
- package/dist/internal/island-resource.js.map +1 -0
- package/dist/internal/island-signals.d.ts +51 -0
- package/dist/internal/island-signals.d.ts.map +1 -0
- package/dist/internal/island-signals.js +220 -0
- package/dist/internal/island-signals.js.map +1 -0
- package/dist/internal/keep-alive-table.d.ts +23 -0
- package/dist/internal/keep-alive-table.d.ts.map +1 -0
- package/dist/internal/keep-alive-table.js +53 -0
- package/dist/internal/keep-alive-table.js.map +1 -0
- package/dist/internal/live-contract.d.ts +96 -0
- package/dist/internal/live-contract.d.ts.map +1 -0
- package/dist/internal/live-contract.js +5 -0
- package/dist/internal/live-contract.js.map +1 -0
- package/dist/internal/live-queries.d.ts +22 -0
- package/dist/internal/live-queries.d.ts.map +1 -0
- package/dist/internal/live-queries.js +54 -0
- package/dist/internal/live-queries.js.map +1 -0
- package/dist/internal/page-events.d.ts +10 -0
- package/dist/internal/page-events.d.ts.map +1 -0
- package/dist/internal/page-events.js +26 -0
- package/dist/internal/page-events.js.map +1 -0
- package/dist/internal/page-execution.d.ts +14 -1
- package/dist/internal/page-execution.d.ts.map +1 -1
- package/dist/internal/page-execution.js +415 -31
- package/dist/internal/page-execution.js.map +1 -1
- package/dist/internal/ppr/admission.d.ts +83 -0
- package/dist/internal/ppr/admission.d.ts.map +1 -0
- package/dist/internal/ppr/admission.js +221 -0
- package/dist/internal/ppr/admission.js.map +1 -0
- package/dist/internal/ppr/canary.d.ts +24 -0
- package/dist/internal/ppr/canary.d.ts.map +1 -0
- package/dist/internal/ppr/canary.js +102 -0
- package/dist/internal/ppr/canary.js.map +1 -0
- package/dist/internal/ppr/compile-shell.d.ts +39 -0
- package/dist/internal/ppr/compile-shell.d.ts.map +1 -0
- package/dist/internal/ppr/compile-shell.js +298 -0
- package/dist/internal/ppr/compile-shell.js.map +1 -0
- package/dist/internal/ppr/contract.d.ts +177 -0
- package/dist/internal/ppr/contract.d.ts.map +1 -0
- package/dist/internal/ppr/contract.js +80 -0
- package/dist/internal/ppr/contract.js.map +1 -0
- package/dist/internal/ppr/frames.d.ts +71 -0
- package/dist/internal/ppr/frames.d.ts.map +1 -0
- package/dist/internal/ppr/frames.js +119 -0
- package/dist/internal/ppr/frames.js.map +1 -0
- package/dist/internal/ppr/integration.d.ts +19 -0
- package/dist/internal/ppr/integration.d.ts.map +1 -0
- package/dist/internal/ppr/integration.js +106 -0
- package/dist/internal/ppr/integration.js.map +1 -0
- package/dist/internal/ppr/options.d.ts +96 -0
- package/dist/internal/ppr/options.d.ts.map +1 -0
- package/dist/internal/ppr/options.js +125 -0
- package/dist/internal/ppr/options.js.map +1 -0
- package/dist/internal/ppr/route-rules.d.ts +12 -0
- package/dist/internal/ppr/route-rules.d.ts.map +1 -0
- package/dist/internal/ppr/route-rules.js +53 -0
- package/dist/internal/ppr/route-rules.js.map +1 -0
- package/dist/internal/ppr/runtime.d.ts +29 -0
- package/dist/internal/ppr/runtime.d.ts.map +1 -0
- package/dist/internal/ppr/runtime.js +32 -0
- package/dist/internal/ppr/runtime.js.map +1 -0
- package/dist/internal/ppr/server.d.ts +29 -0
- package/dist/internal/ppr/server.d.ts.map +1 -0
- package/dist/internal/ppr/server.js +1407 -0
- package/dist/internal/ppr/server.js.map +1 -0
- package/dist/internal/ppr/shell-store.d.ts +38 -0
- package/dist/internal/ppr/shell-store.d.ts.map +1 -0
- package/dist/internal/ppr/shell-store.js +90 -0
- package/dist/internal/ppr/shell-store.js.map +1 -0
- package/dist/internal/prerendered-pages.d.ts +1 -1
- package/dist/internal/prerendered-pages.d.ts.map +1 -1
- package/dist/internal/prerendered-pages.js +7 -4
- package/dist/internal/prerendered-pages.js.map +1 -1
- package/dist/internal/query-digest.d.ts +9 -0
- package/dist/internal/query-digest.d.ts.map +1 -0
- package/dist/internal/query-digest.js +23 -0
- package/dist/internal/query-digest.js.map +1 -0
- package/dist/internal/query-runtime.d.ts +6 -1
- package/dist/internal/query-runtime.d.ts.map +1 -1
- package/dist/internal/query-runtime.js +9 -0
- package/dist/internal/query-runtime.js.map +1 -1
- package/dist/internal/query-seam.d.ts +29 -0
- package/dist/internal/query-seam.d.ts.map +1 -0
- package/dist/internal/query-seam.js +23 -0
- package/dist/internal/query-seam.js.map +1 -0
- package/dist/internal/render-document.d.ts +28 -1
- package/dist/internal/render-document.d.ts.map +1 -1
- package/dist/internal/render-document.js +83 -18
- package/dist/internal/render-document.js.map +1 -1
- package/dist/internal/request-abort.d.ts +8 -0
- package/dist/internal/request-abort.d.ts.map +1 -0
- package/dist/internal/request-abort.js +22 -0
- package/dist/internal/request-abort.js.map +1 -0
- package/dist/internal/server-boundary.d.ts +2 -0
- package/dist/internal/server-boundary.d.ts.map +1 -1
- package/dist/internal/server-boundary.js +8 -1
- package/dist/internal/server-boundary.js.map +1 -1
- package/dist/internal/share-data.d.ts +10 -0
- package/dist/internal/share-data.d.ts.map +1 -0
- package/dist/internal/share-data.js +135 -0
- package/dist/internal/share-data.js.map +1 -0
- package/dist/internal/store-signature.d.ts +15 -0
- package/dist/internal/store-signature.d.ts.map +1 -0
- package/dist/internal/store-signature.js +114 -0
- package/dist/internal/store-signature.js.map +1 -0
- package/dist/internal/vite-plugin-compat.d.ts +2 -2
- package/dist/internal/vite-plugin-compat.js +2 -2
- package/dist/internal/web-app.d.ts +25 -0
- package/dist/internal/web-app.d.ts.map +1 -1
- package/dist/internal/web-app.js +78 -3
- package/dist/internal/web-app.js.map +1 -1
- package/dist/islands-swap.d.ts +41 -0
- package/dist/islands-swap.d.ts.map +1 -0
- package/dist/islands-swap.js +270 -0
- package/dist/islands-swap.js.map +1 -0
- package/dist/islands.d.ts +86 -24
- package/dist/islands.d.ts.map +1 -1
- package/dist/islands.js +138 -78
- package/dist/islands.js.map +1 -1
- package/dist/isr.d.ts +67 -13
- package/dist/isr.d.ts.map +1 -1
- package/dist/isr.js +247 -99
- package/dist/isr.js.map +1 -1
- package/dist/keep-alive.d.ts +147 -0
- package/dist/keep-alive.d.ts.map +1 -0
- package/dist/keep-alive.js +1069 -0
- package/dist/keep-alive.js.map +1 -0
- package/dist/live/bus.d.ts +56 -0
- package/dist/live/bus.d.ts.map +1 -0
- package/dist/live/bus.js +32 -0
- package/dist/live/bus.js.map +1 -0
- package/dist/live/core.d.ts +25 -0
- package/dist/live/core.d.ts.map +1 -0
- package/dist/live/core.js +29 -0
- package/dist/live/core.js.map +1 -0
- package/dist/live/history.d.ts +38 -0
- package/dist/live/history.d.ts.map +1 -0
- package/dist/live/history.js +104 -0
- package/dist/live/history.js.map +1 -0
- package/dist/live/hub-protocol.d.ts +46 -0
- package/dist/live/hub-protocol.d.ts.map +1 -0
- package/dist/live/hub-protocol.js +108 -0
- package/dist/live/hub-protocol.js.map +1 -0
- package/dist/live/hub.d.ts +181 -0
- package/dist/live/hub.d.ts.map +1 -0
- package/dist/live/hub.js +539 -0
- package/dist/live/hub.js.map +1 -0
- package/dist/live/keyring.d.ts +87 -0
- package/dist/live/keyring.d.ts.map +1 -0
- package/dist/live/keyring.js +260 -0
- package/dist/live/keyring.js.map +1 -0
- package/dist/live/read-capped.d.ts +6 -0
- package/dist/live/read-capped.d.ts.map +1 -0
- package/dist/live/read-capped.js +42 -0
- package/dist/live/read-capped.js.map +1 -0
- package/dist/live/revocations.d.ts +52 -0
- package/dist/live/revocations.d.ts.map +1 -0
- package/dist/live/revocations.js +116 -0
- package/dist/live/revocations.js.map +1 -0
- package/dist/live/settings.d.ts +14 -0
- package/dist/live/settings.d.ts.map +1 -0
- package/dist/live/settings.js +38 -0
- package/dist/live/settings.js.map +1 -0
- package/dist/live-client.d.ts +34 -0
- package/dist/live-client.d.ts.map +1 -0
- package/dist/live-client.js +840 -0
- package/dist/live-client.js.map +1 -0
- package/dist/live-hub.d.ts +130 -0
- package/dist/live-hub.d.ts.map +1 -0
- package/dist/live-hub.js +507 -0
- package/dist/live-hub.js.map +1 -0
- package/dist/live-redis.d.ts +48 -0
- package/dist/live-redis.d.ts.map +1 -0
- package/dist/live-redis.js +201 -0
- package/dist/live-redis.js.map +1 -0
- package/dist/live.d.ts +94 -0
- package/dist/live.d.ts.map +1 -0
- package/dist/live.js +869 -0
- package/dist/live.js.map +1 -0
- package/dist/manifest.d.ts +113 -2
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +109 -5
- package/dist/manifest.js.map +1 -1
- package/dist/navigation.d.ts +9 -3
- package/dist/navigation.d.ts.map +1 -1
- package/dist/navigation.js +13 -3
- package/dist/navigation.js.map +1 -1
- package/dist/plugins/vite-zone-guard.d.ts +6 -0
- package/dist/plugins/vite-zone-guard.d.ts.map +1 -1
- package/dist/plugins/vite-zone-guard.js +8 -2
- package/dist/plugins/vite-zone-guard.js.map +1 -1
- package/dist/ppr.d.ts +73 -0
- package/dist/ppr.d.ts.map +1 -0
- package/dist/ppr.js +76 -0
- package/dist/ppr.js.map +1 -0
- package/dist/query.d.ts +8 -13
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +112 -18
- package/dist/query.js.map +1 -1
- package/dist/render-seam.d.ts +30 -0
- package/dist/render-seam.d.ts.map +1 -1
- package/dist/render-seam.js +5 -0
- package/dist/render-seam.js.map +1 -1
- package/dist/route-types.d.ts.map +1 -1
- package/dist/route-types.js +2 -2
- package/dist/route-types.js.map +1 -1
- package/dist/router.d.ts +72 -4
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +374 -99
- package/dist/router.js.map +1 -1
- package/dist/server-components/build.d.ts.map +1 -1
- package/dist/server-components/build.js +3 -3
- package/dist/server-components/build.js.map +1 -1
- package/dist/server-components/client.d.ts +23 -1
- package/dist/server-components/client.d.ts.map +1 -1
- package/dist/server-components/client.js +92 -5
- package/dist/server-components/client.js.map +1 -1
- package/dist/server-components/hole.d.ts +8 -0
- package/dist/server-components/hole.d.ts.map +1 -0
- package/dist/server-components/hole.js +20 -0
- package/dist/server-components/hole.js.map +1 -0
- package/dist/server-components/html-policy.d.ts.map +1 -1
- package/dist/server-components/html-policy.js +62 -4
- package/dist/server-components/html-policy.js.map +1 -1
- package/dist/server-components/html-scan.d.ts +17 -0
- package/dist/server-components/html-scan.d.ts.map +1 -0
- package/dist/server-components/html-scan.js +180 -0
- package/dist/server-components/html-scan.js.map +1 -0
- package/dist/server-components/html.d.ts.map +1 -1
- package/dist/server-components/html.js +19 -4
- package/dist/server-components/html.js.map +1 -1
- package/dist/server-components/router-integration.d.ts +4 -1
- package/dist/server-components/router-integration.d.ts.map +1 -1
- package/dist/server-components/router-integration.js +23 -2
- package/dist/server-components/router-integration.js.map +1 -1
- package/dist/server-components/runtime.d.ts +6 -1
- package/dist/server-components/runtime.d.ts.map +1 -1
- package/dist/server-components/runtime.js +86 -28
- package/dist/server-components/runtime.js.map +1 -1
- package/dist/server-components/state.d.ts +3 -1
- package/dist/server-components/state.d.ts.map +1 -1
- package/dist/server-components/state.js +50 -38
- package/dist/server-components/state.js.map +1 -1
- package/dist/server-components/types.d.ts +9 -0
- package/dist/server-components/types.d.ts.map +1 -1
- package/dist/vite.d.ts +5 -1
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +58 -4
- package/dist/vite.js.map +1 -1
- package/package.json +48 -8
- package/src/boundary.ts +77 -5
- package/src/build-plan.ts +3 -0
- package/src/build-vite.ts +2 -0
- package/src/build.ts +19 -1
- package/src/client.ts +147 -10
- package/src/csp.ts +16 -2
- package/src/deferred.ts +11 -5
- package/src/dev-feed.ts +11 -2
- package/src/dev-session.ts +22 -11
- package/src/dev.ts +29 -8
- package/src/diagnostic-catalog.ts +53 -0
- package/src/diagnostic.ts +1 -0
- package/src/fn.ts +24 -7
- package/src/fs.ts +22 -8
- package/src/identity.ts +61 -0
- package/src/index.ts +19 -0
- package/src/internal/cdn/cloudflare.ts +2 -1
- package/src/internal/certified-components.ts +1 -1
- package/src/internal/codegen.ts +87 -8
- package/src/internal/dev-websocket.ts +230 -0
- package/src/internal/hole-runtime.ts +286 -0
- package/src/internal/identity-epoch.ts +235 -0
- package/src/internal/invalidation-contract.ts +35 -0
- package/src/internal/invalidation.ts +213 -0
- package/src/internal/island-bindings.ts +191 -0
- package/src/internal/island-list.ts +92 -0
- package/src/internal/island-mount.ts +68 -0
- package/src/internal/island-owner.ts +20 -0
- package/src/internal/island-resource.ts +76 -0
- package/src/internal/island-signals.ts +249 -0
- package/src/internal/keep-alive-table.ts +72 -0
- package/src/internal/live-contract.ts +106 -0
- package/src/internal/live-queries.ts +60 -0
- package/src/internal/page-events.ts +27 -0
- package/src/internal/page-execution.ts +579 -40
- package/src/internal/ppr/admission.ts +300 -0
- package/src/internal/ppr/canary.ts +122 -0
- package/src/internal/ppr/compile-shell.ts +379 -0
- package/src/internal/ppr/contract.ts +265 -0
- package/src/internal/ppr/frames.ts +170 -0
- package/src/internal/ppr/integration.ts +137 -0
- package/src/internal/ppr/options.ts +221 -0
- package/src/internal/ppr/route-rules.ts +77 -0
- package/src/internal/ppr/runtime.ts +36 -0
- package/src/internal/ppr/server.ts +1764 -0
- package/src/internal/ppr/shell-store.ts +139 -0
- package/src/internal/prerendered-pages.ts +7 -4
- package/src/internal/query-digest.ts +34 -0
- package/src/internal/query-runtime.ts +14 -1
- package/src/internal/query-seam.ts +48 -0
- package/src/internal/render-document.ts +149 -22
- package/src/internal/request-abort.ts +26 -0
- package/src/internal/server-boundary.ts +10 -1
- package/src/internal/share-data.ts +136 -0
- package/src/internal/store-signature.ts +149 -0
- package/src/internal/vite-plugin-compat.ts +2 -2
- package/src/internal/web-app.ts +121 -3
- package/src/islands-swap.ts +283 -0
- package/src/islands.ts +169 -72
- package/src/isr.ts +348 -106
- package/src/keep-alive.ts +1315 -0
- package/src/live/bus.ts +77 -0
- package/src/live/core.ts +52 -0
- package/src/live/history.ts +109 -0
- package/src/live/hub-protocol.ts +133 -0
- package/src/live/hub.ts +615 -0
- package/src/live/keyring.ts +379 -0
- package/src/live/read-capped.ts +36 -0
- package/src/live/revocations.ts +155 -0
- package/src/live/settings.ts +46 -0
- package/src/live-client.ts +973 -0
- package/src/live-hub.ts +658 -0
- package/src/live-redis.ts +259 -0
- package/src/live.ts +1096 -0
- package/src/manifest.ts +274 -6
- package/src/navigation.ts +14 -2
- package/src/plugins/vite-zone-guard.ts +15 -2
- package/src/ppr.ts +153 -0
- package/src/query.ts +147 -20
- package/src/render-seam.ts +35 -0
- package/src/route-types.ts +3 -1
- package/src/router.ts +494 -101
- package/src/server-components/build.ts +9 -3
- package/src/server-components/client.ts +89 -5
- package/src/server-components/hole.ts +23 -0
- package/src/server-components/html-policy.ts +63 -4
- package/src/server-components/html-scan.ts +159 -0
- package/src/server-components/html.ts +18 -4
- package/src/server-components/router-integration.ts +21 -2
- package/src/server-components/runtime.ts +112 -30
- package/src/server-components/state.ts +66 -44
- package/src/server-components/types.ts +16 -0
- package/src/vite.ts +75 -6
- package/dist/nano.d.ts +0 -125
- package/dist/nano.d.ts.map +0 -1
- package/dist/nano.js +0 -190
- package/dist/nano.js.map +0 -1
- package/src/nano.ts +0 -246
package/src/live/bus.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The live bus: one ordered log of changes and revocations that every process serving the app reads.
|
|
3
|
+
* Its sequence numbers never leave the server.
|
|
4
|
+
*
|
|
5
|
+
* A bus assigns each appended event a seq from a hybrid logical clock in microseconds: never below
|
|
6
|
+
* `Date.now() * 1000` at the authority, always above the previous seq. A restarted authority therefore
|
|
7
|
+
* keeps counting upward, and an entry's age can be read off its seq.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** Something a loader declared with `ctx.live` changed. */
|
|
11
|
+
export interface LiveChange {
|
|
12
|
+
readonly type: "change"
|
|
13
|
+
readonly keys: readonly string[]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Grants issued before this event stop working: every grant (`sub` and `key` null), one subject's
|
|
18
|
+
* (`key` null), or, with `key`, any grant holding that key (for everyone, or for `sub`).
|
|
19
|
+
*/
|
|
20
|
+
export interface LiveRevocation {
|
|
21
|
+
readonly type: "revoke"
|
|
22
|
+
readonly sub: string | null
|
|
23
|
+
readonly key: string | null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type LiveBusInput = LiveChange | LiveRevocation
|
|
27
|
+
|
|
28
|
+
export type LiveBusEvent = LiveBusInput & { readonly seq: number }
|
|
29
|
+
|
|
30
|
+
export interface LiveBusListener {
|
|
31
|
+
/** The next event, in seq order. */
|
|
32
|
+
event(event: LiveBusEvent): void
|
|
33
|
+
/**
|
|
34
|
+
* Events up to `head` may be lost (a trimmed stream, a reconnect): what happened before `head` is
|
|
35
|
+
* unknown, and delivery resumes with the events after it.
|
|
36
|
+
*/
|
|
37
|
+
gap(head: number): void
|
|
38
|
+
/** The bus is unreachable; the next {@link gap} reports where delivery resumes. */
|
|
39
|
+
down(): void
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface LiveBusSubscription {
|
|
43
|
+
/** The seq of the last event before delivery starts. */
|
|
44
|
+
readonly head: number
|
|
45
|
+
close(): void
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface LiveBus {
|
|
49
|
+
/** Append one event; resolves with its seq. */
|
|
50
|
+
append(input: LiveBusInput): Promise<number>
|
|
51
|
+
/** Deliver every event after the returned head to `listener`, in seq order. */
|
|
52
|
+
subscribe(listener: LiveBusListener): Promise<LiveBusSubscription>
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The seq a clock reading allows, never at or below `last`. */
|
|
56
|
+
export const nextSeq = (last: number, nowMs: number): number => Math.max(last + 1, nowMs * 1000)
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The single-process bus: an in-memory clock, events delivered to every subscriber before `append`
|
|
60
|
+
* resolves. Processes do not share it; run several behind a shared bus instead.
|
|
61
|
+
*/
|
|
62
|
+
export function memoryBus(): LiveBus {
|
|
63
|
+
let last = nextSeq(0, Date.now())
|
|
64
|
+
const listeners = new Set<LiveBusListener>()
|
|
65
|
+
return {
|
|
66
|
+
async append(input) {
|
|
67
|
+
last = nextSeq(last, Date.now())
|
|
68
|
+
const event: LiveBusEvent = { ...input, seq: last }
|
|
69
|
+
for (const listener of listeners) listener.event(event)
|
|
70
|
+
return last
|
|
71
|
+
},
|
|
72
|
+
async subscribe(listener) {
|
|
73
|
+
listeners.add(listener)
|
|
74
|
+
return { head: last, close: () => listeners.delete(listener) }
|
|
75
|
+
},
|
|
76
|
+
}
|
|
77
|
+
}
|
package/src/live/core.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a process applies the ordered live events to, wherever they come from: a bus every process
|
|
3
|
+
* reads, or the Workers hub that owns the log.
|
|
4
|
+
*/
|
|
5
|
+
import type { LiveBusEvent, LiveRevocation } from "./bus.ts"
|
|
6
|
+
import type { History } from "./history.ts"
|
|
7
|
+
import type { LiveHub } from "./hub.ts"
|
|
8
|
+
import { KEY_PATTERN, type Keyring } from "./keyring.ts"
|
|
9
|
+
import type { Revocations } from "./revocations.ts"
|
|
10
|
+
|
|
11
|
+
export interface LiveCore {
|
|
12
|
+
readonly ring: Keyring
|
|
13
|
+
readonly history: History
|
|
14
|
+
readonly revocations: Revocations
|
|
15
|
+
readonly hub: LiveHub
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Apply one event. A change is recorded in history and then fanned out in one synchronous step; a
|
|
20
|
+
* revocation is stored before any socket hears of it. Throws on an event it cannot apply, which
|
|
21
|
+
* leaves history and revocations unknown: the caller forgets everything up to its seq.
|
|
22
|
+
*/
|
|
23
|
+
export async function applyEvent(
|
|
24
|
+
core: LiveCore,
|
|
25
|
+
event: LiveBusEvent,
|
|
26
|
+
hash: (key: string) => number,
|
|
27
|
+
): Promise<void> {
|
|
28
|
+
if (event.type === "change") {
|
|
29
|
+
const keys = event.keys.filter((key) => typeof key === "string" && KEY_PATTERN.test(key))
|
|
30
|
+
const tags = await Promise.all(keys.map((key) => core.hub.tagsOf(key)))
|
|
31
|
+
core.history.record(
|
|
32
|
+
event.seq,
|
|
33
|
+
keys.map((key) => hash(key)),
|
|
34
|
+
Date.now(),
|
|
35
|
+
)
|
|
36
|
+
core.hub.change(keys, tags)
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
if (!validRevocation(event)) throw new TypeError("malformed revocation")
|
|
40
|
+
const tags = event.key === null ? [] : await core.hub.tagsOf(event.key)
|
|
41
|
+
core.revocations.apply(event, Date.now())
|
|
42
|
+
core.history.advance(event.seq, Date.now())
|
|
43
|
+
core.hub.revoke(event, tags)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function validRevocation(event: LiveBusEvent): event is LiveRevocation & {
|
|
47
|
+
readonly seq: number
|
|
48
|
+
} {
|
|
49
|
+
if (event.type !== "revoke") return false
|
|
50
|
+
if (event.sub !== null && typeof event.sub !== "string") return false
|
|
51
|
+
return event.key === null || (typeof event.key === "string" && KEY_PATTERN.test(event.key))
|
|
52
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which keys changed since a watermark: a ring of `(seq, key hash)` pairs, 16 bytes each, with the
|
|
3
|
+
* newest seq per hash beside it. History is complete for every seq above {@link History.floor}; a
|
|
4
|
+
* watermark at or below it, or one this process has not reached, is unknown and reads as changed.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export const HISTORY_ENTRIES = 100_000
|
|
8
|
+
export const HISTORY_MS = 120_000
|
|
9
|
+
|
|
10
|
+
export class History {
|
|
11
|
+
private readonly seqs: Float64Array
|
|
12
|
+
private readonly hashes: Float64Array
|
|
13
|
+
private readonly latest = new Map<number, number>()
|
|
14
|
+
private start = 0
|
|
15
|
+
private size = 0
|
|
16
|
+
/** Every event with a seq above this is recorded. */
|
|
17
|
+
floor: number
|
|
18
|
+
/** The last recorded event's seq. */
|
|
19
|
+
last: number
|
|
20
|
+
|
|
21
|
+
constructor(
|
|
22
|
+
head: number,
|
|
23
|
+
private readonly capacity = HISTORY_ENTRIES,
|
|
24
|
+
private readonly maxAgeMs = HISTORY_MS,
|
|
25
|
+
) {
|
|
26
|
+
this.seqs = new Float64Array(capacity)
|
|
27
|
+
this.hashes = new Float64Array(capacity)
|
|
28
|
+
this.floor = head
|
|
29
|
+
this.last = head
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
record(seq: number, hashes: readonly number[], nowMs: number): void {
|
|
33
|
+
for (const hash of hashes) {
|
|
34
|
+
if (this.size === this.capacity) this.evict()
|
|
35
|
+
const at = (this.start + this.size) % this.capacity
|
|
36
|
+
this.seqs[at] = seq
|
|
37
|
+
this.hashes[at] = hash
|
|
38
|
+
this.size++
|
|
39
|
+
this.latest.set(hash, seq)
|
|
40
|
+
}
|
|
41
|
+
this.last = seq
|
|
42
|
+
this.prune(nowMs)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Mark history up to `seq` as seen with nothing to record (a revocation). */
|
|
46
|
+
advance(seq: number, nowMs: number): void {
|
|
47
|
+
this.last = seq
|
|
48
|
+
this.prune(nowMs)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** `true` when the key may have changed after `watermark`. */
|
|
52
|
+
changedSince(hash: number, watermark: number): boolean {
|
|
53
|
+
if (!this.known(watermark)) return true
|
|
54
|
+
const seq = this.latest.get(hash)
|
|
55
|
+
return seq !== undefined && seq > watermark
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
known(watermark: number): boolean {
|
|
59
|
+
return watermark >= this.floor && watermark <= this.last
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Forget everything: what happened up to `head` is unknown. */
|
|
63
|
+
reset(head: number): void {
|
|
64
|
+
this.latest.clear()
|
|
65
|
+
this.start = 0
|
|
66
|
+
this.size = 0
|
|
67
|
+
this.floor = head
|
|
68
|
+
this.last = head
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Seqs count microseconds, so an entry's age is read off its seq. */
|
|
72
|
+
private prune(nowMs: number): void {
|
|
73
|
+
const oldest = (nowMs - this.maxAgeMs) * 1000
|
|
74
|
+
while (this.size > 0 && (this.seqs[this.start] ?? 0) < oldest) this.evict()
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private evict(): void {
|
|
78
|
+
const seq = this.seqs[this.start] ?? 0
|
|
79
|
+
const hash = this.hashes[this.start] ?? 0
|
|
80
|
+
if (this.latest.get(hash) === seq) this.latest.delete(hash)
|
|
81
|
+
if (seq > this.floor) this.floor = seq
|
|
82
|
+
this.start = (this.start + 1) % this.capacity
|
|
83
|
+
this.size--
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* A 53-bit keyed hash (cyrb53 with per-process seeds). A collision only costs a spurious reload, and
|
|
89
|
+
* the seeds keep anyone from choosing keys that collide with another.
|
|
90
|
+
*/
|
|
91
|
+
export function keyHasher(): (key: string) => number {
|
|
92
|
+
const seeds = crypto.getRandomValues(new Uint32Array(2))
|
|
93
|
+
const seed1 = seeds[0] ?? 0
|
|
94
|
+
const seed2 = seeds[1] ?? 0
|
|
95
|
+
return (key) => {
|
|
96
|
+
let h1 = 0xdeadbeef ^ seed1
|
|
97
|
+
let h2 = 0x41c6ce57 ^ seed2
|
|
98
|
+
for (let i = 0; i < key.length; i++) {
|
|
99
|
+
const ch = key.charCodeAt(i)
|
|
100
|
+
h1 = Math.imul(h1 ^ ch, 2654435761)
|
|
101
|
+
h2 = Math.imul(h2 ^ ch, 1597334677)
|
|
102
|
+
}
|
|
103
|
+
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507)
|
|
104
|
+
h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909)
|
|
105
|
+
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507)
|
|
106
|
+
h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909)
|
|
107
|
+
return 4294967296 * (2097151 & h2) + (h1 >>> 0)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The private calls between a Workers isolate and the app's live hub, over the Durable Object binding.
|
|
3
|
+
* Nothing on the internet reaches a Durable Object except through the app's own isolate, which
|
|
4
|
+
* forwards only the socket handshake; the paths below are refused on every public route.
|
|
5
|
+
*/
|
|
6
|
+
import type { LiveBusInput } from "./bus.ts"
|
|
7
|
+
import { KEY_PATTERN, type Keyring, MAX_KEYS_PER_FRAME } from "./keyring.ts"
|
|
8
|
+
import type { RevocationSnapshot } from "./revocations.ts"
|
|
9
|
+
|
|
10
|
+
/** A Durable Object namespace, as far as the runtime uses one (no Cloudflare types dependency). */
|
|
11
|
+
export interface LiveHubNamespace {
|
|
12
|
+
idFromName(name: string): unknown
|
|
13
|
+
get(id: unknown): { fetch(request: Request): Promise<Response> }
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const HUB_APPEND_PATH = "/__nifra_live_hub/append"
|
|
17
|
+
export const HUB_AUTHORITY_PATH = "/__nifra_live_hub/authority"
|
|
18
|
+
/** The host the calls name; a Durable Object stub ignores it. */
|
|
19
|
+
export const HUB_ORIGIN = "https://nifra-live-hub"
|
|
20
|
+
|
|
21
|
+
/** One hub per app. */
|
|
22
|
+
export const hubName = (appId: string): string => `nifra-live:${appId}`
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* What an isolate's live runtime and its hub must agree on, sent with every call: the grant lifetime
|
|
26
|
+
* sets how long the hub keeps a revocation, so a hub with a shorter one would forget revocations of
|
|
27
|
+
* grants still valid. The secret is named by its public id only.
|
|
28
|
+
*/
|
|
29
|
+
export const HUB_CONFIG_HEADER = "x-nifra-live-config"
|
|
30
|
+
|
|
31
|
+
export const hubConfigOf = (ring: Keyring): string =>
|
|
32
|
+
`${ring.ttl} ${ring.secretIds[0] ?? 0} ${encodeURIComponent(ring.appId)}`
|
|
33
|
+
|
|
34
|
+
/** The caller's newest secret must be one the hub holds; the hub may already hold a newer one. */
|
|
35
|
+
export function hubConfigMatches(ring: Keyring, header: string | null): boolean {
|
|
36
|
+
const parts = header?.split(" ")
|
|
37
|
+
if (parts?.length !== 3) return false
|
|
38
|
+
const [ttl, id, appId] = parts
|
|
39
|
+
return (
|
|
40
|
+
ttl === String(ring.ttl) &&
|
|
41
|
+
ring.secretIds.some((own) => String(own) === id) &&
|
|
42
|
+
appId === encodeURIComponent(ring.appId)
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Grants a load presents, at most. */
|
|
47
|
+
const MAX_SUBJECTS = 3
|
|
48
|
+
const MAX_KEYS = MAX_SUBJECTS * MAX_KEYS_PER_FRAME
|
|
49
|
+
/** Keys one append carries; a larger publish goes out as several. */
|
|
50
|
+
export const MAX_APPEND_KEYS = MAX_KEYS
|
|
51
|
+
export const MAX_SUBJECT_CHARS = 1024
|
|
52
|
+
|
|
53
|
+
export interface AuthorityRequest {
|
|
54
|
+
readonly subjects: readonly string[]
|
|
55
|
+
readonly keys: readonly string[]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface AuthorityReply extends RevocationSnapshot {
|
|
59
|
+
/** The hub's seq: a load's watermark. */
|
|
60
|
+
readonly seq: number
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function parseAppend(value: unknown): LiveBusInput | undefined {
|
|
64
|
+
if (!isRecord(value)) return undefined
|
|
65
|
+
if (value.type === "change") {
|
|
66
|
+
const keys = value.keys
|
|
67
|
+
if (!Array.isArray(keys) || keys.length === 0 || keys.length > MAX_APPEND_KEYS) return undefined
|
|
68
|
+
const list: string[] = []
|
|
69
|
+
for (const key of keys) {
|
|
70
|
+
if (typeof key !== "string" || !KEY_PATTERN.test(key)) return undefined
|
|
71
|
+
list.push(key)
|
|
72
|
+
}
|
|
73
|
+
return { type: "change", keys: list }
|
|
74
|
+
}
|
|
75
|
+
if (value.type !== "revoke") return undefined
|
|
76
|
+
const { sub, key } = value
|
|
77
|
+
if (sub !== null && !subjectLike(sub)) return undefined
|
|
78
|
+
if (key !== null && (typeof key !== "string" || !KEY_PATTERN.test(key))) return undefined
|
|
79
|
+
return { type: "revoke", sub, key }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function parseAuthorityRequest(value: unknown): AuthorityRequest | undefined {
|
|
83
|
+
if (!isRecord(value)) return undefined
|
|
84
|
+
const { subjects, keys } = value
|
|
85
|
+
if (!Array.isArray(subjects) || subjects.length > MAX_SUBJECTS) return undefined
|
|
86
|
+
if (!Array.isArray(keys) || keys.length > MAX_KEYS) return undefined
|
|
87
|
+
const subjectList: string[] = []
|
|
88
|
+
for (const sub of subjects) {
|
|
89
|
+
if (!subjectLike(sub)) return undefined
|
|
90
|
+
subjectList.push(sub)
|
|
91
|
+
}
|
|
92
|
+
const keyList: string[] = []
|
|
93
|
+
for (const key of keys) {
|
|
94
|
+
if (typeof key !== "string" || !KEY_PATTERN.test(key)) return undefined
|
|
95
|
+
keyList.push(key)
|
|
96
|
+
}
|
|
97
|
+
return { subjects: subjectList, keys: keyList }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function parseAuthorityReply(value: unknown): AuthorityReply | undefined {
|
|
101
|
+
if (!isRecord(value)) return undefined
|
|
102
|
+
const { seq, floor } = value
|
|
103
|
+
if (!seqLike(seq) || !seqLike(floor)) return undefined
|
|
104
|
+
const subjects = stampsOf(value.subjects)
|
|
105
|
+
const keys = stampsOf(value.keys)
|
|
106
|
+
const subjectKeys = stampsOf(value.subjectKeys)
|
|
107
|
+
if (subjects === undefined || keys === undefined || subjectKeys === undefined) return undefined
|
|
108
|
+
return { seq, floor, subjects, keys, subjectKeys }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function parseSeq(value: unknown): number | undefined {
|
|
112
|
+
return isRecord(value) && seqLike(value.seq) && value.seq > 0 ? value.seq : undefined
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Built from entries, so a subject named `__proto__` keeps its stamp. */
|
|
116
|
+
function stampsOf(value: unknown): Record<string, number> | undefined {
|
|
117
|
+
if (!isRecord(value)) return undefined
|
|
118
|
+
const out: [string, number][] = []
|
|
119
|
+
for (const [name, seq] of Object.entries(value)) {
|
|
120
|
+
if (!seqLike(seq)) return undefined
|
|
121
|
+
out.push([name, seq])
|
|
122
|
+
}
|
|
123
|
+
return Object.fromEntries(out)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const seqLike = (value: unknown): value is number =>
|
|
127
|
+
typeof value === "number" && Number.isSafeInteger(value) && value >= 0
|
|
128
|
+
|
|
129
|
+
const subjectLike = (value: unknown): value is string =>
|
|
130
|
+
typeof value === "string" && value.length <= MAX_SUBJECT_CHARS
|
|
131
|
+
|
|
132
|
+
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
133
|
+
typeof value === "object" && value !== null && !Array.isArray(value)
|