@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
|
@@ -6,14 +6,12 @@
|
|
|
6
6
|
|
|
7
7
|
import type { ManifestSegmentNode } from '../route-matcher.js';
|
|
8
8
|
import { swallow } from '../logger.js';
|
|
9
|
-
import { computeSegmentKeys } from '
|
|
9
|
+
import { computeSegmentKeys, computeSegmentTreePaths } from '../../routing/segment-keys.js';
|
|
10
|
+
import type { SegmentInfo } from '../../shared/segment-info.js';
|
|
10
11
|
import type { SlotSkipEntry } from '../slot-resolver.js';
|
|
11
12
|
import { isClientReference } from '../route-element-builder.js';
|
|
12
13
|
import { isStaticRequestDependent } from '../request-dep.js';
|
|
13
14
|
|
|
14
|
-
/** RSC content type for client navigation payload requests. */
|
|
15
|
-
export const RSC_CONTENT_TYPE = 'text/x-component';
|
|
16
|
-
|
|
17
15
|
/**
|
|
18
16
|
* Create a debug channel sink that discards all debug data.
|
|
19
17
|
*
|
|
@@ -180,16 +178,6 @@ export function parseDebugRows(text: string): DebugComponentEntry[] {
|
|
|
180
178
|
*
|
|
181
179
|
* See design/07-routing.md §"Server Diffing Rules".
|
|
182
180
|
*/
|
|
183
|
-
interface SegmentInfoEntry {
|
|
184
|
-
path: string;
|
|
185
|
-
segmentId?: string;
|
|
186
|
-
isRequestDependent: boolean;
|
|
187
|
-
slot?: boolean;
|
|
188
|
-
parentSegment?: string;
|
|
189
|
-
denied?: boolean;
|
|
190
|
-
skipped?: boolean;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
181
|
export function buildSegmentInfo(
|
|
194
182
|
segments: ManifestSegmentNode[],
|
|
195
183
|
layoutComponents: Array<{
|
|
@@ -197,17 +185,34 @@ export function buildSegmentInfo(
|
|
|
197
185
|
segment: ManifestSegmentNode;
|
|
198
186
|
}>,
|
|
199
187
|
slotSkipInfo?: SlotSkipEntry[],
|
|
200
|
-
skippedSegmentKeys?: string[]
|
|
201
|
-
|
|
188
|
+
skippedSegmentKeys?: string[],
|
|
189
|
+
firstDeniedIndex = Infinity
|
|
190
|
+
): SegmentInfo[] {
|
|
202
191
|
const layoutBySegment = new Map(
|
|
203
192
|
layoutComponents.map(({ component, segment }) => [segment, component])
|
|
204
193
|
);
|
|
205
194
|
|
|
206
195
|
const segmentKeys = computeSegmentKeys(segments);
|
|
196
|
+
// The scope identity interception compares against. Emitted for every
|
|
197
|
+
// layout-bearing segment, which is exactly the set that can own a scope:
|
|
198
|
+
// slots are only resolved for segments whose layout renders
|
|
199
|
+
// (`route-element-builder.ts`), so a segment with no layout can never hold
|
|
200
|
+
// an intercepting slot. See TIM-1282.
|
|
201
|
+
const treePaths = computeSegmentTreePaths(segments);
|
|
207
202
|
const skippedSet = skippedSegmentKeys ? new Set(skippedSegmentKeys) : null;
|
|
208
|
-
const result:
|
|
203
|
+
const result: SegmentInfo[] = [];
|
|
209
204
|
|
|
205
|
+
// Deepest segment that actually mounted, exclusive.
|
|
206
|
+
//
|
|
210
207
|
for (let i = 0; i < segments.length; i++) {
|
|
208
|
+
// A denied segment's AccessGate renders the deny page in place of the
|
|
209
|
+
// layout, so this segment and everything below it never mounted. Their
|
|
210
|
+
// layouts were still loaded — `layoutComponents` holds every module the
|
|
211
|
+
// chain pulled in, not every component that rendered — so without this
|
|
212
|
+
// the client is told it has a tree it does not, and `treePaths` reports a
|
|
213
|
+
// slot owner whose slot was never resolved (TIM-1282).
|
|
214
|
+
if (i >= firstDeniedIndex) break;
|
|
215
|
+
|
|
211
216
|
const segment = segments[i];
|
|
212
217
|
const component = layoutBySegment.get(segment);
|
|
213
218
|
|
|
@@ -225,8 +230,9 @@ export function buildSegmentInfo(
|
|
|
225
230
|
isRequestDependent = true;
|
|
226
231
|
}
|
|
227
232
|
|
|
228
|
-
const entry:
|
|
233
|
+
const entry: SegmentInfo = {
|
|
229
234
|
path: segment.urlPath,
|
|
235
|
+
treePath: treePaths[i],
|
|
230
236
|
isRequestDependent,
|
|
231
237
|
};
|
|
232
238
|
if (segmentId !== segment.urlPath) {
|
|
@@ -237,7 +243,7 @@ export function buildSegmentInfo(
|
|
|
237
243
|
|
|
238
244
|
if (slotSkipInfo) {
|
|
239
245
|
for (const slot of slotSkipInfo) {
|
|
240
|
-
const entry:
|
|
246
|
+
const entry: SegmentInfo = {
|
|
241
247
|
path: slot.slotKey,
|
|
242
248
|
isRequestDependent: slot.isRequestDependent,
|
|
243
249
|
slot: true,
|
|
@@ -252,15 +258,6 @@ export function buildSegmentInfo(
|
|
|
252
258
|
return result;
|
|
253
259
|
}
|
|
254
260
|
|
|
255
|
-
/**
|
|
256
|
-
* Check if a request is asking for an RSC payload (client navigation)
|
|
257
|
-
* rather than full HTML. Client-side navigation sends Accept: text/x-component.
|
|
258
|
-
*/
|
|
259
|
-
export function isRscPayloadRequest(req: Request): boolean {
|
|
260
|
-
const accept = req.headers.get('Accept') ?? '';
|
|
261
|
-
return accept.includes(RSC_CONTENT_TYPE);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
261
|
// Re-export consolidated helpers from their single source of truth.
|
|
265
262
|
// Callers within rsc-entry/ import from this file; the implementations
|
|
266
263
|
// live in pipeline-helpers.ts (buildRedirectResponse) and render-utils.ts
|
|
@@ -270,3 +267,11 @@ export { isAbortError } from '../render-utils.js';
|
|
|
270
267
|
|
|
271
268
|
// escapeHtml is now imported from server/utils/escape-html.ts
|
|
272
269
|
export { escapeHtml } from '../utils/escape-html.js';
|
|
270
|
+
|
|
271
|
+
// Params no longer travel beside the payload. The payload root is the
|
|
272
|
+
// outermost node of every tree the server sends (`server/publish-params.ts`),
|
|
273
|
+
// so the record arrives as props React Flight serializes — which is what
|
|
274
|
+
// makes a `Date`- or `bigint`-valued codec result survive the trip, where
|
|
275
|
+
// `JSON.stringify` flattened it or threw. The response headers, the inline
|
|
276
|
+
// bootstrap scripts, and the two build-time manifest fields that used to
|
|
277
|
+
// carry it are all gone. See TIM-1294, design/41-global-params.md.
|
|
@@ -25,11 +25,6 @@
|
|
|
25
25
|
|
|
26
26
|
// @ts-expect-error — virtual module provided by timber-routing plugin
|
|
27
27
|
import routeManifest from 'virtual:timber-route-manifest';
|
|
28
|
-
// TIM-830: Populate the search-params registry eagerly so <Link> can
|
|
29
|
-
// serialize flat `Partial<T>` values synchronously in RSC renders.
|
|
30
|
-
// The module has side effects only — no exports consumed here.
|
|
31
|
-
// @ts-expect-error — virtual module provided by timber-routing plugin
|
|
32
|
-
import 'virtual:timber-search-params-registry';
|
|
33
28
|
// @ts-expect-error — virtual module provided by timber-entries plugin
|
|
34
29
|
import config from 'virtual:timber-config';
|
|
35
30
|
// @ts-expect-error — virtual module provided by timber-build-manifest plugin
|
|
@@ -54,6 +49,7 @@ import type { InterceptionContext, PipelineConfig, RouteMatch } from '../pipelin
|
|
|
54
49
|
import { createPipeline } from '../pipeline.js';
|
|
55
50
|
import { coerceSegmentParams, setGlobalSchemaCodecs } from '../param-coercion.js';
|
|
56
51
|
import { createMetadataRouteMatcher, createRouteMatcher } from '../route-matcher.js';
|
|
52
|
+
import { createInterceptedRouteMatcher } from '../children-interception.js';
|
|
57
53
|
import { initDevTracing } from '../tracing.js';
|
|
58
54
|
import { createRevalidateRendererFactory } from './revalidate-renderer.js';
|
|
59
55
|
|
|
@@ -307,38 +303,45 @@ async function createRequestHandler(manifest: typeof routeManifest, runtimeConfi
|
|
|
307
303
|
);
|
|
308
304
|
},
|
|
309
305
|
renderNoMatch: async (req: Request, responseHeaders: Headers) => {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
//
|
|
318
|
-
//
|
|
319
|
-
// page that lists available routes and suggests similar paths.
|
|
320
|
-
// Only for GET requests that accept HTML — HEAD/API/RSC clients
|
|
321
|
-
// should receive the bare 404 without an HTML body (TIM-793).
|
|
306
|
+
// In dev mode, when the app authored no 404 page, substitute a helpful
|
|
307
|
+
// page listing available routes and suggesting similar paths in place
|
|
308
|
+
// of the framework default. Only for GET requests that accept HTML —
|
|
309
|
+
// HEAD/API/RSC clients must not receive an HTML body (TIM-793).
|
|
310
|
+
//
|
|
311
|
+
// This is passed *into* the chain rather than applied to its result:
|
|
312
|
+
// the old version detected "no user-defined 404.tsx" by testing the
|
|
313
|
+
// response for an empty body, which stopped being true once the chain
|
|
314
|
+
// grew a real terminal (TIM-1258).
|
|
322
315
|
const acceptsHtml = (req.headers.get('accept') ?? '').includes('text/html');
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
316
|
+
const wantsDev404 = isDev && req.method === 'GET' && acceptsHtml;
|
|
317
|
+
|
|
318
|
+
// The dev page is loaded up front: the chain's terminal is synchronous,
|
|
319
|
+
// and dev-tools must stay out of the production bundle (dynamic import).
|
|
320
|
+
const devPage = wantsDev404
|
|
321
|
+
? await import('../../dev-tools/404-page.js').catch(() => null)
|
|
322
|
+
: null;
|
|
323
|
+
|
|
324
|
+
const defaultPage = devPage
|
|
325
|
+
? (_status: number, headers: Headers) => {
|
|
326
|
+
const routes = devPage.collectRoutes(manifest.root);
|
|
327
|
+
const pathname = new URL(req.url).pathname;
|
|
328
|
+
// devHmrOptions is set by the dev server after this module is
|
|
329
|
+
// imported (read at request time, not closure-capture time).
|
|
330
|
+
const html = devPage.generateDev404Page(pathname, routes, pipelineConfig.devHmrOptions);
|
|
331
|
+
return new Response(html, {
|
|
332
|
+
status: 404,
|
|
333
|
+
headers: {
|
|
334
|
+
...Object.fromEntries(headers.entries()),
|
|
335
|
+
'content-type': 'text/html; charset=utf-8',
|
|
336
|
+
},
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
: undefined;
|
|
338
340
|
|
|
339
|
-
return
|
|
341
|
+
return renderNoMatchPage(req, manifest.root, responseHeaders, clientBootstrap, defaultPage);
|
|
340
342
|
},
|
|
341
343
|
interceptionRewrites: manifest.interceptionRewrites,
|
|
344
|
+
matchInterceptedRoute: createInterceptedRouteMatcher(manifest.root),
|
|
342
345
|
// Slow request threshold from timber.config.ts. Default 3000ms, 0 to disable.
|
|
343
346
|
// See design/17-logging.md §"slowRequestMs"
|
|
344
347
|
slowRequestMs: (runtimeConfig as Record<string, unknown>).slowRequestMs as number | undefined,
|
|
@@ -41,9 +41,11 @@ import { recordTiming } from '../server-timing.js';
|
|
|
41
41
|
import { requestContextAls } from '../als-registry.js';
|
|
42
42
|
import { parseClientStateTree } from '../state-tree-diff.js';
|
|
43
43
|
|
|
44
|
+
import { getFirstDeniedSegmentIndex } from '../deny-boundary.js';
|
|
44
45
|
import { handleApiRoute } from './api-handler.js';
|
|
45
46
|
import { renderErrorPage, renderNoMatchPage } from './error-renderer.js';
|
|
46
|
-
import { buildRedirectResponse, createDebugChannelSink
|
|
47
|
+
import { buildRedirectResponse, createDebugChannelSink } from './helpers.js';
|
|
48
|
+
import { isRscRequest } from '../../shared/rsc-media-type.js';
|
|
47
49
|
import { buildRscPayloadResponse } from './rsc-payload.js';
|
|
48
50
|
import { renderRscStream } from './rsc-stream.js';
|
|
49
51
|
import { renderSsrResponse } from './ssr-renderer.js';
|
|
@@ -97,7 +99,7 @@ export async function renderRoute(
|
|
|
97
99
|
// Only used for RSC requests — HTML requests always get a full render.
|
|
98
100
|
// See design/19-client-navigation.md §"X-Timber-State-Tree Header"
|
|
99
101
|
const clientStateTree =
|
|
100
|
-
deps.clientSegmentCache &&
|
|
102
|
+
deps.clientSegmentCache && isRscRequest(req) ? parseClientStateTree(req) : null;
|
|
101
103
|
|
|
102
104
|
// Build the React element tree — runs access checks eagerly to gate
|
|
103
105
|
// metadata resolution (TIM-1027), then loads modules and resolves
|
|
@@ -140,7 +142,7 @@ export async function renderRoute(
|
|
|
140
142
|
});
|
|
141
143
|
|
|
142
144
|
const {
|
|
143
|
-
|
|
145
|
+
payload,
|
|
144
146
|
layoutComponents,
|
|
145
147
|
deferSuspenseFor,
|
|
146
148
|
skippedSegments,
|
|
@@ -199,7 +201,7 @@ export async function renderRoute(
|
|
|
199
201
|
|
|
200
202
|
// Render to RSC Flight stream with signal tracking.
|
|
201
203
|
const _rscStart = performance.now();
|
|
202
|
-
const { rscStream, signals, getDebugComponents } = renderRscStream(
|
|
204
|
+
const { rscStream, signals, getDebugComponents } = renderRscStream(payload, req);
|
|
203
205
|
if (shellSettled) signals.shellSettled = shellSettled;
|
|
204
206
|
|
|
205
207
|
// Store the debug components getter in ALS so onPipelineError can
|
|
@@ -224,14 +226,15 @@ export async function renderRoute(
|
|
|
224
226
|
// Synchronous deny — deny() in a non-async component throws during
|
|
225
227
|
// renderToReadableStream creation, caught in the try/catch above.
|
|
226
228
|
if (signals.denySignal) {
|
|
227
|
-
if (
|
|
229
|
+
if (isRscRequest(req)) {
|
|
228
230
|
return renderDenyPageAsRsc(
|
|
229
231
|
signals.denySignal,
|
|
230
232
|
segments,
|
|
231
233
|
layoutComponents as LayoutEntry[],
|
|
232
234
|
req,
|
|
233
235
|
responseHeaders,
|
|
234
|
-
createDebugChannelSink
|
|
236
|
+
createDebugChannelSink,
|
|
237
|
+
getFirstDeniedSegmentIndex()
|
|
235
238
|
);
|
|
236
239
|
}
|
|
237
240
|
return renderDenyPage(
|
|
@@ -243,13 +246,18 @@ export async function renderRoute(
|
|
|
243
246
|
responseHeaders,
|
|
244
247
|
clientBootstrap,
|
|
245
248
|
createDebugChannelSink,
|
|
246
|
-
callSsr
|
|
249
|
+
callSsr,
|
|
250
|
+
undefined,
|
|
251
|
+
getFirstDeniedSegmentIndex()
|
|
247
252
|
);
|
|
248
253
|
}
|
|
249
254
|
|
|
250
255
|
// Synchronous render error — renderToReadableStream threw before
|
|
251
256
|
// creating the stream. Render the error page with correct 5xx status.
|
|
252
257
|
// (Async render errors are tracked in onError and handled after SSR.)
|
|
258
|
+
// An RSC payload request gets the hard-navigate envelope rather than an
|
|
259
|
+
// error page — `renderErrorPage` owns that branch, so every one of its call
|
|
260
|
+
// sites is covered rather than this one alone (TIM-1322).
|
|
253
261
|
if (signals.renderError && !rscStream) {
|
|
254
262
|
return renderErrorPage(
|
|
255
263
|
signals.renderError.error,
|
|
@@ -260,21 +268,21 @@ export async function renderRoute(
|
|
|
260
268
|
match,
|
|
261
269
|
responseHeaders,
|
|
262
270
|
clientBootstrap,
|
|
263
|
-
globalError
|
|
271
|
+
globalError,
|
|
272
|
+
getFirstDeniedSegmentIndex()
|
|
264
273
|
);
|
|
265
274
|
}
|
|
266
275
|
|
|
267
276
|
// For RSC payload requests (client navigation), return the RSC Flight
|
|
268
277
|
// stream directly — skip SSR HTML rendering entirely.
|
|
269
278
|
// See design/19-client-navigation.md §"RSC Payload Handling"
|
|
270
|
-
if (
|
|
279
|
+
if (isRscRequest(req)) {
|
|
271
280
|
return buildRscPayloadResponse(
|
|
272
281
|
req,
|
|
273
282
|
rscStream!,
|
|
274
283
|
signals,
|
|
275
284
|
segments,
|
|
276
285
|
layoutComponents,
|
|
277
|
-
match,
|
|
278
286
|
responseHeaders,
|
|
279
287
|
skippedSegments,
|
|
280
288
|
slotSkipInfo
|
|
@@ -40,6 +40,7 @@ import { buildRouteElement } from '../route-element-builder.js';
|
|
|
40
40
|
import type { RevalidateRenderer, RevalidationResult } from '../actions.js';
|
|
41
41
|
|
|
42
42
|
import { runMiddlewareForAction, type CoerceSegmentParamsFn } from './action-middleware-runner.js';
|
|
43
|
+
import { RSC_CONTENT_TYPE } from '../../shared/rsc-media-type.js';
|
|
43
44
|
|
|
44
45
|
/**
|
|
45
46
|
* Sentinel thrown when revalidation cannot proceed because middleware
|
|
@@ -214,7 +215,7 @@ export function createRevalidateRendererFactory(
|
|
|
214
215
|
revalidateUrl.pathname = validation.pathname;
|
|
215
216
|
revalidateUrl.search = validation.search;
|
|
216
217
|
const revalidateHeaders = new Headers(req.headers);
|
|
217
|
-
revalidateHeaders.set('Accept',
|
|
218
|
+
revalidateHeaders.set('Accept', RSC_CONTENT_TYPE);
|
|
218
219
|
// Snapshot the action's post-mutation cookie map (deletions
|
|
219
220
|
// included) and serialize it into a `Cookie:` header value the
|
|
220
221
|
// target middleware can read off `ctx.req.headers.get('cookie')`.
|
|
@@ -342,7 +343,7 @@ export function createRevalidateRendererFactory(
|
|
|
342
343
|
deps.buildManifest.metadataRouteHashes
|
|
343
344
|
);
|
|
344
345
|
return {
|
|
345
|
-
|
|
346
|
+
payload: routeResult.payload,
|
|
346
347
|
};
|
|
347
348
|
};
|
|
348
349
|
};
|
|
@@ -10,21 +10,27 @@
|
|
|
10
10
|
|
|
11
11
|
import type { LayoutEntry } from '../deny-renderer.js';
|
|
12
12
|
import { renderDenyPageAsRsc } from '../deny-renderer.js';
|
|
13
|
-
import type { RouteMatch } from '../pipeline.js';
|
|
14
13
|
import type { RedirectSignal } from '../primitives.js';
|
|
15
14
|
import type { LayoutComponentEntry } from '../route-element-builder.js';
|
|
16
15
|
import type { ManifestSegmentNode } from '../route-matcher.js';
|
|
17
16
|
import type { SlotSkipEntry } from '../slot-resolver.js';
|
|
18
17
|
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
RSC_CONTENT_TYPE,
|
|
24
|
-
} from './helpers.js';
|
|
18
|
+
import { buildRedirectResponse, buildSegmentInfo, createDebugChannelSink } from './helpers.js';
|
|
19
|
+
import { RSC_CONTENT_TYPE } from '../../shared/rsc-media-type.js';
|
|
20
|
+
|
|
21
|
+
import { rscErrorEnvelope } from '../../shared/rsc-error-envelope.js';
|
|
25
22
|
|
|
26
23
|
import type { RenderSignals } from './rsc-stream.js';
|
|
27
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Ceiling on waiting for a first chunk that is already in flight, used only
|
|
27
|
+
* when an addressed deny won the race against it. Matches the 50ms ceiling
|
|
28
|
+
* documented for the race itself: long enough that the error row — which is
|
|
29
|
+
* what the render just produced — arrives, short enough that a stalled stream
|
|
30
|
+
* cannot hold the response open.
|
|
31
|
+
*/
|
|
32
|
+
const FIRST_CHUNK_CEILING_MS = 50;
|
|
33
|
+
|
|
28
34
|
/**
|
|
29
35
|
* Build an RSC payload Response for a client-side navigation request.
|
|
30
36
|
*
|
|
@@ -40,7 +46,6 @@ export async function buildRscPayloadResponse(
|
|
|
40
46
|
signals: RenderSignals,
|
|
41
47
|
segments: ManifestSegmentNode[],
|
|
42
48
|
layoutComponents: LayoutComponentEntry[],
|
|
43
|
-
match: RouteMatch,
|
|
44
49
|
responseHeaders: Headers,
|
|
45
50
|
skippedSegments?: string[],
|
|
46
51
|
slotSkipInfo?: SlotSkipEntry[]
|
|
@@ -70,10 +75,18 @@ export async function buildRscPayloadResponse(
|
|
|
70
75
|
| { type: 'data'; chunk: ReadableStreamReadResult<Uint8Array> }
|
|
71
76
|
| { type: 'signal' };
|
|
72
77
|
|
|
78
|
+
// Retained rather than inlined into the race below. When a signal wins, the
|
|
79
|
+
// usual answer is to cancel — but an ADDRESSED deny is one whose error row
|
|
80
|
+
// the client's boundary is going to render, so that payload has to survive
|
|
81
|
+
// (TIM-1356). Taking delivery of the read already in flight is not the
|
|
82
|
+
// "bare read() after a signal won" the async rules forbid; issuing a second
|
|
83
|
+
// one would be. It still gets a ceiling below.
|
|
84
|
+
const pendingRead = reader.read();
|
|
85
|
+
|
|
73
86
|
let first: RaceResult;
|
|
74
87
|
try {
|
|
75
88
|
first = await Promise.race([
|
|
76
|
-
|
|
89
|
+
pendingRead.then((chunk) => ({ type: 'data' as const, chunk })),
|
|
77
90
|
signalDetected.then(() => ({ type: 'signal' as const })),
|
|
78
91
|
]);
|
|
79
92
|
} catch (_readError) {
|
|
@@ -82,12 +95,7 @@ export async function buildRscPayloadResponse(
|
|
|
82
95
|
// SSR-rendered error page. See design/10-error-handling.md.
|
|
83
96
|
reader.cancel().catch(() => {});
|
|
84
97
|
signals.onSignal = undefined;
|
|
85
|
-
|
|
86
|
-
responseHeaders.set('content-type', 'application/json; charset=utf-8');
|
|
87
|
-
return new Response(JSON.stringify({ error: true, status: 500 }), {
|
|
88
|
-
status: 500,
|
|
89
|
-
headers: responseHeaders,
|
|
90
|
-
});
|
|
98
|
+
return rscErrorEnvelope(500, responseHeaders);
|
|
91
99
|
}
|
|
92
100
|
|
|
93
101
|
// If data arrived first, still check signals — they may have fired
|
|
@@ -124,15 +132,42 @@ export async function buildRscPayloadResponse(
|
|
|
124
132
|
reader.cancel().catch(() => {});
|
|
125
133
|
return buildRedirectResponse(trackedRedirect, req, responseHeaders);
|
|
126
134
|
}
|
|
127
|
-
|
|
135
|
+
// An addressed deny needs no fallback: the payload already carries its error
|
|
136
|
+
// row, aimed at the boundary the client has mounted at that segment, which
|
|
137
|
+
// renders it. Cancelling would throw a finished render away and re-run the
|
|
138
|
+
// whole route — the exact cost TIM-666 removed. The status still lands,
|
|
139
|
+
// because the catch site called setDenyStatus() before re-throwing (read
|
|
140
|
+
// below via getDenyStatus).
|
|
141
|
+
//
|
|
142
|
+
// The question is asked of `unplacedDeny`, not of `denySignal.ownerKey`,
|
|
143
|
+
// because one render can produce both kinds and `denySignal` holds only one
|
|
144
|
+
// of them. A deny nothing could place has no boundary that may claim it, so
|
|
145
|
+
// its row would otherwise stream to a client with nothing to render it —
|
|
146
|
+
// and it needs the fallback whether or not some other deny was addressed
|
|
147
|
+
// first. See TIM-1356.
|
|
148
|
+
if (signals.unplacedDeny) {
|
|
128
149
|
reader.cancel().catch(() => {});
|
|
150
|
+
|
|
151
|
+
// A skipped segment whose access just flipped to deny marks the signal
|
|
152
|
+
// for hard-navigate: rendering a deny page here would execute the denied
|
|
153
|
+
// segment's protected layout and place it inside cached client chrome
|
|
154
|
+
// (TIM-1074). Return rscErrorEnvelope so the client hard-navigates for
|
|
155
|
+
// a clean full render with correct deny page placement. See TIM-1363.
|
|
156
|
+
if (signals.unplacedDeny._hardNavigate) {
|
|
157
|
+
return rscErrorEnvelope(signals.unplacedDeny.status, responseHeaders);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Read the denied segment index from ALS — set by AccessGate during
|
|
161
|
+
// render. See TIM-1358, TIM-1365.
|
|
162
|
+
const { getFirstDeniedSegmentIndex: getDeniedIdx } = await import('../deny-boundary.js');
|
|
129
163
|
return renderDenyPageAsRsc(
|
|
130
|
-
signals.
|
|
164
|
+
signals.unplacedDeny,
|
|
131
165
|
segments,
|
|
132
166
|
layoutComponents as LayoutEntry[],
|
|
133
167
|
req,
|
|
134
168
|
responseHeaders,
|
|
135
|
-
createDebugChannelSink
|
|
169
|
+
createDebugChannelSink,
|
|
170
|
+
getDeniedIdx()
|
|
136
171
|
);
|
|
137
172
|
}
|
|
138
173
|
|
|
@@ -143,15 +178,7 @@ export async function buildRscPayloadResponse(
|
|
|
143
178
|
// §"Error Page Rendering for Client Navigation".
|
|
144
179
|
if (signals.renderError) {
|
|
145
180
|
reader.cancel().catch(() => {});
|
|
146
|
-
|
|
147
|
-
responseHeaders.set('content-type', 'application/json; charset=utf-8');
|
|
148
|
-
return new Response(
|
|
149
|
-
JSON.stringify({
|
|
150
|
-
error: true,
|
|
151
|
-
status: signals.renderError.status,
|
|
152
|
-
}),
|
|
153
|
-
{ status: signals.renderError.status, headers: responseHeaders }
|
|
154
|
-
);
|
|
181
|
+
return rscErrorEnvelope(signals.renderError.status, responseHeaders);
|
|
155
182
|
}
|
|
156
183
|
|
|
157
184
|
// Extract the first chunk from the race result.
|
|
@@ -162,6 +189,41 @@ export async function buildRscPayloadResponse(
|
|
|
162
189
|
let firstRead: ReadableStreamReadResult<Uint8Array>;
|
|
163
190
|
if (first.type === 'data') {
|
|
164
191
|
firstRead = first.chunk;
|
|
192
|
+
} else if (signals.denySignal?.ownerKey) {
|
|
193
|
+
// An addressed deny won the race before its own error row surfaced.
|
|
194
|
+
// Cancelling here would hand the client a 4xx payload with nothing in it
|
|
195
|
+
// — no row for the owning boundary to render, so the navigation shows
|
|
196
|
+
// nothing at all. Take delivery of the read already in flight instead,
|
|
197
|
+
// under a ceiling so a stalled stream still cannot hang the response.
|
|
198
|
+
const TIMED_OUT = Symbol('first-chunk-timeout');
|
|
199
|
+
const raced = await Promise.race([
|
|
200
|
+
pendingRead,
|
|
201
|
+
new Promise<typeof TIMED_OUT>((resolve) =>
|
|
202
|
+
setTimeout(() => resolve(TIMED_OUT), FIRST_CHUNK_CEILING_MS)
|
|
203
|
+
),
|
|
204
|
+
]);
|
|
205
|
+
|
|
206
|
+
// Missing the ceiling must not be reported as a finished stream. Under
|
|
207
|
+
// event-loop contention or slow serialization the row is late rather than
|
|
208
|
+
// absent, and fabricating `done: true` here would produce the very empty
|
|
209
|
+
// 4xx this branch exists to prevent.
|
|
210
|
+
//
|
|
211
|
+
// Nor can it fall to `renderDenyPageAsRsc`. That renderer re-resolves from
|
|
212
|
+
// the FULL leaf chain and wraps whatever it finds, ignoring `ownerKey` —
|
|
213
|
+
// so an `/admin` denial addressed to a root `4xx.tsx` would come back as
|
|
214
|
+
// `/admin/secret/403.tsx` with the denied layouts executed, which is the
|
|
215
|
+
// TIM-1074 leak TIM-1358 still owns. A deny this path already placed
|
|
216
|
+
// correctly must not be handed to it.
|
|
217
|
+
//
|
|
218
|
+
// So: hard-navigate. The document request renders the same deny through
|
|
219
|
+
// the boundary at its owner, with the placement intact. A full page load
|
|
220
|
+
// is a real cost, on a path that needs a status page and a stalled stream
|
|
221
|
+
// to reach at all.
|
|
222
|
+
if (raced === TIMED_OUT) {
|
|
223
|
+
reader.cancel().catch(() => {});
|
|
224
|
+
return rscErrorEnvelope(signals.denySignal.status, responseHeaders);
|
|
225
|
+
}
|
|
226
|
+
firstRead = raced;
|
|
165
227
|
} else {
|
|
166
228
|
await reader.cancel();
|
|
167
229
|
firstRead = { done: true, value: undefined };
|
|
@@ -200,7 +262,30 @@ export async function buildRscPayloadResponse(
|
|
|
200
262
|
// for state tree diffing on subsequent navigations. Request-dependency
|
|
201
263
|
// is determined by static module-graph analysis (not runtime taint).
|
|
202
264
|
// See design/19-client-navigation.md §"X-Timber-State-Tree Header"
|
|
203
|
-
|
|
265
|
+
// Read the deny state before describing the tree — what mounted depends on it.
|
|
266
|
+
// Both values are recorded in ALS during render by AccessGate's catch path.
|
|
267
|
+
const { getDenyStatus, getFirstDeniedSegmentIndex } = await import('../deny-boundary.js');
|
|
268
|
+
const denyStatus = getDenyStatus();
|
|
269
|
+
const resolvedDenyIndex = getFirstDeniedSegmentIndex();
|
|
270
|
+
|
|
271
|
+
// Emitted in full even when a deny rendered, because this header answers two
|
|
272
|
+
// questions and they diverge here. The client picks its merge point out of
|
|
273
|
+
// this list (`buildSegmentUpdates`), and a partial payload with no merge
|
|
274
|
+
// point is never inserted at all — the URL commits over the stale previous
|
|
275
|
+
// UI. So it has to keep describing the chain the payload was BUILT against.
|
|
276
|
+
//
|
|
277
|
+
// What it must not also do on a deny is seed the segment cache, since the
|
|
278
|
+
// deny page replaced a subtree and part of that chain never mounted. That
|
|
279
|
+
// half is handled at the commit site from the response status, where the
|
|
280
|
+
// merge point has already been chosen. See `client/router-pipeline.ts` and
|
|
281
|
+
// TIM-1356.
|
|
282
|
+
const segmentInfo = buildSegmentInfo(
|
|
283
|
+
segments,
|
|
284
|
+
layoutComponents,
|
|
285
|
+
slotSkipInfo,
|
|
286
|
+
skippedSegments,
|
|
287
|
+
resolvedDenyIndex
|
|
288
|
+
);
|
|
204
289
|
responseHeaders.set('X-Timber-Segments', JSON.stringify(segmentInfo));
|
|
205
290
|
|
|
206
291
|
// Send skipped segments so the client can merge the partial RSC payload
|
|
@@ -209,18 +294,6 @@ export async function buildRscPayloadResponse(
|
|
|
209
294
|
responseHeaders.set('X-Timber-Skipped-Segments', JSON.stringify(skippedSegments));
|
|
210
295
|
}
|
|
211
296
|
|
|
212
|
-
// Send route params so the client can populate useSegmentParams() after
|
|
213
|
-
// SPA navigation. Without this, useSegmentParams() returns {}.
|
|
214
|
-
if (Object.keys(match.segmentParams).length > 0) {
|
|
215
|
-
responseHeaders.set('X-Timber-Params', JSON.stringify(match.segmentParams));
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Check if a DenySignal was caught in-tree by AccessGate or
|
|
219
|
-
// PageDenyBoundary. The deny page is in the Flight stream as a normal
|
|
220
|
-
// element — use the deny status for the HTTP response. See TIM-666.
|
|
221
|
-
const { getDenyStatus } = await import('../deny-boundary.js');
|
|
222
|
-
const denyStatus = getDenyStatus();
|
|
223
|
-
|
|
224
297
|
return new Response(patchedStream, {
|
|
225
298
|
status: denyStatus ?? 200,
|
|
226
299
|
headers: responseHeaders,
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { renderToReadableStream } from '../../rsc-runtime/rsc.js';
|
|
14
|
+
import type { RscPayloadRoot } from '../../shared/payload-root.js';
|
|
14
15
|
|
|
15
16
|
import { randomUUID } from 'node:crypto';
|
|
16
17
|
|
|
@@ -39,6 +40,18 @@ import { isDevMode } from '../debug.js';
|
|
|
39
40
|
*/
|
|
40
41
|
export interface RenderSignals {
|
|
41
42
|
denySignal: DenySignal | null;
|
|
43
|
+
/**
|
|
44
|
+
* The first deny that no in-tree catch site could place — no `ownerKey`, so
|
|
45
|
+
* no boundary may claim its error row and only the re-render fallback can
|
|
46
|
+
* produce a page for it.
|
|
47
|
+
*
|
|
48
|
+
* Tracked apart from `denySignal` because one render can produce both. A
|
|
49
|
+
* deep page can hoist a 404 to a boundary that will render it while an
|
|
50
|
+
* outer layout separately throws a 403 nothing matches; whichever lands in
|
|
51
|
+
* `denySignal` first, the unplaced one still needs the fallback, and
|
|
52
|
+
* deciding from `denySignal.ownerKey` alone would strand it. See TIM-1356.
|
|
53
|
+
*/
|
|
54
|
+
unplacedDeny: DenySignal | null;
|
|
42
55
|
redirectSignal: RedirectSignal | null;
|
|
43
56
|
renderError: { error: unknown; status: number } | null;
|
|
44
57
|
/**
|
|
@@ -74,7 +87,7 @@ export interface RscStreamResult {
|
|
|
74
87
|
}
|
|
75
88
|
|
|
76
89
|
/**
|
|
77
|
-
* Render a
|
|
90
|
+
* Render a payload root (tree plus its params) to an RSC Flight stream.
|
|
78
91
|
*
|
|
79
92
|
* The stream serializes server components as rendered output and client
|
|
80
93
|
* components ("use client") as serialized references with module ID + export name.
|
|
@@ -84,9 +97,10 @@ export interface RscStreamResult {
|
|
|
84
97
|
* fires onError during stream consumption. Signals are captured in the
|
|
85
98
|
* returned `signals` object for the caller to handle.
|
|
86
99
|
*/
|
|
87
|
-
export function renderRscStream(
|
|
100
|
+
export function renderRscStream(payload: RscPayloadRoot, req: Request): RscStreamResult {
|
|
88
101
|
const signals: RenderSignals = {
|
|
89
102
|
denySignal: null,
|
|
103
|
+
unplacedDeny: null,
|
|
90
104
|
redirectSignal: null,
|
|
91
105
|
renderError: null,
|
|
92
106
|
lastUnhandledError: null,
|
|
@@ -100,7 +114,7 @@ export function renderRscStream(element: React.ReactElement, req: Request): RscS
|
|
|
100
114
|
|
|
101
115
|
try {
|
|
102
116
|
rscStream = renderToReadableStream(
|
|
103
|
-
|
|
117
|
+
payload,
|
|
104
118
|
{
|
|
105
119
|
signal: req.signal,
|
|
106
120
|
onError(error: unknown) {
|
|
@@ -109,9 +123,18 @@ export function renderRscStream(element: React.ReactElement, req: Request): RscS
|
|
|
109
123
|
if (isAbortError(error) || req.signal?.aborted) return;
|
|
110
124
|
if (error instanceof DenySignal) {
|
|
111
125
|
signals.denySignal = error;
|
|
126
|
+
if (!error.ownerKey) signals.unplacedDeny ??= error;
|
|
112
127
|
signals.onSignal?.();
|
|
113
|
-
// Return structured digest for client-side error boundaries
|
|
114
|
-
|
|
128
|
+
// Return structured digest for client-side error boundaries.
|
|
129
|
+
// `ownerKey` is set when an in-tree catch site hoisted the deny
|
|
130
|
+
// to the segment owning the matched file — it addresses the
|
|
131
|
+
// boundary that may render it. See TIM-1356.
|
|
132
|
+
return JSON.stringify({
|
|
133
|
+
type: 'deny',
|
|
134
|
+
status: error.status,
|
|
135
|
+
data: error.data,
|
|
136
|
+
ownerKey: error.ownerKey,
|
|
137
|
+
});
|
|
115
138
|
}
|
|
116
139
|
if (error instanceof RedirectSignal) {
|
|
117
140
|
signals.redirectSignal = error;
|
|
@@ -217,6 +240,7 @@ export function renderRscStream(element: React.ReactElement, req: Request): RscS
|
|
|
217
240
|
} catch (error) {
|
|
218
241
|
if (error instanceof DenySignal) {
|
|
219
242
|
signals.denySignal = error;
|
|
243
|
+
if (!error.ownerKey) signals.unplacedDeny ??= error;
|
|
220
244
|
} else if (error instanceof RedirectSignal) {
|
|
221
245
|
signals.redirectSignal = error;
|
|
222
246
|
} else {
|