@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/adapters/shared.ts
CHANGED
|
@@ -8,6 +8,25 @@
|
|
|
8
8
|
// IMPORTANT: Do NOT add imports from ../server/, ../client/, ../plugins/,
|
|
9
9
|
// or any module that transitively depends on Vite. A test in
|
|
10
10
|
// tests/adapters/shared-no-forbidden-imports.test.ts enforces this.
|
|
11
|
+
// ../shared/ is exempt: it is dependency-free by the same rule.
|
|
12
|
+
|
|
13
|
+
import { withBasePath } from '../shared/base-path.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The deployment base an adapter should write platform rules against.
|
|
17
|
+
*
|
|
18
|
+
* `config.base` is optional on `TimberConfig` because adapters can be invoked
|
|
19
|
+
* by callers that predate it (a hand-written adapter, a test harness). Falling
|
|
20
|
+
* back to `'/'` reproduces the pre-TIM-1274 output exactly, which is correct
|
|
21
|
+
* for the only deployments those callers can have: a build under a non-root
|
|
22
|
+
* base always goes through `plugins/adapter-build.ts`, which populates it.
|
|
23
|
+
*
|
|
24
|
+
* Typed structurally rather than as `TimberConfig` so this module keeps no
|
|
25
|
+
* import of `./types.js` at runtime.
|
|
26
|
+
*/
|
|
27
|
+
export function adapterBase(config: { base?: string }): string {
|
|
28
|
+
return config.base ?? '/';
|
|
29
|
+
}
|
|
11
30
|
|
|
12
31
|
// ─── Static asset cache headers ──────────────────────────────────────────
|
|
13
32
|
|
|
@@ -33,27 +52,165 @@ export const STATIC_CACHE = 'public, max-age=3600, must-revalidate';
|
|
|
33
52
|
* A `/*` catch-all is NOT used — it would be additive on Cloudflare,
|
|
34
53
|
* producing duplicate Cache-Control directives on hashed assets
|
|
35
54
|
* (invalid per RFC 9111).
|
|
55
|
+
*
|
|
56
|
+
* **Every rule path is written in deployed-URL space** (TIM-1274). The
|
|
57
|
+
* platform matches these patterns against the request path, which under a
|
|
58
|
+
* non-root `base` sits below the prefix: with `base: '/repo/'` the assets are
|
|
59
|
+
* at `/repo/assets/*`, so a rule saying `/assets/*` matches nothing. Silent
|
|
60
|
+
* for the cache rules (slower, still correct) and not silent for the
|
|
61
|
+
* Content-Type rules — an extensionless API route output is unreadable on
|
|
62
|
+
* Cloudflare Workers Static Assets without one.
|
|
63
|
+
*
|
|
64
|
+
* The prefix is applied at exactly one point, `rulePath()`, which every rule
|
|
65
|
+
* in this function goes through. Prefixing the three wildcards and
|
|
66
|
+
* hand-checking that the per-file rules agree is how the next rule gets
|
|
67
|
+
* missed.
|
|
36
68
|
*/
|
|
37
69
|
// Cloudflare Workers Static Assets caps _headers at 100 rules.
|
|
38
70
|
// Reserve 3 for the wildcard hashed-asset rules (/assets/*, /_timber/fonts/*, /_rsc/*).
|
|
39
71
|
const MAX_PER_FILE_RULES = 97;
|
|
40
72
|
|
|
41
|
-
export
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
73
|
+
export interface HeadersFileOptions {
|
|
74
|
+
/**
|
|
75
|
+
* The deployment base, already normalized by `normalizeBasePath()` —
|
|
76
|
+
* leading and trailing slash, `'/'` at the domain root.
|
|
77
|
+
*
|
|
78
|
+
* Required rather than defaulted: a caller that forgets it would emit
|
|
79
|
+
* root-relative rules that match nothing, which is the bug this closes.
|
|
80
|
+
*/
|
|
81
|
+
base: string;
|
|
82
|
+
/** Unhashed static files (route-space paths) needing per-file cache rules. */
|
|
83
|
+
unhashedPaths?: string[];
|
|
84
|
+
/** Route-space path → Content-Type, for extensionless API route outputs. */
|
|
85
|
+
contentTypes?: Record<string, string>;
|
|
86
|
+
/**
|
|
87
|
+
* Verbatim contents of the user's `public/_headers`, appended after the
|
|
88
|
+
* generated rules. See `mergeUserHeaders` for why it goes last.
|
|
89
|
+
*/
|
|
90
|
+
userRules?: string;
|
|
91
|
+
}
|
|
47
92
|
|
|
48
|
-
|
|
49
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Count the rules in a `_headers` file — a rule is a path line, which is any
|
|
95
|
+
* line that is neither blank, a comment, nor an indented header.
|
|
96
|
+
*
|
|
97
|
+
* @internal Exported for testing.
|
|
98
|
+
*/
|
|
99
|
+
export function countHeaderRules(content: string): number {
|
|
100
|
+
return content
|
|
101
|
+
.split('\n')
|
|
102
|
+
.filter((line) => line.trim() !== '' && !line.startsWith('#') && !/^\s/.test(line)).length;
|
|
103
|
+
}
|
|
50
104
|
|
|
51
|
-
|
|
52
|
-
|
|
105
|
+
/** One `_headers` block: a path pattern and the headers it sets. */
|
|
106
|
+
export interface HeaderRule {
|
|
107
|
+
pattern: string;
|
|
108
|
+
headers: Array<{ name: string; value: string }>;
|
|
109
|
+
}
|
|
53
110
|
|
|
54
|
-
|
|
55
|
-
|
|
111
|
+
/**
|
|
112
|
+
* Parse a `_headers` file. A non-indented, non-comment line opens a rule; the
|
|
113
|
+
* indented `Name: value` lines beneath it belong to that rule.
|
|
114
|
+
*
|
|
115
|
+
* @internal Exported for testing.
|
|
116
|
+
*/
|
|
117
|
+
export function parseHeaderRules(content: string): HeaderRule[] {
|
|
118
|
+
const rules: HeaderRule[] = [];
|
|
119
|
+
for (const raw of content.split('\n')) {
|
|
120
|
+
const line = raw.replace(/\r$/, '');
|
|
121
|
+
if (line.trim() === '' || line.trimStart().startsWith('#')) continue;
|
|
122
|
+
if (/^\s/.test(line)) {
|
|
123
|
+
const current = rules[rules.length - 1];
|
|
124
|
+
if (!current) continue; // header with no rule above it — not ours to fix
|
|
125
|
+
const colon = line.indexOf(':');
|
|
126
|
+
if (colon === -1) continue;
|
|
127
|
+
current.headers.push({
|
|
128
|
+
name: line.slice(0, colon).trim(),
|
|
129
|
+
value: line.slice(colon + 1).trim(),
|
|
130
|
+
});
|
|
131
|
+
} else {
|
|
132
|
+
rules.push({ pattern: line.trim(), headers: [] });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return rules;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Whether a user pattern can match paths a generated pattern also matches.
|
|
140
|
+
*
|
|
141
|
+
* Deliberately narrow: equality, or a prefix-glob (`/x/*`, `/*`) whose literal
|
|
142
|
+
* prefix contains the other's. Anything cleverer risks claiming an overlap
|
|
143
|
+
* that is not there, and the only thing this drives is a warning — a false
|
|
144
|
+
* positive is noise a developer cannot act on.
|
|
145
|
+
*/
|
|
146
|
+
function patternsOverlap(a: string, b: string): boolean {
|
|
147
|
+
if (a === b) return true;
|
|
148
|
+
const prefixOf = (p: string) => (p.endsWith('*') ? p.slice(0, -1) : null);
|
|
149
|
+
const [pa, pb] = [prefixOf(a), prefixOf(b)];
|
|
150
|
+
if (pa !== null && (b.startsWith(pa) || pa === '')) return true;
|
|
151
|
+
if (pb !== null && (a.startsWith(pb) || pb === '')) return true;
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function generateHeadersFile({
|
|
156
|
+
base,
|
|
157
|
+
unhashedPaths,
|
|
158
|
+
contentTypes,
|
|
159
|
+
userRules,
|
|
160
|
+
}: HeadersFileOptions): string {
|
|
161
|
+
/** Route-space rule path → deployed-URL rule path. */
|
|
162
|
+
const rulePath = (path: string) => withBasePath(base, path);
|
|
163
|
+
|
|
164
|
+
// Cloudflare and Netlify apply *every* matching rule, so emitting a header
|
|
165
|
+
// the user also sets on the same path yields two definitions of it — a
|
|
166
|
+
// comma-joined `Cache-Control` that is invalid per RFC 9111, and neither
|
|
167
|
+
// side's intent. This module already refuses to emit a `/*` catch-all for
|
|
168
|
+
// that reason; appending the user's file verbatim reintroduced it through
|
|
169
|
+
// the back door (TIM-1326).
|
|
170
|
+
//
|
|
171
|
+
// An explicit app rule beats a framework default, so where the user names
|
|
172
|
+
// the same pattern and the same header, ours is dropped. Only exact pattern
|
|
173
|
+
// matches are resolved this way: silently dropping asset immutability
|
|
174
|
+
// because a `/*` rule *might* subsume it would be a worse surprise than the
|
|
175
|
+
// duplicate, so overlapping-but-different patterns warn instead.
|
|
176
|
+
const userRuleList = userRules ? parseHeaderRules(userRules) : [];
|
|
177
|
+
const overriddenExactly = new Set(
|
|
178
|
+
userRuleList.flatMap((rule) =>
|
|
179
|
+
rule.headers.map((h) => `${rule.pattern}\0${h.name.toLowerCase()}`)
|
|
180
|
+
)
|
|
181
|
+
);
|
|
182
|
+
const overridden = (pattern: string, header: string) =>
|
|
183
|
+
overriddenExactly.has(`${pattern}\0${header.toLowerCase()}`);
|
|
184
|
+
|
|
185
|
+
const warnedOverlaps = new Set<string>();
|
|
186
|
+
const emit = (pattern: string, header: string, value: string): string => {
|
|
187
|
+
if (overridden(pattern, header)) return '';
|
|
188
|
+
for (const rule of userRuleList) {
|
|
189
|
+
if (rule.pattern === pattern) continue;
|
|
190
|
+
if (!patternsOverlap(rule.pattern, pattern)) continue;
|
|
191
|
+
for (const h of rule.headers) {
|
|
192
|
+
if (h.name.toLowerCase() !== header.toLowerCase()) continue;
|
|
193
|
+
const key = `${rule.pattern}\0${pattern}\0${header.toLowerCase()}`;
|
|
194
|
+
if (warnedOverlaps.has(key)) continue;
|
|
195
|
+
warnedOverlaps.add(key);
|
|
196
|
+
console.warn(
|
|
197
|
+
`[timber] _headers: your rule "${rule.pattern}" sets ${header}, which ` +
|
|
198
|
+
`timber also sets on "${pattern}". Cloudflare and Netlify apply both, ` +
|
|
199
|
+
`so the response gets two ${header} values. Use the exact pattern ` +
|
|
200
|
+
`"${pattern}" to replace timber's rule, or narrow yours so they do ` +
|
|
201
|
+
`not overlap.`
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return `\n${pattern}\n ${header}: ${value}\n`;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
let content = `# Auto-generated by @timber-js/app — static asset cache headers.
|
|
209
|
+
# See design/25-production-deployments.md §"CDN / Edge Cache"
|
|
56
210
|
`;
|
|
211
|
+
for (const path of ['/assets/*', '/_timber/fonts/*', '/_rsc/*']) {
|
|
212
|
+
content += emit(rulePath(path), 'Cache-Control', IMMUTABLE_CACHE);
|
|
213
|
+
}
|
|
57
214
|
|
|
58
215
|
// Content-Type rules are higher priority than cache headers for
|
|
59
216
|
// extensionless files — without them the file is unreadable.
|
|
@@ -80,18 +237,36 @@ export function generateHeadersFile(
|
|
|
80
237
|
`files will get Cache-Control headers.`
|
|
81
238
|
);
|
|
82
239
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
240
|
+
// Section bodies are built before their comment header so a section the
|
|
241
|
+
// user has overridden entirely does not leave a heading over nothing.
|
|
242
|
+
const body = capped.map((p) => emit(rulePath(p), 'Cache-Control', STATIC_CACHE)).join('');
|
|
243
|
+
if (body !== '') {
|
|
244
|
+
content += `\n# Unhashed static assets — short-lived cache with revalidation\n${body}`;
|
|
88
245
|
}
|
|
89
246
|
}
|
|
90
247
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
248
|
+
const ctBody = ctEntries
|
|
249
|
+
.map(([urlPath, ct]) => emit(rulePath(urlPath), 'Content-Type', ct))
|
|
250
|
+
.join('');
|
|
251
|
+
if (ctBody !== '') {
|
|
252
|
+
content += `\n# Content-Type for extensionless API routes\n${ctBody}`;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// User rules last, matching the `_redirects` merge. Their paths are left
|
|
256
|
+
// verbatim: `_redirects` prefixes the rules it generates because it wrote
|
|
257
|
+
// them in route space, but these are the user's own text, already authored
|
|
258
|
+
// against the URLs the site is deployed at.
|
|
259
|
+
if (userRules && userRules.trim() !== '') {
|
|
260
|
+
content += `\n# User-authored rules from public/_headers\n\n${userRules.trim()}\n`;
|
|
261
|
+
|
|
262
|
+
const total = countHeaderRules(content);
|
|
263
|
+
if (total > MAX_PER_FILE_RULES + 3) {
|
|
264
|
+
console.warn(
|
|
265
|
+
`[timber] _headers: ${total} rules after merging public/_headers, over ` +
|
|
266
|
+
`Cloudflare's ${MAX_PER_FILE_RULES + 3}-rule limit. Rules past the ` +
|
|
267
|
+
`limit are dropped by the platform — remove some, or move static ` +
|
|
268
|
+
`assets under a directory the wildcard rules already cover.`
|
|
269
|
+
);
|
|
95
270
|
}
|
|
96
271
|
}
|
|
97
272
|
|
package/src/adapters/types.ts
CHANGED
|
@@ -11,6 +11,18 @@
|
|
|
11
11
|
export interface TimberConfig {
|
|
12
12
|
/** Absolute path to the project root (Vite root). */
|
|
13
13
|
root?: string;
|
|
14
|
+
/**
|
|
15
|
+
* The resolved deployment base, normalized by `normalizeBasePath()` —
|
|
16
|
+
* leading and trailing slash, `'/'` at the domain root.
|
|
17
|
+
*
|
|
18
|
+
* Adapters write platform files (`_headers`, `_redirects`) whose rules the
|
|
19
|
+
* host matches against the *request* path, so those rules live in deployed-
|
|
20
|
+
* URL space rather than route space and need this prefix (TIM-1274).
|
|
21
|
+
*
|
|
22
|
+
* Populated by `plugins/adapter-build.ts` from `ctx.base`, the single copy
|
|
23
|
+
* lifted in TIM-1261. Adapters must not re-derive it from `config.base`.
|
|
24
|
+
*/
|
|
25
|
+
base?: string;
|
|
14
26
|
output: 'server' | 'static';
|
|
15
27
|
clientJavascriptDisabled?: boolean;
|
|
16
28
|
/**
|
|
@@ -23,7 +35,7 @@ export interface TimberConfig {
|
|
|
23
35
|
* Absolute path to the directory containing pre-rendered static HTML,
|
|
24
36
|
* RSC flight files, and API route outputs. Present only when
|
|
25
37
|
* output: 'static' and the static generation pass completed.
|
|
26
|
-
* Layout: path/index.html, _rsc/path.rsc, api-route-path
|
|
38
|
+
* Layout: path/index.html, _rsc/path/index.rsc, api-route-path
|
|
27
39
|
*/
|
|
28
40
|
staticPagesDir?: string;
|
|
29
41
|
/**
|
package/src/cache/fast-hash.ts
CHANGED
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
* Uses BigInt for 64-bit arithmetic — supported in all modern runtimes
|
|
10
10
|
* including Cloudflare Workers. No node:crypto dependency.
|
|
11
11
|
*
|
|
12
|
+
* Server-side only. The `_rsc` payload cache key deliberately does NOT use
|
|
13
|
+
* this — the origin compares that value, which makes it a check rather than
|
|
14
|
+
* a cache key, and FNV is solvable for a chosen digest. See
|
|
15
|
+
* `shared/rsc-cache-key.ts`.
|
|
16
|
+
*
|
|
12
17
|
* See TIM-370.
|
|
13
18
|
*/
|
|
14
19
|
|
|
@@ -18,6 +18,7 @@ import { setServerCallback, encodeReply, createFromFetch } from '../../rsc-runti
|
|
|
18
18
|
import { getRouter, getRouterOrNull } from '#client-internal';
|
|
19
19
|
import { setHardNavigating } from '../navigation-root.js';
|
|
20
20
|
import { getClientDeploymentId, DEPLOYMENT_ID_HEADER, RELOAD_HEADER } from '../rsc-fetch.js';
|
|
21
|
+
import { markClientStale } from '../stale-client.js';
|
|
21
22
|
export function setupServerActions(): void {
|
|
22
23
|
setServerCallback(async (id: string, args: unknown[]) => {
|
|
23
24
|
const body = await encodeReply(args);
|
|
@@ -44,11 +45,15 @@ export function setupServerActions(): void {
|
|
|
44
45
|
headers: actionHeaders,
|
|
45
46
|
body,
|
|
46
47
|
}).then((res) => {
|
|
47
|
-
// Version skew detection (TIM-446):
|
|
48
|
-
//
|
|
48
|
+
// Version skew detection (TIM-446): the server has signalled that this
|
|
49
|
+
// client is stale.
|
|
49
50
|
if (res.headers.get(RELOAD_HEADER) === '1') {
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
// Record it and fail the action. No page load: the action cannot
|
|
52
|
+
// succeed either way (its ID does not exist in the new build), and
|
|
53
|
+
// tearing the document down here would destroy whatever the user had
|
|
54
|
+
// typed into the form. Their next navigation recovers (TIM-1275).
|
|
55
|
+
markClientStale();
|
|
56
|
+
throw new Error('Version skew detected — this client is out of date');
|
|
52
57
|
}
|
|
53
58
|
hasRevalidation = res.headers.get('X-Timber-Revalidation') === '1';
|
|
54
59
|
hasRedirect = res.headers.get('X-Timber-Redirect') != null;
|
|
@@ -6,20 +6,25 @@
|
|
|
6
6
|
* 2. No RSC payload: deferred root creation via installDeferredNavigation
|
|
7
7
|
*
|
|
8
8
|
* Pre-hydration ordering contract (MUST execute in this order):
|
|
9
|
-
* 1. initRouter()
|
|
10
|
-
*
|
|
11
|
-
* 2.
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
9
|
+
* 1. initRouter() — creates the global router so useRouter()
|
|
10
|
+
* works during render
|
|
11
|
+
* 2. setNavigationState() — populates pathname/search so usePathname()
|
|
12
|
+
* returns correct values during hydration
|
|
13
|
+
* 3. hydrateRoot() — synchronously executes component render
|
|
14
|
+
* functions that depend on steps 1-2
|
|
15
|
+
*
|
|
16
|
+
* Params are not part of this sequence. They arrive on the payload root and
|
|
17
|
+
* are published by `PayloadRoot` at the top of the tree, so
|
|
18
|
+
* `useSegmentParams()` is correct during hydration without anything having to
|
|
19
|
+
* run before it (TIM-1294, TIM-1297).
|
|
16
20
|
*
|
|
17
21
|
* See design/19-client-navigation.md §"NavigationContext"
|
|
18
22
|
*/
|
|
19
23
|
|
|
20
24
|
import { createElement } from 'react';
|
|
21
25
|
import { hydrateRoot, createRoot } from 'react-dom/client';
|
|
22
|
-
import { getRouterOrNull
|
|
26
|
+
import { getRouterOrNull } from '#client-internal';
|
|
27
|
+
import { PayloadRoot } from '../params-context.js';
|
|
23
28
|
import {
|
|
24
29
|
NavigationProvider,
|
|
25
30
|
getNavigationState,
|
|
@@ -79,26 +84,21 @@ interface HydrateOptions {
|
|
|
79
84
|
}
|
|
80
85
|
|
|
81
86
|
/**
|
|
82
|
-
* Run the pre-hydration sequence:
|
|
83
|
-
*
|
|
87
|
+
* Run the pre-hydration sequence: set navigation state from the address bar.
|
|
88
|
+
* Must be called AFTER initRouter().
|
|
89
|
+
*
|
|
90
|
+
* Params are no longer seeded here. They used to arrive on two inline scripts
|
|
91
|
+
* that had to execute before `hydrateRoot()`, because hydration synchronously
|
|
92
|
+
* renders components that read them. They now ride the payload root, and the
|
|
93
|
+
* provider that publishes them renders before its own descendants — so the
|
|
94
|
+
* ordering the step existed to guarantee is now a property of the tree rather
|
|
95
|
+
* than of the bootstrap sequence (TIM-1294, TIM-1297).
|
|
84
96
|
*/
|
|
85
97
|
export function runPreHydration(): void {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
params: earlyParams as Record<string, string | string[]>,
|
|
91
|
-
pathname: window.location.pathname,
|
|
92
|
-
search: window.location.search,
|
|
93
|
-
});
|
|
94
|
-
delete (self as unknown as Record<string, unknown>).__timber_params;
|
|
95
|
-
} else {
|
|
96
|
-
setNavigationState({
|
|
97
|
-
params: {},
|
|
98
|
-
pathname: window.location.pathname,
|
|
99
|
-
search: window.location.search,
|
|
100
|
-
});
|
|
101
|
-
}
|
|
98
|
+
setNavigationState({
|
|
99
|
+
pathname: window.location.pathname,
|
|
100
|
+
search: window.location.search,
|
|
101
|
+
});
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
/**
|
|
@@ -123,11 +123,22 @@ export function hydrateApp({ rscResult, config }: HydrateOptions): void {
|
|
|
123
123
|
// startTransition, so React keeps old UI visible while new Suspense
|
|
124
124
|
// boundaries resolve during navigation. See design/05-streaming.md.
|
|
125
125
|
const navState = getNavigationState();
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
126
|
+
// The same chain `renderTree()` in router-init.ts builds on every
|
|
127
|
+
// navigation, in the same order. It has to be: an element type that is
|
|
128
|
+
// present here and absent on the first navigation (or the reverse)
|
|
129
|
+
// changes the type at that position, and React remounts everything below
|
|
130
|
+
// it — losing the layout state partial navigation exists to preserve.
|
|
131
|
+
//
|
|
132
|
+
// `source` is the payload's params promise, not a resolved record: the
|
|
133
|
+
// inlined Flight stream is still arriving and hydration cannot wait for
|
|
134
|
+
// it. PayloadRoot suspends on it exactly where React was going to suspend
|
|
135
|
+
// on `element` (TIM-1297).
|
|
136
|
+
const withParams = createElement(
|
|
137
|
+
PayloadRoot,
|
|
138
|
+
{ source: rscResult.params },
|
|
129
139
|
element as React.ReactNode
|
|
130
140
|
);
|
|
141
|
+
const withNav = createElement(NavigationProvider, { value: navState }, withParams);
|
|
131
142
|
const withUpdates = createElement(
|
|
132
143
|
SegmentUpdateContext.Provider,
|
|
133
144
|
{ value: EMPTY_SEGMENT_UPDATES },
|
|
@@ -40,11 +40,13 @@ import schemaCodecs from 'virtual:timber-schema';
|
|
|
40
40
|
import { setLinkCodecs } from '../../params/codec-registry.js';
|
|
41
41
|
setLinkCodecs(schemaCodecs);
|
|
42
42
|
|
|
43
|
-
import { setClientDeploymentId, setStaticMode } from '../rsc-fetch.js';
|
|
43
|
+
import { setClientDeploymentId, setStaticMode, setBasePath } from '../rsc-fetch.js';
|
|
44
|
+
import { initStaleClient } from '../stale-client.js';
|
|
44
45
|
|
|
45
46
|
import { setupServerActions } from './action-dispatch.js';
|
|
46
47
|
import { createRscPayloadStream } from './rsc-stream.js';
|
|
47
48
|
import { createTimberRouter } from './router-init.js';
|
|
49
|
+
import { readPublishedParams } from '../../shared/payload-root.js';
|
|
48
50
|
import { runPreHydration, hydrateApp } from './hydrate.js';
|
|
49
51
|
import { setupPostHydration } from './post-hydration.js';
|
|
50
52
|
import { setupHmr } from './hmr.js';
|
|
@@ -63,6 +65,16 @@ function bootstrap(runtimeConfig: typeof config): void {
|
|
|
63
65
|
setClientDeploymentId(deploymentId);
|
|
64
66
|
}
|
|
65
67
|
|
|
68
|
+
// Reactive skew recovery for failures that never reach the router —
|
|
69
|
+
// lazily imported client components, preloaded stylesheets. No-ops when
|
|
70
|
+
// there is no deployment ID, i.e. in dev (TIM-1260).
|
|
71
|
+
initStaleClient(deploymentId);
|
|
72
|
+
|
|
73
|
+
// Deployment base path (Vite's resolved `base`). Must be set before any
|
|
74
|
+
// RSC fetch — under a non-root base the payloads live below the prefix
|
|
75
|
+
// like every other emitted file (TIM-1261).
|
|
76
|
+
setBasePath((runtimeConfig as Record<string, unknown>).base as string | undefined);
|
|
77
|
+
|
|
66
78
|
// Static mode: fetch _rsc/*.rsc files instead of route URLs with
|
|
67
79
|
// Accept headers. Static hosts ignore Accept headers (TIM-1243).
|
|
68
80
|
if ((runtimeConfig as Record<string, unknown>).output === 'static') {
|
|
@@ -75,6 +87,7 @@ function bootstrap(runtimeConfig: typeof config): void {
|
|
|
75
87
|
// Step 3: Create router + Navigation API integration
|
|
76
88
|
const { router, navApiController } = createTimberRouter({
|
|
77
89
|
initialElement: rscResult?.element ?? undefined,
|
|
90
|
+
initialParams: rscResult?.params,
|
|
78
91
|
clientSegmentCache:
|
|
79
92
|
((runtimeConfig as Record<string, unknown>).clientSegmentCache as boolean) ?? false,
|
|
80
93
|
});
|
|
@@ -90,6 +103,7 @@ function bootstrap(runtimeConfig: typeof config): void {
|
|
|
90
103
|
router,
|
|
91
104
|
navApiController,
|
|
92
105
|
initialElement: rscResult?.element ?? null,
|
|
106
|
+
initialParams: rscResult?.params ?? readPublishedParams(undefined),
|
|
93
107
|
});
|
|
94
108
|
|
|
95
109
|
// Step 7: HMR (dev-only, no-op in production)
|
|
@@ -15,12 +15,15 @@ import type { RouterInstance } from '#client-internal';
|
|
|
15
15
|
import { getNavigationState } from '../navigation-context.js';
|
|
16
16
|
import type { NavigationApiController } from '../navigation-api.js';
|
|
17
17
|
import { getScrollY } from './scroll.js';
|
|
18
|
+
import type { ParamsSource } from '../../shared/payload-root.js';
|
|
18
19
|
|
|
19
20
|
interface PostHydrationOptions {
|
|
20
21
|
router: RouterInstance;
|
|
21
22
|
navApiController: NavigationApiController | null;
|
|
22
23
|
/** Decoded RSC element from initial SSR (null if no RSC payload) */
|
|
23
24
|
initialElement: unknown;
|
|
25
|
+
/** The params that payload published, from the same decode. */
|
|
26
|
+
initialParams: ParamsSource;
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
/**
|
|
@@ -30,20 +33,14 @@ export function setupPostHydration({
|
|
|
30
33
|
router,
|
|
31
34
|
navApiController,
|
|
32
35
|
initialElement,
|
|
36
|
+
initialParams,
|
|
33
37
|
}: PostHydrationOptions): void {
|
|
34
|
-
// Store the initial page in the history stack so back-button works
|
|
35
|
-
// after the first navigation. We store the decoded RSC element so
|
|
36
|
-
// back navigation can replay it instantly without a server fetch.
|
|
37
|
-
// Params were captured from the server-embedded __timber_params by
|
|
38
|
-
// runPreHydration() — without them on the entry, the cached replay on
|
|
39
|
-
// back-navigation would normalize params to {} and wipe every
|
|
40
|
-
// useSegmentParams() consumer on the initial page (TIM-1037).
|
|
41
38
|
// Populate the segment cache from server-embedded segment metadata.
|
|
42
39
|
// This enables state tree diffing from the very first client navigation.
|
|
43
40
|
// See design/19-client-navigation.md §"X-Timber-State-Tree Header"
|
|
44
41
|
const timberSegments = (self as unknown as Record<string, unknown>).__timber_segments;
|
|
45
42
|
const initialSegmentInfo = Array.isArray(timberSegments)
|
|
46
|
-
? (timberSegments as import('
|
|
43
|
+
? (timberSegments as import('../../shared/segment-info.js').SegmentInfo[])
|
|
47
44
|
: null;
|
|
48
45
|
if (initialSegmentInfo) {
|
|
49
46
|
router.initSegmentCache(initialSegmentInfo);
|
|
@@ -53,9 +50,13 @@ export function setupPostHydration({
|
|
|
53
50
|
// Store the initial page in the history stack so back-button works
|
|
54
51
|
// after the first navigation. Include segmentInfo so the segment
|
|
55
52
|
// cache is restored correctly on popstate cached replay.
|
|
53
|
+
// Replaying this entry on a Back re-renders `initialElement` and republishes
|
|
54
|
+
// `initialParams`. Both came out of one split of one decoded payload root
|
|
55
|
+
// (rsc-stream.ts), so the entry cannot replay with another route's params —
|
|
56
|
+
// which is what a separately-seeded copy used to do (TIM-1037, TIM-1285).
|
|
56
57
|
router.historyStack.push(window.location.pathname + window.location.search, {
|
|
57
58
|
payload: initialElement,
|
|
58
|
-
params:
|
|
59
|
+
params: initialParams,
|
|
59
60
|
segmentInfo: initialSegmentInfo,
|
|
60
61
|
});
|
|
61
62
|
|