@timber-js/app 0.2.0-alpha.184 → 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-C539Pa2O.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-segment-params-ClyUNq4d.js +128 -0
- package/dist/_chunks/use-segment-params-ClyUNq4d.js.map +1 -0
- package/dist/_chunks/{walkers-BtTlKviE.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 +872 -305
- 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 +39 -16
- 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 +915 -320
- package/dist/index.js.map +1 -1
- package/dist/plugin-context.d.ts +33 -1
- 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 +149 -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/types.d.ts +1 -3
- 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/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-boundary.d.ts +4 -7
- package/dist/server/deny-boundary.d.ts.map +1 -1
- 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 +528 -373
- 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/prebuilt-builder.d.ts.map +1 -1
- package/dist/server/primitives.d.ts +0 -12
- 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 +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/sitemap-generator.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/server/status-code-resolver.d.ts +3 -10
- package/dist/server/status-code-resolver.d.ts.map +1 -1
- 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/dist/shims/navigation-client.d.ts +8 -19
- package/dist/shims/navigation-client.d.ts.map +1 -1
- package/dist/shims/navigation.d.ts +0 -1
- package/dist/shims/navigation.d.ts.map +1 -1
- 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 +4 -3
- 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 -690
- package/src/client/rsc-fetch.ts +180 -122
- 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 +34 -1
- 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 +60 -3
- package/src/plugins/static-build.ts +333 -63
- 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 +6 -9
- package/src/routing/scanner.ts +114 -113
- 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 +1 -3
- 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-boundary.ts +5 -31
- package/src/server/deny-renderer.ts +83 -22
- 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 +3 -5
- package/src/server/pipeline-phases.ts +68 -16
- package/src/server/pipeline.ts +57 -10
- package/src/server/prebuilt-builder.ts +1 -7
- package/src/server/prebuilt-runtime.ts +4 -4
- package/src/server/primitives.ts +0 -18
- 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 +12 -7
- 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/sitemap-generator.ts +1 -7
- 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 +67 -45
- package/src/server/static-not-found.ts +134 -0
- package/src/server/status-code-resolver.ts +4 -47
- 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/src/shims/navigation-client.ts +8 -26
- package/src/shims/navigation.ts +0 -4
- 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-D3naS3eH.js +0 -688
- package/dist/_chunks/cli-schema-sync-D3naS3eH.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/segment-classify-Byy425ng.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-BtTlKviE.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
package/src/client/rsc-fetch.ts
CHANGED
|
@@ -11,14 +11,40 @@
|
|
|
11
11
|
* See design/19-client-navigation.md §"RSC Payload Handling"
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import
|
|
15
|
-
|
|
14
|
+
import {
|
|
15
|
+
rscCacheKey,
|
|
16
|
+
recordLookup,
|
|
17
|
+
randomRscCacheKey,
|
|
18
|
+
RSC_KEY_PARAM,
|
|
19
|
+
} from '../shared/rsc-cache-key.js';
|
|
20
|
+
import type { RscManifest, RscManifestEntry } from '../shared/rsc-manifest.js';
|
|
21
|
+
import {
|
|
22
|
+
splitPayloadRoot,
|
|
23
|
+
readPublishedParams,
|
|
24
|
+
type PublishedParams,
|
|
25
|
+
} from '../shared/payload-root.js';
|
|
26
|
+
import { normalizeBasePath, stripBasePath, withBasePath } from '../shared/base-path.js';
|
|
27
|
+
import { rscPayloadPath } from '../shared/rsc-payload-path.js';
|
|
28
|
+
import type { SegmentInfo, StateTree } from '../shared/segment-info.js';
|
|
29
|
+
import { TIMBER_ERROR_HEADER } from '../shared/rsc-error-envelope.js';
|
|
30
|
+
import type { RouterDeps } from './router-types.js';
|
|
16
31
|
|
|
17
32
|
// ─── Types ───────────────────────────────────────────────────────
|
|
18
33
|
|
|
19
34
|
/** Result of fetching an RSC payload — includes segment metadata. */
|
|
20
35
|
export interface FetchResult {
|
|
36
|
+
/** The renderable tree, split off the payload root. */
|
|
21
37
|
payload: unknown;
|
|
38
|
+
/**
|
|
39
|
+
* The params the same payload published beside that tree.
|
|
40
|
+
*
|
|
41
|
+
* Not a separate channel: both fields come out of one `splitPayloadRoot`
|
|
42
|
+
* call on one decoded root, so a response cannot deliver a tree and another
|
|
43
|
+
* route's params. They used to arrive on their own header, which is how a
|
|
44
|
+
* navigation could render one route and publish another's (TIM-1285,
|
|
45
|
+
* TIM-1294).
|
|
46
|
+
*/
|
|
47
|
+
params: PublishedParams | Promise<PublishedParams>;
|
|
22
48
|
/**
|
|
23
49
|
* Promise that settles when the RSC decode completes or fails.
|
|
24
50
|
* The payload thenable is NOT awaited before returning (for streaming),
|
|
@@ -29,8 +55,6 @@ export interface FetchResult {
|
|
|
29
55
|
decodePromise: Promise<void> | null;
|
|
30
56
|
/** Segment metadata from X-Timber-Segments header for populating the segment cache. */
|
|
31
57
|
segmentInfo: SegmentInfo[] | null;
|
|
32
|
-
/** Route params from X-Timber-Params header for populating useSegmentParams(). */
|
|
33
|
-
params: Record<string, string | string[]> | null;
|
|
34
58
|
/** Segment paths that were skipped by the server (for client-side merging). */
|
|
35
59
|
skippedSegments: string[] | null;
|
|
36
60
|
}
|
|
@@ -42,31 +66,39 @@ export const RSC_CONTENT_TYPE = 'text/x-component';
|
|
|
42
66
|
// ─── URL Helpers ─────────────────────────────────────────────────
|
|
43
67
|
|
|
44
68
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
69
|
+
* Append a `_rsc=<key>` query parameter to the URL (TIM-1268).
|
|
70
|
+
*
|
|
71
|
+
* The key is a deterministic hash of the RSC request headers, so identical
|
|
72
|
+
* requests share a URL and the payload becomes cacheable. It also keeps the
|
|
73
|
+
* document and the payload on different URLs, so a shared cache cannot
|
|
74
|
+
* return HTML for an RSC request (or vice versa) even where `Vary` is
|
|
75
|
+
* opt-in configuration rather than default behaviour — Cloudflare and
|
|
76
|
+
* CloudFront both. See GHSA-wfc6-r584-vfw7, design/13-security.md.
|
|
77
|
+
*
|
|
78
|
+
* The origin recomputes this key from the received headers and refuses to
|
|
79
|
+
* let a mismatched response be shared-cached, so a caller cannot claim
|
|
80
|
+
* another client's key while sending its own headers.
|
|
81
|
+
*
|
|
82
|
+
* Falls back to a random value when no key can be derived — `crypto.subtle`
|
|
83
|
+
* is absent in non-secure browsing contexts (plain `http://` on a LAN
|
|
84
|
+
* address). Dropping the parameter instead would put the payload back on
|
|
85
|
+
* the document URL, where a query-keyed cache that ignores `Vary` could
|
|
86
|
+
* store a Flight response under `/about` and serve it to an HTML
|
|
87
|
+
* navigation. The random parameter this feature replaced did separate those
|
|
88
|
+
* representations, and the fallback must not be worse than what it
|
|
89
|
+
* replaced. The origin cannot verify a random key, so it refuses to
|
|
90
|
+
* share-cache the response — which is exactly the old behaviour, where a
|
|
91
|
+
* unique URL was never a cache hit anyway.
|
|
61
92
|
*
|
|
62
93
|
* Strips any #fragment before appending — fragments are client-only and
|
|
63
94
|
* fetch() discards them, so _rsc would land inside the hash and be lost.
|
|
64
95
|
*/
|
|
65
|
-
function appendRscParam(url: string): string {
|
|
96
|
+
async function appendRscParam(url: string, headers: Record<string, string>): Promise<string> {
|
|
66
97
|
const hashIndex = url.indexOf('#');
|
|
67
98
|
const urlWithoutHash = hashIndex === -1 ? url : url.slice(0, hashIndex);
|
|
99
|
+
const key = (await rscCacheKey(recordLookup(headers))) ?? randomRscCacheKey();
|
|
68
100
|
const separator = urlWithoutHash.includes('?') ? '&' : '?';
|
|
69
|
-
return `${urlWithoutHash}${separator}
|
|
101
|
+
return `${urlWithoutHash}${separator}${RSC_KEY_PARAM}=${key}`;
|
|
70
102
|
}
|
|
71
103
|
|
|
72
104
|
// ─── Deployment ID ───────────────────────────────────────────────
|
|
@@ -88,6 +120,30 @@ export function getClientDeploymentId(): string | null {
|
|
|
88
120
|
return clientDeploymentId;
|
|
89
121
|
}
|
|
90
122
|
|
|
123
|
+
// ─── Base Path ───────────────────────────────────────────────────
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The deployment base path (Vite's resolved `base`), normalized.
|
|
127
|
+
* Set at bootstrap from `virtual:timber-config`; `'/'` unless the app is
|
|
128
|
+
* deployed under a sub-path. See design/11-platform.md, TIM-1261.
|
|
129
|
+
*
|
|
130
|
+
* **The base is applied in exactly one place: at fetch time**
|
|
131
|
+
* (`toStaticRscUrl`). The static build writes the RSC manifest with
|
|
132
|
+
* root-relative keys *and* root-relative URLs, so the generated site stays
|
|
133
|
+
* relocatable and there is no second copy of the base to drift from this one.
|
|
134
|
+
*/
|
|
135
|
+
let basePath = '/';
|
|
136
|
+
|
|
137
|
+
/** Set the deployment base path. Called once at bootstrap. */
|
|
138
|
+
export function setBasePath(base: string | undefined): void {
|
|
139
|
+
basePath = normalizeBasePath(base);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Get the normalized deployment base path. @internal Exported for testing. */
|
|
143
|
+
export function getBasePath(): string {
|
|
144
|
+
return basePath;
|
|
145
|
+
}
|
|
146
|
+
|
|
91
147
|
// ─── Static Mode ────────────────────────────────────────────────
|
|
92
148
|
|
|
93
149
|
/**
|
|
@@ -107,13 +163,14 @@ export function isStaticMode(): boolean {
|
|
|
107
163
|
}
|
|
108
164
|
|
|
109
165
|
/**
|
|
110
|
-
* RSC manifest mapping unhashed → hashed URLs. Populated from
|
|
111
|
-
* `window.__TIMBER_RSC_MANIFEST__` (injected into HTML during
|
|
112
|
-
*
|
|
166
|
+
* RSC manifest mapping unhashed → content-hashed `.rsc` URLs. Populated from
|
|
167
|
+
* `window.__TIMBER_RSC_MANIFEST__` (injected into HTML during static
|
|
168
|
+
* generation). Shape shared with the static build that writes it — see
|
|
169
|
+
* `shared/rsc-manifest.ts`. See TIM-1254.
|
|
113
170
|
*/
|
|
114
|
-
let rscManifest:
|
|
171
|
+
let rscManifest: RscManifest | null = null;
|
|
115
172
|
|
|
116
|
-
function getRscManifest():
|
|
173
|
+
function getRscManifest(): RscManifest | null {
|
|
117
174
|
if (rscManifest) return rscManifest;
|
|
118
175
|
if (typeof window !== 'undefined' && (window as any).__TIMBER_RSC_MANIFEST__) {
|
|
119
176
|
rscManifest = (window as any).__TIMBER_RSC_MANIFEST__;
|
|
@@ -122,28 +179,49 @@ function getRscManifest(): Record<string, string> | null {
|
|
|
122
179
|
}
|
|
123
180
|
|
|
124
181
|
/** @internal Exported for testing. */
|
|
125
|
-
export function setRscManifest(manifest:
|
|
182
|
+
export function setRscManifest(manifest: RscManifest | null): void {
|
|
126
183
|
rscManifest = manifest;
|
|
127
184
|
}
|
|
128
185
|
|
|
186
|
+
/** A resolved static payload target: the URL to fetch, and how it was resolved. */
|
|
187
|
+
interface StaticRscTarget {
|
|
188
|
+
/** Fetchable URL — base-prefixed. */
|
|
189
|
+
url: string;
|
|
190
|
+
/**
|
|
191
|
+
* True when the RSC manifest supplied a content-hashed filename. The hash
|
|
192
|
+
* guarantees freshness, so no `_rsc` cache key is appended (TIM-1254).
|
|
193
|
+
*/
|
|
194
|
+
hashed: boolean;
|
|
195
|
+
}
|
|
196
|
+
|
|
129
197
|
/**
|
|
130
|
-
*
|
|
131
|
-
*
|
|
198
|
+
* Resolve a route URL to the `_rsc/*.rsc` file to fetch. The naming rule is
|
|
199
|
+
* `shared/rsc-payload-path.ts`, shared with the build that writes the files.
|
|
132
200
|
*
|
|
133
201
|
* When an RSC manifest is available (hashed filenames from TIM-1254),
|
|
134
202
|
* the manifest is consulted to resolve to the hashed path.
|
|
135
203
|
*
|
|
136
204
|
* / → /_rsc/index.rsc (or /_rsc/index-B7YxEKdN.rsc with manifest)
|
|
137
|
-
* /about → /_rsc/about.rsc (or /_rsc/about-C8ZzFLfO.rsc
|
|
138
|
-
* /blog/hello → /_rsc/blog/hello.rsc
|
|
205
|
+
* /about → /_rsc/about/index.rsc (or /_rsc/about/index-C8ZzFLfO.rsc)
|
|
206
|
+
* /blog/hello → /_rsc/blog/hello/index.rsc
|
|
207
|
+
*
|
|
208
|
+
* The manifest is written and read in route space (no base prefix), so the
|
|
209
|
+
* base is applied here — once, on the way out. See client/base-path.ts.
|
|
139
210
|
*/
|
|
140
|
-
function toStaticRscUrl(url: string):
|
|
211
|
+
function toStaticRscUrl(url: string): StaticRscTarget {
|
|
141
212
|
const unhashed = toUnhashedRscUrl(url);
|
|
142
|
-
|
|
213
|
+
const hashedUrl = manifestLookup(unhashed);
|
|
214
|
+
return { url: withBasePath(basePath, hashedUrl ?? unhashed), hashed: hashedUrl !== null };
|
|
143
215
|
}
|
|
144
216
|
|
|
145
217
|
/**
|
|
146
|
-
* Compute the unhashed _rsc/*.rsc
|
|
218
|
+
* Compute the unhashed `_rsc/*.rsc` path for a route URL.
|
|
219
|
+
*
|
|
220
|
+
* Returns a **route-space** path: the deployment base is stripped from the
|
|
221
|
+
* incoming URL and is NOT re-applied, because this value doubles as the RSC
|
|
222
|
+
* manifest key, and the manifest is base-less. `toStaticRscUrl` applies the
|
|
223
|
+
* base to whatever is actually fetched.
|
|
224
|
+
*
|
|
147
225
|
* @internal Exported for testing.
|
|
148
226
|
*/
|
|
149
227
|
export function toUnhashedRscUrl(url: string): string {
|
|
@@ -152,27 +230,19 @@ export function toUnhashedRscUrl(url: string): string {
|
|
|
152
230
|
const hashEnd = hashIndex === -1 ? url.length : hashIndex;
|
|
153
231
|
const queryEnd = queryIndex === -1 ? url.length : queryIndex;
|
|
154
232
|
const end = Math.min(hashEnd, queryEnd);
|
|
155
|
-
let pathname = url.slice(0, end);
|
|
233
|
+
let pathname = stripBasePath(basePath, url.slice(0, end));
|
|
156
234
|
// Strip trailing slash (unless root) to match static build output naming
|
|
157
235
|
if (pathname.length > 1 && pathname.endsWith('/')) {
|
|
158
236
|
pathname = pathname.slice(0, -1);
|
|
159
237
|
}
|
|
160
|
-
|
|
161
|
-
return `/_rsc${rscPath}.rsc`;
|
|
238
|
+
return rscPayloadPath(pathname);
|
|
162
239
|
}
|
|
163
240
|
|
|
164
|
-
/**
|
|
165
|
-
* Convert a route URL to the corresponding _rsc/*.params.json sidecar path.
|
|
166
|
-
* Used in static mode to fetch route params that are normally carried
|
|
167
|
-
* in the X-Timber-Params response header. See TIM-1246.
|
|
168
|
-
*
|
|
169
|
-
* Consults the RSC manifest for hashed filenames (TIM-1254).
|
|
170
|
-
*/
|
|
171
241
|
/**
|
|
172
242
|
* Look up a key in the RSC manifest, falling back to percent-decoded
|
|
173
|
-
* lookup for encoded browser URLs. Returns
|
|
243
|
+
* lookup for encoded browser URLs. Returns the entry or null.
|
|
174
244
|
*/
|
|
175
|
-
function
|
|
245
|
+
function manifestEntry(key: string): RscManifestEntry | null {
|
|
176
246
|
const manifest = getRscManifest();
|
|
177
247
|
if (!manifest) return null;
|
|
178
248
|
if (manifest[key]) return manifest[key];
|
|
@@ -185,11 +255,19 @@ function manifestLookup(key: string): string | null {
|
|
|
185
255
|
return null;
|
|
186
256
|
}
|
|
187
257
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
258
|
+
/**
|
|
259
|
+
* Look up the hashed URL for an unhashed RSC path.
|
|
260
|
+
*/
|
|
261
|
+
function manifestLookup(key: string): string | null {
|
|
262
|
+
return manifestEntry(key)?.url ?? null;
|
|
191
263
|
}
|
|
192
264
|
|
|
265
|
+
// The manifest used to inline each route's params, because a generated
|
|
266
|
+
// `.rsc` file carries no response headers and the header channel therefore
|
|
267
|
+
// did not exist on the static path (TIM-1255, TIM-1285). The `.rsc` file now
|
|
268
|
+
// carries them itself — they are props inside the payload — so the static
|
|
269
|
+
// path needs no special case at all.
|
|
270
|
+
|
|
193
271
|
// ─── Reload Signal ───────────────────────────────────────────────
|
|
194
272
|
|
|
195
273
|
/** Header name used by the server to signal a version skew reload. */
|
|
@@ -208,8 +286,19 @@ export function checkReloadSignal(response: Response): boolean {
|
|
|
208
286
|
|
|
209
287
|
// ─── Header Builder ──────────────────────────────────────────────
|
|
210
288
|
|
|
289
|
+
/**
|
|
290
|
+
* Build the headers for an RSC payload request.
|
|
291
|
+
*
|
|
292
|
+
* **Every header added here is hashed into the payload URL** as the `_rsc`
|
|
293
|
+
* cache key (see `rscCacheKey`), and URLs are logged by proxies, CDNs, and
|
|
294
|
+
* origin access logs. So: no credentials, no session identifiers, no
|
|
295
|
+
* user-supplied content. `fnv1aHash` is a cache-key hash, not a KDF — a
|
|
296
|
+
* low-entropy secret would be recoverable from the URL by brute force.
|
|
297
|
+
* Authentication already travels on cookies, which are not part of this
|
|
298
|
+
* object and never enter the key.
|
|
299
|
+
*/
|
|
211
300
|
export function buildRscHeaders(
|
|
212
|
-
stateTree:
|
|
301
|
+
stateTree: StateTree | undefined,
|
|
213
302
|
currentUrl?: string
|
|
214
303
|
): Record<string, string> {
|
|
215
304
|
const headers: Record<string, string> = {
|
|
@@ -287,23 +376,6 @@ export function extractSkippedSegments(response: Response): string[] | null {
|
|
|
287
376
|
}
|
|
288
377
|
}
|
|
289
378
|
|
|
290
|
-
/**
|
|
291
|
-
* Extract route params from the X-Timber-Params response header.
|
|
292
|
-
* Returns null if the header is missing or malformed.
|
|
293
|
-
*
|
|
294
|
-
* Used to populate useSegmentParams() after client-side navigation.
|
|
295
|
-
*/
|
|
296
|
-
export function extractParams(response: Response): Record<string, string | string[]> | null {
|
|
297
|
-
const header = response.headers.get('X-Timber-Params');
|
|
298
|
-
if (!header) return null;
|
|
299
|
-
try {
|
|
300
|
-
return JSON.parse(header);
|
|
301
|
-
} catch {
|
|
302
|
-
warnMalformedHeader('X-Timber-Params', header);
|
|
303
|
-
return null;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
379
|
// ─── Redirect Error ──────────────────────────────────────────────
|
|
308
380
|
|
|
309
381
|
/**
|
|
@@ -374,9 +446,10 @@ export class NonRscResponse extends Error {
|
|
|
374
446
|
* a `done` promise that resolves when the last chunk is read (or
|
|
375
447
|
* rejects if the stream errors).
|
|
376
448
|
*
|
|
377
|
-
*
|
|
378
|
-
*
|
|
379
|
-
*
|
|
449
|
+
* createFromFetch's thenable resolves on shell arrival, but callers need to
|
|
450
|
+
* know when the stream is fully decoded: that is what `navigateTransition`'s
|
|
451
|
+
* returned promise means, and what the router's pending store, the Navigation
|
|
452
|
+
* API deferred and `<Link>`'s `isPending` are all timed against.
|
|
380
453
|
*/
|
|
381
454
|
function trackStreamCompletion(body: ReadableStream<Uint8Array>): {
|
|
382
455
|
body: ReadableStream<Uint8Array>;
|
|
@@ -431,13 +504,23 @@ export async function fetchRscPayload(
|
|
|
431
504
|
// In static mode, fetch the pre-generated _rsc/*.rsc file directly
|
|
432
505
|
// instead of the route URL with Accept headers. Static hosts ignore
|
|
433
506
|
// Accept headers, so the route URL would return HTML.
|
|
434
|
-
const
|
|
435
|
-
|
|
507
|
+
const staticTarget = staticMode ? toStaticRscUrl(url) : null;
|
|
508
|
+
const fetchTarget = staticTarget ? staticTarget.url : url;
|
|
509
|
+
// Static hosts resolve neither partial payloads nor intercepting routes,
|
|
510
|
+
// so neither the state tree nor the source URL is sent — and neither may
|
|
511
|
+
// fragment the cache key of a file that does not vary on them.
|
|
512
|
+
const headers = staticMode
|
|
513
|
+
? buildRscHeaders(undefined, undefined)
|
|
514
|
+
: buildRscHeaders(stateTree, currentUrl);
|
|
515
|
+
// Skip the cache key when the manifest supplies a content-hashed
|
|
436
516
|
// URL — the hash itself guarantees freshness, and the param would defeat
|
|
437
517
|
// the immutable cache headers on /_rsc/* (TIM-1254).
|
|
438
|
-
const
|
|
439
|
-
|
|
440
|
-
|
|
518
|
+
const rscUrl = staticTarget?.hashed ? fetchTarget : await appendRscParam(fetchTarget, headers);
|
|
519
|
+
// Deriving the cache key is async (SubtleCrypto), so the fetch is no
|
|
520
|
+
// longer issued in the same tick as the call. A navigation superseded
|
|
521
|
+
// during that window would otherwise still hit the network with an
|
|
522
|
+
// already-aborted signal.
|
|
523
|
+
signal?.throwIfAborted();
|
|
441
524
|
if (deps.decodeRsc) {
|
|
442
525
|
// Production path: use createFromFetch for streaming RSC decoding.
|
|
443
526
|
// createFromFetch takes a Promise<Response> and progressively parses
|
|
@@ -446,23 +529,7 @@ export async function fetchRscPayload(
|
|
|
446
529
|
// Intercept the response to read segment metadata before createFromFetch
|
|
447
530
|
// consumes the body. Reading headers does NOT consume the body stream.
|
|
448
531
|
const fetchPromise = deps.fetch(rscUrl, { headers, redirect: 'manual', signal });
|
|
449
|
-
// In static mode, fetch the params sidecar in parallel. The .rsc
|
|
450
|
-
// file has no HTTP headers, so params come from a .params.json
|
|
451
|
-
// sidecar written at build time. See TIM-1246.
|
|
452
|
-
const staticParamsFetch = staticMode
|
|
453
|
-
? deps
|
|
454
|
-
.fetch(toStaticParamsUrl(url), { signal })
|
|
455
|
-
.then((r) => (r.ok ? (r.json() as Promise<Record<string, string | string[]>>) : null))
|
|
456
|
-
.catch((e) => {
|
|
457
|
-
if (e instanceof DOMException && e.name === 'AbortError') throw e;
|
|
458
|
-
return null;
|
|
459
|
-
})
|
|
460
|
-
: null;
|
|
461
|
-
// Observe the sidecar promise so its rejection doesn't become
|
|
462
|
-
// unhandled if wrappedPromise rejects first (TIM-1248).
|
|
463
|
-
staticParamsFetch?.catch(() => {});
|
|
464
532
|
let segmentInfo: SegmentInfo[] | null = null;
|
|
465
|
-
let params: Record<string, string | string[]> | null = null;
|
|
466
533
|
let skippedSegments: string[] | null = null;
|
|
467
534
|
// Track when the full RSC body stream is consumed (not just shell).
|
|
468
535
|
// Initialized to resolved for bodyless responses; overwritten when
|
|
@@ -490,7 +557,7 @@ export async function fetchRscPayload(
|
|
|
490
557
|
// with a JSON body instead of a broken RSC stream for any RenderError
|
|
491
558
|
// (4xx or 5xx). Hard-navigate so the server renders the error page as HTML.
|
|
492
559
|
// See design/10-error-handling.md §"Error Page Rendering for Client Navigation"
|
|
493
|
-
if (response.headers.get(
|
|
560
|
+
if (response.headers.get(TIMBER_ERROR_HEADER) === '1') {
|
|
494
561
|
throw new ServerErrorResponse(response.status, url);
|
|
495
562
|
}
|
|
496
563
|
// Content-Type guard: reject non-RSC responses before createFromFetch
|
|
@@ -518,13 +585,12 @@ export async function fetchRscPayload(
|
|
|
518
585
|
// Metadata (<title>/<meta>/<link>) now rides the RSC Flight payload
|
|
519
586
|
// as React elements — React 19 Float handles them. See TIM-1151.
|
|
520
587
|
segmentInfo = extractSegmentInfo(response);
|
|
521
|
-
params = extractParams(response);
|
|
522
588
|
skippedSegments = extractSkippedSegments(response);
|
|
523
589
|
|
|
524
590
|
// Wrap the body to track full stream consumption. createFromFetch's
|
|
525
591
|
// thenable resolves when the root model (shell) arrives, but we need
|
|
526
|
-
// to know when ALL chunks are read
|
|
527
|
-
//
|
|
592
|
+
// to know when ALL chunks are read — that is what the navigation
|
|
593
|
+
// promise resolving means, and what keeps the pending store set.
|
|
528
594
|
if (response.body) {
|
|
529
595
|
const tracked = trackStreamCompletion(response.body);
|
|
530
596
|
streamDone = tracked.done;
|
|
@@ -536,33 +602,37 @@ export async function fetchRscPayload(
|
|
|
536
602
|
}
|
|
537
603
|
return response;
|
|
538
604
|
});
|
|
539
|
-
// Await headers so segmentInfo
|
|
605
|
+
// Await headers so segmentInfo is populated.
|
|
540
606
|
await wrappedPromise;
|
|
541
|
-
// In static mode, params come from the sidecar (header extraction
|
|
542
|
-
// returns null because .rsc files have no HTTP headers). TIM-1246.
|
|
543
|
-
if (staticParamsFetch && !params) {
|
|
544
|
-
params = await staticParamsFetch;
|
|
545
|
-
}
|
|
546
607
|
// Start decoding but do NOT await — return the in-progress thenable.
|
|
547
608
|
// React can render a Flight thenable directly: it suspends on unresolved
|
|
548
609
|
// parts and progressively renders as chunks arrive, spreading work across
|
|
549
610
|
// frames instead of blocking the main thread in one burst.
|
|
550
|
-
const
|
|
611
|
+
const root = deps.decodeRsc(wrappedPromise);
|
|
612
|
+
// One split, one root: the tree and the params it was rendered with come
|
|
613
|
+
// apart here and nowhere else, so nothing downstream has to keep them
|
|
614
|
+
// paired by hand.
|
|
615
|
+
const { tree: payload, params } = splitPayloadRoot(root);
|
|
551
616
|
// Combine stream completion with payload error propagation.
|
|
552
|
-
// streamDone
|
|
553
|
-
//
|
|
617
|
+
// streamDone marks the end of the RSC stream, which is what the
|
|
618
|
+
// navigation promise resolves on. payloadError propagates
|
|
554
619
|
// decode failures (stale client references, Flight parse errors)
|
|
555
620
|
// so the router's catch block can trigger recovery (stale reload).
|
|
556
621
|
const payloadError = new Promise<void>((_, reject) => {
|
|
557
|
-
Promise.resolve(
|
|
622
|
+
Promise.resolve(root).then(() => {}, reject);
|
|
558
623
|
});
|
|
559
624
|
payloadError.catch(() => {});
|
|
560
625
|
const decodePromise = Promise.race([streamDone, payloadError]);
|
|
626
|
+
// Mark handled, like payloadError above. The same rejection reaches the
|
|
627
|
+
// router through `payload`, and a navigation that recovers by hard
|
|
628
|
+
// navigating (version skew, non-RSC response) hangs deliberately and never
|
|
629
|
+
// awaits this one — leaving it to surface as an unhandled rejection.
|
|
630
|
+
decodePromise.catch(() => {});
|
|
561
631
|
return {
|
|
562
632
|
payload,
|
|
633
|
+
params,
|
|
563
634
|
decodePromise,
|
|
564
635
|
segmentInfo,
|
|
565
|
-
params,
|
|
566
636
|
skippedSegments,
|
|
567
637
|
};
|
|
568
638
|
}
|
|
@@ -576,7 +646,7 @@ export async function fetchRscPayload(
|
|
|
576
646
|
}
|
|
577
647
|
}
|
|
578
648
|
// Server error guard (same as production path above).
|
|
579
|
-
if (response.headers.get(
|
|
649
|
+
if (response.headers.get(TIMBER_ERROR_HEADER) === '1') {
|
|
580
650
|
throw new ServerErrorResponse(response.status, url);
|
|
581
651
|
}
|
|
582
652
|
// Content-Type guard (same as production path above). See TIM-1231, TIM-1243, TIM-1247.
|
|
@@ -600,24 +670,12 @@ export async function fetchRscPayload(
|
|
|
600
670
|
throw new NonRscResponse(url);
|
|
601
671
|
}
|
|
602
672
|
}
|
|
603
|
-
|
|
604
|
-
// In static mode, params come from the sidecar. TIM-1246.
|
|
605
|
-
if (staticMode && !fallbackParams) {
|
|
606
|
-
try {
|
|
607
|
-
const paramsResponse = await deps.fetch(toStaticParamsUrl(url), { signal });
|
|
608
|
-
if (paramsResponse.ok) {
|
|
609
|
-
fallbackParams = (await paramsResponse.json()) as Record<string, string | string[]>;
|
|
610
|
-
}
|
|
611
|
-
} catch (e) {
|
|
612
|
-
if (e instanceof DOMException && e.name === 'AbortError') throw e;
|
|
613
|
-
// No params sidecar — non-dynamic route, params stay null
|
|
614
|
-
}
|
|
615
|
-
}
|
|
673
|
+
// Test/fallback path: raw text carries no payload root, so no params.
|
|
616
674
|
return {
|
|
617
675
|
payload: await response.text(),
|
|
676
|
+
params: readPublishedParams(undefined),
|
|
618
677
|
decodePromise: null,
|
|
619
678
|
segmentInfo: extractSegmentInfo(response),
|
|
620
|
-
params: fallbackParams,
|
|
621
679
|
skippedSegments: extractSkippedSegments(response),
|
|
622
680
|
};
|
|
623
681
|
}
|