@timber-js/app 0.2.0-alpha.196 → 0.2.0-alpha.198
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-CWYtq6ii.js → actions-BS-m5SLv.js} +3 -3
- package/dist/_chunks/{actions-CWYtq6ii.js.map → actions-BS-m5SLv.js.map} +1 -1
- package/dist/_chunks/als-registry-C6kcfprT.js.map +1 -1
- package/dist/_chunks/{build-manifest-DWppEdLB.js → build-manifest-DTmSGLRz.js} +51 -2
- package/dist/_chunks/build-manifest-DTmSGLRz.js.map +1 -0
- package/dist/_chunks/{cache-api-CQeYzA5g.js → cache-api-DqzgTEqk.js} +4 -49
- package/dist/_chunks/cache-api-DqzgTEqk.js.map +1 -0
- package/dist/_chunks/{chains-h7EO-u3n.js → chains-CZG7E5zg.js} +2 -2
- package/dist/_chunks/{chains-h7EO-u3n.js.map → chains-CZG7E5zg.js.map} +1 -1
- package/dist/_chunks/{cli-check-BVthpfLS.js → cli-check-dVDi1GQz.js} +3 -3
- package/dist/_chunks/{cli-check-BVthpfLS.js.map → cli-check-dVDi1GQz.js.map} +1 -1
- package/dist/_chunks/{cli-schema-sync-3Wutm8pH.js → cli-schema-sync-DTy_-Msq.js} +2 -2
- package/dist/_chunks/{cli-schema-sync-3Wutm8pH.js.map → cli-schema-sync-DTy_-Msq.js.map} +1 -1
- package/dist/_chunks/{cloudflare-BKJC3SC_.js → cloudflare-BFb__LYG.js} +2 -2
- package/dist/_chunks/{cloudflare-BKJC3SC_.js.map → cloudflare-BFb__LYG.js.map} +1 -1
- package/dist/_chunks/{convention-lint-DO10_pVl.js → convention-lint-Ph6luW4c.js} +4 -2
- package/dist/_chunks/convention-lint-Ph6luW4c.js.map +1 -0
- package/dist/_chunks/{error-boundary-D-lkwyaD.js → error-boundary-BvRCCmbN.js} +3 -3
- package/dist/_chunks/{error-boundary-D-lkwyaD.js.map → error-boundary-BvRCCmbN.js.map} +1 -1
- package/dist/_chunks/{href-validation-CMc5JRls.js → href-validation-BIrxavIy.js} +74 -2
- package/dist/_chunks/href-validation-BIrxavIy.js.map +1 -0
- package/dist/_chunks/{live-graph-Bx4HodF1.js → live-graph-BXDsdzBv.js} +3 -3
- package/dist/_chunks/{live-graph-Bx4HodF1.js.map → live-graph-BXDsdzBv.js.map} +1 -1
- package/dist/_chunks/{logger-pumCm3Il.js → logger-DDirEsn7.js} +3 -4
- package/dist/_chunks/{logger-pumCm3Il.js.map → logger-DDirEsn7.js.map} +1 -1
- package/dist/_chunks/navigation-root-B00jjGd5.js +233 -0
- package/dist/_chunks/navigation-root-B00jjGd5.js.map +1 -0
- package/dist/_chunks/{segment-context-CjOlyB8Y.js → param-value-C8TNYchQ.js} +2 -33
- package/dist/_chunks/param-value-C8TNYchQ.js.map +1 -0
- package/dist/_chunks/{poison-scan-BAxfTT5L.js → poison-scan-BoDLgbix.js} +2 -2
- package/dist/_chunks/{poison-scan-BAxfTT5L.js.map → poison-scan-BoDLgbix.js.map} +1 -1
- package/dist/_chunks/{router-ref-BzqbPwYC.js → router-ref-8gr8qsxN.js} +2 -2
- package/dist/_chunks/{router-ref-BzqbPwYC.js.map → router-ref-8gr8qsxN.js.map} +1 -1
- package/dist/_chunks/{rsc-cache-key-DD0fl_-s.js → rsc-cache-key-ClUiXQnK.js} +2 -2
- package/dist/_chunks/{rsc-cache-key-DD0fl_-s.js.map → rsc-cache-key-ClUiXQnK.js.map} +1 -1
- package/dist/_chunks/{scanner-BRIOmHE2.js → scanner-tdFPvDYi.js} +174 -7
- package/dist/_chunks/scanner-tdFPvDYi.js.map +1 -0
- package/dist/_chunks/segment-context-D9_89u34.js +34 -0
- package/dist/_chunks/segment-context-D9_89u34.js.map +1 -0
- package/dist/_chunks/singleflight-2lUWfcAk.js +54 -0
- package/dist/_chunks/singleflight-2lUWfcAk.js.map +1 -0
- package/dist/_chunks/{ssr-data-Ya2HJPFp.js → ssr-data-BQGhTPAK.js} +2 -17
- package/dist/_chunks/ssr-data-BQGhTPAK.js.map +1 -0
- package/dist/_chunks/{walkers-BU6z9xRV.js → walkers-DNX05dC0.js} +2 -2
- package/dist/_chunks/{walkers-BU6z9xRV.js.map → walkers-DNX05dC0.js.map} +1 -1
- package/dist/adapters/cloudflare-dev.js +1 -1
- package/dist/adapters/cloudflare-kv-cache.js +1 -1
- package/dist/adapters/cloudflare.js +1 -1
- package/dist/adapters/nitro.d.ts +1 -1
- package/dist/adapters/nitro.d.ts.map +1 -1
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/analyze/crawl-entry.js +2 -2
- package/dist/analyze/graph-command.js +2 -2
- package/dist/cache/index.js +1 -1
- package/dist/cache/singleflight.d.ts +2 -0
- package/dist/cache/singleflight.d.ts.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/client/browser-entry/hydrate.d.ts +21 -15
- package/dist/client/browser-entry/hydrate.d.ts.map +1 -1
- package/dist/client/browser-entry/index.d.ts +4 -3
- package/dist/client/browser-entry/index.d.ts.map +1 -1
- package/dist/client/browser-entry/post-hydration.d.ts.map +1 -1
- package/dist/client/browser-entry/router-init.d.ts +17 -1
- package/dist/client/browser-entry/router-init.d.ts.map +1 -1
- package/dist/client/error-boundary.js +1 -1
- package/dist/client/global-context.d.ts +15 -0
- package/dist/client/global-context.d.ts.map +1 -0
- package/dist/client/index.js +138 -35
- package/dist/client/index.js.map +1 -1
- package/dist/client/internal.d.ts +0 -1
- package/dist/client/internal.d.ts.map +1 -1
- package/dist/client/internal.js +206 -55
- package/dist/client/internal.js.map +1 -1
- package/dist/client/link.d.ts.map +1 -1
- package/dist/client/location-search.d.ts +12 -0
- package/dist/client/location-search.d.ts.map +1 -0
- package/dist/client/navigation-api.d.ts.map +1 -1
- package/dist/client/navigation-commit.d.ts +18 -0
- package/dist/client/navigation-commit.d.ts.map +1 -1
- package/dist/client/navigation-context.d.ts +13 -11
- package/dist/client/navigation-context.d.ts.map +1 -1
- package/dist/client/navigation-root.d.ts +47 -108
- package/dist/client/navigation-root.d.ts.map +1 -1
- package/dist/client/navigation-transition.d.ts +136 -0
- package/dist/client/navigation-transition.d.ts.map +1 -0
- package/dist/client/nuqs-adapter.d.ts.map +1 -1
- package/dist/client/params-context.d.ts +4 -5
- package/dist/client/params-context.d.ts.map +1 -1
- package/dist/client/react-root.d.ts +44 -0
- package/dist/client/react-root.d.ts.map +1 -0
- package/dist/client/router-pipeline.d.ts +2 -2
- package/dist/client/router-pipeline.d.ts.map +1 -1
- package/dist/client/router-types.d.ts +12 -2
- package/dist/client/router-types.d.ts.map +1 -1
- package/dist/client/router.d.ts.map +1 -1
- package/dist/client/segment-cache.d.ts +39 -0
- package/dist/client/segment-cache.d.ts.map +1 -1
- package/dist/client/segment-context.d.ts.map +1 -1
- package/dist/client/segment-outlet.d.ts +25 -14
- package/dist/client/segment-outlet.d.ts.map +1 -1
- package/dist/client/segment-update-context.d.ts +3 -9
- package/dist/client/segment-update-context.d.ts.map +1 -1
- package/dist/client/slot-content-cache-context.d.ts +35 -0
- package/dist/client/slot-content-cache-context.d.ts.map +1 -0
- package/dist/client/ssr-data.d.ts +8 -2
- package/dist/client/ssr-data.d.ts.map +1 -1
- package/dist/client/state.d.ts +0 -15
- package/dist/client/state.d.ts.map +1 -1
- package/dist/client/use-pathname.d.ts +13 -11
- package/dist/client/use-pathname.d.ts.map +1 -1
- package/dist/client/use-search-params.d.ts +13 -13
- package/dist/client/use-search-params.d.ts.map +1 -1
- package/dist/client/use-segment-params.d.ts +18 -68
- package/dist/client/use-segment-params.d.ts.map +1 -1
- package/dist/config-types.d.ts +17 -0
- package/dist/config-types.d.ts.map +1 -1
- package/dist/config-validation.d.ts.map +1 -1
- package/dist/cookies/index.js +1 -1
- package/dist/dev-tools/holding-server.d.ts +15 -10
- package/dist/dev-tools/holding-server.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +44 -43
- package/dist/index.js.map +1 -1
- package/dist/plugins/dev-server.d.ts.map +1 -1
- package/dist/plugins/entries.d.ts.map +1 -1
- package/dist/plugins/shims.d.ts.map +1 -1
- package/dist/plugins/static-build.d.ts +2 -2
- package/dist/plugins/static-build.d.ts.map +1 -1
- package/dist/routing/codegen-write.d.ts.map +1 -1
- package/dist/routing/index.js +2 -2
- package/dist/routing/interception-overlap.d.ts +35 -0
- package/dist/routing/interception-overlap.d.ts.map +1 -0
- package/dist/routing/interception.d.ts.map +1 -1
- package/dist/rsc-runtime/ssr.d.ts +3 -1
- package/dist/rsc-runtime/ssr.d.ts.map +1 -1
- package/dist/server/als-registry.d.ts +6 -0
- package/dist/server/als-registry.d.ts.map +1 -1
- package/dist/server/csp-nonce.d.ts +45 -0
- package/dist/server/csp-nonce.d.ts.map +1 -0
- package/dist/server/default-status-page.d.ts.map +1 -1
- package/dist/server/deny-renderer.d.ts.map +1 -1
- package/dist/server/flight-scripts.d.ts +5 -2
- package/dist/server/flight-scripts.d.ts.map +1 -1
- package/dist/server/html-injector-core.d.ts +17 -2
- package/dist/server/html-injector-core.d.ts.map +1 -1
- package/dist/server/html-injectors.d.ts +3 -2
- package/dist/server/html-injectors.d.ts.map +1 -1
- package/dist/server/index.js +2 -2
- package/dist/server/internal.js +86 -37
- package/dist/server/internal.js.map +1 -1
- package/dist/server/metadata-render.d.ts.map +1 -1
- package/dist/server/node-stream-transforms.d.ts +3 -17
- package/dist/server/node-stream-transforms.d.ts.map +1 -1
- package/dist/server/nuqs-ssr-provider.d.ts +7 -3
- package/dist/server/nuqs-ssr-provider.d.ts.map +1 -1
- package/dist/server/pipeline-phases.d.ts.map +1 -1
- package/dist/server/prebuilt/key-discipline.d.ts +32 -3
- package/dist/server/prebuilt/key-discipline.d.ts.map +1 -1
- package/dist/server/primitives.d.ts.map +1 -1
- package/dist/server/render-utils.d.ts +4 -3
- package/dist/server/render-utils.d.ts.map +1 -1
- package/dist/server/rsc-entry/action-middleware-runner.d.ts.map +1 -1
- package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
- package/dist/server/ssr-bridge-types.d.ts +22 -2
- 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-render.d.ts +5 -1
- package/dist/server/ssr-render.d.ts.map +1 -1
- package/dist/server/ssr-wrappers.d.ts +59 -27
- package/dist/server/ssr-wrappers.d.ts.map +1 -1
- package/dist/server/types.d.ts +10 -0
- package/dist/server/types.d.ts.map +1 -1
- package/dist/shims/navigation-rsc.d.ts +21 -0
- package/dist/shims/navigation-rsc.d.ts.map +1 -0
- package/docs/api/30-api-server.mdx +1 -0
- package/docs/api/35-api-typescript.mdx +4 -83
- package/docs/learn/03-fetching-data.mdx +1 -1
- package/docs/learn/{03b-access-control.mdx → 04-access-control.mdx} +2 -17
- package/docs/learn/05-the-flush-point.mdx +175 -0
- package/docs/learn/{05-typed-params.mdx → 06-typed-params.mdx} +1 -1
- package/docs/learn/07-typed-routes.mdx +25 -49
- package/docs/learn/{08-streaming.mdx → 09-streaming.mdx} +1 -7
- package/docs/learn/{10-middleware.mdx → 11-middleware.mdx} +1 -0
- package/package.json +3 -3
- package/src/adapters/nitro.ts +7 -7
- package/src/cache/singleflight.ts +5 -0
- package/src/client/browser-entry/hydrate.ts +54 -104
- package/src/client/browser-entry/index.ts +16 -6
- package/src/client/browser-entry/post-hydration.ts +3 -2
- package/src/client/browser-entry/router-init.ts +84 -33
- package/src/client/global-context.ts +31 -0
- package/src/client/internal.ts +1 -2
- package/src/client/link.tsx +18 -18
- package/src/client/location-search.ts +15 -0
- package/src/client/navigation-api.ts +4 -2
- package/src/client/navigation-commit.ts +48 -2
- package/src/client/navigation-context.ts +25 -37
- package/src/client/navigation-root.tsx +55 -411
- package/src/client/navigation-transition.ts +278 -0
- package/src/client/nuqs-adapter.tsx +4 -5
- package/src/client/params-context.ts +13 -18
- package/src/client/react-root.ts +72 -0
- package/src/client/router-lifecycle.ts +1 -1
- package/src/client/router-pipeline.ts +96 -22
- package/src/client/router-types.ts +12 -2
- package/src/client/router.ts +48 -36
- package/src/client/segment-cache.ts +70 -2
- package/src/client/segment-context.ts +7 -4
- package/src/client/segment-outlet.tsx +41 -86
- package/src/client/segment-update-context.ts +7 -26
- package/src/client/slot-content-cache-context.ts +43 -0
- package/src/client/ssr-data.ts +8 -2
- package/src/client/state.ts +0 -26
- package/src/client/use-pathname.ts +21 -31
- package/src/client/use-search-params.ts +31 -29
- package/src/client/use-segment-params.ts +27 -126
- package/src/config-types.ts +17 -0
- package/src/config-validation.ts +17 -0
- package/src/dev-tools/holding-server.ts +23 -12
- package/src/index.ts +26 -11
- package/src/plugins/dev-server.ts +9 -12
- package/src/plugins/entries.ts +3 -0
- package/src/plugins/shims.ts +8 -7
- package/src/plugins/static-build.ts +9 -5
- package/src/react-canary.d.ts +2 -0
- package/src/routing/codegen-write.ts +2 -0
- package/src/routing/interception-overlap.ts +141 -0
- package/src/routing/interception.ts +118 -5
- package/src/rsc-runtime/ssr.ts +3 -2
- package/src/server/als-registry.ts +6 -0
- package/src/server/csp-nonce.ts +70 -0
- package/src/server/default-status-page.ts +1 -0
- package/src/server/deny-renderer.ts +7 -3
- package/src/server/flight-scripts.ts +9 -4
- package/src/server/html-injector-core.ts +26 -9
- package/src/server/html-injectors.ts +8 -8
- package/src/server/metadata-render.ts +26 -4
- package/src/server/node-stream-transforms.ts +7 -20
- package/src/server/nuqs-ssr-provider.tsx +8 -7
- package/src/server/pipeline-phases.ts +5 -0
- package/src/server/prebuilt/key-discipline.ts +82 -13
- package/src/server/prebuilt-runtime.ts +2 -2
- package/src/server/primitives.ts +4 -4
- package/src/server/render-utils.ts +8 -4
- package/src/server/rsc-entry/action-middleware-runner.ts +7 -0
- package/src/server/rsc-entry/error-renderer.ts +5 -2
- package/src/server/rsc-entry/index.ts +8 -0
- package/src/server/rsc-entry/ssr-renderer.ts +11 -4
- package/src/server/ssr-bridge-types.ts +22 -2
- package/src/server/ssr-entry.ts +35 -28
- package/src/server/ssr-render.ts +13 -4
- package/src/server/ssr-wrappers.tsx +81 -61
- package/src/server/types.ts +10 -0
- package/src/shared/slot-params.ts +3 -4
- package/src/shims/navigation-rsc.ts +47 -0
- package/dist/_chunks/build-manifest-DWppEdLB.js.map +0 -1
- package/dist/_chunks/cache-api-CQeYzA5g.js.map +0 -1
- package/dist/_chunks/convention-lint-DO10_pVl.js.map +0 -1
- package/dist/_chunks/href-validation-CMc5JRls.js.map +0 -1
- package/dist/_chunks/scanner-BRIOmHE2.js.map +0 -1
- package/dist/_chunks/segment-context-CjOlyB8Y.js.map +0 -1
- package/dist/_chunks/slot-params-BCTmZkQB.js +0 -76
- package/dist/_chunks/slot-params-BCTmZkQB.js.map +0 -1
- package/dist/_chunks/ssr-data-Ya2HJPFp.js.map +0 -1
- package/dist/_chunks/use-segment-params-DzTBpkvj.js +0 -398
- package/dist/_chunks/use-segment-params-DzTBpkvj.js.map +0 -1
- package/docs/learn/04-loading-states.mdx +0 -67
- package/docs/learn/04b-the-flush-point.mdx +0 -115
- package/docs/learn/12-client-navigation.mdx +0 -176
- package/docs/learn/13-configuration.mdx +0 -166
- package/docs/more/01-advanced-routing.mdx +0 -344
- package/docs/more/02-advanced-forms.mdx +0 -137
- package/docs/more/03-coming-from-nextjs.mdx +0 -186
- package/docs/more/04-metadata-and-fonts.mdx +0 -193
- package/docs/more/04b-mdx.mdx +0 -229
- package/docs/more/05-content-collections.mdx +0 -90
- package/docs/more/06-instrumentation.mdx +0 -214
- package/docs/more/07-security.mdx +0 -129
- package/docs/more/08-developer-experience.mdx +0 -134
- package/docs/more/40-why-timber.mdx +0 -50
- package/docs/more/41-timber-vs-nextjs.mdx +0 -81
- package/docs/more/42-timber-vs-others.mdx +0 -68
- package/docs/more/50-ai-agent-instructions.mdx +0 -171
- /package/docs/learn/{06-forms-and-actions.mdx → 08-forms-and-actions.mdx} +0 -0
- /package/docs/learn/{09-caching.mdx → 10-caching.mdx} +0 -0
- /package/docs/learn/{11-error-handling.mdx → 12-error-handling.mdx} +0 -0
- /package/docs/learn/{14-deploying.mdx → 13-deploying.mdx} +0 -0
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
//#region src/shared/slot-params.ts
|
|
2
|
-
/**
|
|
3
|
-
* The params a slot sees: the main route's as a base, the slot's own on top.
|
|
4
|
-
*
|
|
5
|
-
* Params from segments *above* the slot are not re-derived by the slot's own
|
|
6
|
-
* chain, so they have to come from the main route; params the slot does
|
|
7
|
-
* re-derive may have a different type than the main route gave them, so the
|
|
8
|
-
* slot's win.
|
|
9
|
-
*
|
|
10
|
-
* Null-prototype, like every other param record the framework hands out
|
|
11
|
-
* (`coerceSegmentParams` installs one unconditionally). A spread literal would
|
|
12
|
-
* inherit `Object.prototype`, so a lookup for a param the route does not
|
|
13
|
-
* define would resolve to an inherited member — `params.constructor` returning
|
|
14
|
-
* a function instead of `undefined` — in the one param record that was built
|
|
15
|
-
* by merging. See design/13-security.md #36c.
|
|
16
|
-
*/
|
|
17
|
-
function mergeSlotParams(mainParams, slotParams) {
|
|
18
|
-
return Object.assign(Object.create(null), mainParams, slotParams);
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Resolve the params for a segment path against a published slot map.
|
|
22
|
-
*
|
|
23
|
-
* Returns the merge when `segmentPath` names a slot that published params,
|
|
24
|
-
* and the main route's record otherwise — an ordinary (non-slot) segment
|
|
25
|
-
* path, an unknown path, or a request with no slots at all. Mirrors the
|
|
26
|
-
* server's `getSegmentParams(segmentPath)` branch exactly.
|
|
27
|
-
*
|
|
28
|
-
* `Object.hasOwn` rather than `in` or a truthiness test: the map arrives from
|
|
29
|
-
* JSON, so a segment path of `constructor` or `toString` would otherwise
|
|
30
|
-
* resolve to an inherited member of `Object.prototype`.
|
|
31
|
-
*/
|
|
32
|
-
function resolveSegmentParams(mainParams, slotParams, segmentPath) {
|
|
33
|
-
if (!segmentPath || !slotParams || !Object.hasOwn(slotParams, segmentPath)) return mainParams;
|
|
34
|
-
return cachedMerge(mainParams, slotParams, segmentPath);
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* The merged record for one (main record, slot map, path) triple, reused until
|
|
38
|
-
* one of the three changes.
|
|
39
|
-
*
|
|
40
|
-
* `useSegmentParams(slotPath)` calls this on every render. Returning a fresh
|
|
41
|
-
* object each time makes the hook's result change by reference on renders where
|
|
42
|
-
* nothing navigated, so a component using it as a `useEffect` dependency
|
|
43
|
-
* re-runs the effect — and re-renders in a loop if that effect sets state —
|
|
44
|
-
* while memoized children below it lose their memoization.
|
|
45
|
-
*
|
|
46
|
-
* Keyed on the *identities* of the two records, not their contents: both are
|
|
47
|
-
* replaced rather than mutated on every navigation (`setCurrentParams`,
|
|
48
|
-
* `setCurrentSlotParams`, and the server's per-request store), so identity is
|
|
49
|
-
* the correct equality here and a deep comparison would be slower and no more
|
|
50
|
-
* accurate.
|
|
51
|
-
*
|
|
52
|
-
* A `WeakMap` chain rather than a `Map`, so a superseded navigation's records
|
|
53
|
-
* and their merges become collectable as soon as nothing else holds them.
|
|
54
|
-
*/
|
|
55
|
-
var mergeCache = /* @__PURE__ */ new WeakMap();
|
|
56
|
-
function cachedMerge(mainParams, slotParams, segmentPath) {
|
|
57
|
-
let bySlotMap = mergeCache.get(mainParams);
|
|
58
|
-
if (!bySlotMap) {
|
|
59
|
-
bySlotMap = /* @__PURE__ */ new WeakMap();
|
|
60
|
-
mergeCache.set(mainParams, bySlotMap);
|
|
61
|
-
}
|
|
62
|
-
let byPath = bySlotMap.get(slotParams);
|
|
63
|
-
if (!byPath) {
|
|
64
|
-
byPath = /* @__PURE__ */ new Map();
|
|
65
|
-
bySlotMap.set(slotParams, byPath);
|
|
66
|
-
}
|
|
67
|
-
const cached = byPath.get(segmentPath);
|
|
68
|
-
if (cached) return cached;
|
|
69
|
-
const merged = mergeSlotParams(mainParams, slotParams[segmentPath]);
|
|
70
|
-
byPath.set(segmentPath, merged);
|
|
71
|
-
return merged;
|
|
72
|
-
}
|
|
73
|
-
//#endregion
|
|
74
|
-
export { resolveSegmentParams as t };
|
|
75
|
-
|
|
76
|
-
//# sourceMappingURL=slot-params-BCTmZkQB.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"slot-params-BCTmZkQB.js","names":[],"sources":["../../src/shared/slot-params.ts"],"sourcesContent":["/**\n * Per-slot segment params — the one definition both halves of the API read.\n *\n * A parallel slot matches the URL through its *own* sub-tree, so it can derive\n * a param the main route never had, or derive the same name with a different\n * type (`[...year]` → `string[]` where the main route's `[year]` gave\n * `string`). The server answers `getSegmentParams(slotPath)` from a per-slot\n * map; the client answers `useSegmentParams(slotPath)` from the same map,\n * published on the RSC payload (TIM-1285).\n *\n * The merge lives here rather than in either half because the two must agree\n * about what \"the slot's params\" are — a divergence is invisible until\n * somebody reads a param of the wrong type, which is exactly the class of bug\n * this module closes.\n *\n * Isomorphic: no server or client imports.\n *\n * See design/41-global-params.md §\"Params in a parallel slot\".\n */\n\nimport type { CoercedParams } from './param-value.ts';\n\n/** Slot tree path → that slot's own coerced params. */\nexport type SlotParamsRecord = Record<string, CoercedParams>;\n\n/**\n * The params a slot sees: the main route's as a base, the slot's own on top.\n *\n * Params from segments *above* the slot are not re-derived by the slot's own\n * chain, so they have to come from the main route; params the slot does\n * re-derive may have a different type than the main route gave them, so the\n * slot's win.\n *\n * Null-prototype, like every other param record the framework hands out\n * (`coerceSegmentParams` installs one unconditionally). A spread literal would\n * inherit `Object.prototype`, so a lookup for a param the route does not\n * define would resolve to an inherited member — `params.constructor` returning\n * a function instead of `undefined` — in the one param record that was built\n * by merging. See design/13-security.md #36c.\n */\nexport function mergeSlotParams(\n mainParams: CoercedParams,\n slotParams: CoercedParams\n): CoercedParams {\n return Object.assign(Object.create(null), mainParams, slotParams);\n}\n\n/**\n * Resolve the params for a segment path against a published slot map.\n *\n * Returns the merge when `segmentPath` names a slot that published params,\n * and the main route's record otherwise — an ordinary (non-slot) segment\n * path, an unknown path, or a request with no slots at all. Mirrors the\n * server's `getSegmentParams(segmentPath)` branch exactly.\n *\n * `Object.hasOwn` rather than `in` or a truthiness test: the map arrives from\n * JSON, so a segment path of `constructor` or `toString` would otherwise\n * resolve to an inherited member of `Object.prototype`.\n */\nexport function resolveSegmentParams(\n mainParams: CoercedParams,\n slotParams: SlotParamsRecord | null | undefined,\n segmentPath: string | undefined\n): CoercedParams {\n if (!segmentPath || !slotParams || !Object.hasOwn(slotParams, segmentPath)) return mainParams;\n return cachedMerge(mainParams, slotParams, segmentPath);\n}\n\n/**\n * The merged record for one (main record, slot map, path) triple, reused until\n * one of the three changes.\n *\n * `useSegmentParams(slotPath)` calls this on every render. Returning a fresh\n * object each time makes the hook's result change by reference on renders where\n * nothing navigated, so a component using it as a `useEffect` dependency\n * re-runs the effect — and re-renders in a loop if that effect sets state —\n * while memoized children below it lose their memoization.\n *\n * Keyed on the *identities* of the two records, not their contents: both are\n * replaced rather than mutated on every navigation (`setCurrentParams`,\n * `setCurrentSlotParams`, and the server's per-request store), so identity is\n * the correct equality here and a deep comparison would be slower and no more\n * accurate.\n *\n * A `WeakMap` chain rather than a `Map`, so a superseded navigation's records\n * and their merges become collectable as soon as nothing else holds them.\n */\nconst mergeCache = new WeakMap<object, WeakMap<object, Map<string, CoercedParams>>>();\n\nfunction cachedMerge(\n mainParams: CoercedParams,\n slotParams: SlotParamsRecord,\n segmentPath: string\n): CoercedParams {\n let bySlotMap = mergeCache.get(mainParams);\n if (!bySlotMap) {\n bySlotMap = new WeakMap();\n mergeCache.set(mainParams, bySlotMap);\n }\n let byPath = bySlotMap.get(slotParams);\n if (!byPath) {\n byPath = new Map();\n bySlotMap.set(slotParams, byPath);\n }\n const cached = byPath.get(segmentPath);\n if (cached) return cached;\n const merged = mergeSlotParams(mainParams, slotParams[segmentPath]);\n byPath.set(segmentPath, merged);\n return merged;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAwCA,SAAgB,gBACd,YACA,YACe;CACf,OAAO,OAAO,OAAO,OAAO,OAAO,IAAI,GAAG,YAAY,UAAU;AAClE;;;;;;;;;;;;;AAcA,SAAgB,qBACd,YACA,YACA,aACe;CACf,IAAI,CAAC,eAAe,CAAC,cAAc,CAAC,OAAO,OAAO,YAAY,WAAW,GAAG,OAAO;CACnF,OAAO,YAAY,YAAY,YAAY,WAAW;AACxD;;;;;;;;;;;;;;;;;;;;AAqBA,IAAM,6BAAa,IAAI,QAA6D;AAEpF,SAAS,YACP,YACA,YACA,aACe;CACf,IAAI,YAAY,WAAW,IAAI,UAAU;CACzC,IAAI,CAAC,WAAW;EACd,4BAAY,IAAI,QAAQ;EACxB,WAAW,IAAI,YAAY,SAAS;CACtC;CACA,IAAI,SAAS,UAAU,IAAI,UAAU;CACrC,IAAI,CAAC,QAAQ;EACX,yBAAS,IAAI,IAAI;EACjB,UAAU,IAAI,YAAY,MAAM;CAClC;CACA,MAAM,SAAS,OAAO,IAAI,WAAW;CACrC,IAAI,QAAQ,OAAO;CACnB,MAAM,SAAS,gBAAgB,YAAY,WAAW,YAAY;CAClE,OAAO,IAAI,aAAa,MAAM;CAC9B,OAAO;AACT"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ssr-data-Ya2HJPFp.js","names":[],"sources":["../../src/client/state.ts","../../src/client/ssr-data.ts"],"sourcesContent":["/**\n * Centralized client singleton state registry.\n *\n * ALL mutable module-level state that must have singleton semantics across\n * the client bundle lives here. Individual modules (router-ref.ts, ssr-data.ts,\n * use-segment-params.ts, use-search-params.ts, unload-guard.ts) import from this file\n * and re-export thin wrapper functions.\n *\n * Why: In Vite dev, a module is instantiated separately if reached via different\n * import paths (e.g., relative `./foo.js` vs barrel `@timber-js/app/client`).\n * By centralizing all mutable state in a single module that is always reached\n * through the same dependency chain (barrel → wrapper → state.ts), we guarantee\n * a single instance of every piece of shared state.\n *\n * DO NOT import this file from outside client/. Server code must never depend\n * on client state. The barrel (client/index.ts) is the public entry point.\n *\n * See design/18-build-system.md §\"Module Singleton Strategy\" and\n * §\"Singleton State Registry\".\n */\n\nimport type { CoercedParams } from '../shared/param-value.ts';\nimport type { SlotParamsRecord } from '../shared/slot-params.ts';\nimport type { RouterInstance } from './router-types.ts';\nimport type { SsrData } from './ssr-data.ts';\n\n// ─── Router (from router-ref.ts) ──────────────────────────────────────────\n\n/** The global router singleton — set once during bootstrap. */\nexport let globalRouter: RouterInstance | null = null;\n\nexport function _setGlobalRouter(router: RouterInstance | null): void {\n globalRouter = router;\n}\n\n// ─── SSR Data Provider (from ssr-data.ts) ──────────────────────────────────\n\n/**\n * ALS-backed SSR data provider. When registered, getSsrData() reads from\n * this function (ALS store) instead of module-level currentSsrData.\n */\nexport let ssrDataProvider: (() => SsrData | undefined) | undefined;\n\nexport function _setSsrDataProvider(provider: (() => SsrData | undefined) | undefined): void {\n ssrDataProvider = provider;\n}\n\n/** Fallback SSR data for tests and environments without ALS. */\nexport let currentSsrData: SsrData | undefined;\n\nexport function _setCurrentSsrData(data: SsrData | undefined): void {\n currentSsrData = data;\n}\n\n// ─── Route Params (from use-segment-params.ts) ──────────────────────────────────────\n\n/** Current route params snapshot — replaced (not mutated) on each navigation. */\nexport let currentParams: CoercedParams = {};\n\nexport function _setCurrentParams(params: CoercedParams): void {\n currentParams = params;\n}\n\n/**\n * Per-slot params snapshot, keyed by slot tree path — the module-level\n * fallback behind `useSegmentParams(slotPath)` when no NavigationContext is\n * mounted (tests, calls outside a component). Replaced, never mutated, on\n * each navigation. See TIM-1285.\n */\nexport let currentSlotParams: SlotParamsRecord | null = null;\n\nexport function _setCurrentSlotParams(slotParams: SlotParamsRecord | null): void {\n currentSlotParams = slotParams;\n}\n\n/** Listeners notified when currentParams changes. */\nexport const paramsListeners = new Set<() => void>();\n\n// ─── Search Params Cache (from use-search-params.ts) ────────────────────────\n\n/** Cached search string — avoids reparsing when URL hasn't changed. */\nexport let cachedSearch = '';\nexport let cachedSearchParams = new URLSearchParams();\n\nexport function _setCachedSearch(search: string, params: URLSearchParams): void {\n cachedSearch = search;\n cachedSearchParams = params;\n}\n\n// ─── Unload Guard (from unload-guard.ts) ─────────────────────────────────────\n\n/** Whether the page is currently being unloaded. */\nexport let unloading = false;\n\nexport function _setUnloading(value: boolean): void {\n unloading = value;\n}\n","/**\n * SSR Data — per-request state for client hooks during server-side rendering.\n *\n * RSC and SSR are separate Vite module graphs (see design/18-build-system.md),\n * so the RSC environment's request-context ALS is not visible to SSR modules.\n * This module provides getter/setter functions that ssr-entry.ts uses to\n * populate per-request data for React's render.\n *\n * Request isolation: On the server, ssr-entry.ts registers an ALS-backed\n * provider via registerSsrDataProvider(). getSsrData() reads from the ALS\n * store, ensuring correct per-request data even when Suspense boundaries\n * resolve asynchronously across concurrent requests. The module-level\n * setSsrData/clearSsrData functions are kept as a fallback for tests\n * and environments without ALS.\n *\n * IMPORTANT: This module must NOT import node:async_hooks or any Node.js-only\n * APIs, as it's imported by 'use client' hooks that are bundled for the browser.\n * The ALS instance lives in ssr-entry.ts (server-only); this module only holds\n * a reference to the provider function.\n *\n * All mutable state is delegated to client/state.ts for singleton guarantees.\n * See design/18-build-system.md §\"Singleton State Registry\"\n */\n\nimport {\n ssrDataProvider,\n currentSsrData,\n _setSsrDataProvider,\n _setCurrentSsrData,\n} from './state.ts';\nimport type { CoercedParams } from '../shared/param-value.ts';\nimport type { SlotParamsRecord } from '../shared/slot-params.ts';\n\n// ─── Types ────────────────────────────────────────────────────────\n\nexport interface SsrData {\n /** The request's URL pathname (e.g. '/dashboard/settings') */\n pathname: string;\n /** The request's search params as a plain record */\n searchParams: Record<string, string>;\n /** The request's cookies as name→value pairs */\n cookies: Map<string, string>;\n /** The request's route params (e.g. { id: '123' }) */\n params: CoercedParams;\n /**\n * Per-slot params keyed by slot tree path, absent when the request rendered\n * no slot with params of its own (TIM-1285).\n */\n slotParams?: SlotParamsRecord;\n /**\n * Mutable reference to NavContext for error boundary → pipeline communication.\n *\n * When TimberErrorBoundary catches a DenySignal during SSR, it:\n * 1. Sets `statusCode` to the deny status (e.g., 403) — so the HTTP\n * Response has the correct status code without a re-render.\n * 2. Sets `_denyHandledByBoundary = true` — so the pipeline skips\n * the redundant renderDenyPage() re-render.\n *\n * This runs synchronously during Fizz rendering, BEFORE onShellReady,\n * so the status code is committed before any bytes are sent.\n *\n * See TIM-664, design/04-authorization.md §\"React.cache Scope in Deny/Error Re-renders\"\n */\n _navContext?: { statusCode?: number; _denyHandledByBoundary?: boolean };\n}\n\n// ─── ALS-Backed Provider ─────────────────────────────────────────\n//\n// Server-side code (ssr-entry.ts) registers a provider that reads\n// from AsyncLocalStorage. This avoids importing node:async_hooks\n// in this browser-bundled module.\n//\n// Module singleton guarantee: In Vite's SSR environment, both\n// ssr-entry.ts (via #/client/ssr-data.js) and client component hooks\n// (via @timber-js/app/client) must resolve to the SAME module instance\n// of this file. The timber-shims plugin ensures this by remapping\n// @timber-js/app/client → src/client/index.ts in the SSR environment.\n// Without this remap, @timber-js/app/client resolves to dist/ (via\n// package.json exports), creating a split where registerSsrDataProvider\n// writes to one instance but getSsrData reads from another.\n// See timber-shims plugin resolveId for details.\n\n/**\n * Register an ALS-backed SSR data provider. Called once at module load\n * by ssr-entry.ts to wire up per-request data via AsyncLocalStorage.\n *\n * When registered, getSsrData() reads from the provider (ALS store)\n * instead of module-level state, ensuring correct isolation for\n * concurrent requests with streaming Suspense.\n */\nexport function registerSsrDataProvider(provider: () => SsrData | undefined): void {\n _setSsrDataProvider(provider);\n}\n\n// ─── Module-Level Fallback ────────────────────────────────────────\n//\n// Used by tests and as a fallback when no ALS provider is registered.\n\n/**\n * Set the SSR data for the current request via module-level state.\n *\n * In production, ssr-entry.ts uses ALS (runWithSsrData) instead.\n * This function is retained for tests and as a fallback.\n */\nexport function setSsrData(data: SsrData): void {\n _setCurrentSsrData(data);\n}\n\n/**\n * Clear the SSR data after rendering completes.\n *\n * In production, ALS scope handles cleanup automatically.\n * This function is retained for tests and as a fallback.\n */\nexport function clearSsrData(): void {\n _setCurrentSsrData(undefined);\n}\n\n/**\n * Read the current request's SSR data. Returns undefined when called\n * outside an SSR render (i.e. on the client after hydration).\n *\n * Prefers the ALS-backed provider when registered (server-side),\n * falling back to module-level state (tests, legacy).\n *\n * Used by client hooks' server snapshot functions.\n */\nexport function getSsrData(): SsrData | undefined {\n if (ssrDataProvider) {\n return ssrDataProvider();\n }\n return currentSsrData;\n}\n"],"mappings":";;AA6BA,IAAW,eAAsC;AAEjD,SAAgB,iBAAiB,QAAqC;CACpE,eAAe;AACjB;;AAeA,IAAW;AAEX,SAAgB,mBAAmB,MAAiC;CAClE,iBAAiB;AACnB;;AAKA,IAAW,gBAA+B,CAAC;AAE3C,SAAgB,kBAAkB,QAA6B;CAC7D,gBAAgB;AAClB;;;;;;;AAQA,IAAW,oBAA6C;AAExD,SAAgB,sBAAsB,YAA2C;CAC/E,oBAAoB;AACtB;;AAQA,IAAW,eAAe;AAC1B,IAAW,qBAAqB,IAAI,gBAAgB;AAEpD,SAAgB,iBAAiB,QAAgB,QAA+B;CAC9E,eAAe;CACf,qBAAqB;AACvB;;AAKA,IAAW,YAAY;AAEvB,SAAgB,cAAc,OAAsB;CAClD,YAAY;AACd;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACQA,SAAgB,WAAW,MAAqB;CAC9C,mBAAmB,IAAI;AACzB;;;;;;;AAQA,SAAgB,eAAqB;CACnC,mBAAmB,KAAA,CAAS;AAC9B;;;;;;;;;;AAWA,SAAgB,aAAkC;CAIhD,OAAO;AACT"}
|
|
@@ -1,398 +0,0 @@
|
|
|
1
|
-
import { t as resolveSegmentParams } from "./slot-params-BCTmZkQB.js";
|
|
2
|
-
import { a as _setCurrentParams, d as currentParams, f as currentSlotParams, n as getSsrData, o as _setCurrentSlotParams } from "./ssr-data-Ya2HJPFp.js";
|
|
3
|
-
import { n as getRouterOrNull } from "./router-ref-BzqbPwYC.js";
|
|
4
|
-
import "./segment-context-CjOlyB8Y.js";
|
|
5
|
-
import React, { createElement, useSyncExternalStore } from "react";
|
|
6
|
-
//#region src/client/use-pending-navigation.ts
|
|
7
|
-
function subscribe(onStoreChange) {
|
|
8
|
-
const router = getRouterOrNull();
|
|
9
|
-
if (!router) return () => {};
|
|
10
|
-
return router.onPendingChange(onStoreChange);
|
|
11
|
-
}
|
|
12
|
-
function getSnapshot() {
|
|
13
|
-
const router = getRouterOrNull();
|
|
14
|
-
return router ? router.isPending() : false;
|
|
15
|
-
}
|
|
16
|
-
var getServerSnapshot = getSnapshot;
|
|
17
|
-
/**
|
|
18
|
-
* Returns true while an RSC navigation is in flight.
|
|
19
|
-
*
|
|
20
|
-
* Reads from the router's external pending store via useSyncExternalStore.
|
|
21
|
-
* Only components that call this hook re-render when pending state
|
|
22
|
-
* changes — no full-tree re-render.
|
|
23
|
-
*
|
|
24
|
-
* ```tsx
|
|
25
|
-
* 'use client'
|
|
26
|
-
* import { usePendingNavigation } from '@timber-js/app/client'
|
|
27
|
-
*
|
|
28
|
-
* export function NavBar() {
|
|
29
|
-
* const isPending = usePendingNavigation()
|
|
30
|
-
* return (
|
|
31
|
-
* <nav className={isPending ? 'opacity-50' : ''}>
|
|
32
|
-
* <Link href="/dashboard">Dashboard</Link>
|
|
33
|
-
* </nav>
|
|
34
|
-
* )
|
|
35
|
-
* }
|
|
36
|
-
* ```
|
|
37
|
-
*/
|
|
38
|
-
function usePendingNavigation() {
|
|
39
|
-
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
40
|
-
}
|
|
41
|
-
//#endregion
|
|
42
|
-
//#region src/client/navigation-context.ts
|
|
43
|
-
/**
|
|
44
|
-
* NavigationContext — React context for navigation state.
|
|
45
|
-
*
|
|
46
|
-
* Holds the current pathname and search, updated atomically with the RSC
|
|
47
|
-
* tree on each navigation. This replaces the previous useSyncExternalStore
|
|
48
|
-
* approach for usePathname() and useSearchParams(), which suffered from a
|
|
49
|
-
* timing gap: the new tree could commit before the external store
|
|
50
|
-
* re-renders fired, causing a frame where both old and new active states
|
|
51
|
-
* were visible simultaneously.
|
|
52
|
-
*
|
|
53
|
-
* Segment params are NOT here — see the note on NavigationState below.
|
|
54
|
-
*
|
|
55
|
-
* By wrapping the RSC payload element in NavigationProvider inside
|
|
56
|
-
* renderRoot(), the context value and the element tree are passed to
|
|
57
|
-
* reactRoot.render() in the same call — atomic by construction.
|
|
58
|
-
* All consumers (usePathname, useSearchParams) see the new values in the
|
|
59
|
-
* same render pass as the new tree.
|
|
60
|
-
*
|
|
61
|
-
* During SSR, no NavigationProvider is mounted. Hooks fall back to
|
|
62
|
-
* the ALS-backed getSsrData() for per-request isolation.
|
|
63
|
-
*
|
|
64
|
-
* IMPORTANT: createContext and useContext are NOT available in the RSC
|
|
65
|
-
* environment (React Server Components use a stripped-down React).
|
|
66
|
-
* The context is lazily initialized on first access, and all functions
|
|
67
|
-
* that depend on these APIs are safe to call from any environment —
|
|
68
|
-
* they return null or no-op when the APIs aren't available.
|
|
69
|
-
*
|
|
70
|
-
* SINGLETON GUARANTEE: All shared mutable state uses globalThis via
|
|
71
|
-
* Symbol.for keys. The RSC client bundler can duplicate this module
|
|
72
|
-
* across chunks (browser-entry graph + client-reference graph). With
|
|
73
|
-
* ESM output, each chunk gets its own module scope — module-level
|
|
74
|
-
* variables would create separate singleton instances per chunk.
|
|
75
|
-
* globalThis guarantees a single instance regardless of duplication.
|
|
76
|
-
*
|
|
77
|
-
* This workaround will be removed when Rolldown ships `format: 'app'`
|
|
78
|
-
* (module registry format that deduplicates like webpack/Turbopack).
|
|
79
|
-
* See design/27-chunking-strategy.md.
|
|
80
|
-
*
|
|
81
|
-
* See design/19-client-navigation.md §"NavigationContext"
|
|
82
|
-
*/
|
|
83
|
-
/**
|
|
84
|
-
* The context is created lazily to avoid calling createContext at module
|
|
85
|
-
* level. In the RSC environment, React.createContext doesn't exist —
|
|
86
|
-
* calling it at import time would crash the server.
|
|
87
|
-
*
|
|
88
|
-
* Context instances are stored on globalThis (NOT in module-level
|
|
89
|
-
* variables) because the ESM bundler can duplicate this module across
|
|
90
|
-
* chunks. Module-level variables would create separate instances per
|
|
91
|
-
* chunk — the provider in NavigationRoot (index chunk) would use
|
|
92
|
-
* context A while the consumer in usePendingNavigation (shared chunk)
|
|
93
|
-
* reads from context B. globalThis guarantees a single instance.
|
|
94
|
-
*
|
|
95
|
-
* See design/27-chunking-strategy.md §"Singleton Safety"
|
|
96
|
-
*/
|
|
97
|
-
var NAV_CTX_KEY = Symbol.for("__timber_nav_ctx");
|
|
98
|
-
function getOrCreateContext$1() {
|
|
99
|
-
const existing = globalThis[NAV_CTX_KEY];
|
|
100
|
-
if (existing !== void 0) return existing;
|
|
101
|
-
if (typeof React.createContext === "function") {
|
|
102
|
-
const ctx = React.createContext(null);
|
|
103
|
-
globalThis[NAV_CTX_KEY] = ctx;
|
|
104
|
-
return ctx;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Read the navigation context. Returns null during SSR (no provider)
|
|
109
|
-
* or in the RSC environment (no context available).
|
|
110
|
-
* Internal — used by usePathname() and useSearchParams().
|
|
111
|
-
*/
|
|
112
|
-
function useNavigationContext() {
|
|
113
|
-
const ctx = getOrCreateContext$1();
|
|
114
|
-
if (!ctx) return null;
|
|
115
|
-
if (typeof React.useContext !== "function") return null;
|
|
116
|
-
return React.useContext(ctx);
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Wraps children with NavigationContext.Provider.
|
|
120
|
-
*
|
|
121
|
-
* Used in browser-entry.ts renderRoot to wrap the RSC payload element
|
|
122
|
-
* so that navigation state updates atomically with the tree render.
|
|
123
|
-
*/
|
|
124
|
-
function NavigationProvider({ value, children }) {
|
|
125
|
-
const ctx = getOrCreateContext$1();
|
|
126
|
-
if (!ctx) return children;
|
|
127
|
-
return createElement(ctx.Provider, { value }, children);
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Navigation state communicated between the router and renderRoot.
|
|
131
|
-
*
|
|
132
|
-
* The router calls setNavigationState() before renderRoot(). The
|
|
133
|
-
* renderRoot callback reads via getNavigationState() to create the
|
|
134
|
-
* NavigationProvider with the correct params/pathname.
|
|
135
|
-
*
|
|
136
|
-
* This is NOT used by hooks directly — hooks read from React context.
|
|
137
|
-
*
|
|
138
|
-
* Stored on globalThis (like the context instances above) because the
|
|
139
|
-
* router lives in one chunk while renderRoot lives in another. Module-
|
|
140
|
-
* level variables would be separate per chunk.
|
|
141
|
-
*/
|
|
142
|
-
var NAV_STATE_KEY = Symbol.for("__timber_nav_state");
|
|
143
|
-
function _getNavStateStore() {
|
|
144
|
-
const g = globalThis;
|
|
145
|
-
if (!g[NAV_STATE_KEY]) g[NAV_STATE_KEY] = { current: {
|
|
146
|
-
pathname: "/",
|
|
147
|
-
search: ""
|
|
148
|
-
} };
|
|
149
|
-
return g[NAV_STATE_KEY];
|
|
150
|
-
}
|
|
151
|
-
function setNavigationState(state) {
|
|
152
|
-
_getNavStateStore().current = state;
|
|
153
|
-
}
|
|
154
|
-
function getNavigationState() {
|
|
155
|
-
return _getNavStateStore().current;
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* There was a second React context here — `PendingNavigationContext`, holding
|
|
159
|
-
* the in-flight navigation URL, provided by `NavigationRoot` out of a
|
|
160
|
-
* `useState` — plus a `usePendingNavigationUrl()` reader for it. Both are gone
|
|
161
|
-
* (TIM-1307).
|
|
162
|
-
*
|
|
163
|
-
* Nothing read them. `usePendingNavigation()` (use-pending-navigation.ts) and
|
|
164
|
-
* `TopLoader` both subscribe to the router's external pending store via
|
|
165
|
-
* `useSyncExternalStore`: sync priority, immune to transition entanglement,
|
|
166
|
-
* and cleared by `runNavigation`'s supersession-guarded `finally` (TIM-1034).
|
|
167
|
-
* The context was a parallel representation of the same fact that no consumer
|
|
168
|
-
* ever migrated to, and one that could not even agree with the store — a
|
|
169
|
-
* navigation superseded by a cached popstate replay left its URL set until the
|
|
170
|
-
* next full navigation, because the staleness guard skipped the clear and no
|
|
171
|
-
* other path touched it.
|
|
172
|
-
*
|
|
173
|
-
* One representation, and it is the router's. See
|
|
174
|
-
* design/19-client-navigation.md §"How Pending State Works".
|
|
175
|
-
*/
|
|
176
|
-
//#endregion
|
|
177
|
-
//#region src/client/top-loader.tsx
|
|
178
|
-
/**
|
|
179
|
-
* TopLoader — Built-in progress bar for client navigations.
|
|
180
|
-
*
|
|
181
|
-
* Shows an animated progress bar at the top of the viewport while an RSC
|
|
182
|
-
* navigation is in flight. Injected automatically by the framework into
|
|
183
|
-
* NavigationRoot — users never render this component directly.
|
|
184
|
-
*
|
|
185
|
-
* Configuration is via timber.config.ts `topLoader` key. Enabled by default.
|
|
186
|
-
* Users who want a fully custom progress indicator disable the built-in one
|
|
187
|
-
* (`topLoader: { enabled: false }`) and use `usePendingNavigation()` directly.
|
|
188
|
-
*
|
|
189
|
-
* Animation approach: pure CSS @keyframes. The bar crawls from 0% to ~90%
|
|
190
|
-
* width over ~30s using ease-out timing. When navigation completes, the bar
|
|
191
|
-
* snaps to 100% and fades out over 200ms. No JS animation loops (RAF, setInterval).
|
|
192
|
-
*
|
|
193
|
-
* Phase transitions are derived synchronously during render (React's
|
|
194
|
-
* getDerivedStateFromProps pattern) — no useEffect needed for state tracking.
|
|
195
|
-
* The finishing → hidden cleanup uses onTransitionEnd from the CSS transition.
|
|
196
|
-
*
|
|
197
|
-
* When delay > 0, CSS animation-delay + a visibility keyframe ensure the bar
|
|
198
|
-
* stays invisible during the delay period. If navigation finishes before the
|
|
199
|
-
* delay, the bar was never visible so the finish transition is also invisible.
|
|
200
|
-
*
|
|
201
|
-
* See design/19-client-navigation.md §"usePendingNavigation()"
|
|
202
|
-
* See LOCAL-336 for design decisions.
|
|
203
|
-
*/
|
|
204
|
-
//#endregion
|
|
205
|
-
//#region src/client/navigation-root.tsx
|
|
206
|
-
var NAV_TRANSITION_KEY = Symbol.for("__timber_nav_transition_counter");
|
|
207
|
-
function getTransitionCounter() {
|
|
208
|
-
const g = globalThis;
|
|
209
|
-
const existing = g[NAV_TRANSITION_KEY];
|
|
210
|
-
if (!existing) {
|
|
211
|
-
const created = {
|
|
212
|
-
id: 0,
|
|
213
|
-
waiters: /* @__PURE__ */ new Set()
|
|
214
|
-
};
|
|
215
|
-
g[NAV_TRANSITION_KEY] = created;
|
|
216
|
-
return created;
|
|
217
|
-
}
|
|
218
|
-
existing.waiters ??= /* @__PURE__ */ new Set();
|
|
219
|
-
return existing;
|
|
220
|
-
}
|
|
221
|
-
/** Bump the counter and wake everything waiting on an older transition. */
|
|
222
|
-
function bumpTransitionCounter() {
|
|
223
|
-
const counter = getTransitionCounter();
|
|
224
|
-
counter.id += 1;
|
|
225
|
-
for (const wake of [...counter.waiters]) wake();
|
|
226
|
-
return counter.id;
|
|
227
|
-
}
|
|
228
|
-
/**
|
|
229
|
-
* Invalidate all in-flight navigation transitions. Any navigateTransition()
|
|
230
|
-
* call whose perform() has not yet committed will reject with AbortError
|
|
231
|
-
* instead of committing its element.
|
|
232
|
-
*
|
|
233
|
-
* Called by the router when a render supersedes in-flight navigations
|
|
234
|
-
* WITHOUT going through navigateTransition() — the cached popstate replay
|
|
235
|
-
* renders via transitionRender(), which doesn't bump the counter, so a
|
|
236
|
-
* stale forward navigation's setElement would otherwise pass the
|
|
237
|
-
* `counter.id !== transId` guard and commit the forward page over the
|
|
238
|
-
* replayed back page (TIM-1022).
|
|
239
|
-
*/
|
|
240
|
-
function supersedeNavigationTransitions() {
|
|
241
|
-
bumpTransitionCounter();
|
|
242
|
-
}
|
|
243
|
-
/**
|
|
244
|
-
* Module-level flag indicating a hard (MPA) navigation is in progress.
|
|
245
|
-
*
|
|
246
|
-
* When true:
|
|
247
|
-
* - NavigationRoot throws an unresolved thenable to suspend forever,
|
|
248
|
-
* preventing React from rendering children during page teardown
|
|
249
|
-
* (avoids "Rendered more hooks" crashes).
|
|
250
|
-
* - The Navigation API handler skips interception, letting the browser
|
|
251
|
-
* perform a full page load (prevents infinite loops where
|
|
252
|
-
* window.location.href → navigate event → router.navigate → 500 →
|
|
253
|
-
* window.location.href → ...).
|
|
254
|
-
*
|
|
255
|
-
* Uses globalThis for singleton guarantee across chunks (same pattern
|
|
256
|
-
* as NavigationContext). See design/19-client-navigation.md §"Singleton
|
|
257
|
-
* Guarantee via globalThis".
|
|
258
|
-
*/
|
|
259
|
-
var HARD_NAV_KEY = Symbol.for("__timber_hard_navigating");
|
|
260
|
-
function getHardNavStore() {
|
|
261
|
-
const g = globalThis;
|
|
262
|
-
if (!g[HARD_NAV_KEY]) g[HARD_NAV_KEY] = { value: false };
|
|
263
|
-
return g[HARD_NAV_KEY];
|
|
264
|
-
}
|
|
265
|
-
/**
|
|
266
|
-
* Set the hard-navigating flag. Call this BEFORE setting
|
|
267
|
-
* window.location.href or window.location.reload() to prevent:
|
|
268
|
-
* 1. React from rendering children during page teardown
|
|
269
|
-
* 2. Navigation API from intercepting the hard navigation
|
|
270
|
-
*/
|
|
271
|
-
function setHardNavigating(value) {
|
|
272
|
-
getHardNavStore().value = value;
|
|
273
|
-
}
|
|
274
|
-
//#endregion
|
|
275
|
-
//#region src/client/params-context.ts
|
|
276
|
-
/**
|
|
277
|
-
* Segment params context — the one channel params use to reach the browser.
|
|
278
|
-
*
|
|
279
|
-
* Params ride the RSC payload's root row as a sibling of the tree
|
|
280
|
-
* (`{ tree, params, slotParams }`), rather than in four side channels that
|
|
281
|
-
* raced to seed them: a response header, an inline script, and two build-time
|
|
282
|
-
* manifest fields all previously carried the same record, each with its own
|
|
283
|
-
* `JSON.stringify` (TIM-1294).
|
|
284
|
-
*
|
|
285
|
-
* Riding the payload is what makes them *typed*. `defineSchema` takes any
|
|
286
|
-
* `Codec<T>`, so a coerced param is whatever the codec returned — a `Date`, a
|
|
287
|
-
* `bigint` — and `JSON.stringify` either flattened it to a string or threw
|
|
288
|
-
* mid-response. React Flight carries those values natively, so the client
|
|
289
|
-
* reads the value the server produced instead of a lossy copy of it. See
|
|
290
|
-
* design/41-global-params.md §"Transport".
|
|
291
|
-
*
|
|
292
|
-
* **The client owns the provider.** There is exactly one `ParamsProvider` in
|
|
293
|
-
* the browser's tree, rendered by `PayloadRoot` above the point where a
|
|
294
|
-
* partial navigation splices the new payload into the retained tree. It has to
|
|
295
|
-
* be there and it has to be alone: a provider *inside* the payload lands below
|
|
296
|
-
* the retained region, whose own root is the departing route's provider, so
|
|
297
|
-
* every reader in a skipped layout resolves to the departing record and no
|
|
298
|
-
* amount of wrapping above it helps (TIM-1297).
|
|
299
|
-
*
|
|
300
|
-
* Ordering still holds without a bootstrap contract, for the same reason it
|
|
301
|
-
* did when the provider was in the tree: a provider renders before its own
|
|
302
|
-
* descendants by construction, so `useSegmentParams()` is correct during
|
|
303
|
-
* hydration without anything having to run before `hydrateRoot()`.
|
|
304
|
-
*/
|
|
305
|
-
/**
|
|
306
|
-
* SINGLETON GUARANTEE: globalThis + `Symbol.for`, the same pattern as
|
|
307
|
-
* `NavigationContext` and `SegmentUpdateContext`.
|
|
308
|
-
*
|
|
309
|
-
* The RSC client bundler can duplicate a module across chunks, and with ESM
|
|
310
|
-
* output each chunk gets its own module scope — so a bare `createContext` at
|
|
311
|
-
* module level yields one context per chunk. This module is now reached from
|
|
312
|
-
* *both* graphs: `PayloadRoot` is imported by the browser entry, while
|
|
313
|
-
* `useParamsContext()` arrives through the client-reference graph with the
|
|
314
|
-
* app's own components. A duplicate would put the provider on instance A and
|
|
315
|
-
* every reader on instance B, so `useContext` returns `null` and every
|
|
316
|
-
* `useSegmentParams()` call silently falls back to the module snapshot.
|
|
317
|
-
*
|
|
318
|
-
* This module was the one client context without the guard — harmless while
|
|
319
|
-
* the provider travelled inside the payload, in the same graph as its readers,
|
|
320
|
-
* and load-bearing the moment the client started rendering it (TIM-1297).
|
|
321
|
-
*
|
|
322
|
-
* The React APIs are reached through the namespace rather than named imports,
|
|
323
|
-
* for the same reason `segment-update-context.ts` and `navigation-context.ts`
|
|
324
|
-
* do it: React's `react-server` export provides neither `createContext` nor
|
|
325
|
-
* `useContext`, and a *named* ESM import of a missing export fails at module
|
|
326
|
-
* instantiation — before any feature check could run. This module is reachable
|
|
327
|
-
* from every entry a Server Component imports, so the named form crashed
|
|
328
|
-
* those entries outright (codex, PR #992; originally reproduced against
|
|
329
|
-
* `@timber-js/app/segment-params`, an entry point since deleted by TIM-1342 —
|
|
330
|
-
* the hazard is unchanged for the entries that remain).
|
|
331
|
-
*
|
|
332
|
-
* See design/19-client-navigation.md §"Singleton Guarantee via globalThis"
|
|
333
|
-
*/
|
|
334
|
-
var PARAMS_CTX_KEY = Symbol.for("__timber_params_ctx");
|
|
335
|
-
function getOrCreateContext() {
|
|
336
|
-
const store = globalThis;
|
|
337
|
-
const existing = store[PARAMS_CTX_KEY];
|
|
338
|
-
if (existing !== void 0) return existing;
|
|
339
|
-
if (typeof React.createContext !== "function") return;
|
|
340
|
-
const ctx = React.createContext(null);
|
|
341
|
-
store[PARAMS_CTX_KEY] = ctx;
|
|
342
|
-
return ctx;
|
|
343
|
-
}
|
|
344
|
-
var ParamsContext = getOrCreateContext();
|
|
345
|
-
/**
|
|
346
|
-
* Read the params provided by the tree. Returns null when no provider is
|
|
347
|
-
* above the caller — a component rendered outside a timber route, a
|
|
348
|
-
* `useSegmentParams()` call from outside React entirely, or any component
|
|
349
|
-
* during SSR (where the params reach the hook through the ALS-backed SSR data
|
|
350
|
-
* context instead, and there is no client-owned tree to hold a provider).
|
|
351
|
-
*/
|
|
352
|
-
function useParamsContext() {
|
|
353
|
-
return React.useContext(ParamsContext);
|
|
354
|
-
}
|
|
355
|
-
//#endregion
|
|
356
|
-
//#region src/client/use-segment-params.ts
|
|
357
|
-
/**
|
|
358
|
-
* Set the current route params in the module-level store.
|
|
359
|
-
*
|
|
360
|
-
* Called by the router on each navigation. This updates the fallback
|
|
361
|
-
* snapshot used by tests and by the hook when called outside a React
|
|
362
|
-
* component (no NavigationContext available).
|
|
363
|
-
*
|
|
364
|
-
* On the client, the primary reactivity path is NavigationContext —
|
|
365
|
-
* the router calls setNavigationState() then renderRoot() which wraps
|
|
366
|
-
* the element in NavigationProvider. setCurrentParams is still called
|
|
367
|
-
* for the module-level fallback.
|
|
368
|
-
*
|
|
369
|
-
* During SSR, params are also available via getSsrData().params
|
|
370
|
-
* (ALS-backed).
|
|
371
|
-
*/
|
|
372
|
-
function setCurrentParams(params) {
|
|
373
|
-
_setCurrentParams(params);
|
|
374
|
-
}
|
|
375
|
-
/**
|
|
376
|
-
* Set the per-slot params snapshot in the module-level store.
|
|
377
|
-
*
|
|
378
|
-
* Paired with `setCurrentParams`: the router calls both on every navigation,
|
|
379
|
-
* including with `null` when a response carries no slot params, so a slot's
|
|
380
|
-
* params from the *previous* route cannot be read on the next one. Fill and
|
|
381
|
-
* serve are paired; so are fill and clear. See TIM-1285.
|
|
382
|
-
*/
|
|
383
|
-
function setCurrentSlotParams(slotParams) {
|
|
384
|
-
_setCurrentSlotParams(slotParams);
|
|
385
|
-
}
|
|
386
|
-
function useSegmentParams(segmentPath) {
|
|
387
|
-
try {
|
|
388
|
-
const paramsContext = useParamsContext();
|
|
389
|
-
if (paramsContext !== null) return resolveSegmentParams(paramsContext.params, paramsContext.slotParams, segmentPath);
|
|
390
|
-
} catch {}
|
|
391
|
-
const ssrData = getSsrData();
|
|
392
|
-
if (ssrData) return resolveSegmentParams(ssrData.params, ssrData.slotParams, segmentPath);
|
|
393
|
-
return resolveSegmentParams(currentParams, currentSlotParams, segmentPath);
|
|
394
|
-
}
|
|
395
|
-
//#endregion
|
|
396
|
-
export { supersedeNavigationTransitions as a, setNavigationState as c, setHardNavigating as i, useNavigationContext as l, setCurrentSlotParams as n, NavigationProvider as o, useSegmentParams as r, getNavigationState as s, setCurrentParams as t, usePendingNavigation as u };
|
|
397
|
-
|
|
398
|
-
//# sourceMappingURL=use-segment-params-DzTBpkvj.js.map
|