@timber-js/app 0.2.0-alpha.16 → 0.2.0-alpha.161
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/agent-skill.md +344 -0
- package/dist/_chunks/actions-cjklt63G.js +317 -0
- package/dist/_chunks/actions-cjklt63G.js.map +1 -0
- package/dist/_chunks/build-output-helper-DF5uScqS.js +158 -0
- package/dist/_chunks/build-output-helper-DF5uScqS.js.map +1 -0
- package/dist/_chunks/cache-api-CzYUlgXA.js +1125 -0
- package/dist/_chunks/cache-api-CzYUlgXA.js.map +1 -0
- package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js → canonicalize-P41GR6tY.js} +94 -18
- package/dist/_chunks/canonicalize-P41GR6tY.js.map +1 -0
- package/dist/_chunks/cli-schema-sync-NfLbLnDw.js +638 -0
- package/dist/_chunks/cli-schema-sync-NfLbLnDw.js.map +1 -0
- package/dist/_chunks/codec-registry-oOUxugz3.js +21 -0
- package/dist/_chunks/codec-registry-oOUxugz3.js.map +1 -0
- package/dist/_chunks/define-Bssfp6ot.js +82 -0
- package/dist/_chunks/define-Bssfp6ot.js.map +1 -0
- package/dist/_chunks/define-CFmvb4Bt.js +203 -0
- package/dist/_chunks/define-CFmvb4Bt.js.map +1 -0
- package/dist/_chunks/dist-BA3u1z3W.js +423 -0
- package/dist/_chunks/dist-BA3u1z3W.js.map +1 -0
- package/dist/_chunks/fast-hash-D6hIVt1Y.js +36 -0
- package/dist/_chunks/fast-hash-D6hIVt1Y.js.map +1 -0
- package/dist/_chunks/format-CfwjgPz9.js +14 -0
- package/dist/_chunks/format-CfwjgPz9.js.map +1 -0
- package/dist/_chunks/href-validation-CMc5JRls.js +141 -0
- package/dist/_chunks/href-validation-CMc5JRls.js.map +1 -0
- package/dist/_chunks/logger-B_O6-mdJ.js +1485 -0
- package/dist/_chunks/logger-B_O6-mdJ.js.map +1 -0
- package/dist/_chunks/navigation-root-bUDZLw74.js +124 -0
- package/dist/_chunks/navigation-root-bUDZLw74.js.map +1 -0
- package/dist/_chunks/plugin-context-DeAxFRMq.js +280 -0
- package/dist/_chunks/plugin-context-DeAxFRMq.js.map +1 -0
- package/dist/_chunks/registry-DbJPKoBp.js +20 -0
- package/dist/_chunks/registry-DbJPKoBp.js.map +1 -0
- package/dist/_chunks/resolve-schema-3iUvBV5T.js +50 -0
- package/dist/_chunks/resolve-schema-3iUvBV5T.js.map +1 -0
- package/dist/_chunks/router-ref-CtmF-aPv.js +28 -0
- package/dist/_chunks/router-ref-CtmF-aPv.js.map +1 -0
- package/dist/_chunks/schema-bridge-BY3QLBL7.js +119 -0
- package/dist/_chunks/schema-bridge-BY3QLBL7.js.map +1 -0
- package/dist/_chunks/segment-classify-CDDRVKs7.js +137 -0
- package/dist/_chunks/segment-classify-CDDRVKs7.js.map +1 -0
- package/dist/_chunks/segment-context-ZDnXDkbz.js +34 -0
- package/dist/_chunks/segment-context-ZDnXDkbz.js.map +1 -0
- package/dist/_chunks/sizeof-lXffQ1Vr.js +35 -0
- package/dist/_chunks/sizeof-lXffQ1Vr.js.map +1 -0
- package/dist/_chunks/{ssr-data-MjmprTmO.js → ssr-data-BOWsq18U.js} +1 -1
- package/dist/_chunks/ssr-data-BOWsq18U.js.map +1 -0
- package/dist/_chunks/tree-match-D2l830j2.js +102 -0
- package/dist/_chunks/tree-match-D2l830j2.js.map +1 -0
- package/dist/_chunks/{use-query-states-D5KaffOK.js → use-query-states-CbeQmext.js} +7 -20
- package/dist/_chunks/use-query-states-CbeQmext.js.map +1 -0
- package/dist/_chunks/use-segment-params-D1CzlgBo.js +151 -0
- package/dist/_chunks/use-segment-params-D1CzlgBo.js.map +1 -0
- package/dist/_chunks/walkers-9mz9T7mb.js +988 -0
- package/dist/_chunks/walkers-9mz9T7mb.js.map +1 -0
- package/dist/adapters/build-output-helper.d.ts +29 -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 +117 -0
- package/dist/adapters/cloudflare-kv-cache.js.map +1 -0
- package/dist/adapters/cloudflare.d.ts +177 -20
- package/dist/adapters/cloudflare.d.ts.map +1 -1
- package/dist/adapters/cloudflare.js +337 -64
- package/dist/adapters/cloudflare.js.map +1 -1
- package/dist/adapters/compress-module.d.ts +5 -4
- package/dist/adapters/compress-module.d.ts.map +1 -1
- package/dist/adapters/nitro.d.ts +1 -3
- package/dist/adapters/nitro.d.ts.map +1 -1
- package/dist/adapters/nitro.js +144 -152
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/adapters/shared.d.ts +6 -0
- package/dist/adapters/shared.d.ts.map +1 -0
- package/dist/adapters/types.d.ts +0 -6
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/cache/cache-api.d.ts +26 -0
- package/dist/cache/cache-api.d.ts.map +1 -0
- package/dist/cache/handler-store.d.ts +33 -0
- package/dist/cache/handler-store.d.ts.map +1 -0
- package/dist/cache/index.d.ts +27 -7
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +3 -326
- package/dist/cache/invalidation-epoch.d.ts +43 -0
- package/dist/cache/invalidation-epoch.d.ts.map +1 -0
- package/dist/cache/redis-handler.d.ts +97 -8
- package/dist/cache/redis-handler.d.ts.map +1 -1
- package/dist/cache/singleflight.d.ts +19 -2
- 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/stable-stringify.d.ts +9 -0
- package/dist/cache/stable-stringify.d.ts.map +1 -1
- package/dist/cache/store.d.ts +53 -0
- package/dist/cache/store.d.ts.map +1 -0
- package/dist/cache/stores/cloudflare-kv.d.ts +38 -0
- package/dist/cache/stores/cloudflare-kv.d.ts.map +1 -0
- package/dist/cache/stores/cloudflare-kv.js +29 -0
- package/dist/cache/stores/cloudflare-kv.js.map +1 -0
- package/dist/cache/stores/memory.d.ts +42 -0
- package/dist/cache/stores/memory.d.ts.map +1 -0
- package/dist/cache/stores/memory.js +90 -0
- package/dist/cache/stores/memory.js.map +1 -0
- package/dist/cache/stores/redis.d.ts +69 -0
- package/dist/cache/stores/redis.d.ts.map +1 -0
- package/dist/cache/stores/redis.js +35 -0
- package/dist/cache/stores/redis.js.map +1 -0
- package/dist/cache/stores/vercel.d.ts +36 -0
- package/dist/cache/stores/vercel.d.ts.map +1 -0
- package/dist/cache/stores/vercel.js +33 -0
- package/dist/cache/stores/vercel.js.map +1 -0
- package/dist/cache/tag-aware-handler.d.ts +51 -0
- package/dist/cache/tag-aware-handler.d.ts.map +1 -0
- package/dist/cache/timber-cache.d.ts +20 -10
- package/dist/cache/timber-cache.d.ts.map +1 -1
- package/dist/cdn/cloudflare-purge.d.ts +23 -0
- package/dist/cdn/cloudflare-purge.d.ts.map +1 -0
- package/dist/cdn/fastly-purge.d.ts +25 -0
- package/dist/cdn/fastly-purge.d.ts.map +1 -0
- package/dist/cdn/headers.d.ts +97 -0
- package/dist/cdn/headers.d.ts.map +1 -0
- package/dist/cdn/index.d.ts +5 -0
- package/dist/cdn/index.d.ts.map +1 -0
- package/dist/cdn/purge-store.d.ts +23 -0
- package/dist/cdn/purge-store.d.ts.map +1 -0
- package/dist/cdn/workers-cache-purge.d.ts +16 -0
- package/dist/cdn/workers-cache-purge.d.ts.map +1 -0
- package/dist/cli-schema-sync.d.ts +59 -0
- package/dist/cli-schema-sync.d.ts.map +1 -0
- package/dist/cli.d.ts +34 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +107 -33
- package/dist/cli.js.map +1 -1
- package/dist/client/browser-dev.d.ts +37 -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 +29 -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 +26 -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 +28 -0
- package/dist/client/browser-entry/scroll.d.ts.map +1 -0
- package/dist/client/child-segment-context.d.ts +22 -0
- package/dist/client/child-segment-context.d.ts.map +1 -0
- package/dist/client/child-segment-outlet.d.ts +18 -0
- package/dist/client/child-segment-outlet.d.ts.map +1 -0
- package/dist/client/child-segment-provider.d.ts +21 -0
- package/dist/client/child-segment-provider.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 +47 -10
- package/dist/client/error-boundary.js.map +1 -1
- package/dist/client/error-reconstituter.d.ts +61 -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 +11 -6
- package/dist/client/history.d.ts.map +1 -1
- package/dist/client/index.d.ts +10 -24
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +182 -1134
- 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 +825 -0
- package/dist/client/internal.js.map +1 -0
- package/dist/client/link.d.ts +69 -60
- package/dist/client/link.d.ts.map +1 -1
- package/dist/client/navigation-api.d.ts +120 -0
- package/dist/client/navigation-api.d.ts.map +1 -0
- package/dist/client/navigation-context.d.ts +3 -2
- package/dist/client/navigation-context.d.ts.map +1 -1
- package/dist/client/navigation-root.d.ts +119 -0
- package/dist/client/navigation-root.d.ts.map +1 -0
- package/dist/client/nuqs-adapter.d.ts +1 -1
- 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 +87 -14
- package/dist/client/router.d.ts.map +1 -1
- package/dist/client/rsc-fetch.d.ts +47 -14
- package/dist/client/rsc-fetch.d.ts.map +1 -1
- package/dist/client/segment-cache.d.ts +4 -4
- package/dist/client/segment-cache.d.ts.map +1 -1
- package/dist/client/segment-outlet.d.ts +49 -0
- package/dist/client/segment-outlet.d.ts.map +1 -0
- package/dist/client/segment-update-context.d.ts +21 -0
- package/dist/client/segment-update-context.d.ts.map +1 -0
- package/dist/client/shallow-url.d.ts +10 -0
- package/dist/client/shallow-url.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/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 +12 -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-pathname.d.ts.map +1 -1
- package/dist/client/use-pending-navigation.d.ts +23 -0
- 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-router.d.ts.map +1 -1
- package/dist/client/use-search-params.d.ts +12 -5
- package/dist/client/use-search-params.d.ts.map +1 -1
- package/dist/client/{use-params.d.ts → use-segment-params.d.ts} +8 -6
- package/dist/client/use-segment-params.d.ts.map +1 -0
- package/dist/codec.d.ts +69 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/codec.js +75 -0
- package/dist/codec.js.map +1 -0
- package/dist/config-types.d.ts +304 -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 +58 -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 +257 -36
- 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 +34 -0
- package/dist/cookies/validation.d.ts.map +1 -0
- package/dist/dev-tools/404-page.d.ts +50 -0
- package/dist/dev-tools/404-page.d.ts.map +1 -0
- package/dist/dev-tools/browser-logs.d.ts +40 -0
- package/dist/dev-tools/browser-logs.d.ts.map +1 -0
- package/dist/dev-tools/dev-page-shell.d.ts +78 -0
- package/dist/dev-tools/dev-page-shell.d.ts.map +1 -0
- package/dist/dev-tools/error-page.d.ts +32 -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/hydration-overlay.d.ts +11 -0
- package/dist/dev-tools/hydration-overlay.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/dev-tools/instrumentation.d.ts +59 -0
- 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/fonts/ast-walk.d.ts +68 -0
- package/dist/fonts/ast-walk.d.ts.map +1 -0
- package/dist/fonts/ast.d.ts +110 -6
- package/dist/fonts/ast.d.ts.map +1 -1
- package/dist/fonts/bundle.d.ts +50 -0
- package/dist/fonts/bundle.d.ts.map +1 -0
- package/dist/fonts/dev-middleware.d.ts +22 -0
- package/dist/fonts/dev-middleware.d.ts.map +1 -0
- package/dist/fonts/fallbacks.d.ts +0 -7
- package/dist/fonts/fallbacks.d.ts.map +1 -1
- package/dist/fonts/google.d.ts +4 -2
- package/dist/fonts/google.d.ts.map +1 -1
- package/dist/fonts/local.d.ts +4 -4
- package/dist/fonts/local.d.ts.map +1 -1
- package/dist/fonts/paths.d.ts +43 -0
- package/dist/fonts/paths.d.ts.map +1 -0
- package/dist/fonts/pipeline.d.ts +158 -0
- package/dist/fonts/pipeline.d.ts.map +1 -0
- package/dist/fonts/transform.d.ts +82 -0
- package/dist/fonts/transform.d.ts.map +1 -0
- package/dist/fonts/types.d.ts +47 -1
- package/dist/fonts/types.d.ts.map +1 -1
- package/dist/fonts/virtual-modules.d.ts +77 -0
- package/dist/fonts/virtual-modules.d.ts.map +1 -0
- package/dist/index.d.ts +46 -180
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8471 -14108
- package/dist/index.js.map +1 -1
- package/dist/params/codec-registry.d.ts +26 -0
- package/dist/params/codec-registry.d.ts.map +1 -0
- package/dist/params/define-schema.d.ts +81 -0
- package/dist/params/define-schema.d.ts.map +1 -0
- package/dist/params/index.d.ts +5 -0
- package/dist/params/index.d.ts.map +1 -0
- package/dist/params/index.js +34 -0
- package/dist/params/index.js.map +1 -0
- package/dist/params/resolve-schema.d.ts +34 -0
- package/dist/params/resolve-schema.d.ts.map +1 -0
- package/dist/plugin-context.d.ts +190 -0
- package/dist/plugin-context.d.ts.map +1 -0
- package/dist/plugins/adapter-build.d.ts +20 -1
- package/dist/plugins/adapter-build.d.ts.map +1 -1
- package/dist/plugins/build-manifest.d.ts +7 -3
- package/dist/plugins/build-manifest.d.ts.map +1 -1
- package/dist/plugins/build-report.d.ts +43 -7
- package/dist/plugins/build-report.d.ts.map +1 -1
- package/dist/plugins/cache.d.ts +64 -0
- package/dist/plugins/cache.d.ts.map +1 -0
- package/dist/plugins/callsite-ast.d.ts +117 -0
- package/dist/plugins/callsite-ast.d.ts.map +1 -0
- package/dist/plugins/callsite-jsx.d.ts +35 -0
- package/dist/plugins/callsite-jsx.d.ts.map +1 -0
- package/dist/plugins/chunks.d.ts +11 -4
- package/dist/plugins/chunks.d.ts.map +1 -1
- package/dist/plugins/client-chunks.d.ts +40 -0
- package/dist/plugins/client-chunks.d.ts.map +1 -0
- package/dist/plugins/cloudflare-wasm.d.ts +21 -0
- package/dist/plugins/cloudflare-wasm.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 +17 -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 +16 -73
- 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/prebuilt-capture.d.ts +60 -0
- package/dist/plugins/prebuilt-capture.d.ts.map +1 -0
- package/dist/plugins/prebuilt.d.ts +82 -0
- package/dist/plugins/prebuilt.d.ts.map +1 -0
- package/dist/plugins/prerender-sugar.d.ts +42 -0
- package/dist/plugins/prerender-sugar.d.ts.map +1 -0
- 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 +29 -5
- package/dist/plugins/shims.d.ts.map +1 -1
- package/dist/plugins/static-build.d.ts +5 -26
- package/dist/plugins/static-build.d.ts.map +1 -1
- package/dist/routing/codegen-shared.d.ts +67 -0
- package/dist/routing/codegen-shared.d.ts.map +1 -0
- package/dist/routing/codegen-types.d.ts +64 -0
- package/dist/routing/codegen-types.d.ts.map +1 -0
- package/dist/routing/codegen.d.ts +7 -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/export-detect.d.ts +56 -0
- package/dist/routing/export-detect.d.ts.map +1 -0
- package/dist/routing/file-cache.d.ts +8 -0
- package/dist/routing/file-cache.d.ts.map +1 -0
- package/dist/routing/index.d.ts +7 -1
- package/dist/routing/index.d.ts.map +1 -1
- package/dist/routing/index.js +4 -2
- package/dist/routing/interception.d.ts +0 -5
- package/dist/routing/interception.d.ts.map +1 -1
- package/dist/routing/link-codegen.d.ts +90 -0
- package/dist/routing/link-codegen.d.ts.map +1 -0
- package/dist/routing/manifest-codegen.d.ts +32 -0
- package/dist/routing/manifest-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/schema-validation.d.ts +35 -0
- package/dist/routing/schema-validation.d.ts.map +1 -0
- package/dist/routing/segment-classify.d.ts +75 -0
- package/dist/routing/segment-classify.d.ts.map +1 -0
- package/dist/routing/segment-codegen.d.ts +38 -0
- package/dist/routing/segment-codegen.d.ts.map +1 -0
- 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 +91 -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 +37 -440
- 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 +89 -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 +6 -30
- package/dist/server/actions.d.ts.map +1 -1
- package/dist/server/als-registry.d.ts +86 -11
- package/dist/server/als-registry.d.ts.map +1 -1
- package/dist/server/asset-headers.d.ts +8 -18
- package/dist/server/asset-headers.d.ts.map +1 -1
- package/dist/server/body-limits.d.ts.map +1 -1
- package/dist/server/build-manifest.d.ts +6 -29
- package/dist/server/build-manifest.d.ts.map +1 -1
- package/dist/server/canonicalize.d.ts +10 -0
- package/dist/server/canonicalize.d.ts.map +1 -1
- package/dist/server/coalesce-tick.d.ts +40 -0
- package/dist/server/coalesce-tick.d.ts.map +1 -0
- 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 +65 -0
- package/dist/server/cookie-parsing.d.ts.map +1 -0
- package/dist/server/csrf.d.ts +3 -2
- package/dist/server/csrf.d.ts.map +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-boundary.d.ts +115 -0
- package/dist/server/deny-boundary.d.ts.map +1 -0
- package/dist/server/deny-renderer.d.ts +2 -2
- 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 -11
- 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/form-data.d.ts +41 -0
- package/dist/server/form-data.d.ts.map +1 -1
- package/dist/server/html-injector-core.d.ts +221 -0
- package/dist/server/html-injector-core.d.ts.map +1 -0
- package/dist/server/html-injectors.d.ts +82 -41
- package/dist/server/html-injectors.d.ts.map +1 -1
- package/dist/server/index.d.ts +11 -47
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +170 -2911
- package/dist/server/index.js.map +1 -1
- package/dist/server/internal.d.ts +49 -0
- package/dist/server/internal.d.ts.map +1 -0
- package/dist/server/internal.js +3652 -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/metadata-routes.d.ts +23 -7
- package/dist/server/metadata-routes.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 +119 -0
- package/dist/server/node-stream-transforms.d.ts.map +1 -0
- package/dist/server/param-coercion.d.ts +54 -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 +56 -0
- package/dist/server/pipeline-phases.d.ts.map +1 -0
- package/dist/server/pipeline.d.ts +122 -19
- 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/prebuilt/cache-key.d.ts +58 -0
- package/dist/server/prebuilt/cache-key.d.ts.map +1 -0
- package/dist/server/prebuilt/capture-state.d.ts +57 -0
- package/dist/server/prebuilt/capture-state.d.ts.map +1 -0
- package/dist/server/prebuilt/manifest-tag-index.d.ts +27 -0
- package/dist/server/prebuilt/manifest-tag-index.d.ts.map +1 -0
- package/dist/server/prebuilt/overlay.d.ts +100 -0
- package/dist/server/prebuilt/overlay.d.ts.map +1 -0
- package/dist/server/prebuilt/payload-source.d.ts +78 -0
- package/dist/server/prebuilt/payload-source.d.ts.map +1 -0
- package/dist/server/prebuilt/synthetic-store.d.ts +66 -0
- package/dist/server/prebuilt/synthetic-store.d.ts.map +1 -0
- package/dist/server/prebuilt-builder.d.ts +120 -0
- package/dist/server/prebuilt-builder.d.ts.map +1 -0
- package/dist/server/prebuilt-runtime.d.ts +101 -0
- package/dist/server/prebuilt-runtime.d.ts.map +1 -0
- package/dist/server/primitives.d.ts +88 -43
- 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/render-utils.d.ts +25 -0
- package/dist/server/render-utils.d.ts.map +1 -0
- package/dist/server/request-context.d.ts +62 -107
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/route-element-builder.d.ts +46 -25
- 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 +25 -36
- 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 +12 -3
- 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 +51 -28
- package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts +28 -6
- 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/revalidate-renderer.d.ts +108 -0
- package/dist/server/rsc-entry/revalidate-renderer.d.ts.map +1 -0
- package/dist/server/rsc-entry/rsc-payload.d.ts +5 -6
- package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-stream.d.ts +17 -1
- package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-bridge.d.ts +5 -1
- package/dist/server/rsc-entry/ssr-bridge.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-renderer.d.ts +34 -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/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 +7 -5
- package/dist/server/slot-resolver.d.ts.map +1 -1
- package/dist/server/ssr-bridge-types.d.ts +121 -0
- package/dist/server/ssr-bridge-types.d.ts.map +1 -0
- package/dist/server/ssr-entry.d.ts +2 -43
- package/dist/server/ssr-entry.d.ts.map +1 -1
- package/dist/server/ssr-render.d.ts +64 -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 +12 -5
- package/dist/server/state-tree-diff.d.ts.map +1 -1
- package/dist/server/status-code-resolver.d.ts +22 -11
- package/dist/server/status-code-resolver.d.ts.map +1 -1
- package/dist/server/stream-utils.d.ts +47 -0
- package/dist/server/stream-utils.d.ts.map +1 -0
- package/dist/server/tracing.d.ts +44 -9
- 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/tree-match.d.ts +49 -0
- package/dist/server/tree-match.d.ts.map +1 -0
- package/dist/server/types.d.ts +71 -6
- 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/utils/mdx-file.d.ts +15 -0
- package/dist/server/utils/mdx-file.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/workers-cache-bridge.d.ts +26 -0
- package/dist/server/workers-cache-bridge.d.ts.map +1 -0
- package/dist/shared/href-validation.d.ts +49 -0
- package/dist/shared/href-validation.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/shared/redirect-type.d.ts +17 -0
- package/dist/shared/redirect-type.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 +53 -1
- package/dist/shims/headers.d.ts.map +1 -1
- package/dist/shims/image.d.ts +16 -16
- package/dist/shims/navigation-client.d.ts +35 -11
- 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/state-machine.d.ts +80 -0
- package/dist/utils/state-machine.d.ts.map +1 -0
- package/docs/api/30-api-server.mdx +233 -0
- package/docs/api/31-api-client.mdx +161 -0
- package/docs/api/32-api-cache.mdx +199 -0
- package/docs/api/33-api-search-params.mdx +141 -0
- package/docs/api/34-api-config.mdx +189 -0
- package/docs/api/35-api-typescript.mdx +170 -0
- package/docs/api/36-cli.mdx +143 -0
- package/docs/learn/00-introduction.mdx +76 -0
- package/docs/learn/01-quick-start.mdx +94 -0
- package/docs/learn/02-pages-and-layouts.mdx +147 -0
- package/docs/learn/03-fetching-data.mdx +211 -0
- package/docs/learn/03b-access-control.mdx +130 -0
- package/docs/learn/04-loading-states.mdx +67 -0
- package/docs/learn/04b-the-flush-point.mdx +115 -0
- package/docs/learn/05-typed-params.mdx +275 -0
- package/docs/learn/06-forms-and-actions.mdx +146 -0
- package/docs/learn/07-typed-routes.mdx +127 -0
- package/docs/learn/08-streaming.mdx +146 -0
- package/docs/learn/09-caching.mdx +136 -0
- package/docs/learn/10-middleware.mdx +157 -0
- package/docs/learn/11-error-handling.mdx +156 -0
- package/docs/learn/12-client-navigation.mdx +168 -0
- package/docs/learn/13-configuration.mdx +145 -0
- package/docs/learn/14-deploying.mdx +264 -0
- package/docs/more/01-advanced-routing.mdx +139 -0
- package/docs/more/02-advanced-forms.mdx +137 -0
- package/docs/more/03-coming-from-nextjs.mdx +186 -0
- package/docs/more/04-metadata-and-fonts.mdx +193 -0
- package/docs/more/04b-mdx.mdx +209 -0
- package/docs/more/05-content-collections.mdx +90 -0
- package/docs/more/06-instrumentation.mdx +214 -0
- package/docs/more/07-security.mdx +129 -0
- package/docs/more/08-developer-experience.mdx +134 -0
- package/docs/more/40-why-timber.mdx +50 -0
- package/docs/more/41-timber-vs-nextjs.mdx +81 -0
- package/docs/more/42-timber-vs-others.mdx +68 -0
- package/docs/more/50-ai-agent-instructions.mdx +169 -0
- package/llms.txt +55 -0
- package/package.json +103 -26
- package/src/adapters/build-output-helper.ts +199 -0
- package/src/adapters/cloudflare-dev.ts +177 -0
- package/src/adapters/cloudflare-kv-cache.ts +179 -0
- package/src/adapters/cloudflare.ts +571 -118
- package/src/adapters/compress-module.ts +27 -92
- package/src/adapters/nitro.ts +131 -107
- package/src/adapters/raw-imports.d.ts +5 -0
- package/src/adapters/shared.ts +68 -0
- package/src/adapters/types.ts +0 -7
- package/src/adapters/wrangler.d.ts +7 -0
- package/src/cache/cache-api.ts +107 -0
- package/src/cache/handler-store.ts +76 -0
- package/src/cache/index.ts +88 -18
- package/src/cache/invalidation-epoch.ts +93 -0
- package/src/cache/redis-handler.ts +122 -15
- package/src/cache/singleflight.ts +64 -6
- package/src/cache/sizeof.ts +33 -0
- package/src/cache/stable-stringify.ts +65 -2
- package/src/cache/store.ts +71 -0
- package/src/cache/stores/cloudflare-kv.ts +52 -0
- package/src/cache/stores/memory.ts +125 -0
- package/src/cache/stores/redis.ts +97 -0
- package/src/cache/stores/vercel.ts +59 -0
- package/src/cache/tag-aware-handler.ts +233 -0
- package/src/cache/timber-cache.ts +142 -36
- package/src/cdn/cloudflare-purge.ts +50 -0
- package/src/cdn/fastly-purge.ts +63 -0
- package/src/cdn/headers.ts +153 -0
- package/src/cdn/index.ts +17 -0
- package/src/cdn/purge-store.ts +34 -0
- package/src/cdn/workers-cache-purge.ts +30 -0
- package/src/cli-schema-sync.ts +388 -0
- package/src/cli.ts +157 -34
- package/src/client/browser-dev.ts +156 -1
- package/src/client/browser-entry/action-dispatch.ts +124 -0
- package/src/client/browser-entry/hmr.ts +70 -0
- package/src/client/browser-entry/hydrate.ts +187 -0
- package/src/client/browser-entry/index.ts +111 -0
- package/src/client/browser-entry/post-hydration.ts +103 -0
- package/src/client/browser-entry/router-init.ts +287 -0
- package/src/client/browser-entry/rsc-stream.ts +157 -0
- package/src/client/browser-entry/scroll.ts +50 -0
- package/src/client/child-segment-context.ts +40 -0
- package/src/client/child-segment-outlet.tsx +25 -0
- package/src/client/child-segment-provider.tsx +27 -0
- package/src/client/error-boundary.tsx +127 -22
- package/src/client/error-reconstituter.tsx +79 -0
- package/src/client/form.tsx +31 -32
- package/src/client/history.ts +11 -7
- package/src/client/index.ts +67 -45
- package/src/client/internal.ts +58 -0
- package/src/client/link.tsx +346 -150
- package/src/client/navigation-api.ts +341 -0
- package/src/client/navigation-context.ts +15 -7
- package/src/client/navigation-root.tsx +355 -0
- package/src/client/nuqs-adapter.tsx +16 -3
- package/src/client/router-ref.ts +1 -1
- package/src/client/router.ts +482 -166
- package/src/client/rsc-fetch.ts +214 -33
- package/src/client/segment-cache.ts +7 -7
- package/src/client/segment-outlet.tsx +77 -0
- package/src/client/segment-update-context.ts +42 -0
- package/src/client/shallow-url.ts +22 -0
- package/src/client/ssr-data.ts +13 -5
- package/src/client/state.ts +2 -2
- package/src/client/top-loader.tsx +11 -9
- package/src/client/use-cookie.ts +60 -15
- package/src/client/use-link-status.ts +7 -7
- package/src/client/use-pathname.ts +1 -0
- package/src/client/use-pending-navigation.ts +40 -0
- package/src/client/use-query-states.ts +9 -3
- package/src/client/use-router.ts +4 -1
- package/src/client/use-search-params.ts +31 -44
- package/src/client/{use-params.ts → use-segment-params.ts} +9 -6
- package/src/codec.ts +147 -0
- package/src/config-types.ts +302 -0
- package/src/config-validation.ts +328 -0
- package/src/content/index.ts +5 -13
- package/src/cookies/define-cookie.ts +217 -53
- package/src/cookies/index.ts +12 -1
- package/src/cookies/json-cookie.ts +105 -0
- package/src/cookies/validation.ts +192 -0
- package/src/dev-tools/404-page.ts +286 -0
- package/src/dev-tools/browser-logs.ts +55 -0
- package/src/dev-tools/dev-page-shell.ts +260 -0
- package/src/dev-tools/error-page.ts +331 -0
- package/src/dev-tools/holding-server.ts +187 -0
- package/src/dev-tools/hydration-overlay.ts +158 -0
- package/src/dev-tools/index.ts +83 -0
- package/src/dev-tools/instrumentation.ts +213 -0
- package/src/{server/dev-logger.ts → dev-tools/logger.ts} +33 -51
- package/src/{plugins/dev-logs.ts → dev-tools/logs.ts} +9 -5
- package/src/dev-tools/overlay.ts +469 -0
- package/src/dev-tools/stack-classifier.ts +75 -0
- package/src/dev-tools/terminal.ts +182 -0
- package/src/fonts/ast-walk.ts +235 -0
- package/src/fonts/ast.ts +477 -126
- package/src/fonts/bundle.ts +138 -0
- package/src/fonts/dev-middleware.ts +85 -0
- package/src/fonts/fallbacks.ts +0 -23
- package/src/fonts/google.ts +63 -8
- package/src/fonts/local.ts +9 -8
- package/src/fonts/paths.ts +58 -0
- package/src/fonts/pipeline.ts +316 -0
- package/src/fonts/transform.ts +305 -0
- package/src/fonts/types.ts +52 -1
- package/src/fonts/virtual-modules.ts +179 -0
- package/src/index.ts +431 -352
- package/src/params/codec-registry.ts +34 -0
- package/src/params/define-schema.ts +115 -0
- package/src/params/index.ts +17 -0
- package/src/params/resolve-schema.ts +84 -0
- package/src/plugin-context.ts +378 -0
- package/src/plugins/adapter-build.ts +98 -4
- package/src/plugins/build-manifest.ts +168 -23
- package/src/plugins/build-report.ts +253 -35
- package/src/plugins/cache.ts +229 -0
- package/src/plugins/callsite-ast.ts +472 -0
- package/src/plugins/callsite-jsx.ts +243 -0
- package/src/plugins/chunks.ts +74 -5
- package/src/plugins/client-chunks.ts +144 -0
- package/src/plugins/cloudflare-wasm.ts +141 -0
- package/src/plugins/content.ts +1 -1
- package/src/plugins/dev-server.ts +293 -46
- package/src/plugins/entries.ts +129 -8
- package/src/plugins/fonts.ts +199 -664
- package/src/plugins/mdx.ts +47 -10
- package/src/plugins/prebuilt-capture.ts +217 -0
- package/src/plugins/prebuilt.ts +700 -0
- package/src/plugins/prerender-sugar.ts +329 -0
- package/src/plugins/routing.ts +237 -241
- package/src/plugins/server-bundle.ts +97 -12
- package/src/plugins/shims.ts +241 -64
- package/src/plugins/static-build.ts +15 -90
- package/src/routing/codegen-shared.ts +115 -0
- package/src/routing/codegen-types.ts +65 -0
- package/src/routing/codegen.ts +360 -178
- package/src/routing/convention-lint.ts +394 -0
- package/src/routing/export-detect.ts +304 -0
- package/src/routing/file-cache.ts +23 -0
- package/src/routing/index.ts +10 -1
- package/src/routing/interception.ts +3 -22
- package/src/routing/link-codegen.ts +310 -0
- package/src/routing/manifest-codegen.ts +281 -0
- package/src/routing/scanner.ts +248 -188
- package/src/routing/schema-validation.ts +149 -0
- package/src/routing/segment-classify.ts +188 -0
- package/src/routing/segment-codegen.ts +98 -0
- package/src/routing/types.ts +78 -25
- package/src/routing/walkers.ts +90 -0
- package/src/rsc-runtime/rsc.ts +9 -0
- package/src/rsc-runtime/ssr.ts +50 -0
- package/src/rsc-runtime/vendor-types.d.ts +7 -0
- package/src/schema-bridge.ts +252 -0
- package/src/search-params/define.ts +570 -0
- package/src/search-params/index.ts +14 -20
- package/src/search-params/registry.ts +2 -2
- package/src/search-params/wrappers.ts +110 -0
- package/src/segment-params/define.ts +249 -0
- package/src/segment-params/index.ts +9 -0
- package/src/server/access-gate.tsx +84 -34
- package/src/server/action-client.ts +119 -28
- package/src/server/action-encryption.ts +144 -0
- package/src/server/action-handler.ts +123 -33
- package/src/server/actions.ts +48 -54
- package/src/server/als-registry.ts +130 -20
- package/src/server/asset-headers.ts +15 -37
- package/src/server/body-limits.ts +10 -2
- package/src/server/build-manifest.ts +13 -56
- package/src/server/canonicalize.ts +6 -3
- package/src/server/coalesce-tick.ts +75 -0
- package/src/server/compress.ts +34 -7
- package/src/server/cookie-context.ts +484 -0
- package/src/server/cookie-parsing.ts +163 -0
- package/src/server/csrf.ts +39 -6
- package/src/server/default-logger.ts +99 -0
- package/src/server/deny-boundary.ts +311 -0
- package/src/server/deny-renderer.ts +85 -77
- package/src/server/dev-source-map.ts +31 -0
- package/src/server/early-hints-sender.ts +3 -2
- package/src/server/early-hints.ts +37 -27
- package/src/server/error-boundary-wrapper.ts +61 -22
- package/src/server/fallback-error.ts +79 -102
- package/src/server/flight-injection-state.ts +113 -0
- package/src/server/flight-scripts.ts +62 -0
- package/src/server/form-data.ts +124 -12
- package/src/server/html-injector-core.ts +437 -0
- package/src/server/html-injectors.ts +267 -254
- package/src/server/index.ts +31 -185
- package/src/server/internal.ts +181 -0
- package/src/server/logger.ts +72 -36
- package/src/server/metadata-routes.ts +44 -19
- package/src/server/middleware-runner.ts +75 -4
- package/src/server/node-stream-transforms.ts +331 -0
- package/src/server/param-coercion.ts +206 -0
- package/src/server/pipeline-helpers.ts +204 -0
- package/src/server/pipeline-interception.ts +32 -18
- package/src/server/pipeline-metadata.ts +14 -0
- package/src/server/pipeline-outcome.ts +197 -0
- package/src/server/pipeline-phases.ts +538 -0
- package/src/server/pipeline.ts +226 -382
- package/src/server/port-resolution.ts +215 -0
- package/src/server/prebuilt/cache-key.ts +224 -0
- package/src/server/prebuilt/capture-state.ts +89 -0
- package/src/server/prebuilt/manifest-tag-index.ts +46 -0
- package/src/server/prebuilt/overlay.ts +256 -0
- package/src/server/prebuilt/payload-source.ts +201 -0
- package/src/server/prebuilt/synthetic-store.ts +121 -0
- package/src/server/prebuilt-builder.ts +774 -0
- package/src/server/prebuilt-runtime.ts +731 -0
- package/src/server/primitives.ts +160 -83
- package/src/server/render-timeout.ts +108 -0
- package/src/server/render-utils.ts +31 -0
- package/src/server/request-context.ts +168 -337
- package/src/server/route-element-builder.ts +548 -233
- package/src/server/route-handler.ts +31 -4
- package/src/server/route-matcher.ts +144 -277
- package/src/server/rsc-entry/action-middleware-runner.ts +167 -0
- package/src/server/rsc-entry/api-handler.ts +69 -32
- package/src/server/rsc-entry/error-renderer.ts +306 -89
- package/src/server/rsc-entry/helpers.ts +160 -76
- package/src/server/rsc-entry/index.ts +394 -330
- package/src/server/rsc-entry/render-route.ts +288 -0
- package/src/server/rsc-entry/revalidate-renderer.ts +349 -0
- package/src/server/rsc-entry/rsc-payload.ts +74 -35
- package/src/server/rsc-entry/rsc-stream.ts +85 -13
- package/src/server/rsc-entry/ssr-bridge.ts +18 -5
- package/src/server/rsc-entry/ssr-renderer.ts +273 -43
- package/src/server/rsc-entry/wrap-action-dispatch.ts +449 -0
- package/src/server/safe-load.ts +60 -0
- package/src/server/sensitive-fields.ts +230 -0
- package/src/server/server-only-guard-noop.js +3 -0
- package/src/server/sitemap-generator.ts +344 -0
- package/src/server/sitemap-handler.ts +157 -0
- package/src/server/slot-resolver.ts +258 -257
- package/src/server/ssr-bridge-types.ts +124 -0
- package/src/server/ssr-entry.ts +206 -78
- package/src/server/ssr-render.ts +345 -82
- package/src/server/ssr-wrappers.tsx +141 -0
- package/src/server/state-tree-diff.ts +30 -23
- package/src/server/status-code-resolver.ts +115 -130
- package/src/server/stream-utils.ts +286 -0
- package/src/server/tracing.ts +126 -14
- package/src/server/tree-builder.ts +204 -90
- package/src/server/tree-match.ts +220 -0
- package/src/server/types.ts +78 -9
- package/src/server/utils/escape-html.ts +20 -0
- package/src/server/utils/mdx-file.ts +22 -0
- package/src/server/version-skew.ts +104 -0
- package/src/server/workers-cache-bridge.ts +33 -0
- package/src/shared/href-validation.ts +159 -0
- package/src/shared/merge-search-params.ts +55 -0
- package/src/shared/redirect-type.ts +17 -0
- package/src/shims/font-google.ts +1 -1
- package/src/shims/font-local.ts +34 -0
- package/src/shims/headers.ts +122 -1
- package/src/shims/navigation-client.ts +51 -29
- package/src/shims/navigation.ts +14 -7
- package/src/utils/state-machine.ts +111 -0
- package/dist/_chunks/als-registry-B7DbZ2hS.js +0 -41
- package/dist/_chunks/als-registry-B7DbZ2hS.js.map +0 -1
- package/dist/_chunks/debug-B3Gypr3D.js +0 -108
- package/dist/_chunks/debug-B3Gypr3D.js.map +0 -1
- package/dist/_chunks/format-RyoGQL74.js +0 -164
- package/dist/_chunks/format-RyoGQL74.js.map +0 -1
- package/dist/_chunks/interception-BOoWmLUA.js +0 -676
- package/dist/_chunks/interception-BOoWmLUA.js.map +0 -1
- package/dist/_chunks/metadata-routes-Cjmvi3rQ.js.map +0 -1
- package/dist/_chunks/request-context-BQUC8PHn.js +0 -330
- package/dist/_chunks/request-context-BQUC8PHn.js.map +0 -1
- package/dist/_chunks/ssr-data-MjmprTmO.js.map +0 -1
- package/dist/_chunks/tracing-CemImE6h.js +0 -187
- 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/index.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/head.d.ts +0 -16
- package/dist/client/head.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/segment-merger.d.ts +0 -119
- package/dist/client/segment-merger.d.ts.map +0 -1
- package/dist/client/stale-reload.d.ts +0 -44
- package/dist/client/stale-reload.d.ts.map +0 -1
- package/dist/client/transition-root.d.ts +0 -74
- package/dist/client/transition-root.d.ts.map +0 -1
- package/dist/client/use-navigation-pending.d.ts +0 -26
- 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/plugins/server-action-exports.d.ts +0 -26
- package/dist/plugins/server-action-exports.d.ts.map +0 -1
- package/dist/routing/status-file-lint.d.ts +0 -33
- package/dist/routing/status-file-lint.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/client-module-map.d.ts +0 -47
- package/dist/server/client-module-map.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 +0 -29
- package/dist/server/dev-span-processor.d.ts.map +0 -1
- package/dist/server/dev-warnings.d.ts +0 -129
- package/dist/server/dev-warnings.d.ts.map +0 -1
- package/dist/server/flush.d.ts +0 -74
- package/dist/server/flush.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/dist/utils/directive-parser.d.ts +0 -70
- package/dist/utils/directive-parser.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/head.ts +0 -61
- package/src/client/link-status-provider.tsx +0 -30
- package/src/client/segment-merger.ts +0 -339
- package/src/client/stale-reload.ts +0 -87
- package/src/client/transition-root.tsx +0 -166
- package/src/client/use-navigation-pending.ts +0 -38
- 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/plugins/server-action-exports.ts +0 -220
- package/src/routing/status-file-lint.ts +0 -112
- package/src/search-params/analyze.ts +0 -192
- package/src/search-params/builtin-codecs.ts +0 -228
- package/src/search-params/codecs.ts +0 -153
- package/src/search-params/create.ts +0 -321
- package/src/server/client-module-map.ts +0 -58
- package/src/server/dev-fetch-instrumentation.ts +0 -96
- package/src/server/dev-span-processor.ts +0 -78
- package/src/server/dev-warnings.ts +0 -283
- package/src/server/flush.ts +0 -182
- package/src/server/manifest-status-resolver.ts +0 -215
- package/src/server/prerender.ts +0 -139
- package/src/utils/directive-parser.ts +0 -529
package/src/client/router.ts
CHANGED
|
@@ -4,11 +4,20 @@
|
|
|
4
4
|
import { SegmentCache, PrefetchCache, buildSegmentTree } from './segment-cache';
|
|
5
5
|
import type { SegmentInfo } from './segment-cache';
|
|
6
6
|
import { HistoryStack } from './history';
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
import { setCurrentParams } from './use-segment-params.js';
|
|
8
|
+
import {
|
|
9
|
+
setNavigationState,
|
|
10
|
+
getNavigationState,
|
|
11
|
+
type NavigationState,
|
|
12
|
+
} from './navigation-context.js';
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
fetchRscPayload,
|
|
16
|
+
RedirectError,
|
|
17
|
+
ServerErrorResponse,
|
|
18
|
+
VersionSkewError,
|
|
19
|
+
} from './rsc-fetch.js';
|
|
20
|
+
import { setHardNavigating, supersedeNavigationTransitions } from './navigation-root.js';
|
|
12
21
|
import type { FetchResult } from './rsc-fetch.js';
|
|
13
22
|
|
|
14
23
|
// ─── Types ───────────────────────────────────────────────────────
|
|
@@ -18,6 +27,19 @@ export interface NavigationOptions {
|
|
|
18
27
|
scroll?: boolean;
|
|
19
28
|
/** Use replaceState instead of pushState (replaces current history entry) */
|
|
20
29
|
replace?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* @internal AbortSignal from the Navigation API's NavigateEvent.
|
|
32
|
+
* When provided, the signal is linked to the router's per-navigation
|
|
33
|
+
* AbortController so in-flight RSC fetches are cancelled when a new
|
|
34
|
+
* navigation starts.
|
|
35
|
+
*/
|
|
36
|
+
_signal?: AbortSignal;
|
|
37
|
+
/**
|
|
38
|
+
* @internal Skip pushState/replaceState — the Navigation API has already
|
|
39
|
+
* updated the URL via event.intercept(). Used for external navigations
|
|
40
|
+
* intercepted by the navigate event handler.
|
|
41
|
+
*/
|
|
42
|
+
_skipHistory?: boolean;
|
|
21
43
|
}
|
|
22
44
|
|
|
23
45
|
/**
|
|
@@ -31,8 +53,12 @@ export type RscDecoder = (fetchPromise: Promise<Response>) => unknown;
|
|
|
31
53
|
* Function that renders a decoded RSC element tree into the DOM.
|
|
32
54
|
* In production: reactRoot.render(element).
|
|
33
55
|
* In tests: a no-op or mock.
|
|
56
|
+
*
|
|
57
|
+
* Receives the current NavigationState explicitly — no temporal
|
|
58
|
+
* coupling with setNavigationState/getNavigationState. The renderer
|
|
59
|
+
* wraps the element in NavigationProvider with this state.
|
|
34
60
|
*/
|
|
35
|
-
export type RootRenderer = (element: unknown) => void;
|
|
61
|
+
export type RootRenderer = (element: unknown, navState: NavigationState) => void;
|
|
36
62
|
|
|
37
63
|
/**
|
|
38
64
|
* Platform dependencies injected for testability. In production these
|
|
@@ -55,8 +81,6 @@ export interface RouterDeps {
|
|
|
55
81
|
* In tests, this runs the callback synchronously.
|
|
56
82
|
*/
|
|
57
83
|
afterPaint?: (callback: () => void) => void;
|
|
58
|
-
/** Apply resolved head elements (title, meta tags) to the DOM after navigation. */
|
|
59
|
-
applyHead?: (elements: HeadElement[]) => void;
|
|
60
84
|
/**
|
|
61
85
|
* Run a navigation inside a React transition with optimistic pending URL.
|
|
62
86
|
* The pending URL shows immediately (useOptimistic urgent update) and
|
|
@@ -64,14 +88,73 @@ export interface RouterDeps {
|
|
|
64
88
|
*
|
|
65
89
|
* The `perform` callback receives a `wrapPayload` function to wrap the
|
|
66
90
|
* decoded RSC payload with NavigationProvider + NuqsAdapter before
|
|
67
|
-
*
|
|
91
|
+
* NavigationRoot sets it as the new element. The `wrapPayload` function
|
|
92
|
+
* receives the NavigationState explicitly — no temporal coupling with
|
|
93
|
+
* getNavigationState().
|
|
68
94
|
*
|
|
69
95
|
* If not provided (tests), the router falls back to renderRoot.
|
|
70
96
|
*/
|
|
71
97
|
navigateTransition?: (
|
|
72
98
|
pendingUrl: string,
|
|
73
|
-
perform: (
|
|
99
|
+
perform: (
|
|
100
|
+
wrapPayload: (
|
|
101
|
+
payload: unknown,
|
|
102
|
+
navState: NavigationState,
|
|
103
|
+
segmentUpdates?: Map<string, unknown>
|
|
104
|
+
) => unknown
|
|
105
|
+
) => Promise<{ element: unknown; decodePromise: Promise<void> | null }>
|
|
74
106
|
) => Promise<void>;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Whether the Navigation API is active and handling traversals.
|
|
110
|
+
* When true, the popstate handler is a no-op — the Navigation API's
|
|
111
|
+
* navigate event covers back/forward button presses.
|
|
112
|
+
*/
|
|
113
|
+
navigationApiActive?: boolean;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Called around pushState/replaceState to set a flag that prevents
|
|
117
|
+
* the Navigation API's navigate listener from double-handling
|
|
118
|
+
* router-initiated navigations.
|
|
119
|
+
*/
|
|
120
|
+
setRouterNavigating?: (value: boolean) => void;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Save scroll position via the Navigation API's per-entry state.
|
|
124
|
+
* When provided, used instead of history.replaceState for scroll storage.
|
|
125
|
+
*/
|
|
126
|
+
saveNavigationEntryScroll?: (scrollY: number) => void;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Signal that a router-initiated navigation has completed. Resolves the
|
|
130
|
+
* deferred promise that ties the browser's native loading state to the
|
|
131
|
+
* navigation lifecycle. Called in the finally block of navigate/refresh,
|
|
132
|
+
* aligned with when the TopLoader's pendingUrl clears.
|
|
133
|
+
*/
|
|
134
|
+
completeRouterNavigation?: () => void;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Get the current unwrapped RSC payload element. Used for partial
|
|
138
|
+
* navigation: the router re-wraps the same element with new
|
|
139
|
+
* NavigationProvider context so mounted SegmentOutlets are preserved.
|
|
140
|
+
*/
|
|
141
|
+
_getCurrentPayload?: () => unknown;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Initiate a navigation via the Navigation API (`navigation.navigate()`).
|
|
145
|
+
* Fires the navigate event BEFORE committing the URL, allowing Chrome
|
|
146
|
+
* to show its native loading indicator. Falls back to pushState when
|
|
147
|
+
* unavailable.
|
|
148
|
+
*/
|
|
149
|
+
navigationNavigate?: (url: string, replace: boolean) => void;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Scroll the element matching a URL #fragment into view. Returns true
|
|
153
|
+
* when a matching element was found and scrolled. When absent or false,
|
|
154
|
+
* the router falls back to scroll-to-top on forward navigation — same
|
|
155
|
+
* as a full page load with an unknown fragment landing at the top.
|
|
156
|
+
*/
|
|
157
|
+
scrollToHash?: (hash: string) => boolean;
|
|
75
158
|
}
|
|
76
159
|
|
|
77
160
|
export interface RouterInstance {
|
|
@@ -80,7 +163,7 @@ export interface RouterInstance {
|
|
|
80
163
|
/** Full re-render of the current URL — no state tree sent */
|
|
81
164
|
refresh(): Promise<void>;
|
|
82
165
|
/** Handle a popstate event (back/forward button). scrollY is read from history.state. */
|
|
83
|
-
handlePopState(url: string, scrollY?: number): Promise<void>;
|
|
166
|
+
handlePopState(url: string, scrollY?: number, externalSignal?: AbortSignal): Promise<void>;
|
|
84
167
|
/** Whether a navigation is currently in flight */
|
|
85
168
|
isPending(): boolean;
|
|
86
169
|
/** The URL currently being navigated to, or null if idle */
|
|
@@ -94,18 +177,13 @@ export interface RouterInstance {
|
|
|
94
177
|
* Renders the element tree and updates head elements without a server fetch.
|
|
95
178
|
* See design/08-forms-and-actions.md §"Single-Roundtrip Revalidation".
|
|
96
179
|
*/
|
|
97
|
-
applyRevalidation(element: unknown
|
|
180
|
+
applyRevalidation(element: unknown): void;
|
|
98
181
|
/**
|
|
99
182
|
* Populate the segment cache from server-provided segment metadata.
|
|
100
183
|
* Called on initial hydration with segment info embedded in the HTML.
|
|
101
184
|
*/
|
|
102
185
|
initSegmentCache(segments: SegmentInfo[]): void;
|
|
103
|
-
|
|
104
|
-
* Cache segment elements from a decoded RSC element tree.
|
|
105
|
-
* Called on initial hydration to populate the element cache so the
|
|
106
|
-
* first client navigation can use partial payloads.
|
|
107
|
-
*/
|
|
108
|
-
cacheElementTree(element: unknown): void;
|
|
186
|
+
|
|
109
187
|
/** The segment cache (exposed for tests and <Link> prefetch) */
|
|
110
188
|
segmentCache: SegmentCache;
|
|
111
189
|
/** The prefetch cache (exposed for tests and <Link> prefetch) */
|
|
@@ -130,24 +208,83 @@ function isAbortError(error: unknown): boolean {
|
|
|
130
208
|
* Create a router instance. In production, called once at app hydration
|
|
131
209
|
* with real browser APIs. In tests, called with mock dependencies.
|
|
132
210
|
*/
|
|
211
|
+
/**
|
|
212
|
+
* Router navigation phase — discriminated union replacing scattered
|
|
213
|
+
* `pending` + `pendingUrl` boolean flags.
|
|
214
|
+
*
|
|
215
|
+
* - `idle`: No navigation in flight. The committed params/pathname
|
|
216
|
+
* are current.
|
|
217
|
+
* - `navigating`: A fetch or render is in progress. `targetUrl` is
|
|
218
|
+
* the destination being navigated to.
|
|
219
|
+
*/
|
|
220
|
+
export type RouterPhase = { phase: 'idle' } | { phase: 'navigating'; targetUrl: string };
|
|
221
|
+
|
|
133
222
|
export function createRouter(deps: RouterDeps): RouterInstance {
|
|
134
223
|
const segmentCache = new SegmentCache();
|
|
135
224
|
const prefetchCache = new PrefetchCache();
|
|
136
225
|
const historyStack = new HistoryStack();
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
let pending = false;
|
|
140
|
-
let pendingUrl: string | null = null;
|
|
226
|
+
let routerPhase: RouterPhase = { phase: 'idle' };
|
|
141
227
|
const pendingListeners = new Set<(pending: boolean) => void>();
|
|
142
228
|
|
|
229
|
+
// AbortController for the current in-flight navigation.
|
|
230
|
+
// When a new navigation starts, the previous controller is aborted,
|
|
231
|
+
// cancelling any in-progress RSC fetch. This provides automatic
|
|
232
|
+
// cancellation of stale fetches regardless of Navigation API support.
|
|
233
|
+
let currentNavAbort: AbortController | null = null;
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Create a new AbortController for a navigation, superseding any
|
|
237
|
+
* previous in-flight navigation. Optionally links to an external
|
|
238
|
+
* signal (e.g., from the Navigation API's NavigateEvent.signal).
|
|
239
|
+
*
|
|
240
|
+
* Superseding is one operation with three parts:
|
|
241
|
+
* 1. Abort the previous navigation's fetch.
|
|
242
|
+
* 2. Invalidate its render transition so a response that already
|
|
243
|
+
* arrived can't commit a stale tree (NavigationRoot's transId guard).
|
|
244
|
+
* 3. Resolve its Navigation API deferred — the superseded navigation's
|
|
245
|
+
* finally block is staleness-guarded (see TIM-1034) and no longer
|
|
246
|
+
* cleans up after itself, so the browser's native loading state for
|
|
247
|
+
* the dead navigation is cleared here.
|
|
248
|
+
*/
|
|
249
|
+
function createNavAbort(externalSignal?: AbortSignal): AbortController {
|
|
250
|
+
if (currentNavAbort) {
|
|
251
|
+
currentNavAbort.abort();
|
|
252
|
+
supersedeNavigationTransitions();
|
|
253
|
+
deps.completeRouterNavigation?.();
|
|
254
|
+
}
|
|
255
|
+
const controller = new AbortController();
|
|
256
|
+
currentNavAbort = controller;
|
|
257
|
+
|
|
258
|
+
// If an external signal is provided (e.g., Navigation API),
|
|
259
|
+
// forward its abort to our controller.
|
|
260
|
+
if (externalSignal) {
|
|
261
|
+
if (externalSignal.aborted) {
|
|
262
|
+
controller.abort();
|
|
263
|
+
} else {
|
|
264
|
+
externalSignal.addEventListener('abort', () => controller.abort(), { once: true });
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return controller;
|
|
269
|
+
}
|
|
270
|
+
|
|
143
271
|
function setPending(value: boolean, url?: string): void {
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
272
|
+
const next: RouterPhase =
|
|
273
|
+
value && url ? { phase: 'navigating', targetUrl: url } : { phase: 'idle' };
|
|
274
|
+
// Skip no-op updates
|
|
275
|
+
if (
|
|
276
|
+
routerPhase.phase === next.phase &&
|
|
277
|
+
(routerPhase.phase === 'idle' ||
|
|
278
|
+
(routerPhase.phase === 'navigating' &&
|
|
279
|
+
next.phase === 'navigating' &&
|
|
280
|
+
routerPhase.targetUrl === next.targetUrl))
|
|
281
|
+
) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
routerPhase = next;
|
|
148
285
|
// Notify external store listeners (non-React consumers).
|
|
149
286
|
// React-facing pending state is handled by useOptimistic in
|
|
150
|
-
//
|
|
287
|
+
// NavigationRoot via navigateTransition — not this function.
|
|
151
288
|
for (const listener of pendingListeners) {
|
|
152
289
|
listener(value);
|
|
153
290
|
}
|
|
@@ -163,100 +300,124 @@ export function createRouter(deps: RouterDeps): RouterInstance {
|
|
|
163
300
|
}
|
|
164
301
|
|
|
165
302
|
/** Render a decoded RSC payload into the DOM if a renderer is available. */
|
|
166
|
-
function renderPayload(payload: unknown): void {
|
|
303
|
+
function renderPayload(payload: unknown, navState: NavigationState): void {
|
|
167
304
|
if (deps.renderRoot) {
|
|
168
|
-
deps.renderRoot(payload);
|
|
305
|
+
deps.renderRoot(payload, navState);
|
|
169
306
|
}
|
|
170
307
|
}
|
|
171
308
|
|
|
309
|
+
function isPartialNavigation(skippedSegments: string[] | null | undefined): boolean {
|
|
310
|
+
return skippedSegments != null && skippedSegments.length > 0;
|
|
311
|
+
}
|
|
312
|
+
|
|
172
313
|
/**
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
* Returns the merged payload ready for rendering.
|
|
314
|
+
* Build a segment updates map for partial navigation. Identifies the
|
|
315
|
+
* first non-skipped segment and maps it to the payload content.
|
|
176
316
|
*/
|
|
177
|
-
function
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
317
|
+
function buildSegmentUpdates(result: FetchResult): Map<string, unknown> {
|
|
318
|
+
const skipped = new Set(result.skippedSegments!);
|
|
319
|
+
const segmentInfo = result.segmentInfo;
|
|
320
|
+
const updates = new Map<string, unknown>();
|
|
321
|
+
if (segmentInfo) {
|
|
322
|
+
for (const info of segmentInfo) {
|
|
323
|
+
if (!skipped.has(info.segmentId ?? info.path)) {
|
|
324
|
+
// Use segmentId (outlet key) when available — route groups
|
|
325
|
+
// use a disambiguated key like "/(marketing)" instead of "/".
|
|
326
|
+
updates.set(info.segmentId ?? info.path, result.payload);
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
186
330
|
}
|
|
187
|
-
|
|
188
|
-
// Cache segment elements from the (merged) payload for future merges
|
|
189
|
-
cacheSegmentElements(merged, segmentElementCache);
|
|
190
|
-
|
|
191
|
-
return merged;
|
|
331
|
+
return updates;
|
|
192
332
|
}
|
|
193
333
|
|
|
194
334
|
/**
|
|
195
335
|
* Update navigation state (params + pathname) for the next render.
|
|
196
336
|
*
|
|
197
|
-
* Sets
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
337
|
+
* Sets the module-level fallback (for tests and SSR) and the
|
|
338
|
+
* globalThis bridge, then returns the NavigationState so callers
|
|
339
|
+
* can pass it explicitly to renderRoot/wrapPayload — eliminating
|
|
340
|
+
* temporal coupling with getNavigationState().
|
|
201
341
|
*/
|
|
202
342
|
function updateNavigationState(
|
|
203
343
|
params: Record<string, string | string[]> | null | undefined,
|
|
204
344
|
url: string
|
|
205
|
-
):
|
|
345
|
+
): NavigationState {
|
|
206
346
|
const resolvedParams = params ?? {};
|
|
207
347
|
// Module-level fallback for tests (no NavigationProvider) and SSR
|
|
208
348
|
setCurrentParams(resolvedParams);
|
|
209
|
-
//
|
|
210
|
-
const
|
|
211
|
-
|
|
349
|
+
// globalThis bridge — kept for backward compat
|
|
350
|
+
const parsed = new URL(url, 'http://localhost');
|
|
351
|
+
const pathname = parsed.pathname || '/';
|
|
352
|
+
const search = parsed.search;
|
|
353
|
+
const navState: NavigationState = { params: resolvedParams, pathname, search };
|
|
354
|
+
setNavigationState(navState);
|
|
355
|
+
return navState;
|
|
212
356
|
}
|
|
213
357
|
|
|
214
358
|
/**
|
|
215
359
|
* Render a payload via navigateTransition (production) or renderRoot (tests).
|
|
216
|
-
* The perform callback should fetch data, update state, and return the
|
|
217
|
-
*
|
|
218
|
-
*
|
|
360
|
+
* The perform callback should fetch data, update state, and return the
|
|
361
|
+
* FetchResult plus the NavigationState (so it can be passed explicitly
|
|
362
|
+
* to wrapPayload/renderRoot without temporal coupling).
|
|
219
363
|
*/
|
|
220
364
|
async function renderViaTransition(
|
|
221
365
|
url: string,
|
|
222
|
-
perform: () => Promise<FetchResult>
|
|
223
|
-
): Promise<
|
|
366
|
+
perform: () => Promise<FetchResult & { navState: NavigationState }>
|
|
367
|
+
): Promise<void> {
|
|
224
368
|
if (deps.navigateTransition) {
|
|
225
|
-
let headElements: HeadElement[] | null = null;
|
|
226
369
|
await deps.navigateTransition(url, async (wrapPayload) => {
|
|
227
370
|
const result = await perform();
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
371
|
+
|
|
372
|
+
if (isPartialNavigation(result.skippedSegments)) {
|
|
373
|
+
const segmentUpdates = buildSegmentUpdates(result);
|
|
374
|
+
|
|
375
|
+
historyStack.push(url, {
|
|
376
|
+
payload: null,
|
|
377
|
+
params: result.params,
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
// Re-wrap the CURRENT element with new context values.
|
|
381
|
+
// SegmentOutlets read updates from SegmentUpdateContext;
|
|
382
|
+
// NavigationProvider gets new params/pathname.
|
|
383
|
+
const element = wrapPayload(
|
|
384
|
+
deps._getCurrentPayload?.() ?? result.payload,
|
|
385
|
+
result.navState,
|
|
386
|
+
segmentUpdates
|
|
387
|
+
);
|
|
388
|
+
return { element, decodePromise: result.decodePromise };
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Full navigation — empty updates, render the new tree.
|
|
233
392
|
historyStack.push(url, {
|
|
234
|
-
payload:
|
|
235
|
-
headElements: result.headElements,
|
|
393
|
+
payload: result.payload,
|
|
236
394
|
params: result.params,
|
|
237
395
|
});
|
|
238
|
-
|
|
396
|
+
const element = wrapPayload(result.payload, result.navState);
|
|
397
|
+
return { element, decodePromise: result.decodePromise };
|
|
239
398
|
});
|
|
240
|
-
return
|
|
399
|
+
return;
|
|
241
400
|
}
|
|
242
401
|
// Fallback: no transition (tests, no React tree)
|
|
243
402
|
const result = await perform();
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
403
|
+
if (isPartialNavigation(result.skippedSegments)) {
|
|
404
|
+
historyStack.push(url, {
|
|
405
|
+
payload: null,
|
|
406
|
+
params: result.params,
|
|
407
|
+
});
|
|
408
|
+
} else {
|
|
409
|
+
// Resolve thenables before storing/rendering in the fallback path
|
|
410
|
+
// (tests without navigateTransition). In production, React handles
|
|
411
|
+
// thenables via Suspense; in tests, renderRoot expects resolved values.
|
|
412
|
+
const payload =
|
|
413
|
+
result.payload != null && typeof result.payload === 'object' && 'then' in result.payload
|
|
414
|
+
? await (result.payload as PromiseLike<unknown>)
|
|
415
|
+
: result.payload;
|
|
416
|
+
historyStack.push(url, {
|
|
417
|
+
payload,
|
|
418
|
+
params: result.params,
|
|
419
|
+
});
|
|
420
|
+
renderPayload(payload, result.navState);
|
|
260
421
|
}
|
|
261
422
|
}
|
|
262
423
|
|
|
@@ -269,21 +430,47 @@ export function createRouter(deps: RouterDeps): RouterInstance {
|
|
|
269
430
|
}
|
|
270
431
|
}
|
|
271
432
|
|
|
433
|
+
/**
|
|
434
|
+
* Schedule scroll restoration after the next paint and fire the
|
|
435
|
+
* scroll-restored event. Used by navigate, popstate, and refresh.
|
|
436
|
+
*/
|
|
437
|
+
function restoreScrollAfterPaint(scrollY: number): void {
|
|
438
|
+
afterPaint(() => {
|
|
439
|
+
deps.scrollTo(0, scrollY);
|
|
440
|
+
window.dispatchEvent(new Event('timber:scroll-restored'));
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Scroll to the element matching the URL #fragment after paint, falling
|
|
446
|
+
* back to scroll-to-top when no element matches (matching full-page-load
|
|
447
|
+
* behavior for an unknown fragment). Used by forward navigation to a
|
|
448
|
+
* hash-bearing URL (TIM-1035).
|
|
449
|
+
*/
|
|
450
|
+
function scrollToHashAfterPaint(hash: string): void {
|
|
451
|
+
afterPaint(() => {
|
|
452
|
+
if (deps.scrollToHash?.(hash) !== true) {
|
|
453
|
+
deps.scrollTo(0, 0);
|
|
454
|
+
}
|
|
455
|
+
window.dispatchEvent(new Event('timber:scroll-restored'));
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
|
|
272
459
|
/**
|
|
273
460
|
* Core navigation logic shared between the transition and fallback paths.
|
|
274
461
|
* Fetches the RSC payload, updates all state, and returns the result.
|
|
275
462
|
*/
|
|
276
463
|
async function performNavigationFetch(
|
|
277
464
|
url: string,
|
|
278
|
-
options: { replace: boolean }
|
|
279
|
-
): Promise<FetchResult> {
|
|
465
|
+
options: { replace: boolean; commitUrl?: string; signal?: AbortSignal; skipHistory?: boolean }
|
|
466
|
+
): Promise<FetchResult & { navState: NavigationState }> {
|
|
280
467
|
// Check prefetch cache first. PrefetchResult has optional segmentInfo/params
|
|
281
468
|
// fields — normalize to null for FetchResult compatibility.
|
|
282
469
|
const prefetched = prefetchCache.consume(url);
|
|
283
470
|
let result: FetchResult | undefined = prefetched
|
|
284
471
|
? {
|
|
285
472
|
payload: prefetched.payload,
|
|
286
|
-
|
|
473
|
+
decodePromise: null,
|
|
287
474
|
segmentInfo: prefetched.segmentInfo ?? null,
|
|
288
475
|
params: prefetched.params ?? null,
|
|
289
476
|
skippedSegments: prefetched.skippedSegments ?? null,
|
|
@@ -293,19 +480,29 @@ export function createRouter(deps: RouterDeps): RouterInstance {
|
|
|
293
480
|
if (result === undefined) {
|
|
294
481
|
// Fetch RSC payload with state tree for partial rendering.
|
|
295
482
|
// Send current URL for intercepting route resolution (modal pattern).
|
|
296
|
-
const stateTree = segmentCache.serializeStateTree(
|
|
483
|
+
const stateTree = segmentCache.serializeStateTree();
|
|
297
484
|
const rawCurrentUrl = deps.getCurrentUrl();
|
|
298
485
|
const currentUrl = rawCurrentUrl.startsWith('http')
|
|
299
486
|
? new URL(rawCurrentUrl).pathname
|
|
300
487
|
: new URL(rawCurrentUrl, 'http://localhost').pathname;
|
|
301
|
-
result = await fetchRscPayload(url, deps, stateTree, currentUrl);
|
|
488
|
+
result = await fetchRscPayload(url, deps, stateTree, currentUrl, options.signal);
|
|
302
489
|
}
|
|
303
490
|
|
|
304
|
-
// Update the browser history —
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
491
|
+
// Update the browser history — skip when the Navigation API has already
|
|
492
|
+
// updated the URL via event.intercept() (external navigations).
|
|
493
|
+
// The committed URL keeps the #fragment (commitUrl) even though the
|
|
494
|
+
// fetch/history-stack URL is hash-less (TIM-1035).
|
|
495
|
+
if (!options.skipHistory) {
|
|
496
|
+
const commitUrl = options.commitUrl ?? url;
|
|
497
|
+
// Set the router-navigating flag so the Navigation API's navigate
|
|
498
|
+
// listener doesn't double-intercept this pushState/replaceState.
|
|
499
|
+
deps.setRouterNavigating?.(true);
|
|
500
|
+
if (options.replace) {
|
|
501
|
+
deps.replaceState({ timber: true, scrollY: 0 }, '', commitUrl);
|
|
502
|
+
} else {
|
|
503
|
+
deps.pushState({ timber: true, scrollY: 0 }, '', commitUrl);
|
|
504
|
+
}
|
|
505
|
+
deps.setRouterNavigating?.(false);
|
|
309
506
|
}
|
|
310
507
|
|
|
311
508
|
// NOTE: History push is deferred — the merged payload (after segment
|
|
@@ -316,124 +513,230 @@ export function createRouter(deps: RouterDeps): RouterInstance {
|
|
|
316
513
|
// Update the segment cache with the new route's segment tree.
|
|
317
514
|
updateSegmentCache(result.segmentInfo);
|
|
318
515
|
|
|
319
|
-
// Update navigation state
|
|
320
|
-
updateNavigationState(result.params, url);
|
|
516
|
+
// Update navigation state and capture it for explicit passing.
|
|
517
|
+
const navState = updateNavigationState(result.params, url);
|
|
321
518
|
|
|
322
|
-
return result;
|
|
519
|
+
return { ...result, navState };
|
|
323
520
|
}
|
|
324
521
|
|
|
325
522
|
async function navigate(url: string, options: NavigationOptions = {}): Promise<void> {
|
|
326
523
|
const scroll = options.scroll !== false;
|
|
327
524
|
const replace = options.replace === true;
|
|
525
|
+
const externalSignal = options._signal as AbortSignal | undefined;
|
|
526
|
+
const skipHistory = options._skipHistory === true;
|
|
527
|
+
|
|
528
|
+
// Split the #fragment off the navigation URL (TIM-1035). The full URL
|
|
529
|
+
// (with hash) is committed to the address bar; the hash-less URL is used
|
|
530
|
+
// for the RSC fetch (fragments are client-only — keeping it would also
|
|
531
|
+
// swallow the ?_rsc cache-bust param into the fragment) and for
|
|
532
|
+
// history-stack/prefetch keys (popstate lookups use pathname + search).
|
|
533
|
+
const hashIndex = url.indexOf('#');
|
|
534
|
+
const hash = hashIndex === -1 ? '' : url.slice(hashIndex);
|
|
535
|
+
const fetchUrl = hashIndex === -1 ? url : url.slice(0, hashIndex);
|
|
536
|
+
|
|
537
|
+
// Create an abort controller for this navigation. Links to the external
|
|
538
|
+
// signal (Navigation API's event.signal) when provided.
|
|
539
|
+
const navAbort = createNavAbort(externalSignal);
|
|
328
540
|
|
|
329
541
|
// Capture the departing page's scroll position for scroll={false} preservation.
|
|
330
542
|
const currentScrollY = deps.getScrollY();
|
|
331
543
|
|
|
332
|
-
// Save the departing page's scroll position
|
|
333
|
-
//
|
|
334
|
-
|
|
335
|
-
|
|
544
|
+
// Save the departing page's scroll position — use Navigation API entry
|
|
545
|
+
// state when available, otherwise fall back to history.state.
|
|
546
|
+
if (deps.saveNavigationEntryScroll) {
|
|
547
|
+
deps.saveNavigationEntryScroll(currentScrollY);
|
|
548
|
+
} else {
|
|
549
|
+
deps.replaceState({ timber: true, scrollY: currentScrollY }, '', deps.getCurrentUrl());
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// When Navigation API is active, initiate the navigation via
|
|
553
|
+
// navigation.navigate() BEFORE the fetch. Unlike history.pushState()
|
|
554
|
+
// which commits the URL synchronously (so Chrome sees it as "done"),
|
|
555
|
+
// navigation.navigate() fires the navigate event before committing.
|
|
556
|
+
// Our handler intercepts with a deferred promise, and Chrome shows
|
|
557
|
+
// its native loading indicator until completeRouterNavigation()
|
|
558
|
+
// resolves it in the finally block (same time as TopLoader clears).
|
|
559
|
+
let effectiveSkipHistory = skipHistory;
|
|
560
|
+
if (!skipHistory && deps.navigationNavigate) {
|
|
561
|
+
deps.setRouterNavigating?.(true);
|
|
562
|
+
deps.navigationNavigate(url, replace);
|
|
563
|
+
deps.setRouterNavigating?.(false);
|
|
564
|
+
effectiveSkipHistory = true;
|
|
565
|
+
}
|
|
336
566
|
|
|
337
567
|
setPending(true, url);
|
|
338
568
|
|
|
339
569
|
try {
|
|
340
|
-
|
|
341
|
-
performNavigationFetch(
|
|
570
|
+
await renderViaTransition(fetchUrl, () =>
|
|
571
|
+
performNavigationFetch(fetchUrl, {
|
|
572
|
+
replace,
|
|
573
|
+
commitUrl: url,
|
|
574
|
+
signal: navAbort.signal,
|
|
575
|
+
skipHistory: effectiveSkipHistory,
|
|
576
|
+
})
|
|
342
577
|
);
|
|
343
578
|
|
|
344
|
-
// Update document.title and <meta> tags with the new page's metadata
|
|
345
|
-
applyHead(headElements);
|
|
346
|
-
|
|
347
579
|
// Notify nuqs adapter (and any other listeners) that navigation completed.
|
|
348
580
|
window.dispatchEvent(new Event('timber:navigation-end'));
|
|
349
581
|
|
|
350
|
-
// Scroll-to-top on forward navigation,
|
|
351
|
-
//
|
|
352
|
-
//
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
window.dispatchEvent(new Event('timber:scroll-restored'));
|
|
360
|
-
});
|
|
582
|
+
// Scroll-to-top on forward navigation, scroll to the #fragment target
|
|
583
|
+
// when the URL has one, or restore captured position for scroll={false}.
|
|
584
|
+
// React's render() on the document root can reset scroll during DOM
|
|
585
|
+
// reconciliation, so all scroll must be actively managed.
|
|
586
|
+
if (scroll && hash) {
|
|
587
|
+
scrollToHashAfterPaint(hash);
|
|
588
|
+
} else {
|
|
589
|
+
restoreScrollAfterPaint(scroll ? 0 : currentScrollY);
|
|
590
|
+
}
|
|
361
591
|
} catch (error) {
|
|
592
|
+
// Version skew — server has been redeployed. Full page reload
|
|
593
|
+
// so the browser fetches the new bundle. See TIM-446.
|
|
594
|
+
if (error instanceof VersionSkewError) {
|
|
595
|
+
setHardNavigating(true);
|
|
596
|
+
window.location.reload();
|
|
597
|
+
return new Promise(() => {}) as never;
|
|
598
|
+
}
|
|
362
599
|
// Server-side redirect during RSC fetch → soft router navigation.
|
|
600
|
+
// The redirect navigate will push/replace its own URL.
|
|
363
601
|
if (error instanceof RedirectError) {
|
|
364
|
-
|
|
602
|
+
// Drop the redirect if a newer navigation superseded this one
|
|
603
|
+
// while the redirect response was in flight — the newer
|
|
604
|
+
// navigation wins, same as the stale-transition guard.
|
|
605
|
+
if (currentNavAbort !== navAbort) return;
|
|
606
|
+
// The recursive navigate() supersedes this one via createNavAbort,
|
|
607
|
+
// which also resolves this navigation's Navigation API deferred.
|
|
365
608
|
await navigate(error.redirectUrl, { replace: true });
|
|
366
609
|
return;
|
|
367
610
|
}
|
|
611
|
+
// Server 5xx error — hard-navigate so the server renders the
|
|
612
|
+
// error page as HTML. See design/10-error-handling.md
|
|
613
|
+
// §"Error Page Rendering for Client Navigation".
|
|
614
|
+
//
|
|
615
|
+
// Set hard-navigating flag BEFORE setting window.location.href:
|
|
616
|
+
// 1. Prevents Navigation API from intercepting → infinite loop
|
|
617
|
+
// 2. Causes NavigationRoot to throw unresolvedThenable → prevents
|
|
618
|
+
// React from rendering children during page teardown (avoids
|
|
619
|
+
// "Rendered more hooks" crashes). See TIM-626.
|
|
620
|
+
if (error instanceof ServerErrorResponse) {
|
|
621
|
+
setHardNavigating(true);
|
|
622
|
+
window.location.href = error.url;
|
|
623
|
+
return new Promise(() => {}) as never;
|
|
624
|
+
}
|
|
368
625
|
// Abort errors are not application errors — swallow silently.
|
|
369
626
|
if (isAbortError(error)) return;
|
|
370
627
|
throw error;
|
|
371
628
|
} finally {
|
|
372
|
-
|
|
629
|
+
// Staleness guard (TIM-1034): only clean up when this navigation
|
|
630
|
+
// still owns the abort controller. When a newer navigation
|
|
631
|
+
// superseded this one, createNavAbort() already replaced
|
|
632
|
+
// currentNavAbort — running setPending(false) /
|
|
633
|
+
// completeRouterNavigation() here would clear the NEWER
|
|
634
|
+
// navigation's pending state and resolve its Navigation API
|
|
635
|
+
// deferred. The superseding navigation resolved OUR deferred in
|
|
636
|
+
// createNavAbort().
|
|
637
|
+
if (currentNavAbort === navAbort) {
|
|
638
|
+
// Clear the abort controller so we don't abort a completed
|
|
639
|
+
// navigation when the next one starts. In dev mode, the RSC body
|
|
640
|
+
// stream stays open after data arrives (React's Flight client
|
|
641
|
+
// waits for debug rows). Aborting a "completed" navigation kills
|
|
642
|
+
// the open stream reader → "BodyStreamBuffer was aborted".
|
|
643
|
+
currentNavAbort = null;
|
|
644
|
+
setPending(false);
|
|
645
|
+
// Resolve the Navigation API deferred — clears the browser's
|
|
646
|
+
// native loading state (tab spinner) at the same time as the
|
|
647
|
+
// TopLoader.
|
|
648
|
+
deps.completeRouterNavigation?.();
|
|
649
|
+
}
|
|
373
650
|
}
|
|
374
651
|
}
|
|
375
652
|
|
|
376
653
|
async function refresh(): Promise<void> {
|
|
377
654
|
const currentUrl = deps.getCurrentUrl();
|
|
655
|
+
const navAbort = createNavAbort();
|
|
378
656
|
|
|
379
657
|
setPending(true, currentUrl);
|
|
380
658
|
|
|
381
659
|
try {
|
|
382
|
-
|
|
660
|
+
await renderViaTransition(currentUrl, async () => {
|
|
383
661
|
// No state tree sent — server renders the complete RSC payload
|
|
384
|
-
const result = await fetchRscPayload(
|
|
662
|
+
const result = await fetchRscPayload(
|
|
663
|
+
currentUrl,
|
|
664
|
+
deps,
|
|
665
|
+
undefined,
|
|
666
|
+
undefined,
|
|
667
|
+
navAbort.signal
|
|
668
|
+
);
|
|
385
669
|
// History push handled by renderViaTransition (stores merged payload)
|
|
386
670
|
updateSegmentCache(result.segmentInfo);
|
|
387
|
-
updateNavigationState(result.params, currentUrl);
|
|
388
|
-
return result;
|
|
671
|
+
const navState = updateNavigationState(result.params, currentUrl);
|
|
672
|
+
return { ...result, navState };
|
|
389
673
|
});
|
|
390
|
-
|
|
391
|
-
|
|
674
|
+
} catch (error) {
|
|
675
|
+
if (isAbortError(error)) return;
|
|
676
|
+
throw error;
|
|
392
677
|
} finally {
|
|
393
|
-
|
|
678
|
+
// Staleness guard (TIM-1034) — see navigate()'s finally block.
|
|
679
|
+
if (currentNavAbort === navAbort) {
|
|
680
|
+
currentNavAbort = null;
|
|
681
|
+
setPending(false);
|
|
682
|
+
deps.completeRouterNavigation?.();
|
|
683
|
+
}
|
|
394
684
|
}
|
|
395
685
|
}
|
|
396
686
|
|
|
397
|
-
async function handlePopState(
|
|
687
|
+
async function handlePopState(
|
|
688
|
+
url: string,
|
|
689
|
+
scrollY: number = 0,
|
|
690
|
+
externalSignal?: AbortSignal
|
|
691
|
+
): Promise<void> {
|
|
398
692
|
// Scroll position is read from history.state by the caller (browser-entry.ts)
|
|
399
693
|
// and passed in. This is more reliable than tracking scroll per-URL in memory
|
|
400
694
|
// because the browser maintains per-entry state even with duplicate URLs.
|
|
401
695
|
const entry = historyStack.get(url);
|
|
402
696
|
|
|
403
697
|
if (entry && entry.payload !== null) {
|
|
404
|
-
// Replay cached payload — no server roundtrip
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
698
|
+
// Replay cached payload — no server roundtrip.
|
|
699
|
+
//
|
|
700
|
+
// A forward navigation may still be in flight (back pressed while a
|
|
701
|
+
// slow RSC fetch was pending). createNavAbort() supersedes it: aborts
|
|
702
|
+
// its fetch and invalidates its render transition so the stale
|
|
703
|
+
// forward payload can't commit over this replay (TIM-1022). The
|
|
704
|
+
// replay itself is synchronous — nothing to abort later — so the
|
|
705
|
+
// controller is cleared immediately, and any pending state left by
|
|
706
|
+
// the superseded navigation is reset.
|
|
707
|
+
createNavAbort(externalSignal);
|
|
708
|
+
currentNavAbort = null;
|
|
709
|
+
setPending(false);
|
|
710
|
+
const navState = updateNavigationState(entry.params, url);
|
|
711
|
+
renderPayload(entry.payload, navState);
|
|
712
|
+
restoreScrollAfterPaint(scrollY);
|
|
412
713
|
} else {
|
|
413
714
|
// No cached payload — fetch from server.
|
|
414
715
|
// This happens when navigating back to the initial SSR'd page
|
|
415
716
|
// (its payload is null since it was rendered via SSR, not RSC fetch)
|
|
416
717
|
// or when the entry doesn't exist at all.
|
|
718
|
+
const navAbort = createNavAbort(externalSignal);
|
|
417
719
|
setPending(true, url);
|
|
418
720
|
try {
|
|
419
|
-
|
|
420
|
-
const stateTree = segmentCache.serializeStateTree(
|
|
421
|
-
|
|
422
|
-
);
|
|
423
|
-
const result = await fetchRscPayload(url, deps, stateTree);
|
|
721
|
+
await renderViaTransition(url, async () => {
|
|
722
|
+
const stateTree = segmentCache.serializeStateTree();
|
|
723
|
+
const result = await fetchRscPayload(url, deps, stateTree, undefined, navAbort.signal);
|
|
424
724
|
updateSegmentCache(result.segmentInfo);
|
|
425
|
-
updateNavigationState(result.params, url);
|
|
725
|
+
const navState = updateNavigationState(result.params, url);
|
|
426
726
|
// History push handled by renderViaTransition (stores merged payload)
|
|
427
|
-
return result;
|
|
727
|
+
return { ...result, navState };
|
|
428
728
|
});
|
|
429
729
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
});
|
|
730
|
+
restoreScrollAfterPaint(scrollY);
|
|
731
|
+
} catch (error) {
|
|
732
|
+
if (isAbortError(error)) return;
|
|
733
|
+
throw error;
|
|
435
734
|
} finally {
|
|
436
|
-
|
|
735
|
+
// Staleness guard (TIM-1034) — see navigate()'s finally block.
|
|
736
|
+
if (currentNavAbort === navAbort) {
|
|
737
|
+
currentNavAbort = null;
|
|
738
|
+
setPending(false);
|
|
739
|
+
}
|
|
437
740
|
}
|
|
438
741
|
}
|
|
439
742
|
}
|
|
@@ -443,15 +746,21 @@ export function createRouter(deps: RouterDeps): RouterInstance {
|
|
|
443
746
|
* Called on hover of <Link prefetch> elements.
|
|
444
747
|
*/
|
|
445
748
|
function prefetch(url: string): void {
|
|
749
|
+
// Strip fragment — it's client-only and would swallow the _rsc cache-bust
|
|
750
|
+
// param into the hash. The hash-less key also matches navigate()'s fetchUrl.
|
|
751
|
+
const hashIndex = url.indexOf('#');
|
|
752
|
+
const fetchUrl = hashIndex === -1 ? url : url.slice(0, hashIndex);
|
|
753
|
+
|
|
446
754
|
// Don't prefetch if already cached
|
|
447
|
-
if (prefetchCache.get(
|
|
448
|
-
if (historyStack.has(
|
|
755
|
+
if (prefetchCache.get(fetchUrl) !== undefined) return;
|
|
756
|
+
if (historyStack.has(fetchUrl)) return;
|
|
449
757
|
|
|
450
758
|
// Fire-and-forget fetch
|
|
451
|
-
const stateTree = segmentCache.serializeStateTree(
|
|
452
|
-
void fetchRscPayload(
|
|
759
|
+
const stateTree = segmentCache.serializeStateTree();
|
|
760
|
+
void fetchRscPayload(fetchUrl, deps, stateTree).then(
|
|
453
761
|
(result) => {
|
|
454
|
-
|
|
762
|
+
result.decodePromise?.catch(() => {});
|
|
763
|
+
prefetchCache.set(fetchUrl, result);
|
|
455
764
|
},
|
|
456
765
|
() => {
|
|
457
766
|
// Prefetch failure is non-fatal — navigation will fetch fresh
|
|
@@ -463,29 +772,36 @@ export function createRouter(deps: RouterDeps): RouterInstance {
|
|
|
463
772
|
navigate,
|
|
464
773
|
refresh,
|
|
465
774
|
handlePopState,
|
|
466
|
-
isPending: () =>
|
|
467
|
-
getPendingUrl: () =>
|
|
775
|
+
isPending: () => routerPhase.phase === 'navigating',
|
|
776
|
+
getPendingUrl: () => (routerPhase.phase === 'navigating' ? routerPhase.targetUrl : null),
|
|
468
777
|
onPendingChange(listener) {
|
|
469
778
|
pendingListeners.add(listener);
|
|
470
779
|
return () => pendingListeners.delete(listener);
|
|
471
780
|
},
|
|
472
781
|
prefetch,
|
|
473
|
-
applyRevalidation(element: unknown
|
|
782
|
+
applyRevalidation(element: unknown): void {
|
|
474
783
|
// Render the piggybacked element tree from a server action response.
|
|
475
|
-
// Updates the current history entry with the fresh payload
|
|
476
|
-
//
|
|
477
|
-
//
|
|
784
|
+
// Updates the current history entry with the fresh payload —
|
|
785
|
+
// same as refresh() but without a server fetch.
|
|
786
|
+
// Revalidation payloads are already resolved (no Flight thenable),
|
|
787
|
+
// so no segment merging is needed — just cache and render.
|
|
478
788
|
const currentUrl = deps.getCurrentUrl();
|
|
479
|
-
|
|
789
|
+
|
|
790
|
+
// Revalidation doesn't change params/pathname — preserve current state.
|
|
791
|
+
// DO NOT call updateNavigationState(null, ...) here: that normalizes
|
|
792
|
+
// params to {}, clearing dynamic route params on every action response.
|
|
793
|
+
const navState = getNavigationState();
|
|
794
|
+
|
|
795
|
+
// Keep params on the overwritten entry — dropping them would make
|
|
796
|
+
// away-and-back navigation replay this page with params normalized
|
|
797
|
+
// to {} (TIM-1037).
|
|
480
798
|
historyStack.push(currentUrl, {
|
|
481
|
-
payload:
|
|
482
|
-
|
|
799
|
+
payload: element,
|
|
800
|
+
params: navState.params,
|
|
483
801
|
});
|
|
484
|
-
renderPayload(
|
|
485
|
-
applyHead(headElements);
|
|
802
|
+
renderPayload(element, navState);
|
|
486
803
|
},
|
|
487
804
|
initSegmentCache: (segments: SegmentInfo[]) => updateSegmentCache(segments),
|
|
488
|
-
cacheElementTree: (element: unknown) => cacheSegmentElements(element, segmentElementCache),
|
|
489
805
|
segmentCache,
|
|
490
806
|
prefetchCache,
|
|
491
807
|
historyStack,
|