@timber-js/app 0.2.0-alpha.8 → 0.2.0-alpha.80
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/LICENSE +8 -0
- package/dist/_chunks/actions-Dg-ANYHb.js +421 -0
- package/dist/_chunks/actions-Dg-ANYHb.js.map +1 -0
- package/dist/_chunks/{als-registry-B7DbZ2hS.js → als-registry-HS0LGUl2.js} +1 -1
- package/dist/_chunks/als-registry-HS0LGUl2.js.map +1 -0
- package/dist/_chunks/chunk-DYhsFzuS.js +33 -0
- package/dist/_chunks/{debug-gwlJkDuf.js → debug-ECi_61pb.js} +2 -2
- package/dist/_chunks/debug-ECi_61pb.js.map +1 -0
- package/dist/_chunks/define-C77ScO0m.js +106 -0
- package/dist/_chunks/define-C77ScO0m.js.map +1 -0
- package/dist/_chunks/define-CZqDwhSu.js +199 -0
- package/dist/_chunks/define-CZqDwhSu.js.map +1 -0
- package/dist/_chunks/define-cookie-C2IkoFGN.js +94 -0
- package/dist/_chunks/define-cookie-C2IkoFGN.js.map +1 -0
- package/dist/_chunks/{format-DviM89f0.js → dev-warnings-DpGRGoDi.js} +5 -44
- package/dist/_chunks/dev-warnings-DpGRGoDi.js.map +1 -0
- package/dist/_chunks/format-CYBGxKtc.js +14 -0
- package/dist/_chunks/format-CYBGxKtc.js.map +1 -0
- package/dist/_chunks/{interception-BOoWmLUA.js → interception-Dpn_UfAD.js} +171 -97
- package/dist/_chunks/interception-Dpn_UfAD.js.map +1 -0
- package/dist/_chunks/merge-search-params-Cm_KIWDX.js +41 -0
- package/dist/_chunks/merge-search-params-Cm_KIWDX.js.map +1 -0
- package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js → metadata-routes-DS3eKNmf.js} +1 -1
- package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js.map → metadata-routes-DS3eKNmf.js.map} +1 -1
- package/dist/_chunks/request-context-qMsWgy9C.js +478 -0
- package/dist/_chunks/request-context-qMsWgy9C.js.map +1 -0
- package/dist/_chunks/schema-bridge-C3xl_vfb.js +86 -0
- package/dist/_chunks/schema-bridge-C3xl_vfb.js.map +1 -0
- package/dist/_chunks/segment-classify-BDNn6EzD.js +65 -0
- package/dist/_chunks/segment-classify-BDNn6EzD.js.map +1 -0
- package/dist/_chunks/segment-context-fHFLF1PE.js +34 -0
- package/dist/_chunks/segment-context-fHFLF1PE.js.map +1 -0
- package/dist/_chunks/{ssr-data-MjmprTmO.js → ssr-data-DzuI0bIV.js} +1 -1
- package/dist/_chunks/{ssr-data-MjmprTmO.js.map → ssr-data-DzuI0bIV.js.map} +1 -1
- package/dist/_chunks/stale-reload-C2plcNtG.js +64 -0
- package/dist/_chunks/stale-reload-C2plcNtG.js.map +1 -0
- package/dist/_chunks/{tracing-CemImE6h.js → tracing-CCYbKn5n.js} +60 -9
- package/dist/_chunks/tracing-CCYbKn5n.js.map +1 -0
- package/dist/_chunks/use-params-Br9YSUFV.js +295 -0
- package/dist/_chunks/use-params-Br9YSUFV.js.map +1 -0
- package/dist/_chunks/{use-query-states-D5KaffOK.js → use-query-states-Lo_s_pw2.js} +4 -4
- package/dist/_chunks/use-query-states-Lo_s_pw2.js.map +1 -0
- package/dist/_chunks/wrappers-_DTmImGt.js +63 -0
- package/dist/_chunks/wrappers-_DTmImGt.js.map +1 -0
- package/dist/adapters/cloudflare-dev.d.ts +109 -0
- package/dist/adapters/cloudflare-dev.d.ts.map +1 -0
- package/dist/adapters/cloudflare-dev.js +73 -0
- package/dist/adapters/cloudflare-dev.js.map +1 -0
- package/dist/adapters/cloudflare-kv-cache.d.ts +64 -0
- package/dist/adapters/cloudflare-kv-cache.d.ts.map +1 -0
- package/dist/adapters/cloudflare-kv-cache.js +95 -0
- package/dist/adapters/cloudflare-kv-cache.js.map +1 -0
- package/dist/adapters/cloudflare.d.ts +148 -12
- package/dist/adapters/cloudflare.d.ts.map +1 -1
- package/dist/adapters/cloudflare.js +135 -11
- package/dist/adapters/cloudflare.js.map +1 -1
- package/dist/adapters/compress-module.d.ts.map +1 -1
- package/dist/adapters/nitro.d.ts +17 -1
- package/dist/adapters/nitro.d.ts.map +1 -1
- package/dist/adapters/nitro.js +56 -13
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/cache/cache-api.d.ts +24 -0
- package/dist/cache/cache-api.d.ts.map +1 -0
- package/dist/cache/handler-store.d.ts +31 -0
- package/dist/cache/handler-store.d.ts.map +1 -0
- package/dist/cache/index.d.ts +23 -7
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +142 -80
- package/dist/cache/index.js.map +1 -1
- package/dist/cache/singleflight.d.ts +18 -1
- package/dist/cache/singleflight.d.ts.map +1 -1
- package/dist/cache/sizeof.d.ts +22 -0
- package/dist/cache/sizeof.d.ts.map +1 -0
- package/dist/cache/timber-cache.d.ts +1 -1
- package/dist/cache/timber-cache.d.ts.map +1 -1
- package/dist/cli.d.ts +6 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +6 -1
- package/dist/cli.js.map +1 -1
- package/dist/client/browser-dev.d.ts +27 -1
- package/dist/client/browser-dev.d.ts.map +1 -1
- package/dist/client/browser-entry/action-dispatch.d.ts +17 -0
- package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -0
- package/dist/client/browser-entry/hmr.d.ts +21 -0
- package/dist/client/browser-entry/hmr.d.ts.map +1 -0
- package/dist/client/browser-entry/hydrate.d.ts +46 -0
- package/dist/client/browser-entry/hydrate.d.ts.map +1 -0
- package/dist/client/browser-entry/index.d.ts +30 -0
- package/dist/client/browser-entry/index.d.ts.map +1 -0
- package/dist/client/browser-entry/post-hydration.d.ts +26 -0
- package/dist/client/browser-entry/post-hydration.d.ts.map +1 -0
- package/dist/client/browser-entry/router-init.d.ts +23 -0
- package/dist/client/browser-entry/router-init.d.ts.map +1 -0
- package/dist/client/browser-entry/rsc-stream.d.ts +24 -0
- package/dist/client/browser-entry/rsc-stream.d.ts.map +1 -0
- package/dist/client/browser-entry/scroll.d.ts +19 -0
- package/dist/client/browser-entry/scroll.d.ts.map +1 -0
- package/dist/client/error-boundary.d.ts +12 -5
- package/dist/client/error-boundary.d.ts.map +1 -1
- package/dist/client/error-boundary.js +10 -4
- package/dist/client/error-boundary.js.map +1 -1
- package/dist/client/error-reconstituter.d.ts +54 -0
- package/dist/client/error-reconstituter.d.ts.map +1 -0
- package/dist/client/form.d.ts +2 -2
- package/dist/client/form.d.ts.map +1 -1
- package/dist/client/history.d.ts +19 -4
- package/dist/client/history.d.ts.map +1 -1
- package/dist/client/index.d.ts +7 -21
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +210 -1017
- package/dist/client/index.js.map +1 -1
- package/dist/client/internal.d.ts +18 -0
- package/dist/client/internal.d.ts.map +1 -0
- package/dist/client/internal.js +890 -0
- package/dist/client/internal.js.map +1 -0
- package/dist/client/link-pending-store.d.ts +63 -0
- package/dist/client/link-pending-store.d.ts.map +1 -0
- package/dist/client/link.d.ts +90 -32
- package/dist/client/link.d.ts.map +1 -1
- package/dist/client/nav-link-store.d.ts +36 -0
- package/dist/client/nav-link-store.d.ts.map +1 -0
- package/dist/client/navigation-api-types.d.ts +90 -0
- package/dist/client/navigation-api-types.d.ts.map +1 -0
- package/dist/client/navigation-api.d.ts +115 -0
- package/dist/client/navigation-api.d.ts.map +1 -0
- package/dist/client/navigation-context.d.ts +13 -2
- package/dist/client/navigation-context.d.ts.map +1 -1
- package/dist/client/{transition-root.d.ts → navigation-root.d.ts} +42 -8
- package/dist/client/navigation-root.d.ts.map +1 -0
- package/dist/client/nuqs-adapter.d.ts.map +1 -1
- package/dist/client/router-ref.d.ts +1 -1
- package/dist/client/router.d.ts +70 -4
- package/dist/client/router.d.ts.map +1 -1
- package/dist/client/rsc-fetch.d.ts +38 -3
- package/dist/client/rsc-fetch.d.ts.map +1 -1
- package/dist/client/segment-cache.d.ts +1 -1
- package/dist/client/segment-cache.d.ts.map +1 -1
- package/dist/client/segment-outlet.d.ts +63 -0
- package/dist/client/segment-outlet.d.ts.map +1 -0
- package/dist/client/ssr-data.d.ts +13 -4
- package/dist/client/ssr-data.d.ts.map +1 -1
- package/dist/client/stale-reload.d.ts +15 -0
- package/dist/client/stale-reload.d.ts.map +1 -1
- package/dist/client/top-loader.d.ts +5 -5
- package/dist/client/top-loader.d.ts.map +1 -1
- package/dist/client/use-link-status.d.ts +5 -5
- package/dist/client/use-link-status.d.ts.map +1 -1
- package/dist/client/use-params.d.ts +6 -4
- package/dist/client/use-params.d.ts.map +1 -1
- package/dist/client/{use-navigation-pending.d.ts → use-pending-navigation.d.ts} +4 -4
- package/dist/client/use-pending-navigation.d.ts.map +1 -0
- package/dist/client/use-query-states.d.ts +1 -1
- package/dist/client/use-query-states.d.ts.map +1 -1
- package/dist/client/use-router.d.ts +1 -1
- package/dist/codec.d.ts +33 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/codec.js +2 -0
- package/dist/config-types.d.ts +210 -0
- package/dist/config-types.d.ts.map +1 -0
- package/dist/content/index.d.ts +1 -10
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/index.js +0 -2
- package/dist/cookies/define-cookie.d.ts +35 -14
- package/dist/cookies/define-cookie.d.ts.map +1 -1
- package/dist/cookies/index.js +1 -83
- package/dist/fonts/css.d.ts +1 -0
- package/dist/fonts/css.d.ts.map +1 -1
- package/dist/index.d.ts +45 -192
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12296 -11901
- package/dist/index.js.map +1 -1
- package/dist/plugin-context.d.ts +84 -0
- package/dist/plugin-context.d.ts.map +1 -0
- package/dist/plugins/adapter-build.d.ts +1 -1
- package/dist/plugins/adapter-build.d.ts.map +1 -1
- package/dist/plugins/build-manifest.d.ts +2 -2
- package/dist/plugins/build-manifest.d.ts.map +1 -1
- package/dist/plugins/build-report.d.ts +3 -3
- package/dist/plugins/build-report.d.ts.map +1 -1
- package/dist/plugins/client-chunks.d.ts +32 -0
- package/dist/plugins/client-chunks.d.ts.map +1 -0
- package/dist/plugins/content.d.ts +1 -1
- package/dist/plugins/content.d.ts.map +1 -1
- package/dist/plugins/dev-browser-logs.d.ts +84 -0
- package/dist/plugins/dev-browser-logs.d.ts.map +1 -0
- package/dist/plugins/dev-error-overlay.d.ts +26 -1
- package/dist/plugins/dev-error-overlay.d.ts.map +1 -1
- package/dist/plugins/dev-logs.d.ts +1 -1
- package/dist/plugins/dev-logs.d.ts.map +1 -1
- package/dist/plugins/dev-server.d.ts +1 -1
- package/dist/plugins/dev-server.d.ts.map +1 -1
- package/dist/plugins/entries.d.ts +1 -1
- package/dist/plugins/entries.d.ts.map +1 -1
- package/dist/plugins/fonts.d.ts +19 -5
- package/dist/plugins/fonts.d.ts.map +1 -1
- package/dist/plugins/mdx.d.ts +1 -1
- package/dist/plugins/mdx.d.ts.map +1 -1
- package/dist/plugins/routing.d.ts +1 -1
- package/dist/plugins/routing.d.ts.map +1 -1
- package/dist/plugins/server-bundle.d.ts.map +1 -1
- package/dist/plugins/shims.d.ts +6 -5
- package/dist/plugins/shims.d.ts.map +1 -1
- package/dist/plugins/static-build.d.ts +4 -4
- package/dist/plugins/static-build.d.ts.map +1 -1
- package/dist/routing/codegen.d.ts +2 -2
- package/dist/routing/codegen.d.ts.map +1 -1
- package/dist/routing/index.d.ts +2 -0
- package/dist/routing/index.d.ts.map +1 -1
- package/dist/routing/index.js +3 -2
- package/dist/routing/scanner.d.ts.map +1 -1
- package/dist/routing/segment-classify.d.ts +46 -0
- package/dist/routing/segment-classify.d.ts.map +1 -0
- package/dist/routing/status-file-lint.d.ts +2 -1
- package/dist/routing/status-file-lint.d.ts.map +1 -1
- package/dist/routing/types.d.ts +16 -4
- package/dist/routing/types.d.ts.map +1 -1
- package/dist/rsc-runtime/rsc.d.ts +1 -1
- package/dist/rsc-runtime/rsc.d.ts.map +1 -1
- package/dist/rsc-runtime/ssr.d.ts +12 -0
- package/dist/rsc-runtime/ssr.d.ts.map +1 -1
- package/dist/schema-bridge.d.ts +76 -0
- package/dist/schema-bridge.d.ts.map +1 -0
- package/dist/search-params/define.d.ts +139 -0
- package/dist/search-params/define.d.ts.map +1 -0
- package/dist/search-params/index.d.ts +4 -7
- package/dist/search-params/index.d.ts.map +1 -1
- package/dist/search-params/index.js +4 -474
- package/dist/search-params/registry.d.ts +2 -2
- package/dist/search-params/registry.d.ts.map +1 -1
- package/dist/search-params/wrappers.d.ts +53 -0
- package/dist/search-params/wrappers.d.ts.map +1 -0
- package/dist/segment-params/define.d.ts +78 -0
- package/dist/segment-params/define.d.ts.map +1 -0
- package/dist/segment-params/index.d.ts +6 -0
- package/dist/segment-params/index.d.ts.map +1 -0
- package/dist/segment-params/index.js +4 -0
- package/dist/server/access-gate.d.ts +4 -0
- package/dist/server/access-gate.d.ts.map +1 -1
- package/dist/server/action-client.d.ts +12 -1
- package/dist/server/action-client.d.ts.map +1 -1
- package/dist/server/action-encryption.d.ts +76 -0
- package/dist/server/action-encryption.d.ts.map +1 -0
- package/dist/server/action-handler.d.ts.map +1 -1
- package/dist/server/actions.d.ts +3 -6
- package/dist/server/actions.d.ts.map +1 -1
- package/dist/server/als-registry.d.ts +32 -4
- package/dist/server/als-registry.d.ts.map +1 -1
- package/dist/server/build-manifest.d.ts +2 -2
- package/dist/server/build-manifest.d.ts.map +1 -1
- package/dist/server/debug.d.ts +1 -1
- package/dist/server/default-logger.d.ts +22 -0
- package/dist/server/default-logger.d.ts.map +1 -0
- package/dist/server/deny-page-resolver.d.ts +52 -0
- package/dist/server/deny-page-resolver.d.ts.map +1 -0
- package/dist/server/deny-renderer.d.ts.map +1 -1
- package/dist/server/dev-holding-server.d.ts +52 -0
- package/dist/server/dev-holding-server.d.ts.map +1 -0
- package/dist/server/dev-warnings.d.ts +1 -21
- package/dist/server/dev-warnings.d.ts.map +1 -1
- package/dist/server/early-hints.d.ts +13 -5
- package/dist/server/early-hints.d.ts.map +1 -1
- package/dist/server/error-boundary-wrapper.d.ts +7 -1
- package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
- package/dist/server/fallback-error.d.ts +4 -3
- package/dist/server/fallback-error.d.ts.map +1 -1
- package/dist/server/flight-injection-state.d.ts +66 -0
- package/dist/server/flight-injection-state.d.ts.map +1 -0
- package/dist/server/flight-scripts.d.ts +42 -0
- package/dist/server/flight-scripts.d.ts.map +1 -0
- package/dist/server/flush.d.ts.map +1 -1
- package/dist/server/form-data.d.ts +29 -0
- package/dist/server/form-data.d.ts.map +1 -1
- package/dist/server/html-injectors.d.ts +51 -11
- package/dist/server/html-injectors.d.ts.map +1 -1
- package/dist/server/index.d.ts +6 -43
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +38 -2798
- package/dist/server/index.js.map +1 -1
- package/dist/server/internal.d.ts +46 -0
- package/dist/server/internal.d.ts.map +1 -0
- package/dist/server/internal.js +2883 -0
- package/dist/server/internal.js.map +1 -0
- package/dist/server/logger.d.ts +25 -7
- package/dist/server/logger.d.ts.map +1 -1
- package/dist/server/middleware-runner.d.ts +19 -4
- package/dist/server/middleware-runner.d.ts.map +1 -1
- package/dist/server/node-stream-transforms.d.ts +113 -0
- package/dist/server/node-stream-transforms.d.ts.map +1 -0
- package/dist/server/page-deny-boundary.d.ts +31 -0
- package/dist/server/page-deny-boundary.d.ts.map +1 -0
- package/dist/server/pipeline-interception.d.ts +1 -1
- package/dist/server/pipeline-interception.d.ts.map +1 -1
- package/dist/server/pipeline-metadata.d.ts +6 -0
- package/dist/server/pipeline-metadata.d.ts.map +1 -1
- package/dist/server/pipeline.d.ts +42 -10
- package/dist/server/pipeline.d.ts.map +1 -1
- package/dist/server/primitives.d.ts +69 -18
- package/dist/server/primitives.d.ts.map +1 -1
- package/dist/server/render-timeout.d.ts +51 -0
- package/dist/server/render-timeout.d.ts.map +1 -0
- package/dist/server/request-context.d.ts +112 -43
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/route-element-builder.d.ts +27 -1
- package/dist/server/route-element-builder.d.ts.map +1 -1
- package/dist/server/route-handler.d.ts.map +1 -1
- package/dist/server/route-matcher.d.ts +9 -2
- package/dist/server/route-matcher.d.ts.map +1 -1
- package/dist/server/rsc-entry/api-handler.d.ts +2 -2
- package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
- package/dist/server/rsc-entry/error-renderer.d.ts +26 -13
- package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
- package/dist/server/rsc-entry/helpers.d.ts +48 -5
- package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts +8 -3
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-payload.d.ts +3 -3
- package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-stream.d.ts +4 -1
- package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-bridge.d.ts +1 -1
- package/dist/server/rsc-entry/ssr-bridge.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-renderer.d.ts +19 -4
- package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
- package/dist/server/safe-load.d.ts +46 -0
- package/dist/server/safe-load.d.ts.map +1 -0
- package/dist/server/sitemap-generator.d.ts +129 -0
- package/dist/server/sitemap-generator.d.ts.map +1 -0
- package/dist/server/sitemap-handler.d.ts +22 -0
- package/dist/server/sitemap-handler.d.ts.map +1 -0
- package/dist/server/slot-resolver.d.ts +1 -1
- package/dist/server/slot-resolver.d.ts.map +1 -1
- package/dist/server/ssr-entry.d.ts +22 -0
- package/dist/server/ssr-entry.d.ts.map +1 -1
- package/dist/server/ssr-render.d.ts +39 -21
- package/dist/server/ssr-render.d.ts.map +1 -1
- package/dist/server/ssr-wrappers.d.ts +50 -0
- package/dist/server/ssr-wrappers.d.ts.map +1 -0
- package/dist/server/status-code-resolver.d.ts +1 -1
- package/dist/server/status-code-resolver.d.ts.map +1 -1
- package/dist/server/stream-utils.d.ts +36 -0
- package/dist/server/stream-utils.d.ts.map +1 -0
- package/dist/server/tracing.d.ts +4 -4
- package/dist/server/tracing.d.ts.map +1 -1
- package/dist/server/tree-builder.d.ts +22 -19
- package/dist/server/tree-builder.d.ts.map +1 -1
- package/dist/server/types.d.ts +1 -4
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/version-skew.d.ts +61 -0
- package/dist/server/version-skew.d.ts.map +1 -0
- package/dist/shared/merge-search-params.d.ts +22 -0
- package/dist/shared/merge-search-params.d.ts.map +1 -0
- package/dist/shims/font-google.d.ts +1 -1
- package/dist/shims/font-google.d.ts.map +1 -1
- package/dist/shims/font-google.js +42 -0
- package/dist/shims/font-google.js.map +1 -0
- package/dist/shims/font-local.d.ts +26 -0
- package/dist/shims/font-local.d.ts.map +1 -0
- package/dist/shims/font-local.js +20 -0
- package/dist/shims/font-local.js.map +1 -0
- package/dist/shims/headers.d.ts +2 -1
- package/dist/shims/headers.d.ts.map +1 -1
- package/dist/shims/navigation-client.d.ts +1 -1
- package/dist/shims/navigation-client.d.ts.map +1 -1
- package/dist/shims/navigation.d.ts +3 -2
- package/dist/shims/navigation.d.ts.map +1 -1
- package/dist/utils/directive-parser.d.ts +5 -2
- package/dist/utils/directive-parser.d.ts.map +1 -1
- package/dist/utils/state-machine.d.ts +80 -0
- package/dist/utils/state-machine.d.ts.map +1 -0
- package/package.json +53 -23
- package/src/adapters/cloudflare-dev.ts +177 -0
- package/src/adapters/cloudflare-kv-cache.ts +142 -0
- package/src/adapters/cloudflare.ts +342 -28
- package/src/adapters/compress-module.ts +24 -4
- package/src/adapters/nitro.ts +52 -8
- package/src/adapters/wrangler.d.ts +7 -0
- package/src/cache/cache-api.ts +38 -0
- package/src/cache/handler-store.ts +68 -0
- package/src/cache/index.ts +81 -18
- package/src/cache/singleflight.ts +62 -4
- package/src/cache/sizeof.ts +31 -0
- package/src/cache/timber-cache.ts +24 -20
- package/src/cli.ts +6 -1
- package/src/client/browser-dev.ts +128 -1
- package/src/client/browser-entry/action-dispatch.ts +116 -0
- package/src/client/browser-entry/hmr.ts +81 -0
- package/src/client/browser-entry/hydrate.ts +145 -0
- package/src/client/browser-entry/index.ts +138 -0
- package/src/client/browser-entry/post-hydration.ts +119 -0
- package/src/client/browser-entry/router-init.ts +193 -0
- package/src/client/browser-entry/rsc-stream.ts +157 -0
- package/src/client/browser-entry/scroll.ts +27 -0
- package/src/client/error-boundary.tsx +48 -16
- package/src/client/error-reconstituter.tsx +65 -0
- package/src/client/form.tsx +2 -2
- package/src/client/history.ts +26 -4
- package/src/client/index.ts +19 -38
- package/src/client/internal.ts +57 -0
- package/src/client/link-pending-store.ts +111 -0
- package/src/client/link.tsx +329 -97
- package/src/client/nav-link-store.ts +47 -0
- package/src/client/navigation-api-types.ts +112 -0
- package/src/client/navigation-api.ts +332 -0
- package/src/client/navigation-context.ts +31 -6
- package/src/client/navigation-root.tsx +342 -0
- package/src/client/nuqs-adapter.tsx +16 -3
- package/src/client/router-ref.ts +1 -1
- package/src/client/router.ts +299 -72
- package/src/client/rsc-fetch.ts +97 -8
- package/src/client/segment-cache.ts +1 -1
- package/src/client/segment-outlet.tsx +86 -0
- package/src/client/ssr-data.ts +13 -5
- package/src/client/stale-reload.ts +72 -3
- package/src/client/top-loader.tsx +16 -8
- package/src/client/use-link-status.ts +7 -7
- package/src/client/use-params.ts +7 -5
- package/src/client/{use-navigation-pending.ts → use-pending-navigation.ts} +6 -6
- package/src/client/use-query-states.ts +3 -3
- package/src/client/use-router.ts +1 -1
- package/src/codec.ts +49 -0
- package/src/config-types.ts +208 -0
- package/src/content/index.ts +5 -13
- package/src/cookies/define-cookie.ts +78 -25
- package/src/cookies/index.ts +8 -0
- package/src/fonts/css.ts +2 -1
- package/src/index.ts +258 -354
- package/src/plugin-context.ts +200 -0
- package/src/plugins/adapter-build.ts +8 -2
- package/src/plugins/build-manifest.ts +13 -2
- package/src/plugins/build-report.ts +3 -3
- package/src/plugins/client-chunks.ts +65 -0
- package/src/plugins/content.ts +1 -1
- package/src/plugins/dev-browser-logs.ts +288 -0
- package/src/plugins/dev-error-overlay.ts +70 -1
- package/src/plugins/dev-logs.ts +1 -1
- package/src/plugins/dev-server.ts +70 -9
- package/src/plugins/entries.ts +71 -10
- package/src/plugins/fonts.ts +168 -61
- package/src/plugins/mdx.ts +1 -1
- package/src/plugins/routing.ts +57 -17
- package/src/plugins/server-action-exports.ts +1 -1
- package/src/plugins/server-bundle.ts +32 -1
- package/src/plugins/shims.ts +135 -35
- package/src/plugins/static-build.ts +17 -11
- package/src/routing/codegen.ts +165 -105
- package/src/routing/index.ts +2 -0
- package/src/routing/scanner.ts +93 -23
- package/src/routing/segment-classify.ts +89 -0
- package/src/routing/status-file-lint.ts +3 -2
- package/src/routing/types.ts +17 -4
- package/src/rsc-runtime/rsc.ts +2 -0
- package/src/rsc-runtime/ssr.ts +50 -0
- package/src/rsc-runtime/vendor-types.d.ts +7 -0
- package/src/{search-params/codecs.ts → schema-bridge.ts} +57 -20
- package/src/search-params/define.ts +482 -0
- package/src/search-params/index.ts +14 -20
- package/src/search-params/registry.ts +2 -2
- package/src/search-params/wrappers.ts +85 -0
- package/src/segment-params/define.ts +279 -0
- package/src/segment-params/index.ts +29 -0
- package/src/server/access-gate.tsx +70 -29
- package/src/server/action-client.ts +21 -2
- package/src/server/action-encryption.ts +144 -0
- package/src/server/action-handler.ts +21 -4
- package/src/server/actions.ts +10 -9
- package/src/server/als-registry.ts +34 -6
- package/src/server/build-manifest.ts +10 -4
- package/src/server/compress.ts +25 -7
- package/src/server/debug.ts +1 -1
- package/src/server/default-logger.ts +99 -0
- package/src/server/deny-page-resolver.ts +154 -0
- package/src/server/deny-renderer.ts +24 -38
- package/src/server/dev-holding-server.ts +185 -0
- package/src/server/dev-warnings.ts +4 -49
- package/src/server/early-hints.ts +36 -15
- package/src/server/error-boundary-wrapper.ts +74 -22
- package/src/server/fallback-error.ts +31 -15
- package/src/server/flight-injection-state.ts +113 -0
- package/src/server/flight-scripts.ts +62 -0
- package/src/server/flush.ts +2 -1
- package/src/server/form-data.ts +76 -0
- package/src/server/html-injectors.ts +280 -120
- package/src/server/index.ts +26 -177
- package/src/server/internal.ts +169 -0
- package/src/server/logger.ts +44 -36
- package/src/server/middleware-runner.ts +31 -4
- package/src/server/node-stream-transforms.ts +509 -0
- package/src/server/page-deny-boundary.tsx +56 -0
- package/src/server/pipeline-interception.ts +17 -16
- package/src/server/pipeline-metadata.ts +13 -0
- package/src/server/pipeline.ts +227 -62
- package/src/server/primitives.ts +111 -28
- package/src/server/render-timeout.ts +108 -0
- package/src/server/request-context.ts +293 -132
- package/src/server/route-element-builder.ts +283 -191
- package/src/server/route-handler.ts +24 -4
- package/src/server/route-matcher.ts +24 -20
- package/src/server/rsc-entry/api-handler.ts +15 -16
- package/src/server/rsc-entry/error-renderer.ts +300 -89
- package/src/server/rsc-entry/helpers.ts +134 -5
- package/src/server/rsc-entry/index.ts +200 -112
- package/src/server/rsc-entry/rsc-payload.ts +65 -18
- package/src/server/rsc-entry/rsc-stream.ts +65 -13
- package/src/server/rsc-entry/ssr-bridge.ts +14 -5
- package/src/server/rsc-entry/ssr-renderer.ts +168 -38
- package/src/server/safe-load.ts +60 -0
- package/src/server/sitemap-generator.ts +338 -0
- package/src/server/sitemap-handler.ts +126 -0
- package/src/server/slot-resolver.ts +244 -229
- package/src/server/ssr-entry.ts +211 -32
- package/src/server/ssr-render.ts +289 -67
- package/src/server/ssr-wrappers.tsx +139 -0
- package/src/server/status-code-resolver.ts +1 -1
- package/src/server/stream-utils.ts +213 -0
- package/src/server/tracing.ts +20 -9
- package/src/server/tree-builder.ts +92 -58
- package/src/server/types.ts +3 -6
- package/src/server/version-skew.ts +104 -0
- package/src/shared/merge-search-params.ts +55 -0
- package/src/shims/font-google.ts +1 -1
- package/src/shims/font-local.ts +34 -0
- package/src/shims/headers.ts +5 -1
- package/src/shims/navigation-client.ts +1 -1
- package/src/shims/navigation.ts +7 -2
- package/src/utils/directive-parser.ts +5 -2
- package/src/utils/state-machine.ts +111 -0
- package/dist/_chunks/als-registry-B7DbZ2hS.js.map +0 -1
- package/dist/_chunks/debug-gwlJkDuf.js.map +0 -1
- package/dist/_chunks/format-DviM89f0.js.map +0 -1
- package/dist/_chunks/interception-BOoWmLUA.js.map +0 -1
- package/dist/_chunks/request-context-DIkVh_jG.js +0 -330
- package/dist/_chunks/request-context-DIkVh_jG.js.map +0 -1
- package/dist/_chunks/tracing-CemImE6h.js.map +0 -1
- package/dist/_chunks/use-cookie-DX-l1_5E.js +0 -91
- package/dist/_chunks/use-cookie-DX-l1_5E.js.map +0 -1
- package/dist/_chunks/use-query-states-D5KaffOK.js.map +0 -1
- package/dist/cache/register-cached-function.d.ts +0 -17
- package/dist/cache/register-cached-function.d.ts.map +0 -1
- package/dist/client/browser-entry.d.ts +0 -21
- package/dist/client/browser-entry.d.ts.map +0 -1
- package/dist/client/link-status-provider.d.ts +0 -11
- package/dist/client/link-status-provider.d.ts.map +0 -1
- package/dist/client/transition-root.d.ts.map +0 -1
- package/dist/client/use-navigation-pending.d.ts.map +0 -1
- package/dist/cookies/index.js.map +0 -1
- package/dist/plugins/cache-transform.d.ts +0 -36
- package/dist/plugins/cache-transform.d.ts.map +0 -1
- package/dist/plugins/dynamic-transform.d.ts +0 -72
- package/dist/plugins/dynamic-transform.d.ts.map +0 -1
- package/dist/search-params/analyze.d.ts +0 -54
- package/dist/search-params/analyze.d.ts.map +0 -1
- package/dist/search-params/builtin-codecs.d.ts +0 -105
- package/dist/search-params/builtin-codecs.d.ts.map +0 -1
- package/dist/search-params/codecs.d.ts +0 -53
- package/dist/search-params/codecs.d.ts.map +0 -1
- package/dist/search-params/create.d.ts +0 -106
- package/dist/search-params/create.d.ts.map +0 -1
- package/dist/search-params/index.js.map +0 -1
- package/dist/server/prerender.d.ts +0 -77
- package/dist/server/prerender.d.ts.map +0 -1
- package/dist/server/response-cache.d.ts +0 -54
- package/dist/server/response-cache.d.ts.map +0 -1
- package/src/cache/register-cached-function.ts +0 -103
- package/src/client/browser-entry.ts +0 -678
- package/src/client/link-status-provider.tsx +0 -30
- package/src/client/transition-root.tsx +0 -166
- package/src/plugins/cache-transform.ts +0 -199
- package/src/plugins/dynamic-transform.ts +0 -161
- package/src/search-params/analyze.ts +0 -192
- package/src/search-params/builtin-codecs.ts +0 -228
- package/src/search-params/create.ts +0 -321
- package/src/server/prerender.ts +0 -139
- package/src/server/response-cache.ts +0 -410
package/src/client/router.ts
CHANGED
|
@@ -6,9 +6,19 @@ import type { SegmentInfo } from './segment-cache';
|
|
|
6
6
|
import { HistoryStack } from './history';
|
|
7
7
|
import type { HeadElement } from './head';
|
|
8
8
|
import { setCurrentParams } from './use-params.js';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
setNavigationState,
|
|
11
|
+
getNavigationState,
|
|
12
|
+
type NavigationState,
|
|
13
|
+
} from './navigation-context.js';
|
|
10
14
|
import { SegmentElementCache, cacheSegmentElements, mergeSegmentTree } from './segment-merger.js';
|
|
11
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
fetchRscPayload,
|
|
17
|
+
RedirectError,
|
|
18
|
+
ServerErrorResponse,
|
|
19
|
+
VersionSkewError,
|
|
20
|
+
} from './rsc-fetch.js';
|
|
21
|
+
import { setHardNavigating } from './navigation-root.js';
|
|
12
22
|
import type { FetchResult } from './rsc-fetch.js';
|
|
13
23
|
|
|
14
24
|
// ─── Types ───────────────────────────────────────────────────────
|
|
@@ -18,6 +28,19 @@ export interface NavigationOptions {
|
|
|
18
28
|
scroll?: boolean;
|
|
19
29
|
/** Use replaceState instead of pushState (replaces current history entry) */
|
|
20
30
|
replace?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* @internal AbortSignal from the Navigation API's NavigateEvent.
|
|
33
|
+
* When provided, the signal is linked to the router's per-navigation
|
|
34
|
+
* AbortController so in-flight RSC fetches are cancelled when a new
|
|
35
|
+
* navigation starts.
|
|
36
|
+
*/
|
|
37
|
+
_signal?: AbortSignal;
|
|
38
|
+
/**
|
|
39
|
+
* @internal Skip pushState/replaceState — the Navigation API has already
|
|
40
|
+
* updated the URL via event.intercept(). Used for external navigations
|
|
41
|
+
* intercepted by the navigate event handler.
|
|
42
|
+
*/
|
|
43
|
+
_skipHistory?: boolean;
|
|
21
44
|
}
|
|
22
45
|
|
|
23
46
|
/**
|
|
@@ -31,8 +54,12 @@ export type RscDecoder = (fetchPromise: Promise<Response>) => unknown;
|
|
|
31
54
|
* Function that renders a decoded RSC element tree into the DOM.
|
|
32
55
|
* In production: reactRoot.render(element).
|
|
33
56
|
* In tests: a no-op or mock.
|
|
57
|
+
*
|
|
58
|
+
* Receives the current NavigationState explicitly — no temporal
|
|
59
|
+
* coupling with setNavigationState/getNavigationState. The renderer
|
|
60
|
+
* wraps the element in NavigationProvider with this state.
|
|
34
61
|
*/
|
|
35
|
-
export type RootRenderer = (element: unknown) => void;
|
|
62
|
+
export type RootRenderer = (element: unknown, navState: NavigationState) => void;
|
|
36
63
|
|
|
37
64
|
/**
|
|
38
65
|
* Platform dependencies injected for testability. In production these
|
|
@@ -64,14 +91,54 @@ export interface RouterDeps {
|
|
|
64
91
|
*
|
|
65
92
|
* The `perform` callback receives a `wrapPayload` function to wrap the
|
|
66
93
|
* decoded RSC payload with NavigationProvider + NuqsAdapter before
|
|
67
|
-
*
|
|
94
|
+
* NavigationRoot sets it as the new element. The `wrapPayload` function
|
|
95
|
+
* receives the NavigationState explicitly — no temporal coupling with
|
|
96
|
+
* getNavigationState().
|
|
68
97
|
*
|
|
69
98
|
* If not provided (tests), the router falls back to renderRoot.
|
|
70
99
|
*/
|
|
71
100
|
navigateTransition?: (
|
|
72
101
|
pendingUrl: string,
|
|
73
|
-
perform: (
|
|
102
|
+
perform: (
|
|
103
|
+
wrapPayload: (payload: unknown, navState: NavigationState) => unknown
|
|
104
|
+
) => Promise<unknown>
|
|
74
105
|
) => Promise<void>;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Whether the Navigation API is active and handling traversals.
|
|
109
|
+
* When true, the popstate handler is a no-op — the Navigation API's
|
|
110
|
+
* navigate event covers back/forward button presses.
|
|
111
|
+
*/
|
|
112
|
+
navigationApiActive?: boolean;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Called around pushState/replaceState to set a flag that prevents
|
|
116
|
+
* the Navigation API's navigate listener from double-handling
|
|
117
|
+
* router-initiated navigations.
|
|
118
|
+
*/
|
|
119
|
+
setRouterNavigating?: (value: boolean) => void;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Save scroll position via the Navigation API's per-entry state.
|
|
123
|
+
* When provided, used instead of history.replaceState for scroll storage.
|
|
124
|
+
*/
|
|
125
|
+
saveNavigationEntryScroll?: (scrollY: number) => void;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Signal that a router-initiated navigation has completed. Resolves the
|
|
129
|
+
* deferred promise that ties the browser's native loading state to the
|
|
130
|
+
* navigation lifecycle. Called in the finally block of navigate/refresh,
|
|
131
|
+
* aligned with when the TopLoader's pendingUrl clears.
|
|
132
|
+
*/
|
|
133
|
+
completeRouterNavigation?: () => void;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Initiate a navigation via the Navigation API (`navigation.navigate()`).
|
|
137
|
+
* Fires the navigate event BEFORE committing the URL, allowing Chrome
|
|
138
|
+
* to show its native loading indicator. Falls back to pushState when
|
|
139
|
+
* unavailable.
|
|
140
|
+
*/
|
|
141
|
+
navigationNavigate?: (url: string, replace: boolean) => void;
|
|
75
142
|
}
|
|
76
143
|
|
|
77
144
|
export interface RouterInstance {
|
|
@@ -80,7 +147,7 @@ export interface RouterInstance {
|
|
|
80
147
|
/** Full re-render of the current URL — no state tree sent */
|
|
81
148
|
refresh(): Promise<void>;
|
|
82
149
|
/** Handle a popstate event (back/forward button). scrollY is read from history.state. */
|
|
83
|
-
handlePopState(url: string, scrollY?: number): Promise<void>;
|
|
150
|
+
handlePopState(url: string, scrollY?: number, externalSignal?: AbortSignal): Promise<void>;
|
|
84
151
|
/** Whether a navigation is currently in flight */
|
|
85
152
|
isPending(): boolean;
|
|
86
153
|
/** The URL currently being navigated to, or null if idle */
|
|
@@ -130,24 +197,73 @@ function isAbortError(error: unknown): boolean {
|
|
|
130
197
|
* Create a router instance. In production, called once at app hydration
|
|
131
198
|
* with real browser APIs. In tests, called with mock dependencies.
|
|
132
199
|
*/
|
|
200
|
+
/**
|
|
201
|
+
* Router navigation phase — discriminated union replacing scattered
|
|
202
|
+
* `pending` + `pendingUrl` boolean flags.
|
|
203
|
+
*
|
|
204
|
+
* - `idle`: No navigation in flight. The committed params/pathname
|
|
205
|
+
* are current.
|
|
206
|
+
* - `navigating`: A fetch or render is in progress. `targetUrl` is
|
|
207
|
+
* the destination being navigated to.
|
|
208
|
+
*/
|
|
209
|
+
export type RouterPhase = { phase: 'idle' } | { phase: 'navigating'; targetUrl: string };
|
|
210
|
+
|
|
133
211
|
export function createRouter(deps: RouterDeps): RouterInstance {
|
|
134
212
|
const segmentCache = new SegmentCache();
|
|
135
213
|
const prefetchCache = new PrefetchCache();
|
|
136
214
|
const historyStack = new HistoryStack();
|
|
137
215
|
const segmentElementCache = new SegmentElementCache();
|
|
138
216
|
|
|
139
|
-
let
|
|
140
|
-
let pendingUrl: string | null = null;
|
|
217
|
+
let routerPhase: RouterPhase = { phase: 'idle' };
|
|
141
218
|
const pendingListeners = new Set<(pending: boolean) => void>();
|
|
142
219
|
|
|
220
|
+
// AbortController for the current in-flight navigation.
|
|
221
|
+
// When a new navigation starts, the previous controller is aborted,
|
|
222
|
+
// cancelling any in-progress RSC fetch. This provides automatic
|
|
223
|
+
// cancellation of stale fetches regardless of Navigation API support.
|
|
224
|
+
let currentNavAbort: AbortController | null = null;
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Create a new AbortController for a navigation, aborting any
|
|
228
|
+
* previous in-flight navigation. Optionally links to an external
|
|
229
|
+
* signal (e.g., from the Navigation API's NavigateEvent.signal).
|
|
230
|
+
*/
|
|
231
|
+
function createNavAbort(externalSignal?: AbortSignal): AbortController {
|
|
232
|
+
// Abort previous navigation's fetch
|
|
233
|
+
currentNavAbort?.abort();
|
|
234
|
+
const controller = new AbortController();
|
|
235
|
+
currentNavAbort = controller;
|
|
236
|
+
|
|
237
|
+
// If an external signal is provided (e.g., Navigation API),
|
|
238
|
+
// forward its abort to our controller.
|
|
239
|
+
if (externalSignal) {
|
|
240
|
+
if (externalSignal.aborted) {
|
|
241
|
+
controller.abort();
|
|
242
|
+
} else {
|
|
243
|
+
externalSignal.addEventListener('abort', () => controller.abort(), { once: true });
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return controller;
|
|
248
|
+
}
|
|
249
|
+
|
|
143
250
|
function setPending(value: boolean, url?: string): void {
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
251
|
+
const next: RouterPhase =
|
|
252
|
+
value && url ? { phase: 'navigating', targetUrl: url } : { phase: 'idle' };
|
|
253
|
+
// Skip no-op updates
|
|
254
|
+
if (
|
|
255
|
+
routerPhase.phase === next.phase &&
|
|
256
|
+
(routerPhase.phase === 'idle' ||
|
|
257
|
+
(routerPhase.phase === 'navigating' &&
|
|
258
|
+
next.phase === 'navigating' &&
|
|
259
|
+
routerPhase.targetUrl === next.targetUrl))
|
|
260
|
+
) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
routerPhase = next;
|
|
148
264
|
// Notify external store listeners (non-React consumers).
|
|
149
265
|
// React-facing pending state is handled by useOptimistic in
|
|
150
|
-
//
|
|
266
|
+
// NavigationRoot via navigateTransition — not this function.
|
|
151
267
|
for (const listener of pendingListeners) {
|
|
152
268
|
listener(value);
|
|
153
269
|
}
|
|
@@ -163,9 +279,9 @@ export function createRouter(deps: RouterDeps): RouterInstance {
|
|
|
163
279
|
}
|
|
164
280
|
|
|
165
281
|
/** Render a decoded RSC payload into the DOM if a renderer is available. */
|
|
166
|
-
function renderPayload(payload: unknown): void {
|
|
282
|
+
function renderPayload(payload: unknown, navState: NavigationState): void {
|
|
167
283
|
if (deps.renderRoot) {
|
|
168
|
-
deps.renderRoot(payload);
|
|
284
|
+
deps.renderRoot(payload, navState);
|
|
169
285
|
}
|
|
170
286
|
}
|
|
171
287
|
|
|
@@ -194,32 +310,34 @@ export function createRouter(deps: RouterDeps): RouterInstance {
|
|
|
194
310
|
/**
|
|
195
311
|
* Update navigation state (params + pathname) for the next render.
|
|
196
312
|
*
|
|
197
|
-
* Sets
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
313
|
+
* Sets the module-level fallback (for tests and SSR) and the
|
|
314
|
+
* globalThis bridge, then returns the NavigationState so callers
|
|
315
|
+
* can pass it explicitly to renderRoot/wrapPayload — eliminating
|
|
316
|
+
* temporal coupling with getNavigationState().
|
|
201
317
|
*/
|
|
202
318
|
function updateNavigationState(
|
|
203
319
|
params: Record<string, string | string[]> | null | undefined,
|
|
204
320
|
url: string
|
|
205
|
-
):
|
|
321
|
+
): NavigationState {
|
|
206
322
|
const resolvedParams = params ?? {};
|
|
207
323
|
// Module-level fallback for tests (no NavigationProvider) and SSR
|
|
208
324
|
setCurrentParams(resolvedParams);
|
|
209
|
-
//
|
|
325
|
+
// globalThis bridge — kept for backward compat
|
|
210
326
|
const pathname = url.startsWith('http') ? new URL(url).pathname : url.split('?')[0] || '/';
|
|
211
|
-
|
|
327
|
+
const navState: NavigationState = { params: resolvedParams, pathname };
|
|
328
|
+
setNavigationState(navState);
|
|
329
|
+
return navState;
|
|
212
330
|
}
|
|
213
331
|
|
|
214
332
|
/**
|
|
215
333
|
* Render a payload via navigateTransition (production) or renderRoot (tests).
|
|
216
|
-
* The perform callback should fetch data, update state, and return the
|
|
217
|
-
*
|
|
218
|
-
*
|
|
334
|
+
* The perform callback should fetch data, update state, and return the
|
|
335
|
+
* FetchResult plus the NavigationState (so it can be passed explicitly
|
|
336
|
+
* to wrapPayload/renderRoot without temporal coupling).
|
|
219
337
|
*/
|
|
220
338
|
async function renderViaTransition(
|
|
221
339
|
url: string,
|
|
222
|
-
perform: () => Promise<FetchResult>
|
|
340
|
+
perform: () => Promise<FetchResult & { navState: NavigationState }>
|
|
223
341
|
): Promise<HeadElement[] | null> {
|
|
224
342
|
if (deps.navigateTransition) {
|
|
225
343
|
let headElements: HeadElement[] | null = null;
|
|
@@ -235,7 +353,9 @@ export function createRouter(deps: RouterDeps): RouterInstance {
|
|
|
235
353
|
headElements: result.headElements,
|
|
236
354
|
params: result.params,
|
|
237
355
|
});
|
|
238
|
-
|
|
356
|
+
// Pass navState explicitly — wrapPayload wraps element in
|
|
357
|
+
// NavigationProvider with this state, no getNavigationState() needed.
|
|
358
|
+
return wrapPayload(merged, result.navState);
|
|
239
359
|
});
|
|
240
360
|
return headElements;
|
|
241
361
|
}
|
|
@@ -249,7 +369,7 @@ export function createRouter(deps: RouterDeps): RouterInstance {
|
|
|
249
369
|
headElements: result.headElements,
|
|
250
370
|
params: result.params,
|
|
251
371
|
});
|
|
252
|
-
renderPayload(merged);
|
|
372
|
+
renderPayload(merged, result.navState);
|
|
253
373
|
return result.headElements;
|
|
254
374
|
}
|
|
255
375
|
|
|
@@ -269,14 +389,25 @@ export function createRouter(deps: RouterDeps): RouterInstance {
|
|
|
269
389
|
}
|
|
270
390
|
}
|
|
271
391
|
|
|
392
|
+
/**
|
|
393
|
+
* Schedule scroll restoration after the next paint and fire the
|
|
394
|
+
* scroll-restored event. Used by navigate, popstate, and refresh.
|
|
395
|
+
*/
|
|
396
|
+
function restoreScrollAfterPaint(scrollY: number): void {
|
|
397
|
+
afterPaint(() => {
|
|
398
|
+
deps.scrollTo(0, scrollY);
|
|
399
|
+
window.dispatchEvent(new Event('timber:scroll-restored'));
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
272
403
|
/**
|
|
273
404
|
* Core navigation logic shared between the transition and fallback paths.
|
|
274
405
|
* Fetches the RSC payload, updates all state, and returns the result.
|
|
275
406
|
*/
|
|
276
407
|
async function performNavigationFetch(
|
|
277
408
|
url: string,
|
|
278
|
-
options: { replace: boolean }
|
|
279
|
-
): Promise<FetchResult> {
|
|
409
|
+
options: { replace: boolean; signal?: AbortSignal; skipHistory?: boolean }
|
|
410
|
+
): Promise<FetchResult & { navState: NavigationState }> {
|
|
280
411
|
// Check prefetch cache first. PrefetchResult has optional segmentInfo/params
|
|
281
412
|
// fields — normalize to null for FetchResult compatibility.
|
|
282
413
|
const prefetched = prefetchCache.consume(url);
|
|
@@ -298,14 +429,21 @@ export function createRouter(deps: RouterDeps): RouterInstance {
|
|
|
298
429
|
const currentUrl = rawCurrentUrl.startsWith('http')
|
|
299
430
|
? new URL(rawCurrentUrl).pathname
|
|
300
431
|
: new URL(rawCurrentUrl, 'http://localhost').pathname;
|
|
301
|
-
result = await fetchRscPayload(url, deps, stateTree, currentUrl);
|
|
432
|
+
result = await fetchRscPayload(url, deps, stateTree, currentUrl, options.signal);
|
|
302
433
|
}
|
|
303
434
|
|
|
304
|
-
// Update the browser history —
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
435
|
+
// Update the browser history — skip when the Navigation API has already
|
|
436
|
+
// updated the URL via event.intercept() (external navigations).
|
|
437
|
+
if (!options.skipHistory) {
|
|
438
|
+
// Set the router-navigating flag so the Navigation API's navigate
|
|
439
|
+
// listener doesn't double-intercept this pushState/replaceState.
|
|
440
|
+
deps.setRouterNavigating?.(true);
|
|
441
|
+
if (options.replace) {
|
|
442
|
+
deps.replaceState({ timber: true, scrollY: 0 }, '', url);
|
|
443
|
+
} else {
|
|
444
|
+
deps.pushState({ timber: true, scrollY: 0 }, '', url);
|
|
445
|
+
}
|
|
446
|
+
deps.setRouterNavigating?.(false);
|
|
309
447
|
}
|
|
310
448
|
|
|
311
449
|
// NOTE: History push is deferred — the merged payload (after segment
|
|
@@ -316,29 +454,57 @@ export function createRouter(deps: RouterDeps): RouterInstance {
|
|
|
316
454
|
// Update the segment cache with the new route's segment tree.
|
|
317
455
|
updateSegmentCache(result.segmentInfo);
|
|
318
456
|
|
|
319
|
-
// Update navigation state
|
|
320
|
-
updateNavigationState(result.params, url);
|
|
457
|
+
// Update navigation state and capture it for explicit passing.
|
|
458
|
+
const navState = updateNavigationState(result.params, url);
|
|
321
459
|
|
|
322
|
-
return result;
|
|
460
|
+
return { ...result, navState };
|
|
323
461
|
}
|
|
324
462
|
|
|
325
463
|
async function navigate(url: string, options: NavigationOptions = {}): Promise<void> {
|
|
326
464
|
const scroll = options.scroll !== false;
|
|
327
465
|
const replace = options.replace === true;
|
|
466
|
+
const externalSignal = options._signal as AbortSignal | undefined;
|
|
467
|
+
const skipHistory = options._skipHistory === true;
|
|
468
|
+
|
|
469
|
+
// Create an abort controller for this navigation. Links to the external
|
|
470
|
+
// signal (Navigation API's event.signal) when provided.
|
|
471
|
+
const navAbort = createNavAbort(externalSignal);
|
|
328
472
|
|
|
329
473
|
// Capture the departing page's scroll position for scroll={false} preservation.
|
|
330
474
|
const currentScrollY = deps.getScrollY();
|
|
331
475
|
|
|
332
|
-
// Save the departing page's scroll position
|
|
333
|
-
//
|
|
334
|
-
|
|
335
|
-
|
|
476
|
+
// Save the departing page's scroll position — use Navigation API entry
|
|
477
|
+
// state when available, otherwise fall back to history.state.
|
|
478
|
+
if (deps.saveNavigationEntryScroll) {
|
|
479
|
+
deps.saveNavigationEntryScroll(currentScrollY);
|
|
480
|
+
} else {
|
|
481
|
+
deps.replaceState({ timber: true, scrollY: currentScrollY }, '', deps.getCurrentUrl());
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// When Navigation API is active, initiate the navigation via
|
|
485
|
+
// navigation.navigate() BEFORE the fetch. Unlike history.pushState()
|
|
486
|
+
// which commits the URL synchronously (so Chrome sees it as "done"),
|
|
487
|
+
// navigation.navigate() fires the navigate event before committing.
|
|
488
|
+
// Our handler intercepts with a deferred promise, and Chrome shows
|
|
489
|
+
// its native loading indicator until completeRouterNavigation()
|
|
490
|
+
// resolves it in the finally block (same time as TopLoader clears).
|
|
491
|
+
let effectiveSkipHistory = skipHistory;
|
|
492
|
+
if (!skipHistory && deps.navigationNavigate) {
|
|
493
|
+
deps.setRouterNavigating?.(true);
|
|
494
|
+
deps.navigationNavigate(url, replace);
|
|
495
|
+
deps.setRouterNavigating?.(false);
|
|
496
|
+
effectiveSkipHistory = true;
|
|
497
|
+
}
|
|
336
498
|
|
|
337
499
|
setPending(true, url);
|
|
338
500
|
|
|
339
501
|
try {
|
|
340
502
|
const headElements = await renderViaTransition(url, () =>
|
|
341
|
-
performNavigationFetch(url, {
|
|
503
|
+
performNavigationFetch(url, {
|
|
504
|
+
replace,
|
|
505
|
+
signal: navAbort.signal,
|
|
506
|
+
skipHistory: effectiveSkipHistory,
|
|
507
|
+
})
|
|
342
508
|
);
|
|
343
509
|
|
|
344
510
|
// Update document.title and <meta> tags with the new page's metadata
|
|
@@ -350,51 +516,105 @@ export function createRouter(deps: RouterDeps): RouterInstance {
|
|
|
350
516
|
// Scroll-to-top on forward navigation, or restore captured position
|
|
351
517
|
// for scroll={false}. React's render() on the document root can reset
|
|
352
518
|
// scroll during DOM reconciliation, so all scroll must be actively managed.
|
|
353
|
-
|
|
354
|
-
if (scroll) {
|
|
355
|
-
deps.scrollTo(0, 0);
|
|
356
|
-
} else {
|
|
357
|
-
deps.scrollTo(0, currentScrollY);
|
|
358
|
-
}
|
|
359
|
-
window.dispatchEvent(new Event('timber:scroll-restored'));
|
|
360
|
-
});
|
|
519
|
+
restoreScrollAfterPaint(scroll ? 0 : currentScrollY);
|
|
361
520
|
} catch (error) {
|
|
521
|
+
// Version skew — server has been redeployed. Trigger full page reload
|
|
522
|
+
// so the browser fetches the new bundle. See TIM-446.
|
|
523
|
+
// Set hard-navigating flag to prevent Navigation API interception
|
|
524
|
+
// and React from rendering during page teardown. See TIM-626.
|
|
525
|
+
if (error instanceof VersionSkewError) {
|
|
526
|
+
setHardNavigating(true);
|
|
527
|
+
// Import triggerStaleReload dynamically to avoid circular deps
|
|
528
|
+
// and keep the reload logic centralized with its loop guard.
|
|
529
|
+
const { triggerStaleReload } = await import('./stale-reload.js');
|
|
530
|
+
triggerStaleReload();
|
|
531
|
+
// Return a never-resolving promise — page is reloading.
|
|
532
|
+
return new Promise(() => {}) as never;
|
|
533
|
+
}
|
|
362
534
|
// Server-side redirect during RSC fetch → soft router navigation.
|
|
535
|
+
// The redirect navigate will push/replace its own URL.
|
|
363
536
|
if (error instanceof RedirectError) {
|
|
364
537
|
setPending(false);
|
|
538
|
+
deps.completeRouterNavigation?.();
|
|
365
539
|
await navigate(error.redirectUrl, { replace: true });
|
|
366
540
|
return;
|
|
367
541
|
}
|
|
542
|
+
// Server 5xx error — hard-navigate so the server renders the
|
|
543
|
+
// error page as HTML. See design/10-error-handling.md
|
|
544
|
+
// §"Error Page Rendering for Client Navigation".
|
|
545
|
+
//
|
|
546
|
+
// Set hard-navigating flag BEFORE setting window.location.href:
|
|
547
|
+
// 1. Prevents Navigation API from intercepting → infinite loop
|
|
548
|
+
// 2. Causes NavigationRoot to throw unresolvedThenable → prevents
|
|
549
|
+
// React from rendering children during page teardown (avoids
|
|
550
|
+
// "Rendered more hooks" crashes). See TIM-626.
|
|
551
|
+
if (error instanceof ServerErrorResponse) {
|
|
552
|
+
setHardNavigating(true);
|
|
553
|
+
window.location.href = error.url;
|
|
554
|
+
return new Promise(() => {}) as never;
|
|
555
|
+
}
|
|
368
556
|
// Abort errors are not application errors — swallow silently.
|
|
369
557
|
if (isAbortError(error)) return;
|
|
370
558
|
throw error;
|
|
371
559
|
} finally {
|
|
560
|
+
// Clear the abort controller so we don't abort a completed navigation
|
|
561
|
+
// when the next one starts. In dev mode, the RSC body stream stays
|
|
562
|
+
// open after data arrives (React's Flight client waits for debug rows).
|
|
563
|
+
// Aborting a "completed" navigation kills the open stream reader →
|
|
564
|
+
// "BodyStreamBuffer was aborted". By clearing the controller here,
|
|
565
|
+
// createNavAbort() becomes a no-op for completed navigations.
|
|
566
|
+
if (currentNavAbort === navAbort) {
|
|
567
|
+
currentNavAbort = null;
|
|
568
|
+
}
|
|
372
569
|
setPending(false);
|
|
570
|
+
// Resolve the Navigation API deferred — clears the browser's native
|
|
571
|
+
// loading state (tab spinner) at the same time as the TopLoader.
|
|
572
|
+
deps.completeRouterNavigation?.();
|
|
373
573
|
}
|
|
374
574
|
}
|
|
375
575
|
|
|
376
576
|
async function refresh(): Promise<void> {
|
|
377
577
|
const currentUrl = deps.getCurrentUrl();
|
|
578
|
+
const navAbort = createNavAbort();
|
|
378
579
|
|
|
379
580
|
setPending(true, currentUrl);
|
|
380
581
|
|
|
381
582
|
try {
|
|
382
583
|
const headElements = await renderViaTransition(currentUrl, async () => {
|
|
383
584
|
// No state tree sent — server renders the complete RSC payload
|
|
384
|
-
const result = await fetchRscPayload(
|
|
585
|
+
const result = await fetchRscPayload(
|
|
586
|
+
currentUrl,
|
|
587
|
+
deps,
|
|
588
|
+
undefined,
|
|
589
|
+
undefined,
|
|
590
|
+
navAbort.signal
|
|
591
|
+
);
|
|
385
592
|
// History push handled by renderViaTransition (stores merged payload)
|
|
386
593
|
updateSegmentCache(result.segmentInfo);
|
|
387
|
-
updateNavigationState(result.params, currentUrl);
|
|
388
|
-
return result;
|
|
594
|
+
const navState = updateNavigationState(result.params, currentUrl);
|
|
595
|
+
return { ...result, navState };
|
|
389
596
|
});
|
|
390
597
|
|
|
391
598
|
applyHead(headElements);
|
|
599
|
+
} catch (error) {
|
|
600
|
+
// Stale transition (superseded by a newer navigation) or aborted
|
|
601
|
+
// fetch — silently ignore. See TIM-629.
|
|
602
|
+
if (isAbortError(error)) return;
|
|
603
|
+
throw error;
|
|
392
604
|
} finally {
|
|
605
|
+
if (currentNavAbort === navAbort) {
|
|
606
|
+
currentNavAbort = null;
|
|
607
|
+
}
|
|
393
608
|
setPending(false);
|
|
609
|
+
deps.completeRouterNavigation?.();
|
|
394
610
|
}
|
|
395
611
|
}
|
|
396
612
|
|
|
397
|
-
async function handlePopState(
|
|
613
|
+
async function handlePopState(
|
|
614
|
+
url: string,
|
|
615
|
+
scrollY: number = 0,
|
|
616
|
+
externalSignal?: AbortSignal
|
|
617
|
+
): Promise<void> {
|
|
398
618
|
// Scroll position is read from history.state by the caller (browser-entry.ts)
|
|
399
619
|
// and passed in. This is more reliable than tracking scroll per-URL in memory
|
|
400
620
|
// because the browser maintains per-entry state even with duplicate URLs.
|
|
@@ -402,37 +622,40 @@ export function createRouter(deps: RouterDeps): RouterInstance {
|
|
|
402
622
|
|
|
403
623
|
if (entry && entry.payload !== null) {
|
|
404
624
|
// Replay cached payload — no server roundtrip
|
|
405
|
-
updateNavigationState(entry.params, url);
|
|
406
|
-
renderPayload(entry.payload);
|
|
625
|
+
const navState = updateNavigationState(entry.params, url);
|
|
626
|
+
renderPayload(entry.payload, navState);
|
|
407
627
|
applyHead(entry.headElements);
|
|
408
|
-
|
|
409
|
-
deps.scrollTo(0, scrollY);
|
|
410
|
-
window.dispatchEvent(new Event('timber:scroll-restored'));
|
|
411
|
-
});
|
|
628
|
+
restoreScrollAfterPaint(scrollY);
|
|
412
629
|
} else {
|
|
413
630
|
// No cached payload — fetch from server.
|
|
414
631
|
// This happens when navigating back to the initial SSR'd page
|
|
415
632
|
// (its payload is null since it was rendered via SSR, not RSC fetch)
|
|
416
633
|
// or when the entry doesn't exist at all.
|
|
634
|
+
const navAbort = createNavAbort(externalSignal);
|
|
417
635
|
setPending(true, url);
|
|
418
636
|
try {
|
|
419
637
|
const headElements = await renderViaTransition(url, async () => {
|
|
420
638
|
const stateTree = segmentCache.serializeStateTree(
|
|
421
639
|
segmentElementCache.getMergeablePaths()
|
|
422
640
|
);
|
|
423
|
-
const result = await fetchRscPayload(url, deps, stateTree);
|
|
641
|
+
const result = await fetchRscPayload(url, deps, stateTree, undefined, navAbort.signal);
|
|
424
642
|
updateSegmentCache(result.segmentInfo);
|
|
425
|
-
updateNavigationState(result.params, url);
|
|
643
|
+
const navState = updateNavigationState(result.params, url);
|
|
426
644
|
// History push handled by renderViaTransition (stores merged payload)
|
|
427
|
-
return result;
|
|
645
|
+
return { ...result, navState };
|
|
428
646
|
});
|
|
429
647
|
|
|
430
648
|
applyHead(headElements);
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
649
|
+
restoreScrollAfterPaint(scrollY);
|
|
650
|
+
} catch (error) {
|
|
651
|
+
// Stale transition (superseded by a newer navigation) or aborted
|
|
652
|
+
// fetch — silently ignore. See TIM-629.
|
|
653
|
+
if (isAbortError(error)) return;
|
|
654
|
+
throw error;
|
|
435
655
|
} finally {
|
|
656
|
+
if (currentNavAbort === navAbort) {
|
|
657
|
+
currentNavAbort = null;
|
|
658
|
+
}
|
|
436
659
|
setPending(false);
|
|
437
660
|
}
|
|
438
661
|
}
|
|
@@ -463,8 +686,8 @@ export function createRouter(deps: RouterDeps): RouterInstance {
|
|
|
463
686
|
navigate,
|
|
464
687
|
refresh,
|
|
465
688
|
handlePopState,
|
|
466
|
-
isPending: () =>
|
|
467
|
-
getPendingUrl: () =>
|
|
689
|
+
isPending: () => routerPhase.phase === 'navigating',
|
|
690
|
+
getPendingUrl: () => (routerPhase.phase === 'navigating' ? routerPhase.targetUrl : null),
|
|
468
691
|
onPendingChange(listener) {
|
|
469
692
|
pendingListeners.add(listener);
|
|
470
693
|
return () => pendingListeners.delete(listener);
|
|
@@ -481,7 +704,11 @@ export function createRouter(deps: RouterDeps): RouterInstance {
|
|
|
481
704
|
payload: merged,
|
|
482
705
|
headElements,
|
|
483
706
|
});
|
|
484
|
-
|
|
707
|
+
// Revalidation doesn't change params/pathname — preserve current state.
|
|
708
|
+
// DO NOT call updateNavigationState(null, ...) here: that normalizes
|
|
709
|
+
// params to {}, clearing dynamic route params on every action response.
|
|
710
|
+
const navState = getNavigationState();
|
|
711
|
+
renderPayload(merged, navState);
|
|
485
712
|
applyHead(headElements);
|
|
486
713
|
},
|
|
487
714
|
initSegmentCache: (segments: SegmentInfo[]) => updateSegmentCache(segments),
|