@timber-js/app 0.2.0-alpha.185 → 0.2.0-alpha.187
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/dist/_chunks/{actions-O_LsyCE4.js → actions-35jnMdeJ.js} +4 -3
- package/dist/_chunks/{actions-O_LsyCE4.js.map → actions-35jnMdeJ.js.map} +1 -1
- package/dist/_chunks/als-registry-C6kcfprT.js +41 -0
- package/dist/_chunks/als-registry-C6kcfprT.js.map +1 -0
- package/dist/_chunks/als-slots-BEEIPKYm.js +26 -0
- package/dist/_chunks/als-slots-BEEIPKYm.js.map +1 -0
- package/dist/_chunks/base-path-DaQrzbez.js +80 -0
- package/dist/_chunks/base-path-DaQrzbez.js.map +1 -0
- package/dist/_chunks/build-manifest-DWppEdLB.js +86 -0
- package/dist/_chunks/build-manifest-DWppEdLB.js.map +1 -0
- package/dist/_chunks/build-output-helper-C3DrfzZR.js +369 -0
- package/dist/_chunks/build-output-helper-C3DrfzZR.js.map +1 -0
- package/dist/_chunks/{cache-api-B-lhk9p4.js → cache-api-DjNrIWRR.js} +8 -14
- package/dist/_chunks/cache-api-DjNrIWRR.js.map +1 -0
- package/dist/_chunks/cli-check-CpmN7Nh-.js +256 -0
- package/dist/_chunks/cli-check-CpmN7Nh-.js.map +1 -0
- package/dist/_chunks/cli-schema-sync-CGMp_Psg.js +298 -0
- package/dist/_chunks/cli-schema-sync-CGMp_Psg.js.map +1 -0
- package/dist/_chunks/{cloudflare-CnT5Lr7U.js → cloudflare-CGP6BZKO.js} +92 -14
- package/dist/_chunks/{cloudflare-CnT5Lr7U.js.map → cloudflare-CGP6BZKO.js.map} +1 -1
- package/dist/_chunks/convention-lint-kXsgc_-7.js +784 -0
- package/dist/_chunks/convention-lint-kXsgc_-7.js.map +1 -0
- package/dist/_chunks/error-boundary-D-ODYX41.js +353 -0
- package/dist/_chunks/error-boundary-D-ODYX41.js.map +1 -0
- package/dist/_chunks/{fast-hash-D6hIVt1Y.js → fast-hash-C-tVYrLb.js} +6 -1
- package/dist/_chunks/fast-hash-C-tVYrLb.js.map +1 -0
- package/dist/_chunks/file-cache-DmX7OqZP.js +454 -0
- package/dist/_chunks/file-cache-DmX7OqZP.js.map +1 -0
- package/dist/_chunks/fs-identity-D7vb6GdI.js +208 -0
- package/dist/_chunks/fs-identity-D7vb6GdI.js.map +1 -0
- package/dist/_chunks/{logger-AWfuX-KJ.js → logger-D8xJZXIN.js} +34 -51
- package/dist/_chunks/logger-D8xJZXIN.js.map +1 -0
- package/dist/_chunks/mdx-file-C005ay-P.js +25 -0
- package/dist/_chunks/mdx-file-C005ay-P.js.map +1 -0
- package/dist/_chunks/{plugin-context---kTF5v8.js → plugin-context-rCinWLiE.js} +8 -2
- package/dist/_chunks/plugin-context-rCinWLiE.js.map +1 -0
- package/dist/_chunks/purge-store-Byr8XOjU.js +14 -0
- package/dist/_chunks/purge-store-Byr8XOjU.js.map +1 -0
- package/dist/_chunks/{resolve-schema-Dz3fcFUo.js → resolve-schema-5ma5pp1b.js} +2 -2
- package/dist/_chunks/{resolve-schema-Dz3fcFUo.js.map → resolve-schema-5ma5pp1b.js.map} +1 -1
- package/dist/_chunks/{router-ref-CtmF-aPv.js → router-ref-DuYuV_0Q.js} +2 -2
- package/dist/_chunks/router-ref-DuYuV_0Q.js.map +1 -0
- package/dist/_chunks/rsc-cache-key-DD0fl_-s.js +243 -0
- package/dist/_chunks/rsc-cache-key-DD0fl_-s.js.map +1 -0
- package/dist/_chunks/rsc-error-envelope-tT5PJs4q.js +40 -0
- package/dist/_chunks/rsc-error-envelope-tT5PJs4q.js.map +1 -0
- package/dist/_chunks/rsc-media-type-DRqE_lD_.js +46 -0
- package/dist/_chunks/rsc-media-type-DRqE_lD_.js.map +1 -0
- package/dist/_chunks/rsc-payload-path-B_LBodc2.js +48 -0
- package/dist/_chunks/rsc-payload-path-B_LBodc2.js.map +1 -0
- package/dist/_chunks/scanner-C8b0Gcw3.js +1564 -0
- package/dist/_chunks/scanner-C8b0Gcw3.js.map +1 -0
- package/dist/_chunks/schema-bridge-Cc2Gngu1.js +199 -0
- package/dist/_chunks/schema-bridge-Cc2Gngu1.js.map +1 -0
- package/dist/_chunks/{segment-classify-Byy425ng.js → segment-classify-C539Pa2O.js} +23 -2
- package/dist/_chunks/segment-classify-C539Pa2O.js.map +1 -0
- package/dist/_chunks/segment-context-CjOlyB8Y.js +97 -0
- package/dist/_chunks/segment-context-CjOlyB8Y.js.map +1 -0
- package/dist/_chunks/{canonicalize-DQHyFClh.js → segment-keys-BawYuNFO.js} +61 -2
- package/dist/_chunks/segment-keys-BawYuNFO.js.map +1 -0
- package/dist/_chunks/slot-params-BCTmZkQB.js +76 -0
- package/dist/_chunks/slot-params-BCTmZkQB.js.map +1 -0
- package/dist/_chunks/{ssr-data-BOWsq18U.js → ssr-data-14MXm7Pj.js} +17 -2
- package/dist/_chunks/ssr-data-14MXm7Pj.js.map +1 -0
- package/dist/_chunks/{use-query-states-DFvWd-EA.js → use-query-states-BbU5Ge1V.js} +74 -26
- package/dist/_chunks/use-query-states-BbU5Ge1V.js.map +1 -0
- package/dist/_chunks/use-segment-params-C4r4BD9T.js +398 -0
- package/dist/_chunks/use-segment-params-C4r4BD9T.js.map +1 -0
- package/dist/_chunks/walkers-RzN6AFjr.js +141 -0
- package/dist/_chunks/walkers-RzN6AFjr.js.map +1 -0
- package/dist/adapters/build-output-helper.d.ts +37 -0
- package/dist/adapters/build-output-helper.d.ts.map +1 -1
- package/dist/adapters/cloudflare-dev.js +1 -1
- package/dist/adapters/cloudflare-kv-cache.js +1 -1
- package/dist/adapters/cloudflare.d.ts.map +1 -1
- package/dist/adapters/cloudflare.js +1 -1
- package/dist/adapters/compress-module.d.ts +12 -0
- package/dist/adapters/compress-module.d.ts.map +1 -1
- package/dist/adapters/fs-identity.d.ts +64 -0
- package/dist/adapters/fs-identity.d.ts.map +1 -0
- package/dist/adapters/nitro.d.ts.map +1 -1
- package/dist/adapters/nitro.js +56 -5
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/adapters/shared.d.ts +57 -1
- package/dist/adapters/shared.d.ts.map +1 -1
- package/dist/adapters/types.d.ts +13 -1
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/cache/fast-hash.d.ts +5 -0
- package/dist/cache/fast-hash.d.ts.map +1 -1
- package/dist/cache/index.js +1 -1
- package/dist/cdn/cloudflare-purge.js +30 -0
- package/dist/cdn/cloudflare-purge.js.map +1 -0
- package/dist/cdn/fastly-purge.js +33 -0
- package/dist/cdn/fastly-purge.js.map +1 -0
- package/dist/cdn/index.js +94 -0
- package/dist/cdn/index.js.map +1 -0
- package/dist/cdn/workers-cache-purge.js +35 -0
- package/dist/cdn/workers-cache-purge.js.map +1 -0
- package/dist/cli-check.d.ts +153 -0
- package/dist/cli-check.d.ts.map +1 -0
- package/dist/cli.d.ts +34 -8
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +46 -21
- package/dist/cli.js.map +1 -1
- package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -1
- package/dist/client/browser-entry/hydrate.d.ts +20 -9
- package/dist/client/browser-entry/hydrate.d.ts.map +1 -1
- package/dist/client/browser-entry/index.d.ts +1 -1
- package/dist/client/browser-entry/index.d.ts.map +1 -1
- package/dist/client/browser-entry/post-hydration.d.ts +4 -1
- package/dist/client/browser-entry/post-hydration.d.ts.map +1 -1
- package/dist/client/browser-entry/router-init.d.ts +3 -0
- package/dist/client/browser-entry/router-init.d.ts.map +1 -1
- package/dist/client/browser-entry/rsc-stream.d.ts +8 -0
- package/dist/client/browser-entry/rsc-stream.d.ts.map +1 -1
- package/dist/client/error-boundary.d.ts +7 -0
- package/dist/client/error-boundary.d.ts.map +1 -1
- package/dist/client/error-boundary.js +1 -162
- package/dist/client/history.d.ts +13 -6
- package/dist/client/history.d.ts.map +1 -1
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +42 -41
- package/dist/client/index.js.map +1 -1
- package/dist/client/internal.d.ts +4 -3
- package/dist/client/internal.d.ts.map +1 -1
- package/dist/client/internal.js +877 -311
- package/dist/client/internal.js.map +1 -1
- package/dist/client/link.d.ts +1 -7
- package/dist/client/link.d.ts.map +1 -1
- package/dist/client/navigation-api.d.ts +2 -2
- package/dist/client/navigation-commit.d.ts +87 -0
- package/dist/client/navigation-commit.d.ts.map +1 -0
- package/dist/client/navigation-context.d.ts +37 -20
- package/dist/client/navigation-context.d.ts.map +1 -1
- package/dist/client/navigation-root.d.ts +63 -23
- package/dist/client/navigation-root.d.ts.map +1 -1
- package/dist/client/params-context.d.ts +81 -0
- package/dist/client/params-context.d.ts.map +1 -0
- package/dist/client/router-effects.d.ts +129 -0
- package/dist/client/router-effects.d.ts.map +1 -0
- package/dist/client/router-lifecycle.d.ts +55 -0
- package/dist/client/router-lifecycle.d.ts.map +1 -0
- package/dist/client/router-pipeline.d.ts +84 -0
- package/dist/client/router-pipeline.d.ts.map +1 -0
- package/dist/client/router-ref.d.ts +1 -1
- package/dist/client/router-ref.d.ts.map +1 -1
- package/dist/client/router-skew.d.ts +21 -0
- package/dist/client/router-skew.d.ts.map +1 -0
- package/dist/client/router-types.d.ts +207 -0
- package/dist/client/router-types.d.ts.map +1 -0
- package/dist/client/router.d.ts +1 -182
- package/dist/client/router.d.ts.map +1 -1
- package/dist/client/rsc-fetch.d.ts +47 -26
- package/dist/client/rsc-fetch.d.ts.map +1 -1
- package/dist/client/segment-cache.d.ts +87 -34
- package/dist/client/segment-cache.d.ts.map +1 -1
- package/dist/client/ssr-data.d.ts +6 -0
- package/dist/client/ssr-data.d.ts.map +1 -1
- package/dist/client/stale-client.d.ts +69 -0
- package/dist/client/stale-client.d.ts.map +1 -0
- package/dist/client/state.d.ts +10 -1
- package/dist/client/state.d.ts.map +1 -1
- package/dist/client/top-loader.d.ts +2 -1
- package/dist/client/top-loader.d.ts.map +1 -1
- package/dist/client/unload-guard.d.ts +5 -0
- package/dist/client/unload-guard.d.ts.map +1 -1
- package/dist/client/use-cookie.d.ts.map +1 -1
- package/dist/client/use-query-states.d.ts +9 -3
- package/dist/client/use-query-states.d.ts.map +1 -1
- package/dist/client/use-router.d.ts +1 -1
- package/dist/client/use-segment-params.d.ts +18 -8
- package/dist/client/use-segment-params.d.ts.map +1 -1
- package/dist/codec.js +1 -1
- package/dist/cookies/define-cookie.d.ts.map +1 -1
- package/dist/cookies/index.js +5 -5
- package/dist/cookies/index.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1009 -759
- package/dist/index.js.map +1 -1
- package/dist/params/index.js +1 -1
- package/dist/plugin-context.d.ts +46 -0
- package/dist/plugin-context.d.ts.map +1 -1
- package/dist/plugins/adapter-build.d.ts.map +1 -1
- package/dist/plugins/build-manifest.d.ts +1 -1
- package/dist/plugins/build-manifest.d.ts.map +1 -1
- package/dist/plugins/entries.d.ts.map +1 -1
- package/dist/plugins/fonts.d.ts.map +1 -1
- package/dist/plugins/request-dep/analysis.d.ts +106 -0
- package/dist/plugins/request-dep/analysis.d.ts.map +1 -0
- package/dist/plugins/request-dep/index.d.ts +75 -0
- package/dist/plugins/request-dep/index.d.ts.map +1 -0
- package/dist/plugins/request-dep/origin.d.ts +61 -0
- package/dist/plugins/request-dep/origin.d.ts.map +1 -0
- package/dist/plugins/routing.d.ts.map +1 -1
- package/dist/plugins/shims.d.ts.map +1 -1
- package/dist/plugins/static-build.d.ts +160 -8
- package/dist/plugins/static-build.d.ts.map +1 -1
- package/dist/routing/codegen-shared.d.ts +3 -44
- package/dist/routing/codegen-shared.d.ts.map +1 -1
- package/dist/routing/codegen-types.d.ts +28 -31
- package/dist/routing/codegen-types.d.ts.map +1 -1
- package/dist/routing/codegen-write.d.ts +51 -0
- package/dist/routing/codegen-write.d.ts.map +1 -0
- package/dist/routing/codegen.d.ts.map +1 -1
- package/dist/routing/collision-probe.d.ts +104 -0
- package/dist/routing/collision-probe.d.ts.map +1 -0
- package/dist/routing/collision-spaces.d.ts +26 -0
- package/dist/routing/collision-spaces.d.ts.map +1 -0
- package/dist/routing/convention-lint.d.ts +18 -4
- package/dist/routing/convention-lint.d.ts.map +1 -1
- package/dist/routing/export-detect.d.ts +16 -0
- package/dist/routing/export-detect.d.ts.map +1 -1
- package/dist/routing/index.js +4 -3
- package/dist/routing/interception.d.ts +235 -6
- package/dist/routing/interception.d.ts.map +1 -1
- package/dist/routing/link-codegen.d.ts +19 -4
- package/dist/routing/link-codegen.d.ts.map +1 -1
- package/dist/routing/manifest-codegen.d.ts +1 -7
- package/dist/routing/manifest-codegen.d.ts.map +1 -1
- package/dist/routing/scanner.d.ts.map +1 -1
- package/dist/routing/segment-classify.d.ts +33 -0
- package/dist/routing/segment-classify.d.ts.map +1 -1
- package/dist/routing/segment-keys.d.ts +95 -0
- package/dist/routing/segment-keys.d.ts.map +1 -0
- package/dist/routing/slot-placement.d.ts +48 -0
- package/dist/routing/slot-placement.d.ts.map +1 -0
- package/dist/routing/types.d.ts +0 -6
- package/dist/routing/types.d.ts.map +1 -1
- package/dist/routing/walkers.d.ts +6 -0
- package/dist/routing/walkers.d.ts.map +1 -1
- package/dist/schema-bridge.d.ts +60 -9
- package/dist/schema-bridge.d.ts.map +1 -1
- package/dist/search-params/define.d.ts +62 -14
- package/dist/search-params/define.d.ts.map +1 -1
- package/dist/search-params/index.d.ts +0 -1
- package/dist/search-params/index.d.ts.map +1 -1
- package/dist/search-params/index.js +221 -10
- package/dist/search-params/index.js.map +1 -1
- package/dist/search-params/parse-total.d.ts +70 -0
- package/dist/search-params/parse-total.d.ts.map +1 -0
- package/dist/search-params/wrappers.d.ts +26 -3
- package/dist/search-params/wrappers.d.ts.map +1 -1
- package/dist/server/access-gate.d.ts +19 -8
- package/dist/server/access-gate.d.ts.map +1 -1
- package/dist/server/action-handler.d.ts.map +1 -1
- package/dist/server/actions.d.ts +6 -3
- package/dist/server/actions.d.ts.map +1 -1
- package/dist/server/als-registry.d.ts +18 -1
- package/dist/server/als-registry.d.ts.map +1 -1
- package/dist/server/build-manifest.d.ts +13 -1
- package/dist/server/build-manifest.d.ts.map +1 -1
- package/dist/server/chain-url-parts.d.ts +82 -0
- package/dist/server/chain-url-parts.d.ts.map +1 -0
- package/dist/server/children-interception.d.ts +49 -0
- package/dist/server/children-interception.d.ts.map +1 -0
- package/dist/server/compress.d.ts.map +1 -1
- package/dist/server/default-status-page.d.ts +45 -0
- package/dist/server/default-status-page.d.ts.map +1 -0
- package/dist/server/deny-boundary.d.ts +148 -15
- package/dist/server/deny-boundary.d.ts.map +1 -1
- package/dist/server/deny-renderer.d.ts +15 -4
- package/dist/server/deny-renderer.d.ts.map +1 -1
- package/dist/server/error-boundary-wrapper.d.ts +85 -15
- package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
- package/dist/server/head-response.d.ts +35 -0
- package/dist/server/head-response.d.ts.map +1 -0
- package/dist/server/index.d.ts +0 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +3 -2
- package/dist/server/index.js.map +1 -1
- package/dist/server/internal.d.ts +3 -1
- package/dist/server/internal.d.ts.map +1 -1
- package/dist/server/internal.js +712 -418
- package/dist/server/internal.js.map +1 -1
- package/dist/server/metadata-collector.d.ts +52 -0
- package/dist/server/metadata-collector.d.ts.map +1 -0
- package/dist/server/param-coercion.d.ts +22 -6
- package/dist/server/param-coercion.d.ts.map +1 -1
- package/dist/server/pipeline-helpers.d.ts +0 -18
- package/dist/server/pipeline-helpers.d.ts.map +1 -1
- package/dist/server/pipeline-interception.d.ts +59 -9
- package/dist/server/pipeline-interception.d.ts.map +1 -1
- package/dist/server/pipeline-outcome.d.ts.map +1 -1
- package/dist/server/pipeline-phases.d.ts.map +1 -1
- package/dist/server/pipeline.d.ts +11 -0
- package/dist/server/pipeline.d.ts.map +1 -1
- package/dist/server/primitives.d.ts +23 -0
- package/dist/server/primitives.d.ts.map +1 -1
- package/dist/server/publish-params.d.ts +33 -0
- package/dist/server/publish-params.d.ts.map +1 -0
- package/dist/server/request-context.d.ts +48 -0
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/route-element-builder.d.ts +22 -3
- 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 +13 -0
- package/dist/server/route-matcher.d.ts.map +1 -1
- package/dist/server/rsc-cache-key-guard.d.ts +78 -0
- package/dist/server/rsc-cache-key-guard.d.ts.map +1 -0
- package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
- package/dist/server/rsc-entry/deny-fallback.d.ts.map +1 -1
- package/dist/server/rsc-entry/error-renderer.d.ts +15 -3
- package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
- package/dist/server/rsc-entry/helpers.d.ts +2 -17
- package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts +0 -1
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/dist/server/rsc-entry/render-route.d.ts.map +1 -1
- package/dist/server/rsc-entry/revalidate-renderer.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-payload.d.ts +1 -2
- package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-stream.d.ts +15 -2
- package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
- package/dist/server/skippable-prefix.d.ts +23 -0
- package/dist/server/skippable-prefix.d.ts.map +1 -0
- package/dist/server/slot-interception.d.ts +80 -0
- package/dist/server/slot-interception.d.ts.map +1 -0
- package/dist/server/slot-resolver.d.ts +70 -8
- package/dist/server/slot-resolver.d.ts.map +1 -1
- package/dist/server/ssr-bridge-types.d.ts +17 -0
- package/dist/server/ssr-bridge-types.d.ts.map +1 -1
- package/dist/server/ssr-entry.d.ts +0 -1
- package/dist/server/ssr-entry.d.ts.map +1 -1
- package/dist/server/ssr-wrappers.d.ts +24 -13
- package/dist/server/ssr-wrappers.d.ts.map +1 -1
- package/dist/server/state-tree-diff.d.ts +6 -27
- package/dist/server/state-tree-diff.d.ts.map +1 -1
- package/dist/server/static-generator.d.ts +6 -0
- package/dist/server/static-generator.d.ts.map +1 -1
- package/dist/server/static-not-found.d.ts +52 -0
- package/dist/server/static-not-found.d.ts.map +1 -0
- package/dist/server/status-code-resolver.d.ts +8 -1
- package/dist/server/status-code-resolver.d.ts.map +1 -1
- package/dist/server/tree-builder.d.ts +28 -36
- package/dist/server/tree-builder.d.ts.map +1 -1
- package/dist/server/types.d.ts +12 -7
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/utils/element-type.d.ts +40 -0
- package/dist/server/utils/element-type.d.ts.map +1 -0
- package/dist/shared/als-slots.d.ts +41 -0
- package/dist/shared/als-slots.d.ts.map +1 -0
- package/dist/shared/base-path.d.ts +56 -0
- package/dist/shared/base-path.d.ts.map +1 -0
- package/dist/shared/param-value.d.ts +80 -0
- package/dist/shared/param-value.d.ts.map +1 -0
- package/dist/shared/payload-root.d.ts +108 -0
- package/dist/shared/payload-root.d.ts.map +1 -0
- package/dist/shared/rsc-cache-key.d.ts +163 -0
- package/dist/shared/rsc-cache-key.d.ts.map +1 -0
- package/dist/shared/rsc-error-envelope.d.ts +25 -0
- package/dist/shared/rsc-error-envelope.d.ts.map +1 -0
- package/dist/shared/rsc-manifest.d.ts +28 -0
- package/dist/shared/rsc-manifest.d.ts.map +1 -0
- package/dist/shared/rsc-media-type.d.ts +40 -0
- package/dist/shared/rsc-media-type.d.ts.map +1 -0
- package/dist/shared/rsc-payload-path.d.ts +42 -0
- package/dist/shared/rsc-payload-path.d.ts.map +1 -0
- package/dist/shared/segment-info.d.ts +59 -0
- package/dist/shared/segment-info.d.ts.map +1 -0
- package/dist/shared/slot-params.d.ts +51 -0
- package/dist/shared/slot-params.d.ts.map +1 -0
- package/dist/shared/static-platform-files.d.ts +71 -0
- package/dist/shared/static-platform-files.d.ts.map +1 -0
- package/docs/api/30-api-server.mdx +1 -1
- package/docs/api/31-api-client.mdx +4 -2
- package/docs/api/33-api-search-params.mdx +38 -16
- package/docs/api/35-api-typescript.mdx +3 -3
- package/docs/api/36-cli.mdx +34 -7
- package/docs/learn/00-introduction.mdx +1 -1
- package/docs/learn/02-pages-and-layouts.mdx +2 -2
- package/docs/learn/05-typed-params.mdx +8 -8
- package/docs/learn/07-typed-routes.mdx +12 -7
- package/docs/learn/11-error-handling.mdx +18 -0
- package/docs/learn/12-client-navigation.mdx +2 -2
- package/docs/learn/14-deploying.mdx +23 -0
- package/docs/more/01-advanced-routing.mdx +173 -3
- package/docs/more/03-coming-from-nextjs.mdx +2 -2
- package/docs/more/04-metadata-and-fonts.mdx +1 -1
- package/docs/more/50-ai-agent-instructions.mdx +6 -4
- package/package.json +9 -6
- package/src/adapters/build-output-helper.ts +116 -3
- package/src/adapters/cloudflare.ts +90 -12
- package/src/adapters/compress-module.ts +79 -1
- package/src/adapters/fs-identity.ts +156 -0
- package/src/adapters/nitro.ts +6 -6
- package/src/adapters/shared.ts +196 -21
- package/src/adapters/types.ts +13 -1
- package/src/cache/fast-hash.ts +5 -0
- package/src/cli-check.ts +458 -0
- package/src/cli.ts +59 -24
- package/src/client/browser-entry/action-dispatch.ts +11 -5
- package/src/client/browser-entry/hydrate.ts +40 -29
- package/src/client/browser-entry/index.ts +15 -6
- package/src/client/browser-entry/post-hydration.ts +10 -9
- package/src/client/browser-entry/router-init.ts +104 -48
- package/src/client/browser-entry/rsc-stream.ts +11 -2
- package/src/client/error-boundary.tsx +85 -16
- package/src/client/history.ts +13 -6
- package/src/client/index.ts +14 -3
- package/src/client/internal.ts +4 -3
- package/src/client/link.tsx +120 -72
- package/src/client/navigation-api.ts +3 -3
- package/src/client/navigation-commit.ts +197 -0
- package/src/client/navigation-context.ts +40 -67
- package/src/client/navigation-root.tsx +326 -101
- package/src/client/params-context.ts +192 -0
- package/src/client/router-effects.ts +231 -0
- package/src/client/router-lifecycle.ts +288 -0
- package/src/client/router-pipeline.ts +393 -0
- package/src/client/router-ref.ts +1 -1
- package/src/client/router-skew.ts +30 -0
- package/src/client/router-types.ts +230 -0
- package/src/client/router.ts +243 -707
- package/src/client/rsc-fetch.ts +183 -106
- package/src/client/segment-cache.ts +129 -44
- package/src/client/ssr-data.ts +6 -0
- package/src/client/stale-client.ts +191 -0
- package/src/client/state.ts +14 -1
- package/src/client/top-loader.tsx +2 -1
- package/src/client/unload-guard.ts +34 -2
- package/src/client/use-cookie.ts +9 -2
- package/src/client/use-query-states.ts +102 -39
- package/src/client/use-router.ts +1 -1
- package/src/client/use-segment-params.ts +41 -18
- package/src/cookies/define-cookie.ts +6 -1
- package/src/index.ts +25 -4
- package/src/plugin-context.ts +58 -0
- package/src/plugins/adapter-build.ts +4 -0
- package/src/plugins/build-manifest.ts +34 -13
- package/src/plugins/entries.ts +4 -0
- package/src/plugins/fonts.ts +11 -8
- package/src/plugins/request-dep/analysis.ts +447 -0
- package/src/plugins/request-dep/index.ts +376 -0
- package/src/plugins/request-dep/origin.ts +310 -0
- package/src/plugins/routing.ts +97 -102
- package/src/plugins/shims.ts +0 -1
- package/src/plugins/static-build.ts +391 -95
- package/src/routing/codegen-shared.ts +3 -79
- package/src/routing/codegen-types.ts +28 -31
- package/src/routing/codegen-write.ts +139 -0
- package/src/routing/codegen.ts +86 -172
- package/src/routing/collision-probe.ts +438 -0
- package/src/routing/collision-spaces.ts +281 -0
- package/src/routing/convention-lint.ts +139 -40
- package/src/routing/export-detect.ts +151 -7
- package/src/routing/interception.ts +896 -50
- package/src/routing/link-codegen.ts +32 -65
- package/src/routing/manifest-codegen.ts +6 -62
- package/src/routing/scanner.ts +117 -92
- package/src/routing/segment-classify.ts +57 -0
- package/src/routing/segment-keys.ts +146 -0
- package/src/routing/slot-placement.ts +97 -0
- package/src/routing/types.ts +0 -6
- package/src/routing/walkers.ts +8 -0
- package/src/schema-bridge.ts +180 -58
- package/src/search-params/define.ts +108 -66
- package/src/search-params/index.ts +0 -1
- package/src/search-params/parse-total.ts +78 -0
- package/src/search-params/wrappers.ts +60 -11
- package/src/server/access-gate.tsx +60 -40
- package/src/server/action-handler.ts +4 -5
- package/src/server/actions.ts +6 -3
- package/src/server/als-registry.ts +18 -1
- package/src/server/build-manifest.ts +14 -1
- package/src/server/chain-url-parts.ts +163 -0
- package/src/server/children-interception.ts +115 -0
- package/src/server/compress.ts +9 -1
- package/src/server/default-status-page.ts +127 -0
- package/src/server/deny-boundary.ts +269 -41
- package/src/server/deny-renderer.ts +111 -39
- package/src/server/error-boundary-wrapper.ts +166 -79
- package/src/server/head-response.ts +54 -0
- package/src/server/index.ts +1 -3
- package/src/server/internal.ts +2 -2
- package/src/server/metadata-collector.ts +115 -0
- package/src/server/param-coercion.ts +51 -69
- package/src/server/pipeline-helpers.ts +2 -47
- package/src/server/pipeline-interception.ts +81 -32
- package/src/server/pipeline-outcome.ts +4 -5
- package/src/server/pipeline-phases.ts +77 -25
- package/src/server/pipeline.ts +57 -10
- package/src/server/prebuilt-runtime.ts +4 -4
- package/src/server/primitives.ts +25 -0
- package/src/server/publish-params.ts +42 -0
- package/src/server/request-context.ts +97 -18
- package/src/server/route-element-builder.ts +241 -253
- package/src/server/route-handler.ts +8 -8
- package/src/server/route-matcher.ts +21 -9
- package/src/server/rsc-cache-key-guard.ts +164 -0
- package/src/server/rsc-entry/api-handler.ts +6 -8
- package/src/server/rsc-entry/deny-fallback.ts +3 -2
- package/src/server/rsc-entry/error-renderer.ts +94 -35
- package/src/server/rsc-entry/helpers.ts +33 -28
- package/src/server/rsc-entry/index.ts +36 -33
- package/src/server/rsc-entry/render-route.ts +18 -10
- package/src/server/rsc-entry/revalidate-renderer.ts +3 -2
- package/src/server/rsc-entry/rsc-payload.ts +113 -40
- package/src/server/rsc-entry/rsc-stream.ts +29 -5
- package/src/server/rsc-entry/ssr-renderer.ts +38 -22
- package/src/server/skippable-prefix.ts +92 -0
- package/src/server/slot-interception.ts +168 -0
- package/src/server/slot-resolver.ts +263 -158
- package/src/server/ssr-bridge-types.ts +18 -0
- package/src/server/ssr-entry.ts +20 -10
- package/src/server/ssr-wrappers.tsx +33 -32
- package/src/server/state-tree-diff.ts +14 -55
- package/src/server/static-generator.ts +68 -29
- package/src/server/static-not-found.ts +134 -0
- package/src/server/status-code-resolver.ts +28 -11
- package/src/server/tree-builder.ts +35 -218
- package/src/server/types.ts +12 -7
- package/src/server/utils/element-type.ts +72 -0
- package/src/shared/als-slots.ts +72 -0
- package/src/shared/base-path.ts +80 -0
- package/src/shared/param-value.ts +244 -0
- package/src/shared/payload-root.ts +151 -0
- package/src/shared/rsc-cache-key.ts +269 -0
- package/src/shared/rsc-error-envelope.ts +31 -0
- package/src/shared/rsc-manifest.ts +29 -0
- package/src/shared/rsc-media-type.ts +43 -0
- package/src/shared/rsc-payload-path.ts +45 -0
- package/src/shared/segment-info.ts +60 -0
- package/src/shared/slot-params.ts +111 -0
- package/src/shared/static-platform-files.ts +75 -0
- package/dist/_chunks/build-output-helper-DOGYFb_X.js +0 -196
- package/dist/_chunks/build-output-helper-DOGYFb_X.js.map +0 -1
- package/dist/_chunks/cache-api-B-lhk9p4.js.map +0 -1
- package/dist/_chunks/canonicalize-DQHyFClh.js.map +0 -1
- package/dist/_chunks/cli-schema-sync-BTWEKJXo.js +0 -668
- package/dist/_chunks/cli-schema-sync-BTWEKJXo.js.map +0 -1
- package/dist/_chunks/define-COtkxMRT.js +0 -191
- package/dist/_chunks/define-COtkxMRT.js.map +0 -1
- package/dist/_chunks/define-c4au4I9R.js +0 -82
- package/dist/_chunks/define-c4au4I9R.js.map +0 -1
- package/dist/_chunks/fast-hash-D6hIVt1Y.js.map +0 -1
- package/dist/_chunks/logger-AWfuX-KJ.js.map +0 -1
- package/dist/_chunks/navigation-root-bUDZLw74.js +0 -124
- package/dist/_chunks/navigation-root-bUDZLw74.js.map +0 -1
- package/dist/_chunks/plugin-context---kTF5v8.js.map +0 -1
- package/dist/_chunks/registry-DbJPKoBp.js +0 -20
- package/dist/_chunks/registry-DbJPKoBp.js.map +0 -1
- package/dist/_chunks/router-ref-CtmF-aPv.js.map +0 -1
- package/dist/_chunks/schema-bridge-DT_Tn0Xf.js +0 -119
- package/dist/_chunks/schema-bridge-DT_Tn0Xf.js.map +0 -1
- package/dist/_chunks/segment-classify-Byy425ng.js.map +0 -1
- package/dist/_chunks/segment-context-ZDnXDkbz.js +0 -34
- package/dist/_chunks/segment-context-ZDnXDkbz.js.map +0 -1
- package/dist/_chunks/ssr-data-BOWsq18U.js.map +0 -1
- package/dist/_chunks/use-query-states-DFvWd-EA.js.map +0 -1
- package/dist/_chunks/use-segment-params-D1CzlgBo.js +0 -151
- package/dist/_chunks/use-segment-params-D1CzlgBo.js.map +0 -1
- package/dist/_chunks/walkers-_6zKFlch.js +0 -1002
- package/dist/_chunks/walkers-_6zKFlch.js.map +0 -1
- package/dist/client/error-boundary.js.map +0 -1
- package/dist/plugins/request-dep.d.ts +0 -32
- package/dist/plugins/request-dep.d.ts.map +0 -1
- package/dist/search-params/registry.d.ts +0 -20
- package/dist/search-params/registry.d.ts.map +0 -1
- package/dist/segment-params/define.d.ts +0 -89
- package/dist/segment-params/define.d.ts.map +0 -1
- package/dist/segment-params/index.d.ts +0 -3
- package/dist/segment-params/index.d.ts.map +0 -1
- package/dist/segment-params/index.js +0 -2
- package/src/plugins/request-dep.ts +0 -499
- package/src/search-params/registry.ts +0 -31
- package/src/segment-params/define.ts +0 -249
- package/src/segment-params/index.ts +0 -9
|
@@ -26,11 +26,14 @@ import type { InterceptionContext, RouteMatch } from './pipeline.js';
|
|
|
26
26
|
import { DenySignal, RedirectSignal } from './primitives.js';
|
|
27
27
|
import { logRenderError } from './logger.js';
|
|
28
28
|
import type { ManifestSegmentNode } from './route-matcher.js';
|
|
29
|
-
import { setSlotParams } from './request-context.js';
|
|
29
|
+
import { setSlotParams, runWithSlotSegmentParams } from './request-context.js';
|
|
30
30
|
import { coerceSlotParams } from './param-coercion.js';
|
|
31
31
|
import { matchUrlParts } from './tree-match.js';
|
|
32
|
+
import { extractUrlParts, consumedPartsThroughSegment } from './chain-url-parts.js';
|
|
33
|
+
import { findInterceptingMatch } from './slot-interception.js';
|
|
32
34
|
import { SegmentOutlet } from '../client/segment-outlet.js';
|
|
33
|
-
import {
|
|
35
|
+
import { shouldSkipSlot, type ClientStateTree } from './state-tree-diff.js';
|
|
36
|
+
import { computeSlotKey } from '../routing/segment-keys.js';
|
|
34
37
|
import { isStaticRequestDependent } from './request-dep.js';
|
|
35
38
|
|
|
36
39
|
type CreateElementFn = (...args: unknown[]) => React.ReactElement;
|
|
@@ -96,21 +99,27 @@ export async function resolveSlotElement(
|
|
|
96
99
|
: findSlotMatch(slotNode, match);
|
|
97
100
|
|
|
98
101
|
if (slotMatch) {
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
102
|
+
// Coerce the slot's own chain params through the global schema codecs
|
|
103
|
+
// (same as the main route) and expose them two ways:
|
|
104
|
+
//
|
|
105
|
+
// - keyed, in the ALS slot map, so `getSegmentParams(slotPath)` resolves
|
|
106
|
+
// from anywhere in the request;
|
|
107
|
+
// - scoped, around every component this resolver invokes from the slot's
|
|
108
|
+
// matched chain, so a no-arg `getSegmentParams()` inside the slot
|
|
109
|
+
// returns the slot's params instead of the main route's. An
|
|
110
|
+
// intercepting page has no other way to see its own params: the
|
|
111
|
+
// pipeline re-matched the SOURCE pathname, so the main params describe
|
|
112
|
+
// the page rendering *behind* the modal (TIM-1283).
|
|
113
|
+
//
|
|
114
|
+
// Both derive from the same coerced record, so the keyed overload and the
|
|
115
|
+
// no-arg call inside the slot can never disagree.
|
|
116
|
+
const coerced = coerceSlotParams(slotMatch.chain, slotMatch.slotParams);
|
|
117
|
+
const fullSlotPath = computeSlotSegmentPath(slotMatch.chain, parentTreePath);
|
|
118
|
+
|
|
119
|
+
if (fullSlotPath && Object.keys(coerced).length > 0) {
|
|
112
120
|
setSlotParams(fullSlotPath, coerced);
|
|
113
121
|
}
|
|
122
|
+
const scope = <T>(fn: () => T): T => runWithSlotSegmentParams(fullSlotPath, coerced, fn);
|
|
114
123
|
|
|
115
124
|
const SlotPage = await loadComponent(slotMatch.page);
|
|
116
125
|
if (SlotPage) {
|
|
@@ -137,7 +146,9 @@ export async function resolveSlotElement(
|
|
|
137
146
|
} else {
|
|
138
147
|
const SafeSlotPage = async (props: Record<string, unknown>) => {
|
|
139
148
|
try {
|
|
140
|
-
return await (
|
|
149
|
+
return await scope(() =>
|
|
150
|
+
(SlotPage as (props: Record<string, unknown>) => unknown)(props)
|
|
151
|
+
);
|
|
141
152
|
} catch (error) {
|
|
142
153
|
// RedirectSignal must propagate — the pipeline handles redirects
|
|
143
154
|
// at the top level. Swallowing it here would silently return
|
|
@@ -165,11 +176,17 @@ export async function resolveSlotElement(
|
|
|
165
176
|
// intermediate slot segments (everything between slot root and leaf).
|
|
166
177
|
// Process innermost-first, same order as route-element-builder.ts
|
|
167
178
|
// handles main segments. The slot root (index 0) is handled below.
|
|
168
|
-
element = await wrapWithIntermediateSegments(
|
|
179
|
+
element = await wrapWithIntermediateSegments(
|
|
180
|
+
slotMatch.chain,
|
|
181
|
+
element,
|
|
182
|
+
h,
|
|
183
|
+
accessVerdicts,
|
|
184
|
+
scope
|
|
185
|
+
);
|
|
169
186
|
|
|
170
187
|
// Wrap with slot root's layout — INSIDE the access gate, so the layout
|
|
171
188
|
// server component never executes when access.ts denies. See TIM-1074.
|
|
172
|
-
element = await wrapWithLayout(slotNode, element, h);
|
|
189
|
+
element = await wrapWithLayout(slotNode, element, h, scope);
|
|
173
190
|
|
|
174
191
|
// Wrap in SlotAccessGate if slot root has access.ts. The gate is
|
|
175
192
|
// OUTSIDE the layout, matching the segment model in
|
|
@@ -178,10 +195,14 @@ export async function resolveSlotElement(
|
|
|
178
195
|
// On denial: denied.tsx → default.tsx → null (graceful degradation),
|
|
179
196
|
// rendered WITHOUT the denied slot's own layout.
|
|
180
197
|
// See design/04-authorization.md §"Slot-Level Auth".
|
|
181
|
-
element = await wrapWithAccessGate(slotNode, element, h, accessVerdicts);
|
|
198
|
+
element = await wrapWithAccessGate(slotNode, element, h, accessVerdicts, scope);
|
|
182
199
|
|
|
183
200
|
// Wrap with slot root's error boundaries (outermost)
|
|
184
|
-
element = await wrapSegmentWithErrorBoundaries(slotNode, element,
|
|
201
|
+
element = await wrapSegmentWithErrorBoundaries(slotNode, element, {
|
|
202
|
+
loadModule,
|
|
203
|
+
createElement: h,
|
|
204
|
+
errorBoundaryComponent: TimberErrorBoundary,
|
|
205
|
+
});
|
|
185
206
|
|
|
186
207
|
// Catch-all error boundary: ensures slot errors NEVER propagate to the
|
|
187
208
|
// parent layout. Without this, a slot without error.tsx that throws
|
|
@@ -207,6 +228,33 @@ export async function resolveSlotElement(
|
|
|
207
228
|
return renderDefaultFallback(slotNode, h);
|
|
208
229
|
}
|
|
209
230
|
|
|
231
|
+
/**
|
|
232
|
+
* The full tree path of a slot's matched chain — the key both the ALS slot
|
|
233
|
+
* params map and the generated `getSegmentParams` overloads use.
|
|
234
|
+
*
|
|
235
|
+
* Built from the parent's tree path plus each chain node's directory name,
|
|
236
|
+
* so an intercepting node contributes its marker
|
|
237
|
+
* (`/gallery/@modal/(.)photo/[id]`) and a route group contributes its
|
|
238
|
+
* parentheses. Returns undefined when the caller has no parent tree path,
|
|
239
|
+
* which is how the resolver's test callers and any path-less entry point opt
|
|
240
|
+
* out of keyed lookups.
|
|
241
|
+
*
|
|
242
|
+
* One definition for both the render path and the eager access path: they
|
|
243
|
+
* key the same map, and a divergence would silently strand one of them.
|
|
244
|
+
*/
|
|
245
|
+
function computeSlotSegmentPath(
|
|
246
|
+
chain: ManifestSegmentNode[],
|
|
247
|
+
parentTreePath: string | undefined
|
|
248
|
+
): string | undefined {
|
|
249
|
+
if (!parentTreePath) return undefined;
|
|
250
|
+
const slotSuffix = chain
|
|
251
|
+
.map((s) => s.segmentName)
|
|
252
|
+
.filter(Boolean)
|
|
253
|
+
.join('/');
|
|
254
|
+
const prefix = parentTreePath === '/' ? '' : parentTreePath;
|
|
255
|
+
return `${prefix}/${slotSuffix}`;
|
|
256
|
+
}
|
|
257
|
+
|
|
210
258
|
// ─── Element Wrapping Helpers ───────────────────────────────────────────────
|
|
211
259
|
|
|
212
260
|
/**
|
|
@@ -229,29 +277,80 @@ async function wrapWithIntermediateSegments(
|
|
|
229
277
|
chain: ManifestSegmentNode[],
|
|
230
278
|
element: React.ReactElement,
|
|
231
279
|
h: CreateElementFn,
|
|
232
|
-
accessVerdicts?: SlotAccessVerdict[]
|
|
280
|
+
accessVerdicts?: SlotAccessVerdict[],
|
|
281
|
+
scope?: SlotParamScope
|
|
233
282
|
): Promise<React.ReactElement> {
|
|
234
283
|
for (let i = chain.length - 1; i > 0; i--) {
|
|
235
284
|
const seg = chain[i];
|
|
236
|
-
element = await wrapSegmentWithErrorBoundaries(seg, element,
|
|
237
|
-
|
|
238
|
-
|
|
285
|
+
element = await wrapSegmentWithErrorBoundaries(seg, element, {
|
|
286
|
+
loadModule,
|
|
287
|
+
createElement: h,
|
|
288
|
+
errorBoundaryComponent: TimberErrorBoundary,
|
|
289
|
+
});
|
|
290
|
+
element = await wrapWithLayout(seg, element, h, scope);
|
|
291
|
+
element = await wrapWithAccessGate(seg, element, h, accessVerdicts, scope);
|
|
239
292
|
}
|
|
240
293
|
return element;
|
|
241
294
|
}
|
|
242
295
|
|
|
296
|
+
/**
|
|
297
|
+
* Runs a function with the slot's own segment params in scope.
|
|
298
|
+
*
|
|
299
|
+
* Applied to every component this module invokes from a slot's matched chain
|
|
300
|
+
* — page, layouts, access functions — so all of them agree on what a no-arg
|
|
301
|
+
* `getSegmentParams()` means inside a slot. A partial application would be
|
|
302
|
+
* worse than none: a layout and the page under it would disagree about the
|
|
303
|
+
* params of the same render.
|
|
304
|
+
*/
|
|
305
|
+
type SlotParamScope = <T>(fn: () => T) => T;
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Invokes a slot layout inside that slot's param scope.
|
|
309
|
+
*
|
|
310
|
+
* Declared at module scope, and given the layout and the scope as props,
|
|
311
|
+
* so its component identity is the same on every render. A closure created
|
|
312
|
+
* per render would be a new component type each time, and React would
|
|
313
|
+
* unmount and remount the whole layout subtree — including client
|
|
314
|
+
* components' state — on every navigation that re-renders the slot.
|
|
315
|
+
*
|
|
316
|
+
* Server component: the function-valued props are called here on the server
|
|
317
|
+
* and never reach the Flight stream, the same way `SlotAccessGate` takes
|
|
318
|
+
* `accessFn`.
|
|
319
|
+
*/
|
|
320
|
+
function ScopedSlotLayout(props: {
|
|
321
|
+
layout: (props: Record<string, unknown>) => unknown;
|
|
322
|
+
scope: SlotParamScope;
|
|
323
|
+
children: React.ReactElement;
|
|
324
|
+
}): unknown {
|
|
325
|
+
return props.scope(() => props.layout({ children: props.children }));
|
|
326
|
+
}
|
|
327
|
+
|
|
243
328
|
/**
|
|
244
329
|
* Wrap an element with a segment's layout component, if present.
|
|
330
|
+
*
|
|
331
|
+
* A server-component layout is invoked through the slot param scope, so it
|
|
332
|
+
* sees the slot's params rather than the main route's — a layout and the
|
|
333
|
+
* page beneath it must not disagree about the params of one render. A
|
|
334
|
+
* client-reference layout is passed through untouched: it never executes on
|
|
335
|
+
* the server, and its params arrive over the client channel
|
|
336
|
+
* (`useSegmentParams`).
|
|
245
337
|
*/
|
|
246
338
|
async function wrapWithLayout(
|
|
247
339
|
node: ManifestSegmentNode,
|
|
248
340
|
element: React.ReactElement,
|
|
249
|
-
h: CreateElementFn
|
|
341
|
+
h: CreateElementFn,
|
|
342
|
+
scope?: SlotParamScope
|
|
250
343
|
): Promise<React.ReactElement> {
|
|
251
344
|
if (!node.layout) return element;
|
|
252
345
|
const Layout = await loadComponent(node.layout);
|
|
253
346
|
if (!Layout) return element;
|
|
254
|
-
return h(Layout, { children: element });
|
|
347
|
+
if (!scope || isClientReference(Layout)) return h(Layout, { children: element });
|
|
348
|
+
|
|
349
|
+
return h(ScopedSlotLayout, {
|
|
350
|
+
layout: Layout as (props: Record<string, unknown>) => unknown,
|
|
351
|
+
scope,
|
|
352
|
+
children: element,
|
|
353
|
+
});
|
|
255
354
|
}
|
|
256
355
|
|
|
257
356
|
/**
|
|
@@ -262,12 +361,17 @@ async function wrapWithAccessGate(
|
|
|
262
361
|
slotNode: ManifestSegmentNode,
|
|
263
362
|
element: React.ReactElement,
|
|
264
363
|
h: CreateElementFn,
|
|
265
|
-
accessVerdicts?: SlotAccessVerdict[]
|
|
364
|
+
accessVerdicts?: SlotAccessVerdict[],
|
|
365
|
+
scope?: SlotParamScope
|
|
266
366
|
): Promise<React.ReactElement> {
|
|
267
367
|
if (!slotNode.access) return element;
|
|
268
368
|
|
|
269
|
-
const
|
|
270
|
-
if (!
|
|
369
|
+
const loaded = await loadComponent(slotNode.access);
|
|
370
|
+
if (!loaded) return element;
|
|
371
|
+
|
|
372
|
+
// The gate calls this during render, so the scope has to travel with the
|
|
373
|
+
// function rather than wrap the call site.
|
|
374
|
+
const accessFn = scope ? () => scope(() => loaded()) : loaded;
|
|
271
375
|
|
|
272
376
|
let DeniedComponent: ((...args: unknown[]) => unknown) | null = null;
|
|
273
377
|
if (slotNode.denied) {
|
|
@@ -302,7 +406,7 @@ async function wrapWithAccessGate(
|
|
|
302
406
|
// ─── Slot Matching ──────────────────────────────────────────────────────────
|
|
303
407
|
|
|
304
408
|
/** Result of matching a slot's sub-tree against the current route. */
|
|
305
|
-
interface SlotMatchResult {
|
|
409
|
+
export interface SlotMatchResult {
|
|
306
410
|
/** The page file at the matched leaf. */
|
|
307
411
|
page: NonNullable<ManifestSegmentNode['page']>;
|
|
308
412
|
/** The full chain of slot nodes traversed (slot root → … → leaf with page). */
|
|
@@ -326,25 +430,50 @@ interface SlotMatchResult {
|
|
|
326
430
|
* We compare the matched route's segment chain against the slot's children
|
|
327
431
|
* to find the deepest matching page.
|
|
328
432
|
*/
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
433
|
+
/**
|
|
434
|
+
* The index in the matched chain of the segment that owns this slot.
|
|
435
|
+
*
|
|
436
|
+
* **By identity**, because `urlPath` cannot answer it. Slots add no URL depth,
|
|
437
|
+
* so the owner shares the slot's `urlPath` — but so does every route group
|
|
438
|
+
* above it (`/` for both the root and `(browse)`), and since TIM-1280 so does
|
|
439
|
+
* an intercepting node on the children path, which sits *below* the owner.
|
|
440
|
+
* Picking the deepest urlPath match — the rule before this — therefore chose
|
|
441
|
+
* the intercepting node for a tree like `(browse)/@sidebar/` +
|
|
442
|
+
* `(browse)/(.)item/[id]/`, slicing one segment too many off the chain and
|
|
443
|
+
* handing `@sidebar` a shorter URL than it matches against. It would render
|
|
444
|
+
* its `default.tsx` where a page exists.
|
|
445
|
+
*
|
|
446
|
+
* The index must also agree with `slotUrlParts`, which slices the destination
|
|
447
|
+
* pathname at the same point and already used `indexOf(owner)`. Two derivations
|
|
448
|
+
* of one index is how they came apart; this is the shared one.
|
|
449
|
+
*
|
|
450
|
+
* The `urlPath` search survives only as a fallback for a `slotNode` that is not
|
|
451
|
+
* attached to any segment in the chain. Production never does that —
|
|
452
|
+
* `resolveSlotProps` iterates `segment.slots` — but unit tests construct a slot
|
|
453
|
+
* node standalone, and for those the old behaviour is still the right answer.
|
|
454
|
+
*/
|
|
455
|
+
export function findSlotOwnerIndex(
|
|
456
|
+
slotNode: ManifestSegmentNode,
|
|
457
|
+
segments: ManifestSegmentNode[]
|
|
458
|
+
): number {
|
|
341
459
|
for (let i = segments.length - 1; i >= 0; i--) {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
break;
|
|
460
|
+
for (const candidate of Object.values(segments[i].slots ?? {})) {
|
|
461
|
+
if (candidate === slotNode) return i;
|
|
345
462
|
}
|
|
346
463
|
}
|
|
347
464
|
|
|
465
|
+
for (let i = segments.length - 1; i >= 0; i--) {
|
|
466
|
+
if (segments[i].urlPath === slotNode.urlPath) return i;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
return -1;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function findSlotMatch(slotNode: ManifestSegmentNode, match: RouteMatch): SlotMatchResult | null {
|
|
473
|
+
const segments = match.segments;
|
|
474
|
+
|
|
475
|
+
const parentIndex = findSlotOwnerIndex(slotNode, segments);
|
|
476
|
+
|
|
348
477
|
// The remaining segments after the parent are what we need to match
|
|
349
478
|
// against the slot's children.
|
|
350
479
|
const remainingSegments = parentIndex >= 0 ? segments.slice(parentIndex + 1) : [];
|
|
@@ -367,96 +496,6 @@ function findSlotMatch(slotNode: ManifestSegmentNode, match: RouteMatch): SlotMa
|
|
|
367
496
|
return { page: leaf.page, chain: result.chain, slotParams: result.params };
|
|
368
497
|
}
|
|
369
498
|
|
|
370
|
-
/**
|
|
371
|
-
* Extract raw URL path parts from a main-route segment chain.
|
|
372
|
-
*
|
|
373
|
-
* Each dynamic segment consumed one URL part (its param value).
|
|
374
|
-
* Static segments consumed their own name. Groups are transparent.
|
|
375
|
-
* Catch-all segments consumed multiple parts (their param value is string[]).
|
|
376
|
-
*
|
|
377
|
-
* Uses rawSegmentParams (pre-coercion) to get actual URL strings.
|
|
378
|
-
* Values are coerced to String() defensively in case a codec leaked
|
|
379
|
-
* a non-string value into the params map.
|
|
380
|
-
*/
|
|
381
|
-
export function extractUrlParts(
|
|
382
|
-
segments: ManifestSegmentNode[],
|
|
383
|
-
mainParams: Record<string, string | string[]>
|
|
384
|
-
): string[] {
|
|
385
|
-
const parts: string[] = [];
|
|
386
|
-
for (const seg of segments) {
|
|
387
|
-
if (seg.segmentType === 'group') continue;
|
|
388
|
-
if (seg.segmentType === 'static') {
|
|
389
|
-
parts.push(seg.segmentName);
|
|
390
|
-
} else if (seg.paramName) {
|
|
391
|
-
const value = mainParams[seg.paramName];
|
|
392
|
-
if (Array.isArray(value)) {
|
|
393
|
-
parts.push(...value.map(String));
|
|
394
|
-
} else if (value !== undefined) {
|
|
395
|
-
// Reconstruct the original URL part for affixed segments.
|
|
396
|
-
// The param value is the stripped middle (e.g. "123" for [id].png
|
|
397
|
-
// matching "123.png"), so we re-attach prefix/suffix so that slot
|
|
398
|
-
// matching through matchUrlParts sees the original part.
|
|
399
|
-
const prefix = seg.paramPrefix ?? '';
|
|
400
|
-
const suffix = seg.paramSuffix ?? '';
|
|
401
|
-
parts.push(prefix + String(value) + suffix);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
return parts;
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
/**
|
|
409
|
-
* Find a matching intercepting route in a slot's children for the target pathname.
|
|
410
|
-
*
|
|
411
|
-
* When interception is active, the pipeline has already re-matched the source URL.
|
|
412
|
-
* Here we check the slot's intercepting children (e.g. `(.)photo/[id]`) against
|
|
413
|
-
* the target pathname to find which intercepting page to render.
|
|
414
|
-
*
|
|
415
|
-
* The interceptedSegmentName tells us the first URL segment to look for in the
|
|
416
|
-
* target pathname. We then walk the intercepting child's sub-tree to match
|
|
417
|
-
* remaining segments.
|
|
418
|
-
*/
|
|
419
|
-
function findInterceptingMatch(
|
|
420
|
-
slotNode: ManifestSegmentNode,
|
|
421
|
-
targetPathname: string
|
|
422
|
-
): SlotMatchResult | null {
|
|
423
|
-
const targetParts = targetPathname === '/' ? [] : targetPathname.slice(1).split('/');
|
|
424
|
-
|
|
425
|
-
for (const child of slotNode.children) {
|
|
426
|
-
if (child.segmentType !== 'intercepting' || !child.interceptedSegmentName) continue;
|
|
427
|
-
|
|
428
|
-
const segName = child.interceptedSegmentName;
|
|
429
|
-
|
|
430
|
-
// Find where the intercepted segment name appears in the target parts.
|
|
431
|
-
// Search from the end since intercepted routes match the URL tail.
|
|
432
|
-
let matchIdx = -1;
|
|
433
|
-
for (let i = targetParts.length - 1; i >= 0; i--) {
|
|
434
|
-
if (targetParts[i] === segName) {
|
|
435
|
-
matchIdx = i;
|
|
436
|
-
break;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
if (matchIdx < 0) continue;
|
|
440
|
-
|
|
441
|
-
// Match remaining target parts against the intercepting child's sub-tree.
|
|
442
|
-
const remaining = targetParts.slice(matchIdx + 1);
|
|
443
|
-
const result = matchUrlParts(child, remaining);
|
|
444
|
-
if (result) {
|
|
445
|
-
const leaf = result.chain[result.chain.length - 1];
|
|
446
|
-
if (leaf.page) {
|
|
447
|
-
return {
|
|
448
|
-
page: leaf.page,
|
|
449
|
-
// Prepend slotNode — matchUrlParts starts from child, not slot root
|
|
450
|
-
chain: [slotNode, ...result.chain],
|
|
451
|
-
slotParams: result.params,
|
|
452
|
-
};
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
return null;
|
|
458
|
-
}
|
|
459
|
-
|
|
460
499
|
// ─── Slot Access Evaluation ─────────────────────────────────────────────────
|
|
461
500
|
|
|
462
501
|
/** Result of eagerly evaluating one segment's access.ts. */
|
|
@@ -483,7 +522,8 @@ export interface SlotAccessVerdict {
|
|
|
483
522
|
*/
|
|
484
523
|
async function evaluateSlotAccessChain(
|
|
485
524
|
slotRoot: ManifestSegmentNode,
|
|
486
|
-
chain: ManifestSegmentNode[]
|
|
525
|
+
chain: ManifestSegmentNode[],
|
|
526
|
+
scope?: SlotParamScope
|
|
487
527
|
): Promise<SlotAccessVerdict[]> {
|
|
488
528
|
// Collect all nodes with access.ts: slot root + intermediate chain segments
|
|
489
529
|
const nodesWithAccess: ManifestSegmentNode[] = [];
|
|
@@ -502,7 +542,7 @@ async function evaluateSlotAccessChain(
|
|
|
502
542
|
continue;
|
|
503
543
|
}
|
|
504
544
|
try {
|
|
505
|
-
await accessFn();
|
|
545
|
+
await (scope ? scope(() => accessFn()) : accessFn());
|
|
506
546
|
results.push({ node, verdict: 'pass' });
|
|
507
547
|
} catch (e) {
|
|
508
548
|
if (e instanceof DenySignal) {
|
|
@@ -541,6 +581,68 @@ export interface SlotSkipEntry {
|
|
|
541
581
|
skipped?: boolean;
|
|
542
582
|
}
|
|
543
583
|
|
|
584
|
+
/**
|
|
585
|
+
* The destination URL parts a segment's slots match against.
|
|
586
|
+
*
|
|
587
|
+
* A slot walks its own sub-tree starting where its owning segment stopped
|
|
588
|
+
* consuming the URL, so the parts are sliced at the length its ancestors
|
|
589
|
+
* consumed. Shared by `resolveSlotProps` and `publishSlotSegmentParams` — both
|
|
590
|
+
* have to slice identically or the two would disagree about which page a slot
|
|
591
|
+
* matched, and only one of them would be right.
|
|
592
|
+
*
|
|
593
|
+
* @internal
|
|
594
|
+
*/
|
|
595
|
+
export function slotUrlParts(
|
|
596
|
+
segment: ManifestSegmentNode,
|
|
597
|
+
match: RouteMatch,
|
|
598
|
+
url: string
|
|
599
|
+
): string[] {
|
|
600
|
+
const pathname = new URL(url, 'http://localhost').pathname;
|
|
601
|
+
// Slice through the owning segment inclusive, to match findSlotMatch, which
|
|
602
|
+
// walks the chain from parentIndex + 1. `consumedUrlParts` drops the root
|
|
603
|
+
// (it consumes no URL part) and honors the interception anchor, so a slot on
|
|
604
|
+
// a retained owner above a climbing marker is not handed parts the target
|
|
605
|
+
// does not contain.
|
|
606
|
+
const sliceAt = consumedPartsThroughSegment(segment, match).length;
|
|
607
|
+
const all = pathname === '/' ? [] : pathname.slice(1).split('/');
|
|
608
|
+
return all.slice(sliceAt);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Seed per-slot segment params for a segment whose layout is being SKIPPED.
|
|
613
|
+
*
|
|
614
|
+
* `resolveSlotProps` seeds these as part of resolving slot props, but a skipped
|
|
615
|
+
* layout never gets there — `route-element-builder` continues past it. Its
|
|
616
|
+
* slots are still mounted on the client, though, so leaving them out of the
|
|
617
|
+
* published map made `useSegmentParams(slotPath)` fall back to the main route's
|
|
618
|
+
* record after any navigation that skipped their parent (TIM-1285).
|
|
619
|
+
*
|
|
620
|
+
* Only the params are seeded. Nothing renders, no access chain runs — a skipped
|
|
621
|
+
* layout's slots are unchanged by definition, which is what made it skippable.
|
|
622
|
+
*
|
|
623
|
+
* @internal
|
|
624
|
+
*/
|
|
625
|
+
export function publishSlotSegmentParams(
|
|
626
|
+
segment: ManifestSegmentNode,
|
|
627
|
+
match: RouteMatch,
|
|
628
|
+
destinationUrl: string,
|
|
629
|
+
parentTreePath: string | undefined
|
|
630
|
+
): void {
|
|
631
|
+
const slotEntries = Object.entries(segment.slots ?? {});
|
|
632
|
+
if (slotEntries.length === 0) return;
|
|
633
|
+
const destinationParts = slotUrlParts(segment, match, destinationUrl);
|
|
634
|
+
|
|
635
|
+
for (const [, slotNode] of slotEntries) {
|
|
636
|
+
const destMatch = matchUrlParts(slotNode as ManifestSegmentNode, destinationParts);
|
|
637
|
+
if (!destMatch) continue;
|
|
638
|
+
const coerced = coerceSlotParams(destMatch.chain, destMatch.params);
|
|
639
|
+
const fullSlotPath = computeSlotSegmentPath(destMatch.chain, parentTreePath);
|
|
640
|
+
if (fullSlotPath && Object.keys(coerced).length > 0) {
|
|
641
|
+
setSlotParams(fullSlotPath, coerced);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
544
646
|
interface ResolveSlotPropsArgs {
|
|
545
647
|
segment: ManifestSegmentNode;
|
|
546
648
|
segmentId: string;
|
|
@@ -591,16 +693,7 @@ export async function resolveSlotProps({
|
|
|
591
693
|
const destinationPathname = destParsed.pathname;
|
|
592
694
|
const departingPathname = depParsed?.pathname ?? null;
|
|
593
695
|
|
|
594
|
-
|
|
595
|
-
// Include the owning segment (segIdx + 1) to match findSlotMatch,
|
|
596
|
-
// which slices at parentIndex + 1. Filter out the root segment
|
|
597
|
-
// (segmentName === '') — it maps to '/' and doesn't consume a URL part.
|
|
598
|
-
const segIdx = match.segments.indexOf(segment);
|
|
599
|
-
const parentSegments =
|
|
600
|
-
segIdx >= 0 ? match.segments.slice(0, segIdx + 1).filter((s) => s.segmentName !== '') : [];
|
|
601
|
-
const rawParams = match.rawSegmentParams ?? match.segmentParams ?? {};
|
|
602
|
-
const parentConsumedParts = extractUrlParts(parentSegments, rawParams);
|
|
603
|
-
const sliceAt = parentConsumedParts.length;
|
|
696
|
+
const sliceAt = consumedPartsThroughSegment(segment, match).length;
|
|
604
697
|
|
|
605
698
|
function splitPathname(pathname: string): string[] {
|
|
606
699
|
return pathname === '/' ? [] : pathname.slice(1).split('/');
|
|
@@ -608,7 +701,7 @@ export async function resolveSlotProps({
|
|
|
608
701
|
|
|
609
702
|
const destinationAll = splitPathname(destinationPathname);
|
|
610
703
|
const departingAll = departingPathname ? splitPathname(departingPathname) : null;
|
|
611
|
-
const destinationParts =
|
|
704
|
+
const destinationParts = slotUrlParts(segment, match, destinationUrl);
|
|
612
705
|
const departingParts = departingAll ? departingAll.slice(sliceAt) : null;
|
|
613
706
|
const clientSlots = clientStateTree?.slots ?? null;
|
|
614
707
|
|
|
@@ -633,15 +726,18 @@ export async function resolveSlotProps({
|
|
|
633
726
|
// files calling getSegmentParams() see the slot's own coerced params,
|
|
634
727
|
// not the main route's. This is the same seeding that resolveSlotElement
|
|
635
728
|
// does, but we need it here for the eager path.
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
.map((s) => s.segmentName)
|
|
639
|
-
.filter(Boolean)
|
|
640
|
-
.join('/');
|
|
641
|
-
const prefix = parentTreePath === '/' ? '' : parentTreePath;
|
|
642
|
-
const fullSlotPath = `${prefix}/${slotSuffix}`;
|
|
729
|
+
let eagerScope: SlotParamScope | undefined;
|
|
730
|
+
if (destMatch) {
|
|
643
731
|
const coerced = coerceSlotParams(destMatch.chain, destMatch.params);
|
|
644
|
-
|
|
732
|
+
const fullSlotPath = computeSlotSegmentPath(destMatch.chain, parentTreePath);
|
|
733
|
+
if (fullSlotPath && Object.keys(coerced).length > 0) {
|
|
734
|
+
setSlotParams(fullSlotPath, coerced);
|
|
735
|
+
}
|
|
736
|
+
// The eager path calls access.ts outside the React tree, so the render
|
|
737
|
+
// scope in resolveSlotElement cannot cover it. Without this, the same
|
|
738
|
+
// access.ts would read the main route's params when the slot is a skip
|
|
739
|
+
// candidate and the slot's own params when it is not.
|
|
740
|
+
eagerScope = (fn) => runWithSlotSegmentParams(fullSlotPath, coerced, fn);
|
|
645
741
|
}
|
|
646
742
|
|
|
647
743
|
// Check non-access skip conditions first. If any of these fail,
|
|
@@ -674,7 +770,7 @@ export async function resolveSlotProps({
|
|
|
674
770
|
// "No access check for default.tsx" (destMatch null = unmatched slot).
|
|
675
771
|
const chainVerdicts =
|
|
676
772
|
isSkipCandidate && destMatch
|
|
677
|
-
? await evaluateSlotAccessChain(slotManifest, destMatch.chain)
|
|
773
|
+
? await evaluateSlotAccessChain(slotManifest, destMatch.chain, eagerScope)
|
|
678
774
|
: [];
|
|
679
775
|
const accessBlocked = chainVerdicts.some((v) => v.verdict !== 'pass');
|
|
680
776
|
const canSkip = isSkipCandidate && !accessBlocked;
|
|
@@ -713,7 +809,16 @@ export async function resolveSlotProps({
|
|
|
713
809
|
let slotRequestDep = false;
|
|
714
810
|
const destLeaf = destMatch?.chain[destMatch.chain.length - 1];
|
|
715
811
|
const hasRenderedPage = destMatch && destLeaf?.page;
|
|
716
|
-
if (
|
|
812
|
+
if (interception) {
|
|
813
|
+
// `destMatch` walks the slot's ordinary children against the
|
|
814
|
+
// destination URL; the interception resolver renders an intercepting
|
|
815
|
+
// child instead, which that walk never sees. So the files analyzed
|
|
816
|
+
// below are not the files that rendered — and `false` here tells the
|
|
817
|
+
// client the slot is reusable. Stay conservative: a needlessly
|
|
818
|
+
// request-dependent slot is a wasted refetch, a wrongly static one is
|
|
819
|
+
// a stale modal.
|
|
820
|
+
slotRequestDep = true;
|
|
821
|
+
} else if (hasRenderedPage) {
|
|
717
822
|
// Check slot root layout (rendered by wrapWithLayout)
|
|
718
823
|
if (
|
|
719
824
|
slotManifest.layout?.filePath &&
|
|
@@ -42,6 +42,12 @@ export interface NavContext {
|
|
|
42
42
|
segmentParams: Record<string, string | string[]>;
|
|
43
43
|
/** Search params from the URL */
|
|
44
44
|
searchParams: Record<string, string>;
|
|
45
|
+
/**
|
|
46
|
+
* Per-slot segment params, keyed by slot tree path. Absent when the request
|
|
47
|
+
* rendered no slot with params of its own. Feeds `useSegmentParams(path)`
|
|
48
|
+
* during SSR and, via `__timber_slot_params`, after hydration (TIM-1285).
|
|
49
|
+
*/
|
|
50
|
+
slotParams?: Record<string, Record<string, string | string[]>>;
|
|
45
51
|
/** The committed HTTP status code */
|
|
46
52
|
statusCode: number;
|
|
47
53
|
/** Response headers from middleware/proxy */
|
|
@@ -64,6 +70,18 @@ export interface NavContext {
|
|
|
64
70
|
/** Request cookies as name→value pairs. Used by useCookie() during SSR
|
|
65
71
|
* to return correct cookie values before hydration. */
|
|
66
72
|
cookies?: Map<string, string>;
|
|
73
|
+
/**
|
|
74
|
+
* Lazy thunk that produces the `<script>self.__timber_segments=…</script>`
|
|
75
|
+
* tag. Invoked by the SSR entry AFTER `renderSsrNodeStream` /
|
|
76
|
+
* `renderSsrStream` returns (shell ready ⇒ AccessGates settled), so the
|
|
77
|
+
* `getFirstDeniedSegmentIndex()` ALS value is authoritative. The result
|
|
78
|
+
* is appended to headHtml before the head injector runs.
|
|
79
|
+
*
|
|
80
|
+
* Undefined when client JS is disabled (no client to consume it).
|
|
81
|
+
* See TIM-1365, TIM-1359.
|
|
82
|
+
*/
|
|
83
|
+
getHeadSegmentScript?: () => string;
|
|
84
|
+
|
|
67
85
|
/**
|
|
68
86
|
* Mutable flag: set by TimberErrorBoundary during SSR when it catches
|
|
69
87
|
* a DenySignal (via digest). This tells the RSC entry that the denial
|