@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
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A live bus on Redis Streams, for an app that several processes serve. Every append runs one Lua
|
|
3
|
+
* script: a hybrid logical clock in microseconds (never below the server's clock, always above the
|
|
4
|
+
* last seq), then `XADD` with that seq as the entry id, trimmed to about `maxLength` entries. Each
|
|
5
|
+
* process reads the stream from its last id with a blocking `XREAD` on a connection of its own.
|
|
6
|
+
*
|
|
7
|
+
* import { RedisClient } from "bun"
|
|
8
|
+
* import { redisLiveBus } from "@nifrajs/web/live-redis"
|
|
9
|
+
*
|
|
10
|
+
* const shared = new RedisClient(process.env.REDIS_URL)
|
|
11
|
+
* const reader = new RedisClient(process.env.REDIS_URL)
|
|
12
|
+
* const bus = redisLiveBus({
|
|
13
|
+
* send: (command, args) => shared.send(command, [...args]),
|
|
14
|
+
* read: (command, args) => reader.send(command, [...args]),
|
|
15
|
+
* })
|
|
16
|
+
* const runtime = live({ secret, subject, bus, instances: 4 })
|
|
17
|
+
*
|
|
18
|
+
* Every entry also carries a running count, so a reader that fell behind a trim - or lost the
|
|
19
|
+
* connection - reports the gap instead of skipping what it missed.
|
|
20
|
+
*/
|
|
21
|
+
import type { LiveBus, LiveBusEvent, LiveBusListener } from "./live/bus.ts"
|
|
22
|
+
|
|
23
|
+
/** Send one Redis command and resolve with its reply, as any client's raw-command call does. */
|
|
24
|
+
export type RedisCommand = (command: string, args: readonly string[]) => Promise<unknown>
|
|
25
|
+
|
|
26
|
+
export interface RedisLiveBusOptions {
|
|
27
|
+
/** Appends and reads the clock; any connection. */
|
|
28
|
+
readonly send: RedisCommand
|
|
29
|
+
/** Reads the stream. Its own connection: a blocking `XREAD` holds it. */
|
|
30
|
+
readonly read: RedisCommand
|
|
31
|
+
/** The stream; the clock lives at `<key>:clock`. Default `nifra:live`. */
|
|
32
|
+
readonly key?: string
|
|
33
|
+
/** Entries kept, roughly. Default 100,000, the most the live history keeps. */
|
|
34
|
+
readonly maxLength?: number
|
|
35
|
+
/** How long one `XREAD` blocks, in ms. Default 5,000. */
|
|
36
|
+
readonly blockMs?: number
|
|
37
|
+
/**
|
|
38
|
+
* How long one `XREAD` may take before it counts as a failed read, in ms: a connection that stopped
|
|
39
|
+
* answering would otherwise leave this process's sockets missing every change. Default `blockMs`
|
|
40
|
+
* plus 5,000.
|
|
41
|
+
*/
|
|
42
|
+
readonly readTimeoutMs?: number
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* KEYS[1] the stream, KEYS[2] the clock hash; ARGV[1] the event JSON, ARGV[2] the trim length.
|
|
47
|
+
* Lua numbers are doubles: a microsecond clock stays exact below 2^53 (the year 2255).
|
|
48
|
+
*/
|
|
49
|
+
export const APPEND_SCRIPT = `local t = redis.call('TIME')
|
|
50
|
+
local now = tonumber(t[1]) * 1000000 + tonumber(t[2])
|
|
51
|
+
local last = tonumber(redis.call('HGET', KEYS[2], 'seq') or '0')
|
|
52
|
+
local seq = math.max(last + 1, now)
|
|
53
|
+
local id = string.format('%.0f', seq)
|
|
54
|
+
local n = redis.call('HINCRBY', KEYS[2], 'n', 1)
|
|
55
|
+
redis.call('HSET', KEYS[2], 'seq', id)
|
|
56
|
+
redis.call('XADD', KEYS[1], 'MAXLEN', '~', ARGV[2], id .. '-0', 'n', n, 'e', ARGV[1])
|
|
57
|
+
return id`
|
|
58
|
+
|
|
59
|
+
const RETRY_MS = [100, 500, 1_000, 2_000, 5_000]
|
|
60
|
+
const READ_GRACE_MS = 5_000
|
|
61
|
+
|
|
62
|
+
export function redisLiveBus(options: RedisLiveBusOptions): LiveBus {
|
|
63
|
+
const key = options.key ?? "nifra:live"
|
|
64
|
+
const clock = `${key}:clock`
|
|
65
|
+
const maxLength = String(options.maxLength ?? 100_000)
|
|
66
|
+
const blockMs = String(options.blockMs ?? 5_000)
|
|
67
|
+
const readTimeoutMs = options.readTimeoutMs ?? (options.blockMs ?? 5_000) + READ_GRACE_MS
|
|
68
|
+
|
|
69
|
+
/** The clock's seq and running count, as the last append left them. */
|
|
70
|
+
const position = async (): Promise<{ seq: number; n: number }> => {
|
|
71
|
+
const reply = await options.send("HMGET", [clock, "seq", "n"])
|
|
72
|
+
const [seq, n] = Array.isArray(reply) ? reply : []
|
|
73
|
+
return { seq: countOf(seq), n: countOf(n) }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
async append(input) {
|
|
78
|
+
const reply = await options.send("EVAL", [
|
|
79
|
+
APPEND_SCRIPT,
|
|
80
|
+
"2",
|
|
81
|
+
key,
|
|
82
|
+
clock,
|
|
83
|
+
JSON.stringify(input),
|
|
84
|
+
maxLength,
|
|
85
|
+
])
|
|
86
|
+
const seq = countOf(reply)
|
|
87
|
+
if (seq === 0) throw new Error("[nifra/web] live-redis: the append script returned no seq")
|
|
88
|
+
return seq
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
async subscribe(listener) {
|
|
92
|
+
const start = await position()
|
|
93
|
+
let closed = false
|
|
94
|
+
void follow(start.seq, start.n, listener, () => closed)
|
|
95
|
+
return {
|
|
96
|
+
head: start.seq,
|
|
97
|
+
close: () => {
|
|
98
|
+
closed = true
|
|
99
|
+
},
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function follow(
|
|
105
|
+
head: number,
|
|
106
|
+
count: number,
|
|
107
|
+
listener: LiveBusListener,
|
|
108
|
+
isClosed: () => boolean,
|
|
109
|
+
): Promise<void> {
|
|
110
|
+
let last = head
|
|
111
|
+
// The id the next read starts after, verbatim: an id this bus did not write still moves it on.
|
|
112
|
+
let cursor = `${head}-0`
|
|
113
|
+
let expected = count + 1
|
|
114
|
+
let failures = 0
|
|
115
|
+
// Told `down`: only a read that answers again ends the outage, never the clock alone.
|
|
116
|
+
let down = false
|
|
117
|
+
while (!isClosed()) {
|
|
118
|
+
let entries: Entry[] | undefined
|
|
119
|
+
try {
|
|
120
|
+
const reply = await within(
|
|
121
|
+
options.read("XREAD", ["BLOCK", blockMs, "COUNT", "1000", "STREAMS", key, cursor]),
|
|
122
|
+
readTimeoutMs,
|
|
123
|
+
)
|
|
124
|
+
entries = reply === null || reply === undefined ? [] : entriesOf(reply)
|
|
125
|
+
} catch {
|
|
126
|
+
entries = undefined
|
|
127
|
+
}
|
|
128
|
+
if (entries === undefined) {
|
|
129
|
+
// A reply of no known shape counts as a failed read, so it backs off instead of spinning.
|
|
130
|
+
failures++
|
|
131
|
+
// One failed read may be a hiccup; a second in a row is an outage.
|
|
132
|
+
if (failures === 2) {
|
|
133
|
+
down = true
|
|
134
|
+
listener.down()
|
|
135
|
+
}
|
|
136
|
+
await wait(RETRY_MS[Math.min(failures - 1, RETRY_MS.length - 1)] ?? 5_000)
|
|
137
|
+
continue
|
|
138
|
+
}
|
|
139
|
+
failures = 0
|
|
140
|
+
if (down) {
|
|
141
|
+
try {
|
|
142
|
+
const now = await position()
|
|
143
|
+
if (isClosed()) return
|
|
144
|
+
// Whatever happened while the bus was unreachable is unknown: resume after the clock.
|
|
145
|
+
listener.gap(now.seq)
|
|
146
|
+
last = now.seq
|
|
147
|
+
cursor = `${now.seq}-0`
|
|
148
|
+
expected = now.n + 1
|
|
149
|
+
down = false
|
|
150
|
+
} catch {
|
|
151
|
+
// The clock is out of reach: still down, and the next read tries again.
|
|
152
|
+
}
|
|
153
|
+
continue
|
|
154
|
+
}
|
|
155
|
+
for (const entry of entries) {
|
|
156
|
+
if (isClosed()) return
|
|
157
|
+
cursor = entry.id
|
|
158
|
+
if (entry.seq === 0) {
|
|
159
|
+
listener.gap(last)
|
|
160
|
+
continue
|
|
161
|
+
}
|
|
162
|
+
if (entry.n !== expected) {
|
|
163
|
+
// Entries between were trimmed before this reader got to them.
|
|
164
|
+
listener.gap(entry.seq - 1)
|
|
165
|
+
}
|
|
166
|
+
expected = entry.n + 1
|
|
167
|
+
last = entry.seq
|
|
168
|
+
if (entry.event === undefined) listener.gap(entry.seq)
|
|
169
|
+
else listener.event(entry.event)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
interface Entry {
|
|
176
|
+
readonly id: string
|
|
177
|
+
/** 0 when the id is not one this bus writes. */
|
|
178
|
+
readonly seq: number
|
|
179
|
+
readonly n: number
|
|
180
|
+
/** Undefined when the entry does not hold a bus event. */
|
|
181
|
+
readonly event: LiveBusEvent | undefined
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* The entries of an `XREAD` reply for one stream - RESP2 `[[key, entries]]`, RESP3 `{ key: entries }`
|
|
186
|
+
* or a `Map` - each entry `[id, [field, value, ...]]`. Undefined when the reply has none of these shapes.
|
|
187
|
+
*/
|
|
188
|
+
function entriesOf(reply: unknown): Entry[] | undefined {
|
|
189
|
+
let streams: unknown[]
|
|
190
|
+
if (Array.isArray(reply))
|
|
191
|
+
streams = reply.map((stream) => (Array.isArray(stream) ? stream[1] : undefined))
|
|
192
|
+
else if (reply instanceof Map) streams = [...reply.values()]
|
|
193
|
+
else if (isRecord(reply)) streams = Object.values(reply)
|
|
194
|
+
else return undefined
|
|
195
|
+
const out: Entry[] = []
|
|
196
|
+
for (const entries of streams) {
|
|
197
|
+
if (!Array.isArray(entries)) return undefined
|
|
198
|
+
for (const entry of entries) {
|
|
199
|
+
if (!Array.isArray(entry)) return undefined
|
|
200
|
+
const [id, fields]: unknown[] = entry
|
|
201
|
+
if (typeof id !== "string" || !Array.isArray(fields)) return undefined
|
|
202
|
+
const [time, part] = id.split("-")
|
|
203
|
+
const seq = part === "0" ? countOf(time) : 0
|
|
204
|
+
let n = 0
|
|
205
|
+
let body: unknown
|
|
206
|
+
for (let i = 0; i + 1 < fields.length; i += 2) {
|
|
207
|
+
if (fields[i] === "n") n = countOf(fields[i + 1])
|
|
208
|
+
else if (fields[i] === "e") body = fields[i + 1]
|
|
209
|
+
}
|
|
210
|
+
out.push({ id, seq, n, event: seq === 0 ? undefined : eventOf(body, seq) })
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return out
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function eventOf(body: unknown, seq: number): LiveBusEvent | undefined {
|
|
217
|
+
if (typeof body !== "string") return undefined
|
|
218
|
+
let value: unknown
|
|
219
|
+
try {
|
|
220
|
+
value = JSON.parse(body)
|
|
221
|
+
} catch {
|
|
222
|
+
return undefined
|
|
223
|
+
}
|
|
224
|
+
if (!isRecord(value)) return undefined
|
|
225
|
+
if (value.type === "change" && Array.isArray(value.keys)) {
|
|
226
|
+
const keys = value.keys.filter((name): name is string => typeof name === "string")
|
|
227
|
+
return { type: "change", keys, seq }
|
|
228
|
+
}
|
|
229
|
+
const { sub, key } = value
|
|
230
|
+
if (
|
|
231
|
+
value.type === "revoke" &&
|
|
232
|
+
(sub === null || typeof sub === "string") &&
|
|
233
|
+
(key === null || typeof key === "string")
|
|
234
|
+
) {
|
|
235
|
+
return { type: "revoke", sub, key, seq }
|
|
236
|
+
}
|
|
237
|
+
return undefined
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
241
|
+
typeof value === "object" && value !== null && !Array.isArray(value)
|
|
242
|
+
|
|
243
|
+
/** A Redis integer reply or numeric string as a non-negative safe integer, else 0. */
|
|
244
|
+
function countOf(value: unknown): number {
|
|
245
|
+
const n =
|
|
246
|
+
typeof value === "number" ? value : typeof value === "string" ? Number(value) : Number.NaN
|
|
247
|
+
return Number.isSafeInteger(n) && n > 0 ? n : 0
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const wait = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms))
|
|
251
|
+
|
|
252
|
+
/** `pending`, or a rejection once `ms` pass without it settling. */
|
|
253
|
+
function within<T>(pending: Promise<T>, ms: number): Promise<T> {
|
|
254
|
+
let timer: ReturnType<typeof setTimeout> | undefined
|
|
255
|
+
const expiry = new Promise<never>((_, reject) => {
|
|
256
|
+
timer = setTimeout(() => reject(new Error("[nifra/web] live-redis: the read timed out")), ms)
|
|
257
|
+
})
|
|
258
|
+
return Promise.race([pending, expiry]).finally(() => clearTimeout(timer))
|
|
259
|
+
}
|