@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
|
@@ -30,6 +30,8 @@ import { checkVersionSkew, applyReloadHeaders } from './version-skew.js';
|
|
|
30
30
|
import { serveStaticMetadataFile, serializeSitemap } from './pipeline-metadata.js';
|
|
31
31
|
import { loadModule } from './safe-load.js';
|
|
32
32
|
import { findInterceptionMatch } from './pipeline-interception.js';
|
|
33
|
+
import { computeSegmentTreePaths } from '../routing/segment-keys.js';
|
|
34
|
+
import { parseClientStateTree } from './state-tree-diff.js';
|
|
33
35
|
import { applyCookieJar, cloneWithMutableHeaders, type ProxyResolver } from './pipeline-helpers.js';
|
|
34
36
|
import { coerceSegmentParams } from './param-coercion.js';
|
|
35
37
|
import { outcomeToResponse, type PhaseOutcome } from './pipeline-outcome.js';
|
|
@@ -37,6 +39,7 @@ import type { InterceptionContext, PipelineConfig, RouteMatch } from './pipeline
|
|
|
37
39
|
import type { MetadataHandler, MetadataRoute, MiddlewareContext } from './types.js';
|
|
38
40
|
import { swallow } from './logger.js';
|
|
39
41
|
import { isDebug } from './debug.js';
|
|
42
|
+
import { isRscRequest } from '../shared/rsc-media-type.js';
|
|
40
43
|
|
|
41
44
|
interface RenderContext {
|
|
42
45
|
canonicalPathname: string;
|
|
@@ -293,17 +296,6 @@ export async function handleRequest(
|
|
|
293
296
|
// outer Server-Timing writer can append without hitting an
|
|
294
297
|
// immutable header bag (e.g. user returns Response.redirect()).
|
|
295
298
|
if (handlerResult instanceof Response) {
|
|
296
|
-
if (method === 'HEAD') {
|
|
297
|
-
const headHeaders = new Headers(handlerResult.headers);
|
|
298
|
-
if (handlerResult.ok && !headHeaders.has('Cache-Control')) {
|
|
299
|
-
headHeaders.set('Cache-Control', 'public, max-age=14400, must-revalidate');
|
|
300
|
-
}
|
|
301
|
-
return new Response(null, {
|
|
302
|
-
status: handlerResult.status,
|
|
303
|
-
statusText: handlerResult.statusText,
|
|
304
|
-
headers: headHeaders,
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
299
|
const res = cloneWithMutableHeaders(handlerResult);
|
|
308
300
|
if (res.ok && !res.headers.has('Cache-Control')) {
|
|
309
301
|
res.headers.set('Cache-Control', 'public, max-age=14400, must-revalidate');
|
|
@@ -376,8 +368,8 @@ export async function handleRequest(
|
|
|
376
368
|
// deployment ID matches the current build. On mismatch, signal the
|
|
377
369
|
// client to do a full page reload instead of returning an RSC payload
|
|
378
370
|
// that references mismatched module IDs.
|
|
379
|
-
const
|
|
380
|
-
if (
|
|
371
|
+
const isRsc = isRscRequest(req);
|
|
372
|
+
if (isRsc) {
|
|
381
373
|
const skewCheck = checkVersionSkew(req);
|
|
382
374
|
if (!skewCheck.ok) {
|
|
383
375
|
const reloadHeaders = new Headers();
|
|
@@ -395,20 +387,81 @@ export async function handleRequest(
|
|
|
395
387
|
// to prevent spoofing from plain HTML navigations or raw HTTP clients.
|
|
396
388
|
// The header value must be a valid relative pathname — reject schemes,
|
|
397
389
|
// authority, and control characters. Canonicalize before matching.
|
|
398
|
-
if (
|
|
390
|
+
if (isRsc && config.interceptionRewrites?.length) {
|
|
399
391
|
const rawSourceUrl = req.headers.get('X-Timber-URL');
|
|
400
392
|
const validatedSourceUrl = rawSourceUrl
|
|
401
393
|
? validateInterceptionHeader(rawSourceUrl, stripTrailingSlash)
|
|
402
394
|
: null;
|
|
403
395
|
if (validatedSourceUrl) {
|
|
404
|
-
|
|
396
|
+
// The scope of an interception is the segment that owns the intercepting
|
|
397
|
+
// slot, so the guard asks whether the route the user is LEAVING renders
|
|
398
|
+
// that segment. Two independent answers to "what is on screen?", tried in
|
|
399
|
+
// order — deferred behind a thunk so neither runs unless a rewrite's
|
|
400
|
+
// target pattern matches.
|
|
401
|
+
//
|
|
402
|
+
// 1. What the client says it has mounted (X-Timber-State-Tree
|
|
403
|
+
// `treePaths`). Authoritative about tree shape and nothing else.
|
|
404
|
+
// 2. What re-matching X-Timber-URL implies. Correct while the address bar
|
|
405
|
+
// still holds the source page, which stops being true the moment a
|
|
406
|
+
// modal is open — the address is then the *intercepted* URL and
|
|
407
|
+
// re-matching it resolves through whichever group owns the canonical
|
|
408
|
+
// page (TIM-1282).
|
|
409
|
+
//
|
|
410
|
+
// Neither is trusted: both are client-supplied, and satisfying the scope
|
|
411
|
+
// only selects which build-time rewrite applies. The rendered pathname is
|
|
412
|
+
// always the rewrite's own `interceptingPrefix`, the server re-derives the
|
|
413
|
+
// rendered chain from it below, and every access.ts runs regardless.
|
|
414
|
+
//
|
|
415
|
+
// They are handed over as two sets in that order rather than one union:
|
|
416
|
+
// each describes a single rendered chain, and only within one chain does
|
|
417
|
+
// "the deepest scope" name the innermost owner (TIM-1318). Unioned, a
|
|
418
|
+
// deeper scope from the address-implied canonical branch — which after
|
|
419
|
+
// (2) may be a route group the client never mounted — would outrank the
|
|
420
|
+
// owner actually on screen.
|
|
421
|
+
const resolveSourceScopes = () => {
|
|
422
|
+
const mounted = new Set(parseClientStateTree(req)?.treePaths ?? []);
|
|
423
|
+
const sourceRoute = config.matchRoute(validatedSourceUrl);
|
|
424
|
+
const addressed = new Set(sourceRoute ? computeSegmentTreePaths(sourceRoute.segments) : []);
|
|
425
|
+
const scopes = [mounted, addressed].filter((scope) => scope.size > 0);
|
|
426
|
+
// Neither channel produced a tree — no scope can contain anything, and
|
|
427
|
+
// an origin that matches no route is not a legitimate soft navigation.
|
|
428
|
+
return scopes.length > 0 ? scopes : null;
|
|
429
|
+
};
|
|
430
|
+
const rewrite = findInterceptionMatch(
|
|
405
431
|
canonicalPathname,
|
|
406
|
-
|
|
432
|
+
resolveSourceScopes,
|
|
407
433
|
config.interceptionRewrites
|
|
408
434
|
);
|
|
409
|
-
if (
|
|
410
|
-
|
|
411
|
-
|
|
435
|
+
if (rewrite?.mode === 'children') {
|
|
436
|
+
// Sibling layout swap (TIM-1280). Nothing is re-matched: the resolver
|
|
437
|
+
// addresses the owner directory by tree path and returns the chain
|
|
438
|
+
// `[…owner ancestors, interceptingNode, …descendants]`, which renders
|
|
439
|
+
// as an ordinary route. `interception` stays unset — there is no slot
|
|
440
|
+
// to intercept into, and setting it would additionally arm the modal
|
|
441
|
+
// path for every slot in the tree we are about to render.
|
|
442
|
+
//
|
|
443
|
+
// A null here means the scope named nothing in this manifest, or no
|
|
444
|
+
// intercepting child matched the target. Both fall through to the
|
|
445
|
+
// canonical route, which is what a hard navigation renders.
|
|
446
|
+
const interceptedMatch = config.matchInterceptedRoute?.(
|
|
447
|
+
canonicalPathname,
|
|
448
|
+
rewrite.interceptingScope
|
|
449
|
+
);
|
|
450
|
+
if (interceptedMatch) match = interceptedMatch;
|
|
451
|
+
} else if (rewrite) {
|
|
452
|
+
const sourceMatch = config.matchRoute(rewrite.interceptingPrefix);
|
|
453
|
+
// The scope test above was about the route the user is LEAVING. The
|
|
454
|
+
// route we are about to render is a different one —
|
|
455
|
+
// `interceptingPrefix` is URL-derived, and route groups are
|
|
456
|
+
// URL-invisible, so `/` can resolve into a sibling group that does not
|
|
457
|
+
// own the slot. Rendering that would reproduce the TIM-1278 failure on
|
|
458
|
+
// a valid tree: the rewrite hijacks the navigation and no slot exists
|
|
459
|
+
// to render the intercepting page. Fall through to the canonical
|
|
460
|
+
// target route instead.
|
|
461
|
+
const rendersTheSlot =
|
|
462
|
+
sourceMatch !== null &&
|
|
463
|
+
computeSegmentTreePaths(sourceMatch.segments).includes(rewrite.interceptingScope);
|
|
464
|
+
if (rendersTheSlot) {
|
|
412
465
|
match = sourceMatch;
|
|
413
466
|
interception = { targetPathname: canonicalPathname };
|
|
414
467
|
}
|
|
@@ -459,7 +512,7 @@ export async function handleRequest(
|
|
|
459
512
|
| { route?: unknown; page?: unknown }
|
|
460
513
|
| undefined;
|
|
461
514
|
const isApiRoute = leafSegment?.route && !leafSegment?.page;
|
|
462
|
-
if (config.earlyHints && !
|
|
515
|
+
if (config.earlyHints && !isRsc && !isApiRoute) {
|
|
463
516
|
try {
|
|
464
517
|
await config.earlyHints(match, req, responseHeaders);
|
|
465
518
|
} catch (err) {
|
|
@@ -468,8 +521,8 @@ export async function handleRequest(
|
|
|
468
521
|
}
|
|
469
522
|
|
|
470
523
|
// Stage 2c: Param coercion (before middleware)
|
|
471
|
-
//
|
|
472
|
-
//
|
|
524
|
+
// Coerce raw string params through the app/schema.ts codecs.
|
|
525
|
+
// Coercion failure → 404
|
|
473
526
|
// (middleware never runs). See design/07-routing.md §"Where Coercion Runs"
|
|
474
527
|
//
|
|
475
528
|
// Snapshot raw params before coercion — slot resolution needs the
|
|
@@ -488,9 +541,8 @@ export async function handleRequest(
|
|
|
488
541
|
`[timber] Param coercion failed for ${method} ${canonicalPathname} — responding with 404\n` +
|
|
489
542
|
` Matched segments: ${segmentChain}\n` +
|
|
490
543
|
` Error: ${error.message}\n` +
|
|
491
|
-
` This usually means a
|
|
492
|
-
` Check
|
|
493
|
-
` that don't appear at every route depth (e.g. year, month, day).`
|
|
544
|
+
` This usually means a codec in app/schema.ts rejected the URL value.\n` +
|
|
545
|
+
` Check the codec for the bracket key of the segment named above.`
|
|
494
546
|
);
|
|
495
547
|
}
|
|
496
548
|
// For API routes (route.ts), return a bare 404 — not an HTML page.
|
package/src/server/pipeline.ts
CHANGED
|
@@ -35,6 +35,9 @@ import { makeProxyResolver } from './pipeline-helpers.js';
|
|
|
35
35
|
import { handleRequest, runProxyPhase } from './pipeline-phases.js';
|
|
36
36
|
import { outcomeToResponse } from './pipeline-outcome.js';
|
|
37
37
|
import { canonicalize } from './canonicalize.js';
|
|
38
|
+
import { enforceRscCacheKey, hardenRscPayloadResponse } from './rsc-cache-key-guard.js';
|
|
39
|
+
import { stripHeadBody } from './head-response.js';
|
|
40
|
+
import { RSC_KEY_HEADERS } from '../shared/rsc-cache-key.js';
|
|
38
41
|
import { isDebug } from './debug.js';
|
|
39
42
|
|
|
40
43
|
// ─── Route Match Result ────────────────────────────────────────────────────
|
|
@@ -142,6 +145,17 @@ export interface PipelineConfig {
|
|
|
142
145
|
* See design/07-routing.md §"Intercepting Routes"
|
|
143
146
|
*/
|
|
144
147
|
interceptionRewrites?: import('../routing/interception.js').InterceptionRewrite[];
|
|
148
|
+
/**
|
|
149
|
+
* Resolver for children-path interceptions — the sibling layout swap.
|
|
150
|
+
*
|
|
151
|
+
* Separate from `matchRoute` because it addresses the owner directory by
|
|
152
|
+
* tree path rather than by pathname, which needs the manifest tree itself.
|
|
153
|
+
* Omitting it disables the `'children'` interception shape and leaves the
|
|
154
|
+
* canonical route rendering, which is the fail-safe outcome.
|
|
155
|
+
*
|
|
156
|
+
* See design/07-routing.md §"Intercepting Routes"
|
|
157
|
+
*/
|
|
158
|
+
matchInterceptedRoute?: import('./children-interception.js').InterceptedRouteMatcher;
|
|
145
159
|
/**
|
|
146
160
|
* Control Server-Timing header output.
|
|
147
161
|
*
|
|
@@ -393,17 +407,30 @@ export function createPipeline(config: PipelineConfig): (req: Request) => Promis
|
|
|
393
407
|
// DevSpanProcessor reads this for tree/summary output.
|
|
394
408
|
await setSpanAttribute('http.response.status_code', result.status);
|
|
395
409
|
|
|
396
|
-
// Vary
|
|
397
|
-
// separately for the same URL. Without this, a shared cache may
|
|
398
|
-
// serve an RSC Flight payload to a browser expecting HTML (or
|
|
399
|
-
// vice versa). See GHSA-wfc6-r584-vfw7, design/13-security.md.
|
|
410
|
+
// Vary on exactly the headers the `_rsc` cache key hashes.
|
|
400
411
|
//
|
|
401
|
-
//
|
|
402
|
-
//
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
412
|
+
// Those two mechanisms answer the same question — which request
|
|
413
|
+
// headers change this response — so they are the same list, not
|
|
414
|
+
// two lists that happen to agree. A separate hand-written list
|
|
415
|
+
// here drifted from `RSC_KEY_HEADERS` the moment the key set
|
|
416
|
+
// grew, and the drift is invisible: `Vary` is the *fallback*
|
|
417
|
+
// layer, used only when a cache omits the query from its key,
|
|
418
|
+
// which is precisely when nobody is watching (TIM-1268).
|
|
419
|
+
//
|
|
420
|
+
// Why any of it: without `Vary: Accept` a shared cache may serve
|
|
421
|
+
// an RSC Flight payload to a browser expecting HTML, or the
|
|
422
|
+
// reverse (GHSA-wfc6-r584-vfw7, design/13-security.md). The
|
|
423
|
+
// `_rsc` param puts that distinction in the URL, so correctness
|
|
424
|
+
// no longer *depends* on `Vary` — which is opt-in configuration
|
|
425
|
+
// on Cloudflare and CloudFront. `Vary` covers the requests that
|
|
426
|
+
// arrive without the param: a stripped query, a hand-rolled
|
|
427
|
+
// client.
|
|
428
|
+
//
|
|
429
|
+
// It costs no hit rate. Document requests send no `X-Timber-*`
|
|
430
|
+
// header at all, so they collapse to a single cache key; payload
|
|
431
|
+
// requests are already fragmented by these same values via the
|
|
432
|
+
// URL.
|
|
433
|
+
const varyTokens = [...RSC_KEY_HEADERS];
|
|
407
434
|
const existingVary = result.headers.get('Vary');
|
|
408
435
|
const existingTokens = existingVary
|
|
409
436
|
? existingVary
|
|
@@ -419,6 +446,26 @@ export function createPipeline(config: PipelineConfig): (req: Request) => Promis
|
|
|
419
446
|
);
|
|
420
447
|
}
|
|
421
448
|
|
|
449
|
+
// A request may only claim an `_rsc` cache key that matches its
|
|
450
|
+
// own headers. The key is predictable by design, so without this
|
|
451
|
+
// a caller could render its own variant under another client's
|
|
452
|
+
// key and have a shared cache store it there (TIM-1268).
|
|
453
|
+
await enforceRscCacheKey(canonicalReq, result);
|
|
454
|
+
|
|
455
|
+
// Flight payloads must never be sniffable into a renderable
|
|
456
|
+
// type — applied at this single exit so every producer is
|
|
457
|
+
// covered, not just the success path (TIM-1268).
|
|
458
|
+
hardenRscPayloadResponse(result);
|
|
459
|
+
|
|
460
|
+
// A HEAD response carries the headers its GET would produce and
|
|
461
|
+
// no content (RFC 9110 §9.3.2). Applied at this single exit so
|
|
462
|
+
// every response shape is covered — page render, API route,
|
|
463
|
+
// metadata route, deny page, error page, proxy short-circuit —
|
|
464
|
+
// rather than per-renderer, where it was missing for page
|
|
465
|
+
// renders entirely (TIM-1324). The render still runs: the
|
|
466
|
+
// headers have to be the ones a GET would have sent.
|
|
467
|
+
result = stripHeadBody(result, method);
|
|
468
|
+
|
|
422
469
|
// Append Server-Timing header based on configured mode.
|
|
423
470
|
// Header mutability is guaranteed by the producer-side clone
|
|
424
471
|
// in `outcomeToResponse` and the metadata-route / auto-sitemap
|
|
@@ -173,17 +173,17 @@ const registry = new Map<string, PrebuiltRegistration>();
|
|
|
173
173
|
*/
|
|
174
174
|
function resolveKeyParams(store: {
|
|
175
175
|
segmentParams?: Record<string, string | string[]>;
|
|
176
|
-
|
|
176
|
+
slotParams?: Record<string, Record<string, string | string[]>>;
|
|
177
177
|
}): Record<string, string | string[]> | null {
|
|
178
178
|
const main = store.segmentParams ?? {};
|
|
179
|
-
const
|
|
180
|
-
if (!
|
|
179
|
+
const slots = store.slotParams;
|
|
180
|
+
if (!slots || Object.keys(slots).length === 0) return main;
|
|
181
181
|
// Spread before canonicalizing: param records are framework-produced
|
|
182
182
|
// Object.create(null) objects (prototype-pollution defense) — normalize
|
|
183
183
|
// the records themselves, let nested values face the full allowlist.
|
|
184
184
|
const mainCanonical = tryCanonicalize({ ...main });
|
|
185
185
|
if (mainCanonical === null) return null;
|
|
186
|
-
for (const slotParams of
|
|
186
|
+
for (const slotParams of Object.values(slots)) {
|
|
187
187
|
const merged = tryCanonicalize({ ...main, ...slotParams });
|
|
188
188
|
if (merged === null || merged !== mainCanonical) return null;
|
|
189
189
|
}
|
package/src/server/primitives.ts
CHANGED
|
@@ -113,6 +113,31 @@ export class DenySignal extends Error {
|
|
|
113
113
|
readonly status: number;
|
|
114
114
|
readonly data: JsonSerializable | undefined;
|
|
115
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Segment key of the segment that owns the deny page matched for this
|
|
118
|
+
* signal — set by a framework catch site that matched an entry it may not
|
|
119
|
+
* render at its own position, and re-threw so the boundary at the owning
|
|
120
|
+
* segment renders it instead.
|
|
121
|
+
*
|
|
122
|
+
* Written only by the three in-tree catch sites (`PageDenyBoundary`,
|
|
123
|
+
* `TracedLayout`, `AccessGate`), never by user code. Two readers depend on
|
|
124
|
+
* it: `TimberErrorBoundary`, which declines any deny whose `ownerKey` is
|
|
125
|
+
* not its own `segmentKey`, and `buildRscPayloadResponse`, which keeps
|
|
126
|
+
* streaming instead of re-rendering when it is set.
|
|
127
|
+
*
|
|
128
|
+
* See design/04-authorization.md §"Where a Deny Page Renders", TIM-1356.
|
|
129
|
+
*/
|
|
130
|
+
ownerKey?: string;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* When true, the pipeline must respond with `rscErrorEnvelope` so the
|
|
134
|
+
* client hard-navigates instead of attempting to render a deny page
|
|
135
|
+
* from the RSC payload. Set by AccessGate when a skipped segment
|
|
136
|
+
* denies — rendering the deny page in-tree would appear inside the
|
|
137
|
+
* client's cached layout chrome (TIM-1074). See TIM-1363.
|
|
138
|
+
*/
|
|
139
|
+
_hardNavigate?: boolean;
|
|
140
|
+
|
|
116
141
|
constructor(status: number, data?: JsonSerializable) {
|
|
117
142
|
super(`Access denied with status ${status}`);
|
|
118
143
|
this.name = 'DenySignal';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Publishing the request's params onto the RSC payload.
|
|
3
|
+
*
|
|
4
|
+
* Every tree the server sends the browser goes through here: the ordinary
|
|
5
|
+
* route payload (`route-element-builder.ts`), both deny renderers
|
|
6
|
+
* (`deny-renderer.ts`) and the error renderer. One helper rather than a wrap
|
|
7
|
+
* at each site, because these previously *were* separate sites — a response
|
|
8
|
+
* header, an inline script, and a second copy of each on the deny path — and
|
|
9
|
+
* the deny path shipping empty params was the bug that came of it (TIM-1285).
|
|
10
|
+
*
|
|
11
|
+
* Reads the request store rather than taking params as an argument, so a
|
|
12
|
+
* caller cannot pass a different record than the one the request rendered
|
|
13
|
+
* with. See design/41-global-params.md §"Transport".
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { toPlainRecord } from '../shared/param-value.js';
|
|
17
|
+
import type { RscPayloadRoot } from '../shared/payload-root.js';
|
|
18
|
+
import { getSegmentParamsForClient, getSlotParamsForClient } from './request-context.js';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Build the payload root: the tree, and the params it rendered with beside it.
|
|
22
|
+
*
|
|
23
|
+
* The params are a *sibling* of the tree, not a provider around it. A provider
|
|
24
|
+
* inside the payload is shadowed by the retained tree's own provider on a
|
|
25
|
+
* partial navigation, and its props are not readable at the instant the root
|
|
26
|
+
* row resolves — see `shared/payload-root.ts` for both, and TIM-1297. The
|
|
27
|
+
* client renders the one provider readers see, above the merge point.
|
|
28
|
+
*
|
|
29
|
+
* The records are flattened to plain objects on the way out. Every param
|
|
30
|
+
* record the framework builds is `Object.create(null)` (design/13-security.md
|
|
31
|
+
* #36b/#36c), and React Flight rejects a null prototype — "Classes or null
|
|
32
|
+
* prototypes are not supported". `ParamsProvider` restores the prototype on
|
|
33
|
+
* the other side, so what the app reads is unchanged; only the wire
|
|
34
|
+
* representation differs.
|
|
35
|
+
*/
|
|
36
|
+
export function withPublishedParams(element: React.ReactElement): RscPayloadRoot {
|
|
37
|
+
return {
|
|
38
|
+
tree: element,
|
|
39
|
+
params: toPlainRecord(getSegmentParamsForClient()),
|
|
40
|
+
slotParams: toPlainRecord(getSlotParamsForClient() ?? null),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -15,9 +15,14 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { requestContextAls, type RequestContextStore } from './als-registry.js';
|
|
18
|
-
import { _setGetSearchParamsFn } from '../
|
|
19
|
-
import { _setGetSegmentParamsFn } from '../segment-params/define.js';
|
|
18
|
+
import { _setGetSearchParamsFn, _setGetSegmentParamsFn } from '../shared/als-slots.js';
|
|
20
19
|
import { consumeSeededCookies } from './cookie-context.js';
|
|
20
|
+
import { appVisibleSearch } from '../shared/rsc-cache-key.js';
|
|
21
|
+
import {
|
|
22
|
+
mergeSlotParams,
|
|
23
|
+
resolveSegmentParams,
|
|
24
|
+
type SlotParamsRecord,
|
|
25
|
+
} from '../shared/slot-params.js';
|
|
21
26
|
import { isDebug } from './debug.js';
|
|
22
27
|
|
|
23
28
|
// Re-export the ALS for framework-internal consumers that need direct access.
|
|
@@ -111,17 +116,13 @@ export function getSegmentParams(segmentPath?: string): Record<string, string |
|
|
|
111
116
|
);
|
|
112
117
|
}
|
|
113
118
|
|
|
114
|
-
//
|
|
115
|
-
//
|
|
116
|
-
//
|
|
117
|
-
//
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
// This ensures params from segments above the slot (that the slot
|
|
122
|
-
// doesn't re-derive) are still available.
|
|
123
|
-
return { ...store.segmentParams, ...slotParams };
|
|
124
|
-
}
|
|
119
|
+
// Parallel slots may derive a param the main route never had, or the same
|
|
120
|
+
// name with a different type (catch-all [...year] → string[] vs dynamic
|
|
121
|
+
// [year] → string). `resolveSegmentParams` is the shared definition the
|
|
122
|
+
// client's useSegmentParams() also reads, so the two halves of one
|
|
123
|
+
// documented API cannot drift (TIM-1285).
|
|
124
|
+
const slotResolved = resolveSegmentParams(store.segmentParams, store.slotParams, segmentPath);
|
|
125
|
+
if (slotResolved !== store.segmentParams) return slotResolved;
|
|
125
126
|
|
|
126
127
|
// Dev-mode validation: warn when the segment path expects dynamic
|
|
127
128
|
// segments that don't exist on the actual matched segment. This catches
|
|
@@ -173,8 +174,82 @@ export function setSlotParams(
|
|
|
173
174
|
): void {
|
|
174
175
|
const store = requestContextAls.getStore();
|
|
175
176
|
if (!store) return; // non-throwing — optional diagnostic
|
|
176
|
-
if (!store.
|
|
177
|
-
store.
|
|
177
|
+
if (!store.slotParams) store.slotParams = Object.create(null) as SlotParamsRecord;
|
|
178
|
+
store.slotParams[segmentPath] = params;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Run `fn` with the segment params a parallel slot's own chain produced, so
|
|
183
|
+
* a no-arg `getSegmentParams()` inside that slot returns the slot's params
|
|
184
|
+
* rather than the main route's.
|
|
185
|
+
*
|
|
186
|
+
* ALS is the only channel available: the slot's page and layouts are plain
|
|
187
|
+
* components that receive no params prop and cannot know their own tree
|
|
188
|
+
* path, so they have no way to ask for the keyed overload. The slot resolver
|
|
189
|
+
* invokes each of them itself (page through `SafeSlotPage`, layouts and
|
|
190
|
+
* access functions through their wrappers), and this scopes those calls.
|
|
191
|
+
*
|
|
192
|
+
* The store is wrapped in a Proxy rather than cloned. Everything a slot
|
|
193
|
+
* renders can write to the *live* request store — `setDenyStatus` from an
|
|
194
|
+
* access gate, cookie writes, the flush flag — and a clone would silently
|
|
195
|
+
* strand those writes in a copy the pipeline never reads. Only the two
|
|
196
|
+
* params fields are shadowed; every other read and every write passes
|
|
197
|
+
* straight through to the real store.
|
|
198
|
+
*
|
|
199
|
+
* Returns `fn()`'s result unchanged, and no-ops outside a request context or
|
|
200
|
+
* before route matching has set the main params. See TIM-1283.
|
|
201
|
+
*
|
|
202
|
+
* @internal — framework use only
|
|
203
|
+
*/
|
|
204
|
+
export function runWithSlotSegmentParams<T>(
|
|
205
|
+
slotSegmentPath: string | undefined,
|
|
206
|
+
slotParams: Record<string, string | string[]>,
|
|
207
|
+
fn: () => T
|
|
208
|
+
): T {
|
|
209
|
+
const store = requestContextAls.getStore();
|
|
210
|
+
if (!store?.segmentParams) return fn();
|
|
211
|
+
|
|
212
|
+
const merged = mergeSlotParams(store.segmentParams, slotParams);
|
|
213
|
+
const matchedSegmentPath = slotSegmentPath ?? store.matchedSegmentPath;
|
|
214
|
+
const scoped = new Proxy(store, {
|
|
215
|
+
get(target, prop, receiver) {
|
|
216
|
+
if (prop === 'segmentParams') return merged;
|
|
217
|
+
if (prop === 'matchedSegmentPath') return matchedSegmentPath;
|
|
218
|
+
return Reflect.get(target, prop, receiver);
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
return requestContextAls.run(scoped, fn);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* The main route's coerced params, or an empty record before route matching.
|
|
226
|
+
*
|
|
227
|
+
* Non-throwing, unlike `getSegmentParams()`: the deny payload path publishes
|
|
228
|
+
* params from wherever the pipeline happened to fail, which may be before
|
|
229
|
+
* matching completed.
|
|
230
|
+
*
|
|
231
|
+
* @internal — framework use only. See TIM-1285.
|
|
232
|
+
*/
|
|
233
|
+
export function getSegmentParamsForClient(): Record<string, string | string[]> {
|
|
234
|
+
return requestContextAls.getStore()?.segmentParams ?? {};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* The per-slot params to publish to the client, or `undefined` when the
|
|
239
|
+
* request rendered no slot with params of its own.
|
|
240
|
+
*
|
|
241
|
+
* The client cannot derive these: a slot matches the URL through its own
|
|
242
|
+
* sub-tree, and the browser only ever sees the main route's record. Reading
|
|
243
|
+
* the map here — rather than threading it back out of the element builder —
|
|
244
|
+
* keeps one producer, since `setSlotParams` already writes it during slot
|
|
245
|
+
* resolution and every serialization site runs after that completes.
|
|
246
|
+
*
|
|
247
|
+
* @internal — framework use only. See TIM-1285.
|
|
248
|
+
*/
|
|
249
|
+
export function getSlotParamsForClient(): SlotParamsRecord | undefined {
|
|
250
|
+
const store = requestContextAls.getStore();
|
|
251
|
+
if (!store?.slotParams) return undefined;
|
|
252
|
+
return Object.keys(store.slotParams).length > 0 ? store.slotParams : undefined;
|
|
178
253
|
}
|
|
179
254
|
|
|
180
255
|
/**
|
|
@@ -247,7 +322,7 @@ export type ReadonlyHeaders = Pick<
|
|
|
247
322
|
*/
|
|
248
323
|
export function runWithRequestContext<T>(req: Request, fn: () => T): T {
|
|
249
324
|
const originalCopy = new Headers(req.headers);
|
|
250
|
-
const
|
|
325
|
+
const appVisible = appVisibleSearch(new URL(req.url));
|
|
251
326
|
const seed = consumeSeededCookies(req);
|
|
252
327
|
const store: RequestContextStore = {
|
|
253
328
|
headers: freezeHeaders(req.headers),
|
|
@@ -257,8 +332,12 @@ export function runWithRequestContext<T>(req: Request, fn: () => T): T {
|
|
|
257
332
|
// gated on `parsedCookies` being undefined.
|
|
258
333
|
cookieHeader: seed ? '' : (req.headers.get('cookie') ?? ''),
|
|
259
334
|
parsedCookies: seed,
|
|
260
|
-
searchParams
|
|
261
|
-
|
|
335
|
+
// Not `parsedUrl.searchParams` / `parsedUrl.search`: this store is what
|
|
336
|
+
// `getSearchParams()` and `redirect({ preserveSearchParams })` read, and
|
|
337
|
+
// the RSC payload URL carries the framework's `_rsc` cache key that no
|
|
338
|
+
// other request has (TIM-1272).
|
|
339
|
+
searchParams: appVisible.params,
|
|
340
|
+
searchString: appVisible.search,
|
|
262
341
|
cookieJar: new Map(),
|
|
263
342
|
flushed: false,
|
|
264
343
|
mutableContext: false,
|