@timber-js/app 0.2.0-alpha.1 → 0.2.0-alpha.100
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-CQ8Z8VGL.js +1061 -0
- package/dist/_chunks/actions-CQ8Z8VGL.js.map +1 -0
- package/dist/_chunks/build-output-helper-DXnW0qjz.js +61 -0
- package/dist/_chunks/build-output-helper-DXnW0qjz.js.map +1 -0
- package/dist/_chunks/chunk-BYIpzuS7.js +39 -0
- package/dist/_chunks/define-B-Q_UMOD.js +195 -0
- package/dist/_chunks/define-B-Q_UMOD.js.map +1 -0
- package/dist/_chunks/define-CfBPoJb0.js +123 -0
- package/dist/_chunks/define-CfBPoJb0.js.map +1 -0
- package/dist/_chunks/define-cookie-BjpIt4UC.js +194 -0
- package/dist/_chunks/define-cookie-BjpIt4UC.js.map +1 -0
- package/dist/_chunks/format-Bcn-Iv1x.js +14 -0
- package/dist/_chunks/format-Bcn-Iv1x.js.map +1 -0
- package/dist/_chunks/handler-store-B-lqaGyh.js +54 -0
- package/dist/_chunks/handler-store-B-lqaGyh.js.map +1 -0
- package/dist/_chunks/logger-0m8MsKdc.js +291 -0
- package/dist/_chunks/logger-0m8MsKdc.js.map +1 -0
- package/dist/_chunks/merge-search-params-BphMdht_.js +122 -0
- package/dist/_chunks/merge-search-params-BphMdht_.js.map +1 -0
- package/dist/_chunks/{metadata-routes-CQCnF4VK.js → metadata-routes-BU684ls2.js} +1 -1
- package/dist/_chunks/{metadata-routes-CQCnF4VK.js.map → metadata-routes-BU684ls2.js.map} +1 -1
- package/dist/_chunks/navigation-root-BCYczjml.js +96 -0
- package/dist/_chunks/navigation-root-BCYczjml.js.map +1 -0
- package/dist/_chunks/registry-I2ss-lvy.js +20 -0
- package/dist/_chunks/registry-I2ss-lvy.js.map +1 -0
- package/dist/_chunks/router-ref-h3-UaCQv.js +28 -0
- package/dist/_chunks/router-ref-h3-UaCQv.js.map +1 -0
- package/dist/_chunks/schema-bridge-Cxu4l-7p.js +86 -0
- package/dist/_chunks/schema-bridge-Cxu4l-7p.js.map +1 -0
- package/dist/_chunks/segment-classify-BjfuctV2.js +137 -0
- package/dist/_chunks/segment-classify-BjfuctV2.js.map +1 -0
- package/dist/_chunks/segment-context-Dx_OizxD.js +34 -0
- package/dist/_chunks/segment-context-Dx_OizxD.js.map +1 -0
- package/dist/_chunks/{ssr-data-DLnbYpj1.js → ssr-data-B4CdH7rE.js} +1 -1
- package/dist/_chunks/ssr-data-B4CdH7rE.js.map +1 -0
- package/dist/_chunks/stale-reload-Bab885FO.js +64 -0
- package/dist/_chunks/stale-reload-Bab885FO.js.map +1 -0
- package/dist/_chunks/tracing-C8V-YGsP.js +329 -0
- package/dist/_chunks/tracing-C8V-YGsP.js.map +1 -0
- package/dist/_chunks/{use-query-states-DAhgj8Gx.js → use-query-states-B2XTqxDR.js} +7 -20
- package/dist/_chunks/use-query-states-B2XTqxDR.js.map +1 -0
- package/dist/_chunks/use-segment-params-BkpKAQ7D.js +185 -0
- package/dist/_chunks/use-segment-params-BkpKAQ7D.js.map +1 -0
- package/dist/_chunks/walkers-Tg0Alwcg.js +850 -0
- package/dist/_chunks/walkers-Tg0Alwcg.js.map +1 -0
- package/dist/_chunks/{format-DNt20Kt8.js → warnings-Cg47l5sk.js} +7 -45
- package/dist/_chunks/warnings-Cg47l5sk.js.map +1 -0
- package/dist/adapters/build-output-helper.d.ts +28 -0
- package/dist/adapters/build-output-helper.d.ts.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 +142 -38
- 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 +143 -45
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/adapters/shared.d.ts +16 -0
- package/dist/adapters/shared.d.ts.map +1 -0
- package/dist/cache/cache-api.d.ts +24 -0
- package/dist/cache/cache-api.d.ts.map +1 -0
- package/dist/cache/fast-hash.d.ts +22 -0
- package/dist/cache/fast-hash.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 +195 -84
- 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 +8 -3
- package/dist/cli.js.map +1 -1
- package/dist/client/browser-dev.d.ts +34 -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 +26 -5
- 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 +16 -27
- 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 +9 -24
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +312 -1109
- package/dist/client/index.js.map +1 -1
- package/dist/client/internal.d.ts +19 -0
- package/dist/client/internal.d.ts.map +1 -0
- package/dist/client/internal.js +964 -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 +73 -63
- 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 +22 -5
- 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-context.d.ts +1 -1
- package/dist/client/segment-context.d.ts.map +1 -1
- package/dist/client/segment-merger.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/state.d.ts +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-cookie.d.ts +8 -0
- package/dist/client/use-cookie.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-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/client/{use-params.d.ts → use-segment-params.d.ts} +7 -5
- package/dist/client/use-segment-params.d.ts.map +1 -0
- 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 +294 -0
- package/dist/config-types.d.ts.map +1 -0
- package/dist/config-validation.d.ts +51 -0
- package/dist/config-validation.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 +98 -25
- package/dist/cookies/define-cookie.d.ts.map +1 -1
- package/dist/cookies/index.d.ts +2 -1
- package/dist/cookies/index.d.ts.map +1 -1
- package/dist/cookies/index.js +36 -72
- package/dist/cookies/index.js.map +1 -1
- package/dist/cookies/json-cookie.d.ts +64 -0
- package/dist/cookies/json-cookie.d.ts.map +1 -0
- package/dist/cookies/validation.d.ts +46 -0
- package/dist/cookies/validation.d.ts.map +1 -0
- package/dist/dev-tools/404-page.d.ts +46 -0
- package/dist/dev-tools/404-page.d.ts.map +1 -0
- package/dist/dev-tools/browser-logs.d.ts +84 -0
- package/dist/dev-tools/browser-logs.d.ts.map +1 -0
- package/dist/dev-tools/error-page.d.ts +58 -0
- package/dist/dev-tools/error-page.d.ts.map +1 -0
- package/dist/dev-tools/holding-server.d.ts +54 -0
- package/dist/dev-tools/holding-server.d.ts.map +1 -0
- package/dist/dev-tools/index.d.ts +31 -0
- package/dist/dev-tools/index.d.ts.map +1 -0
- package/dist/{server/dev-span-processor.d.ts → dev-tools/instrumentation.d.ts} +26 -6
- package/dist/dev-tools/instrumentation.d.ts.map +1 -0
- package/dist/{server/dev-logger.d.ts → dev-tools/logger.d.ts} +1 -1
- package/dist/dev-tools/logger.d.ts.map +1 -0
- package/dist/{plugins/dev-logs.d.ts → dev-tools/logs.d.ts} +2 -2
- package/dist/dev-tools/logs.d.ts.map +1 -0
- package/dist/dev-tools/overlay.d.ts +91 -0
- package/dist/dev-tools/overlay.d.ts.map +1 -0
- package/dist/dev-tools/stack-classifier.d.ts +34 -0
- package/dist/dev-tools/stack-classifier.d.ts.map +1 -0
- package/dist/dev-tools/terminal.d.ts +28 -0
- package/dist/dev-tools/terminal.d.ts.map +1 -0
- package/dist/{server/dev-warnings.d.ts → dev-tools/warnings.d.ts} +2 -22
- package/dist/dev-tools/warnings.d.ts.map +1 -0
- package/dist/fonts/bundle.d.ts +48 -0
- package/dist/fonts/bundle.d.ts.map +1 -0
- package/dist/fonts/css.d.ts +1 -0
- package/dist/fonts/css.d.ts.map +1 -1
- package/dist/fonts/dev-middleware.d.ts +22 -0
- package/dist/fonts/dev-middleware.d.ts.map +1 -0
- package/dist/fonts/local.d.ts +4 -2
- package/dist/fonts/local.d.ts.map +1 -1
- package/dist/fonts/pipeline.d.ts +167 -0
- package/dist/fonts/pipeline.d.ts.map +1 -0
- package/dist/fonts/transform.d.ts +64 -0
- package/dist/fonts/transform.d.ts.map +1 -0
- package/dist/fonts/types.d.ts +45 -1
- package/dist/fonts/types.d.ts.map +1 -1
- package/dist/fonts/virtual-modules.d.ts +103 -0
- package/dist/fonts/virtual-modules.d.ts.map +1 -0
- package/dist/index.d.ts +46 -152
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14702 -12350
- package/dist/index.js.map +1 -1
- package/dist/plugin-context.d.ts +107 -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 +16 -4
- package/dist/plugins/build-report.d.ts.map +1 -1
- package/dist/plugins/chunks.d.ts +9 -1
- package/dist/plugins/chunks.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-server.d.ts +1 -1
- package/dist/plugins/dev-server.d.ts.map +1 -1
- package/dist/plugins/entries.d.ts +8 -1
- package/dist/plugins/entries.d.ts.map +1 -1
- package/dist/plugins/fonts.d.ts +17 -65
- package/dist/plugins/fonts.d.ts.map +1 -1
- package/dist/plugins/mdx.d.ts +7 -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-shared.d.ts +55 -0
- package/dist/routing/codegen-shared.d.ts.map +1 -0
- package/dist/routing/codegen-types.d.ts +52 -0
- package/dist/routing/codegen-types.d.ts.map +1 -0
- package/dist/routing/codegen.d.ts +2 -2
- package/dist/routing/codegen.d.ts.map +1 -1
- package/dist/routing/convention-lint.d.ts +41 -0
- package/dist/routing/convention-lint.d.ts.map +1 -0
- package/dist/routing/index.d.ts +5 -1
- package/dist/routing/index.d.ts.map +1 -1
- package/dist/routing/index.js +3 -2
- package/dist/routing/link-codegen.d.ts +90 -0
- package/dist/routing/link-codegen.d.ts.map +1 -0
- package/dist/routing/scanner.d.ts +1 -10
- package/dist/routing/scanner.d.ts.map +1 -1
- package/dist/routing/segment-classify.d.ts +75 -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 +77 -25
- package/dist/routing/types.d.ts.map +1 -1
- package/dist/routing/walkers.d.ts +51 -0
- package/dist/routing/walkers.d.ts.map +1 -0
- 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 +157 -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 +34 -441
- package/dist/search-params/index.js.map +1 -1
- 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 +95 -0
- package/dist/segment-params/define.d.ts.map +1 -0
- package/dist/segment-params/index.d.ts +3 -0
- package/dist/segment-params/index.d.ts.map +1 -0
- package/dist/segment-params/index.js +2 -0
- package/dist/server/access-gate.d.ts +8 -3
- package/dist/server/access-gate.d.ts.map +1 -1
- package/dist/server/action-client.d.ts +41 -6
- 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 +35 -1
- 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 +33 -5
- package/dist/server/als-registry.d.ts.map +1 -1
- package/dist/server/asset-headers.d.ts +1 -15
- package/dist/server/asset-headers.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/cookie-context.d.ts +170 -0
- package/dist/server/cookie-context.d.ts.map +1 -0
- package/dist/server/cookie-parsing.d.ts +51 -0
- package/dist/server/cookie-parsing.d.ts.map +1 -0
- package/dist/server/debug.d.ts +82 -0
- package/dist/server/debug.d.ts.map +1 -0
- package/dist/server/default-logger.d.ts +22 -0
- package/dist/server/default-logger.d.ts.map +1 -0
- package/dist/server/deny-boundary.d.ts +90 -0
- package/dist/server/deny-boundary.d.ts.map +1 -0
- package/dist/server/deny-renderer.d.ts.map +1 -1
- package/dist/server/dev-source-map.d.ts +22 -0
- package/dist/server/dev-source-map.d.ts.map +1 -0
- package/dist/server/early-hints-sender.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 +12 -7
- 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-injector-core.d.ts +212 -0
- package/dist/server/html-injector-core.d.ts.map +1 -0
- package/dist/server/html-injectors.d.ts +81 -41
- package/dist/server/html-injectors.d.ts.map +1 -1
- package/dist/server/index.d.ts +9 -46
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +158 -2899
- package/dist/server/index.js.map +1 -1
- package/dist/server/internal.d.ts +48 -0
- package/dist/server/internal.d.ts.map +1 -0
- package/dist/server/internal.js +2900 -0
- package/dist/server/internal.js.map +1 -0
- package/dist/server/logger.d.ts +39 -7
- package/dist/server/logger.d.ts.map +1 -1
- package/dist/server/middleware-runner.d.ts +36 -4
- package/dist/server/middleware-runner.d.ts.map +1 -1
- package/dist/server/node-stream-transforms.d.ts +110 -0
- package/dist/server/node-stream-transforms.d.ts.map +1 -0
- package/dist/server/param-coercion.d.ts +26 -0
- package/dist/server/param-coercion.d.ts.map +1 -0
- package/dist/server/pipeline-helpers.d.ts +95 -0
- package/dist/server/pipeline-helpers.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-outcome.d.ts +49 -0
- package/dist/server/pipeline-outcome.d.ts.map +1 -0
- package/dist/server/pipeline-phases.d.ts +52 -0
- package/dist/server/pipeline-phases.d.ts.map +1 -0
- package/dist/server/pipeline.d.ts +93 -23
- package/dist/server/pipeline.d.ts.map +1 -1
- package/dist/server/port-resolution.d.ts +117 -0
- package/dist/server/port-resolution.d.ts.map +1 -0
- 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 +41 -109
- 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 +21 -34
- package/dist/server/route-matcher.d.ts.map +1 -1
- package/dist/server/rsc-entry/action-middleware-runner.d.ts +66 -0
- package/dist/server/rsc-entry/action-middleware-runner.d.ts.map +1 -0
- 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 +49 -6
- package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts +20 -3
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/dist/server/rsc-entry/render-route.d.ts +50 -0
- package/dist/server/rsc-entry/render-route.d.ts.map +1 -0
- 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 +20 -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/rsc-entry/wrap-action-dispatch.d.ts +119 -0
- package/dist/server/rsc-entry/wrap-action-dispatch.d.ts.map +1 -0
- package/dist/server/rsc-prop-warnings.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/sensitive-fields.d.ts +74 -0
- package/dist/server/sensitive-fields.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 +23 -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/state-tree-diff.d.ts.map +1 -1
- package/dist/server/status-code-resolver.d.ts +16 -11
- 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 +15 -5
- package/dist/server/tracing.d.ts.map +1 -1
- package/dist/server/tree-builder.d.ts +62 -30
- package/dist/server/tree-builder.d.ts.map +1 -1
- package/dist/server/types.d.ts +49 -4
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/utils/escape-html.d.ts +14 -0
- package/dist/server/utils/escape-html.d.ts.map +1 -0
- package/dist/server/version-skew.d.ts +61 -0
- package/dist/server/version-skew.d.ts.map +1 -0
- package/dist/server/waituntil-bridge.d.ts.map +1 -1
- 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 +3 -1
- package/dist/shims/navigation-client.d.ts.map +1 -1
- package/dist/shims/navigation.d.ts +11 -5
- package/dist/shims/navigation.d.ts.map +1 -1
- package/dist/utils/directive-parser.d.ts +1 -43
- 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 +56 -22
- package/src/adapters/build-output-helper.ts +77 -0
- package/src/adapters/cloudflare-dev.ts +177 -0
- package/src/adapters/cloudflare-kv-cache.ts +142 -0
- package/src/adapters/cloudflare.ts +350 -76
- package/src/adapters/compress-module.ts +24 -4
- package/src/adapters/nitro.ts +148 -60
- package/src/adapters/shared.ts +40 -0
- package/src/adapters/wrangler.d.ts +7 -0
- package/src/cache/cache-api.ts +38 -0
- package/src/cache/fast-hash.ts +34 -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 +41 -29
- package/src/cli.ts +16 -6
- package/src/client/browser-dev.ts +153 -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 +143 -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 +97 -20
- package/src/client/error-reconstituter.tsx +65 -0
- package/src/client/form.tsx +31 -32
- package/src/client/history.ts +26 -4
- package/src/client/index.ts +77 -43
- package/src/client/internal.ts +58 -0
- package/src/client/link-pending-store.ts +111 -0
- package/src/client/link.tsx +432 -140
- 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 +83 -18
- 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 +303 -78
- package/src/client/rsc-fetch.ts +112 -8
- package/src/client/segment-cache.ts +1 -1
- package/src/client/segment-context.ts +6 -1
- package/src/client/segment-merger.ts +2 -8
- package/src/client/segment-outlet.tsx +86 -0
- package/src/client/ssr-data.ts +13 -5
- package/src/client/stale-reload.ts +74 -7
- package/src/client/state.ts +2 -2
- package/src/client/top-loader.tsx +41 -25
- package/src/client/use-cookie.ts +29 -0
- package/src/client/use-link-status.ts +7 -7
- package/src/client/{use-navigation-pending.ts → use-pending-navigation.ts} +6 -6
- package/src/client/use-query-states.ts +9 -3
- package/src/client/use-router.ts +1 -1
- package/src/client/{use-params.ts → use-segment-params.ts} +7 -5
- package/src/codec.ts +45 -0
- package/src/config-types.ts +292 -0
- package/src/config-validation.ts +303 -0
- package/src/content/index.ts +5 -13
- package/src/cookies/define-cookie.ts +301 -55
- package/src/cookies/index.ts +12 -1
- package/src/cookies/json-cookie.ts +105 -0
- package/src/cookies/validation.ts +134 -0
- package/src/dev-tools/404-page.ts +387 -0
- package/src/dev-tools/browser-logs.ts +288 -0
- package/src/dev-tools/error-page.ts +509 -0
- package/src/dev-tools/holding-server.ts +187 -0
- package/src/dev-tools/index.ts +90 -0
- package/src/dev-tools/instrumentation.ts +176 -0
- package/src/{plugins/dev-logs.ts → dev-tools/logs.ts} +3 -3
- package/src/dev-tools/overlay.ts +456 -0
- package/src/dev-tools/stack-classifier.ts +75 -0
- package/src/dev-tools/terminal.ts +183 -0
- package/src/{server/dev-warnings.ts → dev-tools/warnings.ts} +6 -50
- package/src/fonts/bundle.ts +142 -0
- package/src/fonts/css.ts +2 -1
- package/src/fonts/dev-middleware.ts +74 -0
- package/src/fonts/local.ts +7 -3
- package/src/fonts/pipeline.ts +314 -0
- package/src/fonts/transform.ts +406 -0
- package/src/fonts/types.ts +50 -1
- package/src/fonts/virtual-modules.ts +227 -0
- package/src/index.ts +376 -315
- package/src/plugin-context.ts +240 -0
- package/src/plugins/adapter-build.ts +12 -4
- package/src/plugins/build-manifest.ts +13 -2
- package/src/plugins/build-report.ts +32 -21
- package/src/plugins/chunks.ts +9 -1
- package/src/plugins/client-chunks.ts +65 -0
- package/src/plugins/content.ts +1 -1
- package/src/plugins/dev-server.ts +154 -21
- package/src/plugins/entries.ts +119 -12
- package/src/plugins/fonts.ts +177 -524
- package/src/plugins/mdx.ts +52 -15
- package/src/plugins/routing.ts +225 -41
- package/src/plugins/server-action-exports.ts +1 -1
- package/src/plugins/server-bundle.ts +36 -1
- package/src/plugins/shims.ts +136 -35
- package/src/plugins/static-build.ts +17 -11
- package/src/routing/codegen-shared.ts +98 -0
- package/src/routing/codegen-types.ts +53 -0
- package/src/routing/codegen.ts +143 -176
- package/src/routing/convention-lint.ts +357 -0
- package/src/routing/index.ts +5 -1
- package/src/routing/interception.ts +1 -1
- package/src/routing/link-codegen.ts +294 -0
- package/src/routing/scanner.ts +107 -110
- package/src/routing/segment-classify.ts +188 -0
- package/src/routing/status-file-lint.ts +10 -7
- package/src/routing/types.ts +78 -25
- package/src/routing/walkers.ts +90 -0
- 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 +538 -0
- package/src/search-params/index.ts +14 -20
- package/src/search-params/registry.ts +2 -2
- package/src/search-params/wrappers.ts +92 -0
- package/src/segment-params/define.ts +332 -0
- package/src/segment-params/index.ts +9 -0
- package/src/server/access-gate.tsx +79 -36
- package/src/server/action-client.ts +103 -20
- package/src/server/action-encryption.ts +144 -0
- package/src/server/action-handler.ts +112 -14
- package/src/server/actions.ts +10 -9
- package/src/server/als-registry.ts +35 -7
- package/src/server/asset-headers.ts +8 -34
- package/src/server/build-manifest.ts +10 -4
- package/src/server/compress.ts +25 -7
- package/src/server/cookie-context.ts +468 -0
- package/src/server/cookie-parsing.ts +135 -0
- package/src/server/debug.ts +137 -0
- package/src/server/default-logger.ts +99 -0
- package/src/server/deny-boundary.ts +218 -0
- package/src/server/deny-renderer.ts +33 -51
- package/src/server/dev-source-map.ts +31 -0
- package/src/server/early-hints-sender.ts +3 -2
- package/src/server/early-hints.ts +36 -15
- package/src/server/error-boundary-wrapper.ts +74 -22
- package/src/server/fallback-error.ts +74 -102
- 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-injector-core.ts +403 -0
- package/src/server/html-injectors.ts +244 -223
- package/src/server/index.ts +31 -184
- package/src/server/internal.ts +176 -0
- package/src/server/logger.ts +67 -35
- package/src/server/middleware-runner.ts +75 -4
- package/src/server/node-stream-transforms.ts +369 -0
- package/src/server/param-coercion.ts +76 -0
- package/src/server/pipeline-helpers.ts +204 -0
- package/src/server/pipeline-interception.ts +17 -16
- package/src/server/pipeline-metadata.ts +13 -0
- package/src/server/pipeline-outcome.ts +167 -0
- package/src/server/pipeline-phases.ts +409 -0
- package/src/server/pipeline.ts +128 -387
- package/src/server/port-resolution.ts +215 -0
- package/src/server/primitives.ts +113 -29
- package/src/server/render-timeout.ts +108 -0
- package/src/server/request-context.ts +94 -337
- package/src/server/route-element-builder.ts +289 -198
- package/src/server/route-handler.ts +24 -4
- package/src/server/route-matcher.ts +44 -65
- package/src/server/rsc-entry/action-middleware-runner.ts +167 -0
- package/src/server/rsc-entry/api-handler.ts +16 -17
- package/src/server/rsc-entry/error-renderer.ts +305 -89
- package/src/server/rsc-entry/helpers.ts +136 -12
- package/src/server/rsc-entry/index.ts +330 -312
- package/src/server/rsc-entry/render-route.ts +304 -0
- package/src/server/rsc-entry/rsc-payload.ts +100 -18
- package/src/server/rsc-entry/rsc-stream.ts +90 -17
- package/src/server/rsc-entry/ssr-bridge.ts +14 -5
- package/src/server/rsc-entry/ssr-renderer.ts +177 -37
- package/src/server/rsc-entry/wrap-action-dispatch.ts +449 -0
- package/src/server/rsc-prop-warnings.ts +3 -1
- package/src/server/safe-load.ts +60 -0
- package/src/server/sensitive-fields.ts +230 -0
- package/src/server/sitemap-generator.ts +338 -0
- package/src/server/sitemap-handler.ts +126 -0
- package/src/server/slot-resolver.ts +245 -230
- package/src/server/ssr-entry.ts +215 -32
- package/src/server/ssr-render.ts +289 -67
- package/src/server/ssr-wrappers.tsx +139 -0
- package/src/server/state-tree-diff.ts +4 -1
- package/src/server/status-code-resolver.ts +112 -128
- package/src/server/stream-utils.ts +213 -0
- package/src/server/tracing.ts +44 -10
- package/src/server/tree-builder.ts +202 -90
- package/src/server/types.ts +55 -6
- package/src/server/utils/escape-html.ts +20 -0
- package/src/server/version-skew.ts +104 -0
- package/src/server/waituntil-bridge.ts +4 -1
- 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 +4 -3
- package/src/shims/navigation.ts +14 -7
- package/src/utils/directive-parser.ts +1 -390
- package/src/utils/state-machine.ts +111 -0
- package/dist/_chunks/als-registry-k-AtAQ9R.js +0 -41
- package/dist/_chunks/als-registry-k-AtAQ9R.js.map +0 -1
- package/dist/_chunks/format-DNt20Kt8.js.map +0 -1
- package/dist/_chunks/interception-DGDIjDbR.js +0 -676
- package/dist/_chunks/interception-DGDIjDbR.js.map +0 -1
- package/dist/_chunks/request-context-CRj2Zh1E.js +0 -329
- package/dist/_chunks/request-context-CRj2Zh1E.js.map +0 -1
- package/dist/_chunks/ssr-data-DLnbYpj1.js.map +0 -1
- package/dist/_chunks/tracing-DF0G3FB7.js +0 -173
- package/dist/_chunks/tracing-DF0G3FB7.js.map +0 -1
- package/dist/_chunks/use-cookie-dDbpCTx-.js +0 -91
- package/dist/_chunks/use-cookie-dDbpCTx-.js.map +0 -1
- package/dist/_chunks/use-query-states-DAhgj8Gx.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 -22
- package/dist/client/browser-entry.d.ts.map +0 -1
- package/dist/client/browser-links.d.ts +0 -32
- package/dist/client/browser-links.d.ts.map +0 -1
- package/dist/client/link-navigate-interceptor.d.ts +0 -28
- package/dist/client/link-navigate-interceptor.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/client/use-params.d.ts.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/dev-error-overlay.d.ts +0 -60
- package/dist/plugins/dev-error-overlay.d.ts.map +0 -1
- package/dist/plugins/dev-logs.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/server/dev-fetch-instrumentation.d.ts +0 -22
- package/dist/server/dev-fetch-instrumentation.d.ts.map +0 -1
- package/dist/server/dev-logger.d.ts.map +0 -1
- package/dist/server/dev-span-processor.d.ts.map +0 -1
- package/dist/server/dev-warnings.d.ts.map +0 -1
- package/dist/server/manifest-status-resolver.d.ts +0 -58
- package/dist/server/manifest-status-resolver.d.ts.map +0 -1
- package/dist/server/prerender.d.ts +0 -77
- package/dist/server/prerender.d.ts.map +0 -1
- package/src/cache/register-cached-function.ts +0 -99
- package/src/client/browser-entry.ts +0 -686
- package/src/client/browser-links.ts +0 -90
- package/src/client/link-navigate-interceptor.tsx +0 -62
- package/src/client/link-status-provider.tsx +0 -30
- package/src/client/transition-root.tsx +0 -160
- package/src/plugins/cache-transform.ts +0 -199
- package/src/plugins/dev-error-overlay.ts +0 -230
- 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/dev-fetch-instrumentation.ts +0 -96
- package/src/server/dev-span-processor.ts +0 -78
- package/src/server/manifest-status-resolver.ts +0 -215
- package/src/server/prerender.ts +0 -139
- /package/src/{server/dev-logger.ts → dev-tools/logger.ts} +0 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navigation Link Store — passes per-link metadata from Link's onClick
|
|
3
|
+
* to the Navigation API's navigate event handler.
|
|
4
|
+
*
|
|
5
|
+
* When the Navigation API is active, Link does NOT call event.preventDefault()
|
|
6
|
+
* or router.navigate(). Instead it stores metadata (scroll option, link
|
|
7
|
+
* pending instance) here, and lets the <a> click propagate naturally.
|
|
8
|
+
* The navigate event handler reads this metadata to configure the RSC
|
|
9
|
+
* navigation with the correct options.
|
|
10
|
+
*
|
|
11
|
+
* This store is consumed once per navigation — after reading, the metadata
|
|
12
|
+
* is cleared. If no metadata is present (e.g., a plain <a> tag without
|
|
13
|
+
* our Link component), the navigate handler uses default options.
|
|
14
|
+
*
|
|
15
|
+
* See design/19-client-navigation.md §"Navigation API Integration"
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import type { LinkPendingInstance } from './link-pending-store.js';
|
|
19
|
+
|
|
20
|
+
export interface NavLinkMetadata {
|
|
21
|
+
/** Whether to scroll to top after navigation. Default: true. */
|
|
22
|
+
scroll: boolean;
|
|
23
|
+
/** The Link's pending state instance for per-link status tracking. */
|
|
24
|
+
linkInstance: LinkPendingInstance | null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let pendingMetadata: NavLinkMetadata | null = null;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Store metadata from Link's onClick for the next navigate event.
|
|
31
|
+
* Called synchronously in the click handler — the navigate event
|
|
32
|
+
* fires synchronously after onClick returns.
|
|
33
|
+
*/
|
|
34
|
+
export function setNavLinkMetadata(metadata: NavLinkMetadata): void {
|
|
35
|
+
pendingMetadata = metadata;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Consume the stored metadata. Returns null if no Link onClick
|
|
40
|
+
* preceded this navigation (e.g., plain <a> tag, programmatic nav).
|
|
41
|
+
* Clears the store after reading.
|
|
42
|
+
*/
|
|
43
|
+
export function consumeNavLinkMetadata(): NavLinkMetadata | null {
|
|
44
|
+
const metadata = pendingMetadata;
|
|
45
|
+
pendingMetadata = null;
|
|
46
|
+
return metadata;
|
|
47
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ambient type declarations for the Navigation API.
|
|
3
|
+
*
|
|
4
|
+
* The Navigation API is not yet in TypeScript's standard lib. These types
|
|
5
|
+
* are used internally via type assertions — we never import Navigation API
|
|
6
|
+
* types unconditionally. Progressive enhancement only: the API is feature-
|
|
7
|
+
* detected at runtime.
|
|
8
|
+
*
|
|
9
|
+
* See https://developer.mozilla.org/en-US/docs/Web/API/Navigation_API
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// ─── Navigation Entry ────────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
export interface NavigationHistoryEntry {
|
|
15
|
+
readonly key: string;
|
|
16
|
+
readonly id: string;
|
|
17
|
+
readonly url: string | null;
|
|
18
|
+
readonly index: number;
|
|
19
|
+
readonly sameDocument: boolean;
|
|
20
|
+
getState(): unknown;
|
|
21
|
+
addEventListener(type: string, listener: EventListener): void;
|
|
22
|
+
removeEventListener(type: string, listener: EventListener): void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ─── Navigation Destination ──────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
export interface NavigationDestination {
|
|
28
|
+
readonly url: string;
|
|
29
|
+
readonly key: string | null;
|
|
30
|
+
readonly id: string | null;
|
|
31
|
+
readonly index: number;
|
|
32
|
+
readonly sameDocument: boolean;
|
|
33
|
+
getState(): unknown;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ─── Navigate Event ──────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
export interface NavigateEvent extends Event {
|
|
39
|
+
readonly navigationType: 'push' | 'replace' | 'reload' | 'traverse';
|
|
40
|
+
readonly destination: NavigationDestination;
|
|
41
|
+
readonly canIntercept: boolean;
|
|
42
|
+
readonly userInitiated: boolean;
|
|
43
|
+
readonly hashChange: boolean;
|
|
44
|
+
readonly signal: AbortSignal;
|
|
45
|
+
readonly formData: FormData | null;
|
|
46
|
+
readonly downloadRequest: string | null;
|
|
47
|
+
readonly info: unknown;
|
|
48
|
+
intercept(options?: NavigateInterceptOptions): void;
|
|
49
|
+
scroll(): void;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface NavigateInterceptOptions {
|
|
53
|
+
handler?: () => Promise<void>;
|
|
54
|
+
focusReset?: 'after-transition' | 'manual';
|
|
55
|
+
scroll?: 'after-transition' | 'manual';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ─── Navigation Transition ───────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
export interface NavigationTransition {
|
|
61
|
+
readonly navigationType: 'push' | 'replace' | 'reload' | 'traverse';
|
|
62
|
+
readonly from: NavigationHistoryEntry;
|
|
63
|
+
readonly finished: Promise<void>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ─── Navigation Result ───────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
export interface NavigationResult {
|
|
69
|
+
committed: Promise<NavigationHistoryEntry>;
|
|
70
|
+
finished: Promise<NavigationHistoryEntry>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ─── Navigation Interface ────────────────────────────────────────
|
|
74
|
+
|
|
75
|
+
export interface NavigationApi {
|
|
76
|
+
readonly currentEntry: NavigationHistoryEntry | null;
|
|
77
|
+
readonly transition: NavigationTransition | null;
|
|
78
|
+
readonly canGoBack: boolean;
|
|
79
|
+
readonly canGoForward: boolean;
|
|
80
|
+
entries(): NavigationHistoryEntry[];
|
|
81
|
+
navigate(url: string, options?: NavigationNavigateOptions): NavigationResult;
|
|
82
|
+
reload(options?: NavigationReloadOptions): NavigationResult;
|
|
83
|
+
traverseTo(key: string, options?: NavigationOptions): NavigationResult;
|
|
84
|
+
back(options?: NavigationOptions): NavigationResult;
|
|
85
|
+
forward(options?: NavigationOptions): NavigationResult;
|
|
86
|
+
updateCurrentEntry(options: NavigationUpdateCurrentEntryOptions): void;
|
|
87
|
+
addEventListener(type: 'navigate', listener: (event: NavigateEvent) => void): void;
|
|
88
|
+
addEventListener(type: 'navigatesuccess', listener: (event: Event) => void): void;
|
|
89
|
+
addEventListener(type: 'navigateerror', listener: (event: Event) => void): void;
|
|
90
|
+
addEventListener(type: 'currententrychange', listener: (event: Event) => void): void;
|
|
91
|
+
addEventListener(type: string, listener: EventListener): void;
|
|
92
|
+
removeEventListener(type: string, listener: EventListener): void;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface NavigationNavigateOptions {
|
|
96
|
+
state?: unknown;
|
|
97
|
+
history?: 'auto' | 'push' | 'replace';
|
|
98
|
+
info?: unknown;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface NavigationReloadOptions {
|
|
102
|
+
state?: unknown;
|
|
103
|
+
info?: unknown;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface NavigationOptions {
|
|
107
|
+
info?: unknown;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface NavigationUpdateCurrentEntryOptions {
|
|
111
|
+
state: unknown;
|
|
112
|
+
}
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navigation API integration — progressive enhancement for client navigation.
|
|
3
|
+
*
|
|
4
|
+
* When the Navigation API (`window.navigation`) is available, this module
|
|
5
|
+
* provides an intercept-based navigation model that replaces the separate
|
|
6
|
+
* popstate + click handler approach with a single navigate event listener.
|
|
7
|
+
*
|
|
8
|
+
* Key benefits:
|
|
9
|
+
* - Intercepts ALL navigations (link clicks, form submissions, back/forward)
|
|
10
|
+
* - Built-in AbortSignal per navigation (auto-aborts in-flight fetches)
|
|
11
|
+
* - Per-entry state via NavigationHistoryEntry.getState()
|
|
12
|
+
* - navigation.transition for progress tracking
|
|
13
|
+
*
|
|
14
|
+
* When unavailable, all functions are no-ops and the History API fallback
|
|
15
|
+
* in browser-entry.ts handles navigation.
|
|
16
|
+
*
|
|
17
|
+
* See design/19-client-navigation.md
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import type { NavigationApi, NavigateEvent } from './navigation-api-types.js';
|
|
21
|
+
import { consumeNavLinkMetadata } from './nav-link-store.js';
|
|
22
|
+
import { isHardNavigating } from './navigation-root.js';
|
|
23
|
+
|
|
24
|
+
// ─── Feature Detection ───────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Returns true if the Navigation API is available in the current environment.
|
|
28
|
+
* Feature-detected at runtime — no polyfill.
|
|
29
|
+
*/
|
|
30
|
+
export function hasNavigationApi(): boolean {
|
|
31
|
+
return (
|
|
32
|
+
typeof window !== 'undefined' &&
|
|
33
|
+
'navigation' in window &&
|
|
34
|
+
(window as unknown as { navigation: unknown }).navigation != null
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get the Navigation API instance. Returns null if unavailable.
|
|
40
|
+
* Uses type assertion — we never import Navigation API types unconditionally.
|
|
41
|
+
*/
|
|
42
|
+
export function getNavigationApi(): NavigationApi | null {
|
|
43
|
+
if (!hasNavigationApi()) return null;
|
|
44
|
+
return (window as unknown as { navigation: NavigationApi }).navigation;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ─── Navigation API Controller ───────────────────────────────────
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Callbacks for the Navigation API event handler.
|
|
51
|
+
*
|
|
52
|
+
* When the Navigation API intercepts a navigation, it delegates to these
|
|
53
|
+
* callbacks which run the RSC fetch + render pipeline.
|
|
54
|
+
*/
|
|
55
|
+
export interface NavigationApiCallbacks {
|
|
56
|
+
/**
|
|
57
|
+
* Handle a push/replace navigation intercepted by the Navigation API.
|
|
58
|
+
* This covers both Link <a> clicks (user-initiated, with metadata from
|
|
59
|
+
* nav-link-store) and external navigations (plain <a> tags, programmatic).
|
|
60
|
+
* The Navigation API handles the URL update via event.intercept().
|
|
61
|
+
*/
|
|
62
|
+
onExternalNavigate: (
|
|
63
|
+
url: string,
|
|
64
|
+
options: { replace: boolean; signal: AbortSignal; scroll?: boolean }
|
|
65
|
+
) => Promise<void>;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Handle a traversal (back/forward button). The Navigation API intercepts
|
|
69
|
+
* the traversal and delegates to us for RSC replay/fetch.
|
|
70
|
+
*/
|
|
71
|
+
onTraverse: (url: string, scrollY: number, signal: AbortSignal) => Promise<void>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Controller returned by setupNavigationApi. Provides methods to
|
|
76
|
+
* coordinate between the router and the navigate event listener.
|
|
77
|
+
*/
|
|
78
|
+
export interface NavigationApiController {
|
|
79
|
+
/**
|
|
80
|
+
* Set the router-navigating flag. When `true`, the next navigate event
|
|
81
|
+
* (from pushState/replaceState) is recognized as router-initiated. The
|
|
82
|
+
* handler still intercepts it — but ties the browser's native loading
|
|
83
|
+
* state to a deferred promise instead of running the RSC pipeline again.
|
|
84
|
+
*
|
|
85
|
+
* This means `navigation.transition` is active for the full duration of
|
|
86
|
+
* every router-initiated navigation, giving the browser a native loading
|
|
87
|
+
* indicator (tab spinner, address bar) aligned with the TopLoader.
|
|
88
|
+
*
|
|
89
|
+
* Must be called synchronously around pushState/replaceState:
|
|
90
|
+
* controller.setRouterNavigating(true);
|
|
91
|
+
* history.pushState(...); // navigate event fires, intercepted
|
|
92
|
+
* controller.setRouterNavigating(false); // flag off, deferred stays open
|
|
93
|
+
*/
|
|
94
|
+
setRouterNavigating: (value: boolean) => void;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Resolve the deferred promise created by setRouterNavigating(true),
|
|
98
|
+
* clearing the browser's native loading state. Call this when the
|
|
99
|
+
* navigation fully completes — aligned with when the TopLoader's
|
|
100
|
+
* pendingUrl clears (same finally block in router.navigate).
|
|
101
|
+
*/
|
|
102
|
+
completeRouterNavigation: () => void;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Initiate a navigation via the Navigation API (`navigation.navigate()`).
|
|
106
|
+
* Unlike `history.pushState()`, this fires the navigate event BEFORE
|
|
107
|
+
* committing the URL — allowing Chrome to show its native loading
|
|
108
|
+
* indicator while the intercept handler runs.
|
|
109
|
+
*
|
|
110
|
+
* Must be called with setRouterNavigating(true) active so the handler
|
|
111
|
+
* recognizes it as router-initiated and uses the deferred promise.
|
|
112
|
+
*/
|
|
113
|
+
navigate: (url: string, replace: boolean) => void;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Save scroll position into the current navigation entry's state.
|
|
117
|
+
* Uses navigation.updateCurrentEntry() for per-entry scroll storage.
|
|
118
|
+
*/
|
|
119
|
+
saveScrollPosition: (scrollY: number) => void;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Check if the Navigation API has an active transition.
|
|
123
|
+
* Returns the transition object if available, null otherwise.
|
|
124
|
+
*/
|
|
125
|
+
hasActiveTransition: () => boolean;
|
|
126
|
+
|
|
127
|
+
/** Remove the navigate event listener. */
|
|
128
|
+
cleanup: () => void;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Set up the Navigation API navigate event listener.
|
|
133
|
+
*
|
|
134
|
+
* Intercepts same-origin navigations and delegates to the provided callbacks.
|
|
135
|
+
* Router-initiated navigations (pushState from router.navigate) are detected
|
|
136
|
+
* via a synchronous flag and NOT intercepted — the router already handles them.
|
|
137
|
+
*
|
|
138
|
+
* Returns a controller for coordinating with the router.
|
|
139
|
+
*/
|
|
140
|
+
export function setupNavigationApi(callbacks: NavigationApiCallbacks): NavigationApiController {
|
|
141
|
+
const nav = getNavigationApi()!;
|
|
142
|
+
|
|
143
|
+
let routerNavigating = false;
|
|
144
|
+
|
|
145
|
+
// Deferred promise for router-initiated navigations. Created when
|
|
146
|
+
// setRouterNavigating(true) is called, resolved by completeRouterNavigation().
|
|
147
|
+
// The navigate event handler intercepts with this promise so the browser's
|
|
148
|
+
// native loading state (tab spinner) stays active until the navigation
|
|
149
|
+
// completes — aligned with TopLoader's pendingUrl lifecycle.
|
|
150
|
+
let routerNavDeferred: { promise: Promise<void>; resolve: () => void } | null = null;
|
|
151
|
+
|
|
152
|
+
function handleNavigate(event: NavigateEvent): void {
|
|
153
|
+
// Skip non-interceptable navigations (cross-origin, etc.)
|
|
154
|
+
if (!event.canIntercept) return;
|
|
155
|
+
|
|
156
|
+
// Hard navigation guard: when the router has triggered a full page
|
|
157
|
+
// load (500 error, version skew), skip interception entirely so the
|
|
158
|
+
// browser performs the MPA navigation. Without this guard, setting
|
|
159
|
+
// window.location.href fires a navigate event that we'd intercept,
|
|
160
|
+
// running the RSC pipeline again → 500 → window.location.href →
|
|
161
|
+
// navigate event → infinite loop.
|
|
162
|
+
// See design/19-client-navigation.md §"Hard Navigation Guard"
|
|
163
|
+
if (isHardNavigating()) return;
|
|
164
|
+
|
|
165
|
+
// Skip download requests
|
|
166
|
+
if (event.downloadRequest) return;
|
|
167
|
+
|
|
168
|
+
// Skip hash-only changes — let the browser handle scroll-to-anchor
|
|
169
|
+
if (event.hashChange) return;
|
|
170
|
+
|
|
171
|
+
// Shallow URL updates (e.g., nuqs search param changes). The navigation
|
|
172
|
+
// only changes the URL — no server round trip needed. Intercept with a
|
|
173
|
+
// no-op handler so the Navigation API commits the URL change without
|
|
174
|
+
// triggering a full page navigation (which is the default if we don't
|
|
175
|
+
// intercept). The info property is the Navigation API's built-in
|
|
176
|
+
// per-navigation metadata — no side-channel flags needed.
|
|
177
|
+
const info = event.info as { shallow?: boolean } | null | undefined;
|
|
178
|
+
if (info?.shallow) {
|
|
179
|
+
event.intercept({
|
|
180
|
+
handler: () => Promise.resolve(),
|
|
181
|
+
focusReset: 'manual',
|
|
182
|
+
scroll: 'manual',
|
|
183
|
+
});
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Skip form submissions with a body (POST/PUT/etc.). These need the
|
|
188
|
+
// browser's native form handling to send the request body to the server.
|
|
189
|
+
// Intercepting would convert them into GET RSC navigations, dropping
|
|
190
|
+
// the form data. Server actions use fetch() directly (not form navigation),
|
|
191
|
+
// so they are unaffected by this check.
|
|
192
|
+
if (event.formData) return;
|
|
193
|
+
|
|
194
|
+
// Skip cross-origin (defense-in-depth — canIntercept covers this)
|
|
195
|
+
const destUrl = new URL(event.destination.url);
|
|
196
|
+
if (destUrl.origin !== location.origin) return;
|
|
197
|
+
|
|
198
|
+
// Router-initiated navigation (Link click → router.navigate → pushState).
|
|
199
|
+
// The router is already running the RSC pipeline — don't run it again.
|
|
200
|
+
// Instead, intercept with the deferred promise so the browser's native
|
|
201
|
+
// loading state tracks the navigation's full lifecycle. This aligns the
|
|
202
|
+
// tab spinner / address bar indicator with the TopLoader.
|
|
203
|
+
if (routerNavigating && routerNavDeferred) {
|
|
204
|
+
event.intercept({
|
|
205
|
+
scroll: 'manual',
|
|
206
|
+
focusReset: 'manual',
|
|
207
|
+
handler: () => routerNavDeferred!.promise,
|
|
208
|
+
});
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Skip reload navigations — let the browser handle full page reload
|
|
213
|
+
if (event.navigationType === 'reload') return;
|
|
214
|
+
|
|
215
|
+
const url = destUrl.pathname + destUrl.search;
|
|
216
|
+
|
|
217
|
+
if (event.navigationType === 'traverse') {
|
|
218
|
+
// Back/forward button — intercept and delegate to router.
|
|
219
|
+
// Read scroll position from the destination entry's state.
|
|
220
|
+
const entryState = event.destination.getState() as
|
|
221
|
+
| { scrollY?: number; timber?: boolean }
|
|
222
|
+
| null
|
|
223
|
+
| undefined;
|
|
224
|
+
const scrollY = entryState && typeof entryState.scrollY === 'number' ? entryState.scrollY : 0;
|
|
225
|
+
|
|
226
|
+
event.intercept({
|
|
227
|
+
// Manual scroll — we handle scroll restoration ourselves
|
|
228
|
+
// via afterPaint (same as the History API path).
|
|
229
|
+
scroll: 'manual',
|
|
230
|
+
focusReset: 'manual',
|
|
231
|
+
async handler() {
|
|
232
|
+
await callbacks.onTraverse(url, scrollY, event.signal);
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
} else if (event.navigationType === 'push' || event.navigationType === 'replace') {
|
|
236
|
+
// Push/replace — either a Link <a> click (with metadata in
|
|
237
|
+
// nav-link-store) or an external navigation (plain <a>, programmatic).
|
|
238
|
+
// Consume link metadata if present — tells us scroll preference
|
|
239
|
+
// and which Link component to track pending state for.
|
|
240
|
+
const linkMeta = consumeNavLinkMetadata();
|
|
241
|
+
|
|
242
|
+
// Save the departing page's scroll position BEFORE event.intercept()
|
|
243
|
+
// commits the URL change. Once intercept() is called, currentEntry
|
|
244
|
+
// switches to the new (destination) entry — any updateCurrentEntry()
|
|
245
|
+
// call after that would save to the wrong entry.
|
|
246
|
+
// See: router.navigate() also calls saveNavigationEntryScroll(), but
|
|
247
|
+
// for Navigation API <a> click navigations (where Link does NOT call
|
|
248
|
+
// router.navigate directly), the router's save runs inside the
|
|
249
|
+
// intercept handler — too late, currentEntry has already switched.
|
|
250
|
+
try {
|
|
251
|
+
const currentState = (nav.currentEntry?.getState() ?? {}) as Record<string, unknown>;
|
|
252
|
+
nav.updateCurrentEntry({
|
|
253
|
+
state: { ...currentState, timber: true, scrollY: window.scrollY },
|
|
254
|
+
});
|
|
255
|
+
} catch {
|
|
256
|
+
// Ignore — entry may be disposed
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
event.intercept({
|
|
260
|
+
scroll: 'manual',
|
|
261
|
+
focusReset: 'manual',
|
|
262
|
+
async handler() {
|
|
263
|
+
await callbacks.onExternalNavigate(url, {
|
|
264
|
+
replace: event.navigationType === 'replace',
|
|
265
|
+
signal: event.signal,
|
|
266
|
+
scroll: linkMeta?.scroll,
|
|
267
|
+
});
|
|
268
|
+
},
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
nav.addEventListener('navigate', handleNavigate as EventListener);
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
setRouterNavigating(value: boolean): void {
|
|
277
|
+
routerNavigating = value;
|
|
278
|
+
if (value) {
|
|
279
|
+
// Create a new deferred promise. The navigate event handler will
|
|
280
|
+
// intercept and tie the browser's loading state to this promise.
|
|
281
|
+
let resolve!: () => void;
|
|
282
|
+
const promise = new Promise<void>((r) => {
|
|
283
|
+
resolve = r;
|
|
284
|
+
});
|
|
285
|
+
routerNavDeferred = { promise, resolve };
|
|
286
|
+
} else {
|
|
287
|
+
// Flag off — but DON'T resolve the deferred here. The navigation
|
|
288
|
+
// is still in flight (RSC fetch + render). completeRouterNavigation()
|
|
289
|
+
// resolves it when the navigation fully completes.
|
|
290
|
+
routerNavigating = false;
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
|
|
294
|
+
completeRouterNavigation(): void {
|
|
295
|
+
if (routerNavDeferred) {
|
|
296
|
+
routerNavDeferred.resolve();
|
|
297
|
+
routerNavDeferred = null;
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
|
|
301
|
+
navigate(url: string, replace: boolean): void {
|
|
302
|
+
// Use navigation.navigate() instead of history.pushState().
|
|
303
|
+
// This fires the navigate event BEFORE committing the URL,
|
|
304
|
+
// which lets Chrome show its native loading indicator while
|
|
305
|
+
// the intercept handler (deferred promise) is pending.
|
|
306
|
+
// history.pushState() commits the URL synchronously, so Chrome
|
|
307
|
+
// sees the navigation as already complete and skips the indicator.
|
|
308
|
+
nav.navigate(url, {
|
|
309
|
+
history: replace ? 'replace' : 'push',
|
|
310
|
+
});
|
|
311
|
+
},
|
|
312
|
+
|
|
313
|
+
saveScrollPosition(scrollY: number): void {
|
|
314
|
+
try {
|
|
315
|
+
const currentState = (nav.currentEntry?.getState() ?? {}) as Record<string, unknown>;
|
|
316
|
+
nav.updateCurrentEntry({
|
|
317
|
+
state: { ...currentState, timber: true, scrollY },
|
|
318
|
+
});
|
|
319
|
+
} catch {
|
|
320
|
+
// Ignore errors — updateCurrentEntry may throw if entry is disposed
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
hasActiveTransition(): boolean {
|
|
325
|
+
return nav.transition != null;
|
|
326
|
+
},
|
|
327
|
+
|
|
328
|
+
cleanup(): void {
|
|
329
|
+
nav.removeEventListener('navigate', handleNavigate as EventListener);
|
|
330
|
+
},
|
|
331
|
+
};
|
|
332
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Holds the current route params and pathname, updated atomically
|
|
7
7
|
* with the RSC tree on each navigation. This replaces the previous
|
|
8
|
-
* useSyncExternalStore approach for
|
|
8
|
+
* useSyncExternalStore approach for useSegmentParams() and usePathname(),
|
|
9
9
|
* which suffered from a timing gap: the new tree could commit before
|
|
10
10
|
* the external store re-renders fired, causing a frame where both
|
|
11
11
|
* old and new active states were visible simultaneously.
|
|
@@ -25,9 +25,15 @@
|
|
|
25
25
|
* that depend on these APIs are safe to call from any environment —
|
|
26
26
|
* they return null or no-op when the APIs aren't available.
|
|
27
27
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
28
|
+
* SINGLETON GUARANTEE: All shared mutable state uses globalThis via
|
|
29
|
+
* Symbol.for keys. The RSC client bundler can duplicate this module
|
|
30
|
+
* across chunks (browser-entry graph + client-reference graph). With
|
|
31
|
+
* ESM output, each chunk gets its own module scope — module-level
|
|
32
|
+
* variables would create separate singleton instances per chunk.
|
|
33
|
+
* globalThis guarantees a single instance regardless of duplication.
|
|
34
|
+
*
|
|
35
|
+
* This workaround will be removed when Rolldown ships `format: 'app'`
|
|
36
|
+
* (module registry format that deduplicates like webpack/Turbopack).
|
|
31
37
|
* See design/27-chunking-strategy.md.
|
|
32
38
|
*
|
|
33
39
|
* See design/19-client-navigation.md §"NavigationContext"
|
|
@@ -52,17 +58,35 @@ export interface NavigationState {
|
|
|
52
58
|
* The context is created lazily to avoid calling createContext at module
|
|
53
59
|
* level. In the RSC environment, React.createContext doesn't exist —
|
|
54
60
|
* calling it at import time would crash the server.
|
|
61
|
+
*
|
|
62
|
+
* Context instances are stored on globalThis (NOT in module-level
|
|
63
|
+
* variables) because the ESM bundler can duplicate this module across
|
|
64
|
+
* chunks. Module-level variables would create separate instances per
|
|
65
|
+
* chunk — the provider in NavigationRoot (index chunk) would use
|
|
66
|
+
* context A while the consumer in usePendingNavigation (shared chunk)
|
|
67
|
+
* reads from context B. globalThis guarantees a single instance.
|
|
68
|
+
*
|
|
69
|
+
* See design/27-chunking-strategy.md §"Singleton Safety"
|
|
70
|
+
*
|
|
71
|
+
* NOTE: Despite similar naming, `usePendingNavigationUrl()` here is an
|
|
72
|
+
* internal helper — the public hook is `usePendingNavigation()` in
|
|
73
|
+
* use-pending-navigation.ts.
|
|
55
74
|
*/
|
|
56
75
|
|
|
57
|
-
|
|
58
|
-
|
|
76
|
+
// Symbol keys for globalThis storage — prevents collisions with user code
|
|
77
|
+
const NAV_CTX_KEY = Symbol.for('__timber_nav_ctx');
|
|
78
|
+
const PENDING_CTX_KEY = Symbol.for('__timber_pending_nav_ctx');
|
|
59
79
|
|
|
60
80
|
function getOrCreateContext(): React.Context<NavigationState | null> | undefined {
|
|
61
|
-
|
|
81
|
+
const existing = (globalThis as Record<symbol, unknown>)[NAV_CTX_KEY] as
|
|
82
|
+
| React.Context<NavigationState | null>
|
|
83
|
+
| undefined;
|
|
84
|
+
if (existing !== undefined) return existing;
|
|
62
85
|
// createContext may not exist in the RSC environment
|
|
63
86
|
if (typeof React.createContext === 'function') {
|
|
64
|
-
|
|
65
|
-
|
|
87
|
+
const ctx = React.createContext<NavigationState | null>(null);
|
|
88
|
+
(globalThis as Record<symbol, unknown>)[NAV_CTX_KEY] = ctx;
|
|
89
|
+
return ctx;
|
|
66
90
|
}
|
|
67
91
|
return undefined;
|
|
68
92
|
}
|
|
@@ -70,7 +94,7 @@ function getOrCreateContext(): React.Context<NavigationState | null> | undefined
|
|
|
70
94
|
/**
|
|
71
95
|
* Read the navigation context. Returns null during SSR (no provider)
|
|
72
96
|
* or in the RSC environment (no context available).
|
|
73
|
-
* Internal — used by
|
|
97
|
+
* Internal — used by useSegmentParams() and usePathname().
|
|
74
98
|
*/
|
|
75
99
|
export function useNavigationContext(): NavigationState | null {
|
|
76
100
|
const ctx = getOrCreateContext();
|
|
@@ -119,15 +143,27 @@ export function NavigationProvider({
|
|
|
119
143
|
* NavigationProvider with the correct params/pathname.
|
|
120
144
|
*
|
|
121
145
|
* This is NOT used by hooks directly — hooks read from React context.
|
|
146
|
+
*
|
|
147
|
+
* Stored on globalThis (like the context instances above) because the
|
|
148
|
+
* router lives in one chunk while renderRoot lives in another. Module-
|
|
149
|
+
* level variables would be separate per chunk.
|
|
122
150
|
*/
|
|
123
|
-
|
|
151
|
+
const NAV_STATE_KEY = Symbol.for('__timber_nav_state');
|
|
152
|
+
|
|
153
|
+
function _getNavStateStore(): { current: NavigationState } {
|
|
154
|
+
const g = globalThis as Record<symbol, unknown>;
|
|
155
|
+
if (!g[NAV_STATE_KEY]) {
|
|
156
|
+
g[NAV_STATE_KEY] = { current: { params: {}, pathname: '/' } };
|
|
157
|
+
}
|
|
158
|
+
return g[NAV_STATE_KEY] as { current: NavigationState };
|
|
159
|
+
}
|
|
124
160
|
|
|
125
161
|
export function setNavigationState(state: NavigationState): void {
|
|
126
|
-
|
|
162
|
+
_getNavStateStore().current = state;
|
|
127
163
|
}
|
|
128
164
|
|
|
129
165
|
export function getNavigationState(): NavigationState {
|
|
130
|
-
return
|
|
166
|
+
return _getNavStateStore().current;
|
|
131
167
|
}
|
|
132
168
|
|
|
133
169
|
// ---------------------------------------------------------------------------
|
|
@@ -136,15 +172,24 @@ export function getNavigationState(): NavigationState {
|
|
|
136
172
|
|
|
137
173
|
/**
|
|
138
174
|
* Separate context for the in-flight navigation URL. Provided by
|
|
139
|
-
*
|
|
140
|
-
* and
|
|
175
|
+
* NavigationRoot (urgent useState), consumed by usePendingNavigation
|
|
176
|
+
* and TopLoader. Per-link pending state uses useOptimistic instead
|
|
177
|
+
* (see link-pending-store.ts).
|
|
178
|
+
*
|
|
179
|
+
* Uses globalThis via Symbol.for for the same reason as NavigationContext
|
|
180
|
+
* above — the bundler may duplicate this module across chunks, and module-
|
|
181
|
+
* level variables would create separate context instances.
|
|
141
182
|
*/
|
|
142
183
|
|
|
143
184
|
function getOrCreatePendingContext(): React.Context<string | null> | undefined {
|
|
144
|
-
|
|
185
|
+
const existing = (globalThis as Record<symbol, unknown>)[PENDING_CTX_KEY] as
|
|
186
|
+
| React.Context<string | null>
|
|
187
|
+
| undefined;
|
|
188
|
+
if (existing !== undefined) return existing;
|
|
145
189
|
if (typeof React.createContext === 'function') {
|
|
146
|
-
|
|
147
|
-
|
|
190
|
+
const ctx = React.createContext<string | null>(null);
|
|
191
|
+
(globalThis as Record<symbol, unknown>)[PENDING_CTX_KEY] = ctx;
|
|
192
|
+
return ctx;
|
|
148
193
|
}
|
|
149
194
|
return undefined;
|
|
150
195
|
}
|
|
@@ -177,3 +222,23 @@ export function PendingNavigationProvider({
|
|
|
177
222
|
}
|
|
178
223
|
return createElement(ctx.Provider, { value }, children);
|
|
179
224
|
}
|
|
225
|
+
|
|
226
|
+
// ---------------------------------------------------------------------------
|
|
227
|
+
// Navigation API transition state (optional progressive enhancement)
|
|
228
|
+
// ---------------------------------------------------------------------------
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Check if the browser's Navigation API has an active transition.
|
|
232
|
+
*
|
|
233
|
+
* When the Navigation API is available and a navigation has been intercepted
|
|
234
|
+
* via event.intercept(), `navigation.transition` is non-null until the
|
|
235
|
+
* handler resolves. This provides browser-native progress tracking that
|
|
236
|
+
* can be used alongside the existing pendingUrl mechanism.
|
|
237
|
+
*
|
|
238
|
+
* Returns false when Navigation API is unavailable or no transition is active.
|
|
239
|
+
*/
|
|
240
|
+
export function hasNativeNavigationTransition(): boolean {
|
|
241
|
+
if (typeof window === 'undefined') return false;
|
|
242
|
+
const nav = (window as unknown as { navigation?: { transition?: unknown } }).navigation;
|
|
243
|
+
return nav?.transition != null;
|
|
244
|
+
}
|