@timber-js/app 0.2.0-alpha.185 → 0.2.0-alpha.186
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-C-Rw9vPc.js} +3 -3
- package/dist/_chunks/{actions-O_LsyCE4.js.map → actions-C-Rw9vPc.js.map} +1 -1
- package/dist/_chunks/als-slots-mFweg276.js +27 -0
- package/dist/_chunks/als-slots-mFweg276.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-Cd0VZ_Pd.js} +3 -3
- package/dist/_chunks/{cache-api-B-lhk9p4.js.map → cache-api-Cd0VZ_Pd.js.map} +1 -1
- package/dist/_chunks/cli-schema-sync-CKgHC2MB.js +1857 -0
- package/dist/_chunks/cli-schema-sync-CKgHC2MB.js.map +1 -0
- package/dist/_chunks/{cloudflare-CnT5Lr7U.js → cloudflare-Cs0uZXea.js} +89 -12
- package/dist/_chunks/{cloudflare-CnT5Lr7U.js.map → cloudflare-Cs0uZXea.js.map} +1 -1
- package/dist/_chunks/error-boundary-DpYRI_I1.js +330 -0
- package/dist/_chunks/error-boundary-DpYRI_I1.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/fs-identity-D7vb6GdI.js +208 -0
- package/dist/_chunks/fs-identity-D7vb6GdI.js.map +1 -0
- package/dist/_chunks/{logger-AWfuX-KJ.js → logger-N7e5auP0.js} +9 -13
- package/dist/_chunks/logger-N7e5auP0.js.map +1 -0
- package/dist/_chunks/navigation-root-B29qg0_T.js +274 -0
- package/dist/_chunks/navigation-root-B29qg0_T.js.map +1 -0
- package/dist/_chunks/param-value-C8TNYchQ.js +66 -0
- package/dist/_chunks/param-value-C8TNYchQ.js.map +1 -0
- package/dist/_chunks/{plugin-context---kTF5v8.js → plugin-context-DEGLSJs3.js} +2 -1
- package/dist/_chunks/plugin-context-DEGLSJs3.js.map +1 -0
- 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-payload-path-B_LBodc2.js +48 -0
- package/dist/_chunks/rsc-payload-path-B_LBodc2.js.map +1 -0
- package/dist/_chunks/{segment-classify-Byy425ng.js → segment-classify-C539Pa2O.js} +23 -2
- package/dist/_chunks/{segment-classify-Byy425ng.js.map → segment-classify-C539Pa2O.js.map} +1 -1
- 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-segment-params-ClyUNq4d.js +128 -0
- package/dist/_chunks/use-segment-params-ClyUNq4d.js.map +1 -0
- package/dist/_chunks/{walkers-_6zKFlch.js → walkers-BhhwI9TD.js} +21 -87
- package/dist/_chunks/walkers-BhhwI9TD.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/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 +2 -3
- 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/cli.js +2 -2
- 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/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 +1 -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.js +18 -11
- 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 +865 -306
- package/dist/client/internal.js.map +1 -1
- 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 +71 -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 +38 -25
- package/dist/client/rsc-fetch.d.ts.map +1 -1
- package/dist/client/segment-cache.d.ts +79 -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-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/cookies/index.js +3 -3
- package/dist/cookies/index.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +906 -321
- package/dist/index.js.map +1 -1
- package/dist/plugin-context.d.ts +31 -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 +9 -0
- package/dist/plugins/routing.d.ts.map +1 -1
- package/dist/plugins/static-build.d.ts +136 -8
- package/dist/plugins/static-build.d.ts.map +1 -1
- package/dist/routing/codegen-types.d.ts +18 -0
- package/dist/routing/codegen-types.d.ts.map +1 -1
- 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/index.js +3 -3
- package/dist/routing/interception.d.ts +235 -6
- package/dist/routing/interception.d.ts.map +1 -1
- 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/walkers.d.ts +6 -0
- package/dist/routing/walkers.d.ts.map +1 -1
- package/dist/search-params/define.d.ts +0 -6
- package/dist/search-params/define.d.ts.map +1 -1
- package/dist/search-params/index.js +176 -2
- package/dist/search-params/index.js.map +1 -1
- package/dist/segment-params/define.d.ts +0 -6
- package/dist/segment-params/define.d.ts.map +1 -1
- package/dist/segment-params/index.js +69 -1
- package/dist/segment-params/index.js.map +1 -0
- package/dist/server/actions.d.ts +6 -3
- package/dist/server/actions.d.ts.map +1 -1
- package/dist/server/als-registry.d.ts +2 -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/default-status-page.d.ts +45 -0
- package/dist/server/default-status-page.d.ts.map +1 -0
- package/dist/server/deny-renderer.d.ts +14 -3
- package/dist/server/deny-renderer.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.js +2 -2
- package/dist/server/internal.js +393 -212
- package/dist/server/internal.js.map +1 -1
- package/dist/server/param-coercion.d.ts +10 -1
- 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/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 +32 -2
- 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/error-renderer.d.ts +14 -2
- package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
- package/dist/server/rsc-entry/helpers.d.ts +2 -10
- package/dist/server/rsc-entry/helpers.d.ts.map +1 -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/rsc-payload.d.ts +3 -2
- package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-stream.d.ts +3 -2
- package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-renderer.d.ts +2 -0
- 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 +6 -0
- package/dist/server/ssr-bridge-types.d.ts.map +1 -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/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-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/31-api-client.mdx +4 -2
- package/docs/learn/02-pages-and-layouts.mdx +1 -1
- package/docs/learn/11-error-handling.mdx +2 -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 +172 -2
- package/docs/more/04-metadata-and-fonts.mdx +1 -1
- package/package.json +2 -2
- package/src/adapters/build-output-helper.ts +116 -3
- package/src/adapters/cloudflare.ts +86 -11
- 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/client/browser-entry/action-dispatch.ts +9 -4
- package/src/client/browser-entry/hydrate.ts +40 -29
- package/src/client/browser-entry/index.ts +15 -1
- 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 +20 -15
- package/src/client/history.ts +13 -6
- package/src/client/internal.ts +4 -3
- package/src/client/link.tsx +55 -8
- package/src/client/navigation-api.ts +3 -3
- package/src/client/navigation-commit.ts +174 -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 +389 -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 +242 -707
- package/src/client/rsc-fetch.ts +169 -102
- package/src/client/segment-cache.ts +121 -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-router.ts +1 -1
- package/src/client/use-segment-params.ts +41 -18
- package/src/index.ts +5 -1
- package/src/plugin-context.ts +32 -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 +59 -2
- package/src/plugins/static-build.ts +315 -73
- package/src/routing/codegen-types.ts +18 -0
- package/src/routing/codegen.ts +50 -10
- package/src/routing/collision-probe.ts +438 -0
- package/src/routing/collision-spaces.ts +281 -0
- package/src/routing/interception.ts +896 -50
- package/src/routing/manifest-codegen.ts +5 -3
- package/src/routing/scanner.ts +114 -89
- 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/walkers.ts +8 -0
- package/src/search-params/define.ts +6 -29
- package/src/segment-params/define.ts +4 -27
- package/src/server/action-handler.ts +3 -1
- package/src/server/actions.ts +6 -3
- package/src/server/als-registry.ts +2 -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/default-status-page.ts +127 -0
- package/src/server/deny-renderer.ts +81 -20
- package/src/server/head-response.ts +54 -0
- package/src/server/param-coercion.ts +48 -18
- package/src/server/pipeline-helpers.ts +0 -45
- package/src/server/pipeline-interception.ts +81 -32
- package/src/server/pipeline-outcome.ts +2 -4
- package/src/server/pipeline-phases.ts +68 -16
- package/src/server/pipeline.ts +57 -10
- package/src/server/prebuilt-runtime.ts +4 -4
- package/src/server/publish-params.ts +42 -0
- package/src/server/request-context.ts +97 -18
- package/src/server/route-element-builder.ts +89 -98
- package/src/server/route-handler.ts +8 -8
- package/src/server/route-matcher.ts +21 -9
- package/src/server/rsc-cache-key-guard.ts +170 -0
- package/src/server/rsc-entry/api-handler.ts +6 -8
- package/src/server/rsc-entry/error-renderer.ts +64 -25
- package/src/server/rsc-entry/helpers.ts +31 -16
- package/src/server/rsc-entry/index.ts +36 -28
- package/src/server/rsc-entry/render-route.ts +9 -4
- package/src/server/rsc-entry/revalidate-renderer.ts +1 -1
- package/src/server/rsc-entry/rsc-payload.ts +14 -25
- package/src/server/rsc-entry/rsc-stream.ts +4 -3
- package/src/server/rsc-entry/ssr-renderer.ts +13 -9
- package/src/server/skippable-prefix.ts +92 -0
- package/src/server/slot-interception.ts +168 -0
- package/src/server/slot-resolver.ts +253 -156
- package/src/server/ssr-bridge-types.ts +6 -0
- package/src/server/ssr-entry.ts +12 -4
- package/src/server/ssr-wrappers.tsx +33 -32
- package/src/server/state-tree-diff.ts +14 -55
- package/src/server/static-generator.ts +65 -27
- package/src/server/static-not-found.ts +134 -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-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/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/router-ref-CtmF-aPv.js.map +0 -1
- package/dist/_chunks/ssr-data-BOWsq18U.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.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/src/plugins/request-dep.ts +0 -499
|
@@ -17,14 +17,17 @@ import { renderToReadableStream } from '../../rsc-runtime/rsc.js';
|
|
|
17
17
|
|
|
18
18
|
import type { RouteMatch } from '../pipeline.js';
|
|
19
19
|
import { logRenderError } from '../logger.js';
|
|
20
|
+
import { appVisibleSearch } from '../../shared/rsc-cache-key.js';
|
|
21
|
+
import { getSlotParamsForClient } from '../request-context.js';
|
|
20
22
|
import type { ManifestSegmentNode } from '../route-matcher.js';
|
|
21
23
|
import { DenySignal, RenderError } from '../primitives.js';
|
|
22
24
|
import type { ClientBootstrapConfig } from '../html-injectors.js';
|
|
23
25
|
import { flightInitScript, htmlEscapeJsonString } from '../flight-scripts.js';
|
|
24
26
|
import { renderDenyPage, renderDenyPageAsRsc } from '../deny-renderer.js';
|
|
25
|
-
import type { LayoutEntry } from '../deny-renderer.js';
|
|
27
|
+
import type { DefaultPageRenderer, LayoutEntry } from '../deny-renderer.js';
|
|
26
28
|
import type { NavContext } from '../ssr-bridge-types.js';
|
|
27
29
|
import { createDebugChannelSink, isRscPayloadRequest } from './helpers.js';
|
|
30
|
+
import { withPublishedParams } from '../publish-params.js';
|
|
28
31
|
import { getCookiesForSsr } from '../cookie-context.js';
|
|
29
32
|
import { callSsr } from './ssr-bridge.js';
|
|
30
33
|
import { teeWithErrorPropagation } from '../stream-utils.js';
|
|
@@ -34,6 +37,8 @@ import type { SerializableError } from '../../client/error-reconstituter.js';
|
|
|
34
37
|
import { loadModule } from '../safe-load.js';
|
|
35
38
|
import { sourceMapError } from '../dev-source-map.js';
|
|
36
39
|
import { isMdxFilePath as isMdxFile } from '../utils/mdx-file.js';
|
|
40
|
+
import { defaultStatusPageResponse } from '../default-status-page.js';
|
|
41
|
+
import { rscErrorEnvelope } from '../../shared/rsc-error-envelope.js';
|
|
37
42
|
|
|
38
43
|
/**
|
|
39
44
|
* A manifest file reference with lazy import and path.
|
|
@@ -155,6 +160,18 @@ function toSerializableError(error: unknown): SerializableError {
|
|
|
155
160
|
* All error pages (TSX and MDX) go through RSC → SSR:
|
|
156
161
|
* - TSX: ErrorReconstituter wrapper converts SerializableError → real Error
|
|
157
162
|
* - MDX: Plain props ({ status }), no wrapper needed
|
|
163
|
+
*
|
|
164
|
+
* ...except for a client-navigation request, which gets the hard-navigate
|
|
165
|
+
* envelope instead. Every tier below produces an HTML document, and the
|
|
166
|
+
* client's Flight decoder is waiting for `text/x-component`; handing it HTML
|
|
167
|
+
* crashes the decode. The check lives here rather than at the call sites
|
|
168
|
+
* because it holds for all five of them, and the deny path's equivalent split
|
|
169
|
+
* (`renderDenyPage` / `renderDenyPageAsRsc`) had already been forgotten at one
|
|
170
|
+
* of this function's — the synchronous render-error exit in `render-route.ts`
|
|
171
|
+
* (TIM-1322). Four of the five are unreachable for an RSC request today
|
|
172
|
+
* (`render-route.ts` returns the payload before it reaches SSR, and
|
|
173
|
+
* `outcomeToResponse` answers RSC errors itself), so this fires on one path
|
|
174
|
+
* and guards the rest against drift.
|
|
158
175
|
*/
|
|
159
176
|
export async function renderErrorPage(
|
|
160
177
|
error: unknown,
|
|
@@ -167,6 +184,13 @@ export async function renderErrorPage(
|
|
|
167
184
|
clientBootstrap: ClientBootstrapConfig,
|
|
168
185
|
globalError?: GlobalErrorFile
|
|
169
186
|
): Promise<Response> {
|
|
187
|
+
// The error itself is not logged here — every producer of a `renderError`
|
|
188
|
+
// signal already logged it at the point it was caught (`rsc-stream.ts`'s
|
|
189
|
+
// `onError`, `outcomeToResponse`), so a log on this exit would double up.
|
|
190
|
+
if (isRscPayloadRequest(req)) {
|
|
191
|
+
return rscErrorEnvelope(status, responseHeaders);
|
|
192
|
+
}
|
|
193
|
+
|
|
170
194
|
// Source-map the error's stack trace in dev mode so the error page
|
|
171
195
|
// shows original source positions instead of transpiled/bundled ones.
|
|
172
196
|
// See TIM-811.
|
|
@@ -187,9 +211,13 @@ export async function renderErrorPage(
|
|
|
187
211
|
);
|
|
188
212
|
}
|
|
189
213
|
|
|
190
|
-
// Tier 3: No error component found at any level —
|
|
214
|
+
// Tier 3: No error component found at any level — framework default page.
|
|
215
|
+
// The deny chain's terminal is the same page, so an app with no error files
|
|
216
|
+
// gets a readable status page for both 4xx and 5xx rather than a blank
|
|
217
|
+
// document. It carries the status and nothing else — never the error
|
|
218
|
+
// (design/13-security.md principle 4). See TIM-1258.
|
|
191
219
|
if (!resolution) {
|
|
192
|
-
return
|
|
220
|
+
return defaultStatusPageResponse(status, responseHeaders);
|
|
193
221
|
}
|
|
194
222
|
|
|
195
223
|
const { component: errorComponent, segmentIndex, isMdx } = resolution;
|
|
@@ -257,9 +285,12 @@ async function renderErrorPageViaRsc(
|
|
|
257
285
|
element = h(component, null, element);
|
|
258
286
|
}
|
|
259
287
|
|
|
260
|
-
// Render through RSC → SSR (same path as deny pages)
|
|
288
|
+
// Render through RSC → SSR (same path as deny pages) — including the params
|
|
289
|
+
// wrap, which every payload the server sends needs. An error page on a
|
|
290
|
+
// dynamic route reads params like any other page, and SSR would get them
|
|
291
|
+
// from the request ALS while the hydrated client got an empty record.
|
|
261
292
|
try {
|
|
262
|
-
const rscStream = renderToReadableStream(element, {
|
|
293
|
+
const rscStream = renderToReadableStream(withPublishedParams(element), {
|
|
263
294
|
signal: req.signal,
|
|
264
295
|
onError(err: unknown) {
|
|
265
296
|
logRenderError({ method: req.method, path: url.pathname, error: err });
|
|
@@ -269,32 +300,35 @@ async function renderErrorPageViaRsc(
|
|
|
269
300
|
|
|
270
301
|
const [ssrStream, inlineStream] = teeWithErrorPropagation(rscStream);
|
|
271
302
|
|
|
272
|
-
//
|
|
273
|
-
//
|
|
274
|
-
const
|
|
275
|
-
Object.keys(match.segmentParams).length === 0
|
|
276
|
-
? ''
|
|
277
|
-
: `<script>self.__timber_params=${htmlEscapeJsonString(JSON.stringify(match.segmentParams))}</script>`;
|
|
303
|
+
// Params ride the payload root, beside the tree, so there is no
|
|
304
|
+
// bootstrap script. Read here for the SSR-side ALS context below.
|
|
305
|
+
const slotParams = getSlotParamsForClient();
|
|
278
306
|
|
|
279
307
|
const navContext: NavContext = {
|
|
280
308
|
pathname: url.pathname,
|
|
281
309
|
segmentParams: match.segmentParams,
|
|
282
|
-
searchParams: Object.fromEntries(url.
|
|
310
|
+
searchParams: Object.fromEntries(appVisibleSearch(url).params),
|
|
311
|
+
slotParams,
|
|
283
312
|
statusCode: status,
|
|
284
313
|
responseHeaders,
|
|
285
|
-
headHtml:
|
|
314
|
+
headHtml: flightInitScript(),
|
|
286
315
|
bootstrapScriptContent: clientBootstrap.bootstrapScriptContent,
|
|
287
316
|
rscStream: inlineStream,
|
|
288
317
|
signal: req.signal,
|
|
289
318
|
cookies: getCookiesForSsr(),
|
|
290
319
|
};
|
|
291
320
|
|
|
292
|
-
|
|
321
|
+
// `await` inside the try on purpose: SSR of the error page fails
|
|
322
|
+
// asynchronously far more often than synchronously, and returning the
|
|
323
|
+
// promise bare let every such rejection escape this guard.
|
|
324
|
+
return await callSsr(ssrStream, navContext);
|
|
293
325
|
} catch (renderError) {
|
|
294
|
-
//
|
|
295
|
-
//
|
|
326
|
+
// The error page itself failed to render — guards against recursive
|
|
327
|
+
// errors in user error page code. Terminates in the framework default
|
|
328
|
+
// page, not a blank body: a crashing error page is precisely when the
|
|
329
|
+
// reader has nothing else to go on (TIM-1258).
|
|
296
330
|
logRenderError({ method: req.method, path: url.pathname, error: renderError });
|
|
297
|
-
return
|
|
331
|
+
return defaultStatusPageResponse(status, responseHeaders);
|
|
298
332
|
}
|
|
299
333
|
}
|
|
300
334
|
|
|
@@ -315,11 +349,12 @@ async function renderGlobalErrorPage(
|
|
|
315
349
|
responseHeaders: Headers,
|
|
316
350
|
clientBootstrap: ClientBootstrapConfig
|
|
317
351
|
): Promise<Response> {
|
|
318
|
-
// .catch: if global-error.tsx fails to load
|
|
319
|
-
//
|
|
352
|
+
// .catch: if global-error.tsx fails to load or exports no component, it is
|
|
353
|
+
// not a usable Tier 2 match — fall through to the framework default page
|
|
354
|
+
// rather than a blank body. See TIM-584, TIM-1258.
|
|
320
355
|
const mod = await loadModule(globalError).catch(() => null);
|
|
321
356
|
if (!mod?.default) {
|
|
322
|
-
return
|
|
357
|
+
return defaultStatusPageResponse(status, responseHeaders);
|
|
323
358
|
}
|
|
324
359
|
|
|
325
360
|
const component = mod.default as (...args: unknown[]) => unknown;
|
|
@@ -350,15 +385,18 @@ export async function renderNoMatchPage(
|
|
|
350
385
|
req: Request,
|
|
351
386
|
rootSegment: ManifestSegmentNode,
|
|
352
387
|
responseHeaders: Headers,
|
|
353
|
-
clientBootstrap: ClientBootstrapConfig
|
|
388
|
+
clientBootstrap: ClientBootstrapConfig,
|
|
389
|
+
defaultPage?: DefaultPageRenderer
|
|
354
390
|
): Promise<Response> {
|
|
355
391
|
const segments = [rootSegment];
|
|
356
392
|
|
|
357
|
-
// Load root layout if present
|
|
393
|
+
// Load root layout if present. A layout that fails to import or evaluate
|
|
394
|
+
// must not take the 404 down with it — render the status page unwrapped,
|
|
395
|
+
// as renderFallbackError does for the same reason.
|
|
358
396
|
const layoutComponents: LayoutEntry[] = [];
|
|
359
397
|
if (rootSegment.layout) {
|
|
360
|
-
const mod = await loadModule(rootSegment.layout);
|
|
361
|
-
if (mod
|
|
398
|
+
const mod = await loadModule(rootSegment.layout).catch(() => null);
|
|
399
|
+
if (mod?.default) {
|
|
362
400
|
layoutComponents.push({
|
|
363
401
|
component: mod.default as (...args: unknown[]) => unknown,
|
|
364
402
|
segment: rootSegment,
|
|
@@ -389,6 +427,7 @@ export async function renderNoMatchPage(
|
|
|
389
427
|
responseHeaders,
|
|
390
428
|
clientBootstrap,
|
|
391
429
|
createDebugChannelSink,
|
|
392
|
-
callSsr
|
|
430
|
+
callSsr,
|
|
431
|
+
defaultPage
|
|
393
432
|
);
|
|
394
433
|
}
|
|
@@ -6,7 +6,8 @@
|
|
|
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';
|
|
@@ -180,16 +181,6 @@ export function parseDebugRows(text: string): DebugComponentEntry[] {
|
|
|
180
181
|
*
|
|
181
182
|
* See design/07-routing.md §"Server Diffing Rules".
|
|
182
183
|
*/
|
|
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
184
|
export function buildSegmentInfo(
|
|
194
185
|
segments: ManifestSegmentNode[],
|
|
195
186
|
layoutComponents: Array<{
|
|
@@ -197,17 +188,32 @@ export function buildSegmentInfo(
|
|
|
197
188
|
segment: ManifestSegmentNode;
|
|
198
189
|
}>,
|
|
199
190
|
slotSkipInfo?: SlotSkipEntry[],
|
|
200
|
-
skippedSegmentKeys?: string[]
|
|
201
|
-
|
|
191
|
+
skippedSegmentKeys?: string[],
|
|
192
|
+
firstDeniedIndex = Infinity
|
|
193
|
+
): SegmentInfo[] {
|
|
202
194
|
const layoutBySegment = new Map(
|
|
203
195
|
layoutComponents.map(({ component, segment }) => [segment, component])
|
|
204
196
|
);
|
|
205
197
|
|
|
206
198
|
const segmentKeys = computeSegmentKeys(segments);
|
|
199
|
+
// The scope identity interception compares against. Emitted for every
|
|
200
|
+
// layout-bearing segment, which is exactly the set that can own a scope:
|
|
201
|
+
// slots are only resolved for segments whose layout renders
|
|
202
|
+
// (`route-element-builder.ts`), so a segment with no layout can never hold
|
|
203
|
+
// an intercepting slot. See TIM-1282.
|
|
204
|
+
const treePaths = computeSegmentTreePaths(segments);
|
|
207
205
|
const skippedSet = skippedSegmentKeys ? new Set(skippedSegmentKeys) : null;
|
|
208
|
-
const result:
|
|
206
|
+
const result: SegmentInfo[] = [];
|
|
209
207
|
|
|
210
208
|
for (let i = 0; i < segments.length; i++) {
|
|
209
|
+
// A denied segment's AccessGate renders the deny page in place of the
|
|
210
|
+
// layout, so this segment and everything below it never mounted. Their
|
|
211
|
+
// layouts were still loaded — `layoutComponents` holds every module the
|
|
212
|
+
// chain pulled in, not every component that rendered — so without this
|
|
213
|
+
// the client is told it has a tree it does not, and `treePaths` reports a
|
|
214
|
+
// slot owner whose slot was never resolved (TIM-1282).
|
|
215
|
+
if (i >= firstDeniedIndex) break;
|
|
216
|
+
|
|
211
217
|
const segment = segments[i];
|
|
212
218
|
const component = layoutBySegment.get(segment);
|
|
213
219
|
|
|
@@ -225,8 +231,9 @@ export function buildSegmentInfo(
|
|
|
225
231
|
isRequestDependent = true;
|
|
226
232
|
}
|
|
227
233
|
|
|
228
|
-
const entry:
|
|
234
|
+
const entry: SegmentInfo = {
|
|
229
235
|
path: segment.urlPath,
|
|
236
|
+
treePath: treePaths[i],
|
|
230
237
|
isRequestDependent,
|
|
231
238
|
};
|
|
232
239
|
if (segmentId !== segment.urlPath) {
|
|
@@ -237,7 +244,7 @@ export function buildSegmentInfo(
|
|
|
237
244
|
|
|
238
245
|
if (slotSkipInfo) {
|
|
239
246
|
for (const slot of slotSkipInfo) {
|
|
240
|
-
const entry:
|
|
247
|
+
const entry: SegmentInfo = {
|
|
241
248
|
path: slot.slotKey,
|
|
242
249
|
isRequestDependent: slot.isRequestDependent,
|
|
243
250
|
slot: true,
|
|
@@ -270,3 +277,11 @@ export { isAbortError } from '../render-utils.js';
|
|
|
270
277
|
|
|
271
278
|
// escapeHtml is now imported from server/utils/escape-html.ts
|
|
272
279
|
export { escapeHtml } from '../utils/escape-html.js';
|
|
280
|
+
|
|
281
|
+
// Params no longer travel beside the payload. The payload root is the
|
|
282
|
+
// outermost node of every tree the server sends (`server/publish-params.ts`),
|
|
283
|
+
// so the record arrives as props React Flight serializes — which is what
|
|
284
|
+
// makes a `Date`- or `bigint`-valued codec result survive the trip, where
|
|
285
|
+
// `JSON.stringify` flattened it or threw. The response headers, the inline
|
|
286
|
+
// bootstrap scripts, and the two build-time manifest fields that used to
|
|
287
|
+
// carry it are all gone. See TIM-1294, design/41-global-params.md.
|
|
@@ -54,6 +54,7 @@ import type { InterceptionContext, PipelineConfig, RouteMatch } from '../pipelin
|
|
|
54
54
|
import { createPipeline } from '../pipeline.js';
|
|
55
55
|
import { coerceSegmentParams, setGlobalSchemaCodecs } from '../param-coercion.js';
|
|
56
56
|
import { createMetadataRouteMatcher, createRouteMatcher } from '../route-matcher.js';
|
|
57
|
+
import { createInterceptedRouteMatcher } from '../children-interception.js';
|
|
57
58
|
import { initDevTracing } from '../tracing.js';
|
|
58
59
|
import { createRevalidateRendererFactory } from './revalidate-renderer.js';
|
|
59
60
|
|
|
@@ -307,38 +308,45 @@ async function createRequestHandler(manifest: typeof routeManifest, runtimeConfi
|
|
|
307
308
|
);
|
|
308
309
|
},
|
|
309
310
|
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).
|
|
311
|
+
// In dev mode, when the app authored no 404 page, substitute a helpful
|
|
312
|
+
// page listing available routes and suggesting similar paths in place
|
|
313
|
+
// of the framework default. Only for GET requests that accept HTML —
|
|
314
|
+
// HEAD/API/RSC clients must not receive an HTML body (TIM-793).
|
|
315
|
+
//
|
|
316
|
+
// This is passed *into* the chain rather than applied to its result:
|
|
317
|
+
// the old version detected "no user-defined 404.tsx" by testing the
|
|
318
|
+
// response for an empty body, which stopped being true once the chain
|
|
319
|
+
// grew a real terminal (TIM-1258).
|
|
322
320
|
const acceptsHtml = (req.headers.get('accept') ?? '').includes('text/html');
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
321
|
+
const wantsDev404 = isDev && req.method === 'GET' && acceptsHtml;
|
|
322
|
+
|
|
323
|
+
// The dev page is loaded up front: the chain's terminal is synchronous,
|
|
324
|
+
// and dev-tools must stay out of the production bundle (dynamic import).
|
|
325
|
+
const devPage = wantsDev404
|
|
326
|
+
? await import('../../dev-tools/404-page.js').catch(() => null)
|
|
327
|
+
: null;
|
|
328
|
+
|
|
329
|
+
const defaultPage = devPage
|
|
330
|
+
? (_status: number, headers: Headers) => {
|
|
331
|
+
const routes = devPage.collectRoutes(manifest.root);
|
|
332
|
+
const pathname = new URL(req.url).pathname;
|
|
333
|
+
// devHmrOptions is set by the dev server after this module is
|
|
334
|
+
// imported (read at request time, not closure-capture time).
|
|
335
|
+
const html = devPage.generateDev404Page(pathname, routes, pipelineConfig.devHmrOptions);
|
|
336
|
+
return new Response(html, {
|
|
337
|
+
status: 404,
|
|
338
|
+
headers: {
|
|
339
|
+
...Object.fromEntries(headers.entries()),
|
|
340
|
+
'content-type': 'text/html; charset=utf-8',
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
: undefined;
|
|
338
345
|
|
|
339
|
-
return
|
|
346
|
+
return renderNoMatchPage(req, manifest.root, responseHeaders, clientBootstrap, defaultPage);
|
|
340
347
|
},
|
|
341
348
|
interceptionRewrites: manifest.interceptionRewrites,
|
|
349
|
+
matchInterceptedRoute: createInterceptedRouteMatcher(manifest.root),
|
|
342
350
|
// Slow request threshold from timber.config.ts. Default 3000ms, 0 to disable.
|
|
343
351
|
// See design/17-logging.md §"slowRequestMs"
|
|
344
352
|
slowRequestMs: (runtimeConfig as Record<string, unknown>).slowRequestMs as number | undefined,
|
|
@@ -140,12 +140,13 @@ export async function renderRoute(
|
|
|
140
140
|
});
|
|
141
141
|
|
|
142
142
|
const {
|
|
143
|
-
|
|
143
|
+
payload,
|
|
144
144
|
layoutComponents,
|
|
145
145
|
deferSuspenseFor,
|
|
146
146
|
skippedSegments,
|
|
147
147
|
shellSettled,
|
|
148
148
|
slotSkipInfo,
|
|
149
|
+
firstDeniedIndex,
|
|
149
150
|
} = routeResult;
|
|
150
151
|
|
|
151
152
|
// Build head HTML for injection into the SSR output.
|
|
@@ -199,7 +200,7 @@ export async function renderRoute(
|
|
|
199
200
|
|
|
200
201
|
// Render to RSC Flight stream with signal tracking.
|
|
201
202
|
const _rscStart = performance.now();
|
|
202
|
-
const { rscStream, signals, getDebugComponents } = renderRscStream(
|
|
203
|
+
const { rscStream, signals, getDebugComponents } = renderRscStream(payload, req);
|
|
203
204
|
if (shellSettled) signals.shellSettled = shellSettled;
|
|
204
205
|
|
|
205
206
|
// Store the debug components getter in ALS so onPipelineError can
|
|
@@ -250,6 +251,9 @@ export async function renderRoute(
|
|
|
250
251
|
// Synchronous render error — renderToReadableStream threw before
|
|
251
252
|
// creating the stream. Render the error page with correct 5xx status.
|
|
252
253
|
// (Async render errors are tracked in onError and handled after SSR.)
|
|
254
|
+
// An RSC payload request gets the hard-navigate envelope rather than an
|
|
255
|
+
// error page — `renderErrorPage` owns that branch, so every one of its call
|
|
256
|
+
// sites is covered rather than this one alone (TIM-1322).
|
|
253
257
|
if (signals.renderError && !rscStream) {
|
|
254
258
|
return renderErrorPage(
|
|
255
259
|
signals.renderError.error,
|
|
@@ -274,10 +278,10 @@ export async function renderRoute(
|
|
|
274
278
|
signals,
|
|
275
279
|
segments,
|
|
276
280
|
layoutComponents,
|
|
277
|
-
match,
|
|
278
281
|
responseHeaders,
|
|
279
282
|
skippedSegments,
|
|
280
|
-
slotSkipInfo
|
|
283
|
+
slotSkipInfo,
|
|
284
|
+
firstDeniedIndex
|
|
281
285
|
);
|
|
282
286
|
}
|
|
283
287
|
|
|
@@ -296,5 +300,6 @@ export async function renderRoute(
|
|
|
296
300
|
deferSuspenseFor,
|
|
297
301
|
globalError,
|
|
298
302
|
slotSkipInfo,
|
|
303
|
+
firstDeniedIndex,
|
|
299
304
|
});
|
|
300
305
|
}
|
|
@@ -10,7 +10,6 @@
|
|
|
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';
|
|
@@ -23,6 +22,8 @@ import {
|
|
|
23
22
|
RSC_CONTENT_TYPE,
|
|
24
23
|
} from './helpers.js';
|
|
25
24
|
|
|
25
|
+
import { rscErrorEnvelope } from '../../shared/rsc-error-envelope.js';
|
|
26
|
+
|
|
26
27
|
import type { RenderSignals } from './rsc-stream.js';
|
|
27
28
|
|
|
28
29
|
/**
|
|
@@ -40,10 +41,11 @@ export async function buildRscPayloadResponse(
|
|
|
40
41
|
signals: RenderSignals,
|
|
41
42
|
segments: ManifestSegmentNode[],
|
|
42
43
|
layoutComponents: LayoutComponentEntry[],
|
|
43
|
-
match: RouteMatch,
|
|
44
44
|
responseHeaders: Headers,
|
|
45
45
|
skippedSegments?: string[],
|
|
46
|
-
slotSkipInfo?: SlotSkipEntry[]
|
|
46
|
+
slotSkipInfo?: SlotSkipEntry[],
|
|
47
|
+
/** Index of the first denied segment — see RouteElementResult.firstDeniedIndex. */
|
|
48
|
+
firstDeniedIndex = Infinity
|
|
47
49
|
): Promise<Response> {
|
|
48
50
|
// Read the first chunk from the RSC stream before committing headers.
|
|
49
51
|
// Race the first read against signal detection — if an async component
|
|
@@ -82,12 +84,7 @@ export async function buildRscPayloadResponse(
|
|
|
82
84
|
// SSR-rendered error page. See design/10-error-handling.md.
|
|
83
85
|
reader.cancel().catch(() => {});
|
|
84
86
|
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
|
-
});
|
|
87
|
+
return rscErrorEnvelope(500, responseHeaders);
|
|
91
88
|
}
|
|
92
89
|
|
|
93
90
|
// If data arrived first, still check signals — they may have fired
|
|
@@ -143,15 +140,7 @@ export async function buildRscPayloadResponse(
|
|
|
143
140
|
// §"Error Page Rendering for Client Navigation".
|
|
144
141
|
if (signals.renderError) {
|
|
145
142
|
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
|
-
);
|
|
143
|
+
return rscErrorEnvelope(signals.renderError.status, responseHeaders);
|
|
155
144
|
}
|
|
156
145
|
|
|
157
146
|
// Extract the first chunk from the race result.
|
|
@@ -200,7 +189,13 @@ export async function buildRscPayloadResponse(
|
|
|
200
189
|
// for state tree diffing on subsequent navigations. Request-dependency
|
|
201
190
|
// is determined by static module-graph analysis (not runtime taint).
|
|
202
191
|
// See design/19-client-navigation.md §"X-Timber-State-Tree Header"
|
|
203
|
-
const segmentInfo = buildSegmentInfo(
|
|
192
|
+
const segmentInfo = buildSegmentInfo(
|
|
193
|
+
segments,
|
|
194
|
+
layoutComponents,
|
|
195
|
+
slotSkipInfo,
|
|
196
|
+
skippedSegments,
|
|
197
|
+
firstDeniedIndex
|
|
198
|
+
);
|
|
204
199
|
responseHeaders.set('X-Timber-Segments', JSON.stringify(segmentInfo));
|
|
205
200
|
|
|
206
201
|
// Send skipped segments so the client can merge the partial RSC payload
|
|
@@ -209,12 +204,6 @@ export async function buildRscPayloadResponse(
|
|
|
209
204
|
responseHeaders.set('X-Timber-Skipped-Segments', JSON.stringify(skippedSegments));
|
|
210
205
|
}
|
|
211
206
|
|
|
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
207
|
// Check if a DenySignal was caught in-tree by AccessGate or
|
|
219
208
|
// PageDenyBoundary. The deny page is in the Flight stream as a normal
|
|
220
209
|
// element — use the deny status for the HTTP response. See TIM-666.
|
|
@@ -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
|
|
|
@@ -74,7 +75,7 @@ export interface RscStreamResult {
|
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
/**
|
|
77
|
-
* Render a
|
|
78
|
+
* Render a payload root (tree plus its params) to an RSC Flight stream.
|
|
78
79
|
*
|
|
79
80
|
* The stream serializes server components as rendered output and client
|
|
80
81
|
* components ("use client") as serialized references with module ID + export name.
|
|
@@ -84,7 +85,7 @@ export interface RscStreamResult {
|
|
|
84
85
|
* fires onError during stream consumption. Signals are captured in the
|
|
85
86
|
* returned `signals` object for the caller to handle.
|
|
86
87
|
*/
|
|
87
|
-
export function renderRscStream(
|
|
88
|
+
export function renderRscStream(payload: RscPayloadRoot, req: Request): RscStreamResult {
|
|
88
89
|
const signals: RenderSignals = {
|
|
89
90
|
denySignal: null,
|
|
90
91
|
redirectSignal: null,
|
|
@@ -100,7 +101,7 @@ export function renderRscStream(element: React.ReactElement, req: Request): RscS
|
|
|
100
101
|
|
|
101
102
|
try {
|
|
102
103
|
rscStream = renderToReadableStream(
|
|
103
|
-
|
|
104
|
+
payload,
|
|
104
105
|
{
|
|
105
106
|
signal: req.signal,
|
|
106
107
|
onError(error: unknown) {
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
|
|
15
15
|
import type { ClientBootstrapConfig } from '../html-injectors.js';
|
|
16
16
|
import { flightInitScript } from '../flight-scripts.js';
|
|
17
|
+
import { appVisibleSearch } from '../../shared/rsc-cache-key.js';
|
|
18
|
+
import { getSlotParamsForClient } from '../request-context.js';
|
|
17
19
|
import type { LayoutEntry } from '../deny-renderer.js';
|
|
18
20
|
import { renderDenyPage } from '../deny-renderer.js';
|
|
19
21
|
import type { RouteMatch } from '../pipeline.js';
|
|
@@ -102,6 +104,8 @@ interface SsrRenderOptions {
|
|
|
102
104
|
/** Tier 2 global-error.tsx file, if present in app/. */
|
|
103
105
|
globalError?: GlobalErrorFile;
|
|
104
106
|
slotSkipInfo?: SlotSkipEntry[];
|
|
107
|
+
/** Index of the first denied segment — see RouteElementResult.firstDeniedIndex. */
|
|
108
|
+
firstDeniedIndex?: number;
|
|
105
109
|
}
|
|
106
110
|
|
|
107
111
|
/**
|
|
@@ -170,26 +174,26 @@ export async function renderSsrResponse(opts: SsrRenderOptions): Promise<Respons
|
|
|
170
174
|
// Skipped when client JS is disabled — no client JS to consume it.
|
|
171
175
|
const segmentScript = clientJsDisabled
|
|
172
176
|
? ''
|
|
173
|
-
: `<script>self.__timber_segments=${htmlEscapeJsonString(JSON.stringify(buildSegmentInfo(segments, layoutComponents, opts.slotSkipInfo)))}</script>`;
|
|
177
|
+
: `<script>self.__timber_segments=${htmlEscapeJsonString(JSON.stringify(buildSegmentInfo(segments, layoutComponents, opts.slotSkipInfo, undefined, opts.firstDeniedIndex)))}</script>`;
|
|
174
178
|
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
179
|
+
// Params reach the browser inside the payload — a field on the root row of
|
|
180
|
+
// the same Flight stream that is inlined for hydration. SSR itself renders
|
|
181
|
+
// only the tree, and the client components it renders read params from the
|
|
182
|
+
// ALS context seeded below, which is also where server components answer
|
|
183
|
+
// `getSegmentParams(slotPath)` from.
|
|
184
|
+
const slotParams = getSlotParamsForClient();
|
|
181
185
|
|
|
182
186
|
const navContext: NavContext = {
|
|
183
187
|
pathname: new URL(req.url).pathname,
|
|
184
188
|
segmentParams: match.segmentParams,
|
|
185
|
-
searchParams: Object.fromEntries(new URL(req.url).
|
|
189
|
+
searchParams: Object.fromEntries(appVisibleSearch(new URL(req.url)).params),
|
|
190
|
+
slotParams,
|
|
186
191
|
statusCode: 200,
|
|
187
192
|
responseHeaders,
|
|
188
193
|
headHtml:
|
|
189
194
|
headHtml +
|
|
190
195
|
clientBootstrap.preloadLinks +
|
|
191
196
|
segmentScript +
|
|
192
|
-
paramsScript +
|
|
193
197
|
// Initialize __timber_f in <head> so it exists before any streaming
|
|
194
198
|
// chunk scripts arrive in <body>. See flight-scripts.ts, LOCAL-415.
|
|
195
199
|
(clientJsDisabled ? '' : flightInitScript()),
|