@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.
Files changed (289) hide show
  1. package/dist/_chunks/{actions-CWYtq6ii.js → actions-BS-m5SLv.js} +3 -3
  2. package/dist/_chunks/{actions-CWYtq6ii.js.map → actions-BS-m5SLv.js.map} +1 -1
  3. package/dist/_chunks/als-registry-C6kcfprT.js.map +1 -1
  4. package/dist/_chunks/{build-manifest-DWppEdLB.js → build-manifest-DTmSGLRz.js} +51 -2
  5. package/dist/_chunks/build-manifest-DTmSGLRz.js.map +1 -0
  6. package/dist/_chunks/{cache-api-CQeYzA5g.js → cache-api-DqzgTEqk.js} +4 -49
  7. package/dist/_chunks/cache-api-DqzgTEqk.js.map +1 -0
  8. package/dist/_chunks/{chains-h7EO-u3n.js → chains-CZG7E5zg.js} +2 -2
  9. package/dist/_chunks/{chains-h7EO-u3n.js.map → chains-CZG7E5zg.js.map} +1 -1
  10. package/dist/_chunks/{cli-check-BVthpfLS.js → cli-check-dVDi1GQz.js} +3 -3
  11. package/dist/_chunks/{cli-check-BVthpfLS.js.map → cli-check-dVDi1GQz.js.map} +1 -1
  12. package/dist/_chunks/{cli-schema-sync-3Wutm8pH.js → cli-schema-sync-DTy_-Msq.js} +2 -2
  13. package/dist/_chunks/{cli-schema-sync-3Wutm8pH.js.map → cli-schema-sync-DTy_-Msq.js.map} +1 -1
  14. package/dist/_chunks/{cloudflare-BKJC3SC_.js → cloudflare-BFb__LYG.js} +2 -2
  15. package/dist/_chunks/{cloudflare-BKJC3SC_.js.map → cloudflare-BFb__LYG.js.map} +1 -1
  16. package/dist/_chunks/{convention-lint-DO10_pVl.js → convention-lint-Ph6luW4c.js} +4 -2
  17. package/dist/_chunks/convention-lint-Ph6luW4c.js.map +1 -0
  18. package/dist/_chunks/{error-boundary-D-lkwyaD.js → error-boundary-BvRCCmbN.js} +3 -3
  19. package/dist/_chunks/{error-boundary-D-lkwyaD.js.map → error-boundary-BvRCCmbN.js.map} +1 -1
  20. package/dist/_chunks/{href-validation-CMc5JRls.js → href-validation-BIrxavIy.js} +74 -2
  21. package/dist/_chunks/href-validation-BIrxavIy.js.map +1 -0
  22. package/dist/_chunks/{live-graph-Bx4HodF1.js → live-graph-BXDsdzBv.js} +3 -3
  23. package/dist/_chunks/{live-graph-Bx4HodF1.js.map → live-graph-BXDsdzBv.js.map} +1 -1
  24. package/dist/_chunks/{logger-pumCm3Il.js → logger-DDirEsn7.js} +3 -4
  25. package/dist/_chunks/{logger-pumCm3Il.js.map → logger-DDirEsn7.js.map} +1 -1
  26. package/dist/_chunks/navigation-root-B00jjGd5.js +233 -0
  27. package/dist/_chunks/navigation-root-B00jjGd5.js.map +1 -0
  28. package/dist/_chunks/{segment-context-CjOlyB8Y.js → param-value-C8TNYchQ.js} +2 -33
  29. package/dist/_chunks/param-value-C8TNYchQ.js.map +1 -0
  30. package/dist/_chunks/{poison-scan-BAxfTT5L.js → poison-scan-BoDLgbix.js} +2 -2
  31. package/dist/_chunks/{poison-scan-BAxfTT5L.js.map → poison-scan-BoDLgbix.js.map} +1 -1
  32. package/dist/_chunks/{router-ref-BzqbPwYC.js → router-ref-8gr8qsxN.js} +2 -2
  33. package/dist/_chunks/{router-ref-BzqbPwYC.js.map → router-ref-8gr8qsxN.js.map} +1 -1
  34. package/dist/_chunks/{rsc-cache-key-DD0fl_-s.js → rsc-cache-key-ClUiXQnK.js} +2 -2
  35. package/dist/_chunks/{rsc-cache-key-DD0fl_-s.js.map → rsc-cache-key-ClUiXQnK.js.map} +1 -1
  36. package/dist/_chunks/{scanner-BRIOmHE2.js → scanner-tdFPvDYi.js} +174 -7
  37. package/dist/_chunks/scanner-tdFPvDYi.js.map +1 -0
  38. package/dist/_chunks/segment-context-D9_89u34.js +34 -0
  39. package/dist/_chunks/segment-context-D9_89u34.js.map +1 -0
  40. package/dist/_chunks/singleflight-2lUWfcAk.js +54 -0
  41. package/dist/_chunks/singleflight-2lUWfcAk.js.map +1 -0
  42. package/dist/_chunks/{ssr-data-Ya2HJPFp.js → ssr-data-BQGhTPAK.js} +2 -17
  43. package/dist/_chunks/ssr-data-BQGhTPAK.js.map +1 -0
  44. package/dist/_chunks/{walkers-BU6z9xRV.js → walkers-DNX05dC0.js} +2 -2
  45. package/dist/_chunks/{walkers-BU6z9xRV.js.map → walkers-DNX05dC0.js.map} +1 -1
  46. package/dist/adapters/cloudflare-dev.js +1 -1
  47. package/dist/adapters/cloudflare-kv-cache.js +1 -1
  48. package/dist/adapters/cloudflare.js +1 -1
  49. package/dist/adapters/nitro.d.ts +1 -1
  50. package/dist/adapters/nitro.d.ts.map +1 -1
  51. package/dist/adapters/nitro.js.map +1 -1
  52. package/dist/analyze/crawl-entry.js +2 -2
  53. package/dist/analyze/graph-command.js +2 -2
  54. package/dist/cache/index.js +1 -1
  55. package/dist/cache/singleflight.d.ts +2 -0
  56. package/dist/cache/singleflight.d.ts.map +1 -1
  57. package/dist/cli.js +2 -2
  58. package/dist/client/browser-entry/hydrate.d.ts +21 -15
  59. package/dist/client/browser-entry/hydrate.d.ts.map +1 -1
  60. package/dist/client/browser-entry/index.d.ts +4 -3
  61. package/dist/client/browser-entry/index.d.ts.map +1 -1
  62. package/dist/client/browser-entry/post-hydration.d.ts.map +1 -1
  63. package/dist/client/browser-entry/router-init.d.ts +17 -1
  64. package/dist/client/browser-entry/router-init.d.ts.map +1 -1
  65. package/dist/client/error-boundary.js +1 -1
  66. package/dist/client/global-context.d.ts +15 -0
  67. package/dist/client/global-context.d.ts.map +1 -0
  68. package/dist/client/index.js +138 -35
  69. package/dist/client/index.js.map +1 -1
  70. package/dist/client/internal.d.ts +0 -1
  71. package/dist/client/internal.d.ts.map +1 -1
  72. package/dist/client/internal.js +206 -55
  73. package/dist/client/internal.js.map +1 -1
  74. package/dist/client/link.d.ts.map +1 -1
  75. package/dist/client/location-search.d.ts +12 -0
  76. package/dist/client/location-search.d.ts.map +1 -0
  77. package/dist/client/navigation-api.d.ts.map +1 -1
  78. package/dist/client/navigation-commit.d.ts +18 -0
  79. package/dist/client/navigation-commit.d.ts.map +1 -1
  80. package/dist/client/navigation-context.d.ts +13 -11
  81. package/dist/client/navigation-context.d.ts.map +1 -1
  82. package/dist/client/navigation-root.d.ts +47 -108
  83. package/dist/client/navigation-root.d.ts.map +1 -1
  84. package/dist/client/navigation-transition.d.ts +136 -0
  85. package/dist/client/navigation-transition.d.ts.map +1 -0
  86. package/dist/client/nuqs-adapter.d.ts.map +1 -1
  87. package/dist/client/params-context.d.ts +4 -5
  88. package/dist/client/params-context.d.ts.map +1 -1
  89. package/dist/client/react-root.d.ts +44 -0
  90. package/dist/client/react-root.d.ts.map +1 -0
  91. package/dist/client/router-pipeline.d.ts +2 -2
  92. package/dist/client/router-pipeline.d.ts.map +1 -1
  93. package/dist/client/router-types.d.ts +12 -2
  94. package/dist/client/router-types.d.ts.map +1 -1
  95. package/dist/client/router.d.ts.map +1 -1
  96. package/dist/client/segment-cache.d.ts +39 -0
  97. package/dist/client/segment-cache.d.ts.map +1 -1
  98. package/dist/client/segment-context.d.ts.map +1 -1
  99. package/dist/client/segment-outlet.d.ts +25 -14
  100. package/dist/client/segment-outlet.d.ts.map +1 -1
  101. package/dist/client/segment-update-context.d.ts +3 -9
  102. package/dist/client/segment-update-context.d.ts.map +1 -1
  103. package/dist/client/slot-content-cache-context.d.ts +35 -0
  104. package/dist/client/slot-content-cache-context.d.ts.map +1 -0
  105. package/dist/client/ssr-data.d.ts +8 -2
  106. package/dist/client/ssr-data.d.ts.map +1 -1
  107. package/dist/client/state.d.ts +0 -15
  108. package/dist/client/state.d.ts.map +1 -1
  109. package/dist/client/use-pathname.d.ts +13 -11
  110. package/dist/client/use-pathname.d.ts.map +1 -1
  111. package/dist/client/use-search-params.d.ts +13 -13
  112. package/dist/client/use-search-params.d.ts.map +1 -1
  113. package/dist/client/use-segment-params.d.ts +18 -68
  114. package/dist/client/use-segment-params.d.ts.map +1 -1
  115. package/dist/config-types.d.ts +17 -0
  116. package/dist/config-types.d.ts.map +1 -1
  117. package/dist/config-validation.d.ts.map +1 -1
  118. package/dist/cookies/index.js +1 -1
  119. package/dist/dev-tools/holding-server.d.ts +15 -10
  120. package/dist/dev-tools/holding-server.d.ts.map +1 -1
  121. package/dist/index.d.ts.map +1 -1
  122. package/dist/index.js +44 -43
  123. package/dist/index.js.map +1 -1
  124. package/dist/plugins/dev-server.d.ts.map +1 -1
  125. package/dist/plugins/entries.d.ts.map +1 -1
  126. package/dist/plugins/shims.d.ts.map +1 -1
  127. package/dist/plugins/static-build.d.ts +2 -2
  128. package/dist/plugins/static-build.d.ts.map +1 -1
  129. package/dist/routing/codegen-write.d.ts.map +1 -1
  130. package/dist/routing/index.js +2 -2
  131. package/dist/routing/interception-overlap.d.ts +35 -0
  132. package/dist/routing/interception-overlap.d.ts.map +1 -0
  133. package/dist/routing/interception.d.ts.map +1 -1
  134. package/dist/rsc-runtime/ssr.d.ts +3 -1
  135. package/dist/rsc-runtime/ssr.d.ts.map +1 -1
  136. package/dist/server/als-registry.d.ts +6 -0
  137. package/dist/server/als-registry.d.ts.map +1 -1
  138. package/dist/server/csp-nonce.d.ts +45 -0
  139. package/dist/server/csp-nonce.d.ts.map +1 -0
  140. package/dist/server/default-status-page.d.ts.map +1 -1
  141. package/dist/server/deny-renderer.d.ts.map +1 -1
  142. package/dist/server/flight-scripts.d.ts +5 -2
  143. package/dist/server/flight-scripts.d.ts.map +1 -1
  144. package/dist/server/html-injector-core.d.ts +17 -2
  145. package/dist/server/html-injector-core.d.ts.map +1 -1
  146. package/dist/server/html-injectors.d.ts +3 -2
  147. package/dist/server/html-injectors.d.ts.map +1 -1
  148. package/dist/server/index.js +2 -2
  149. package/dist/server/internal.js +86 -37
  150. package/dist/server/internal.js.map +1 -1
  151. package/dist/server/metadata-render.d.ts.map +1 -1
  152. package/dist/server/node-stream-transforms.d.ts +3 -17
  153. package/dist/server/node-stream-transforms.d.ts.map +1 -1
  154. package/dist/server/nuqs-ssr-provider.d.ts +7 -3
  155. package/dist/server/nuqs-ssr-provider.d.ts.map +1 -1
  156. package/dist/server/pipeline-phases.d.ts.map +1 -1
  157. package/dist/server/prebuilt/key-discipline.d.ts +32 -3
  158. package/dist/server/prebuilt/key-discipline.d.ts.map +1 -1
  159. package/dist/server/primitives.d.ts.map +1 -1
  160. package/dist/server/render-utils.d.ts +4 -3
  161. package/dist/server/render-utils.d.ts.map +1 -1
  162. package/dist/server/rsc-entry/action-middleware-runner.d.ts.map +1 -1
  163. package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
  164. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  165. package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
  166. package/dist/server/ssr-bridge-types.d.ts +22 -2
  167. package/dist/server/ssr-bridge-types.d.ts.map +1 -1
  168. package/dist/server/ssr-entry.d.ts.map +1 -1
  169. package/dist/server/ssr-render.d.ts +5 -1
  170. package/dist/server/ssr-render.d.ts.map +1 -1
  171. package/dist/server/ssr-wrappers.d.ts +59 -27
  172. package/dist/server/ssr-wrappers.d.ts.map +1 -1
  173. package/dist/server/types.d.ts +10 -0
  174. package/dist/server/types.d.ts.map +1 -1
  175. package/dist/shims/navigation-rsc.d.ts +21 -0
  176. package/dist/shims/navigation-rsc.d.ts.map +1 -0
  177. package/docs/api/30-api-server.mdx +1 -0
  178. package/docs/api/35-api-typescript.mdx +4 -83
  179. package/docs/learn/03-fetching-data.mdx +1 -1
  180. package/docs/learn/{03b-access-control.mdx → 04-access-control.mdx} +2 -17
  181. package/docs/learn/05-the-flush-point.mdx +175 -0
  182. package/docs/learn/{05-typed-params.mdx → 06-typed-params.mdx} +1 -1
  183. package/docs/learn/07-typed-routes.mdx +25 -49
  184. package/docs/learn/{08-streaming.mdx → 09-streaming.mdx} +1 -7
  185. package/docs/learn/{10-middleware.mdx → 11-middleware.mdx} +1 -0
  186. package/package.json +3 -3
  187. package/src/adapters/nitro.ts +7 -7
  188. package/src/cache/singleflight.ts +5 -0
  189. package/src/client/browser-entry/hydrate.ts +54 -104
  190. package/src/client/browser-entry/index.ts +16 -6
  191. package/src/client/browser-entry/post-hydration.ts +3 -2
  192. package/src/client/browser-entry/router-init.ts +84 -33
  193. package/src/client/global-context.ts +31 -0
  194. package/src/client/internal.ts +1 -2
  195. package/src/client/link.tsx +18 -18
  196. package/src/client/location-search.ts +15 -0
  197. package/src/client/navigation-api.ts +4 -2
  198. package/src/client/navigation-commit.ts +48 -2
  199. package/src/client/navigation-context.ts +25 -37
  200. package/src/client/navigation-root.tsx +55 -411
  201. package/src/client/navigation-transition.ts +278 -0
  202. package/src/client/nuqs-adapter.tsx +4 -5
  203. package/src/client/params-context.ts +13 -18
  204. package/src/client/react-root.ts +72 -0
  205. package/src/client/router-lifecycle.ts +1 -1
  206. package/src/client/router-pipeline.ts +96 -22
  207. package/src/client/router-types.ts +12 -2
  208. package/src/client/router.ts +48 -36
  209. package/src/client/segment-cache.ts +70 -2
  210. package/src/client/segment-context.ts +7 -4
  211. package/src/client/segment-outlet.tsx +41 -86
  212. package/src/client/segment-update-context.ts +7 -26
  213. package/src/client/slot-content-cache-context.ts +43 -0
  214. package/src/client/ssr-data.ts +8 -2
  215. package/src/client/state.ts +0 -26
  216. package/src/client/use-pathname.ts +21 -31
  217. package/src/client/use-search-params.ts +31 -29
  218. package/src/client/use-segment-params.ts +27 -126
  219. package/src/config-types.ts +17 -0
  220. package/src/config-validation.ts +17 -0
  221. package/src/dev-tools/holding-server.ts +23 -12
  222. package/src/index.ts +26 -11
  223. package/src/plugins/dev-server.ts +9 -12
  224. package/src/plugins/entries.ts +3 -0
  225. package/src/plugins/shims.ts +8 -7
  226. package/src/plugins/static-build.ts +9 -5
  227. package/src/react-canary.d.ts +2 -0
  228. package/src/routing/codegen-write.ts +2 -0
  229. package/src/routing/interception-overlap.ts +141 -0
  230. package/src/routing/interception.ts +118 -5
  231. package/src/rsc-runtime/ssr.ts +3 -2
  232. package/src/server/als-registry.ts +6 -0
  233. package/src/server/csp-nonce.ts +70 -0
  234. package/src/server/default-status-page.ts +1 -0
  235. package/src/server/deny-renderer.ts +7 -3
  236. package/src/server/flight-scripts.ts +9 -4
  237. package/src/server/html-injector-core.ts +26 -9
  238. package/src/server/html-injectors.ts +8 -8
  239. package/src/server/metadata-render.ts +26 -4
  240. package/src/server/node-stream-transforms.ts +7 -20
  241. package/src/server/nuqs-ssr-provider.tsx +8 -7
  242. package/src/server/pipeline-phases.ts +5 -0
  243. package/src/server/prebuilt/key-discipline.ts +82 -13
  244. package/src/server/prebuilt-runtime.ts +2 -2
  245. package/src/server/primitives.ts +4 -4
  246. package/src/server/render-utils.ts +8 -4
  247. package/src/server/rsc-entry/action-middleware-runner.ts +7 -0
  248. package/src/server/rsc-entry/error-renderer.ts +5 -2
  249. package/src/server/rsc-entry/index.ts +8 -0
  250. package/src/server/rsc-entry/ssr-renderer.ts +11 -4
  251. package/src/server/ssr-bridge-types.ts +22 -2
  252. package/src/server/ssr-entry.ts +35 -28
  253. package/src/server/ssr-render.ts +13 -4
  254. package/src/server/ssr-wrappers.tsx +81 -61
  255. package/src/server/types.ts +10 -0
  256. package/src/shared/slot-params.ts +3 -4
  257. package/src/shims/navigation-rsc.ts +47 -0
  258. package/dist/_chunks/build-manifest-DWppEdLB.js.map +0 -1
  259. package/dist/_chunks/cache-api-CQeYzA5g.js.map +0 -1
  260. package/dist/_chunks/convention-lint-DO10_pVl.js.map +0 -1
  261. package/dist/_chunks/href-validation-CMc5JRls.js.map +0 -1
  262. package/dist/_chunks/scanner-BRIOmHE2.js.map +0 -1
  263. package/dist/_chunks/segment-context-CjOlyB8Y.js.map +0 -1
  264. package/dist/_chunks/slot-params-BCTmZkQB.js +0 -76
  265. package/dist/_chunks/slot-params-BCTmZkQB.js.map +0 -1
  266. package/dist/_chunks/ssr-data-Ya2HJPFp.js.map +0 -1
  267. package/dist/_chunks/use-segment-params-DzTBpkvj.js +0 -398
  268. package/dist/_chunks/use-segment-params-DzTBpkvj.js.map +0 -1
  269. package/docs/learn/04-loading-states.mdx +0 -67
  270. package/docs/learn/04b-the-flush-point.mdx +0 -115
  271. package/docs/learn/12-client-navigation.mdx +0 -176
  272. package/docs/learn/13-configuration.mdx +0 -166
  273. package/docs/more/01-advanced-routing.mdx +0 -344
  274. package/docs/more/02-advanced-forms.mdx +0 -137
  275. package/docs/more/03-coming-from-nextjs.mdx +0 -186
  276. package/docs/more/04-metadata-and-fonts.mdx +0 -193
  277. package/docs/more/04b-mdx.mdx +0 -229
  278. package/docs/more/05-content-collections.mdx +0 -90
  279. package/docs/more/06-instrumentation.mdx +0 -214
  280. package/docs/more/07-security.mdx +0 -129
  281. package/docs/more/08-developer-experience.mdx +0 -134
  282. package/docs/more/40-why-timber.mdx +0 -50
  283. package/docs/more/41-timber-vs-nextjs.mdx +0 -81
  284. package/docs/more/42-timber-vs-others.mdx +0 -68
  285. package/docs/more/50-ai-agent-instructions.mdx +0 -171
  286. /package/docs/learn/{06-forms-and-actions.mdx → 08-forms-and-actions.mdx} +0 -0
  287. /package/docs/learn/{09-caching.mdx → 10-caching.mdx} +0 -0
  288. /package/docs/learn/{11-error-handling.mdx → 12-error-handling.mdx} +0 -0
  289. /package/docs/learn/{14-deploying.mdx → 13-deploying.mdx} +0 -0
@@ -0,0 +1,15 @@
1
+ import { stripRscCacheKey } from '../shared/rsc-cache-key.ts';
2
+
3
+ /**
4
+ * The app-visible search string from the address bar.
5
+ *
6
+ * Mirrors what `appVisibleSearch()` does on the server: strips key-shaped
7
+ * `_rsc` values so the client and server agree on search state regardless
8
+ * of whether the document URL itself carries a payload cache key (a user
9
+ * pasting/sharing an RSC payload URL). Non-key-shaped `_rsc` values — an
10
+ * application legitimately owning that name — survive, using the same
11
+ * `isRscCacheKeyShape` predicate the server uses.
12
+ */
13
+ export function locationSearch(): string {
14
+ return stripRscCacheKey(window.location.search);
15
+ }
@@ -18,6 +18,7 @@
18
18
  */
19
19
 
20
20
  import { isHardNavigating } from './navigation-root.tsx';
21
+ import { stripRscCacheKey } from '../shared/rsc-cache-key.ts';
21
22
 
22
23
  // ─── Feature Detection ───────────────────────────────────────────
23
24
 
@@ -220,7 +221,7 @@ export function setupNavigationApi(callbacks: NavigationApiCallbacks): Navigatio
220
221
  // Skip reload navigations — let the browser handle full page reload
221
222
  if (event.navigationType === 'reload') return;
222
223
 
223
- const url = destUrl.pathname + destUrl.search;
224
+ const url = destUrl.pathname + stripRscCacheKey(destUrl.search);
224
225
 
225
226
  if (event.navigationType === 'traverse') {
226
227
  // Back/forward button — intercept and delegate to router.
@@ -265,7 +266,8 @@ export function setupNavigationApi(callbacks: NavigationApiCallbacks): Navigatio
265
266
  // destination. Once intercept() is called, currentEntry switches and
266
267
  // getCurrentUrl() returns the destination (TIM-1232).
267
268
  const departingUrl = nav.currentEntry?.url
268
- ? new URL(nav.currentEntry.url).pathname + new URL(nav.currentEntry.url).search
269
+ ? new URL(nav.currentEntry.url).pathname +
270
+ stripRscCacheKey(new URL(nav.currentEntry.url).search)
269
271
  : undefined;
270
272
 
271
273
  event.intercept({
@@ -54,6 +54,51 @@ export interface NavigationCommitInput {
54
54
  * See TIM-1356.
55
55
  */
56
56
  status?: number;
57
+ /**
58
+ * Layouts the server skipped on this response (`X-Timber-Skipped-Segments`),
59
+ * when it came from a fetch. Omitted by replays and revalidation.
60
+ *
61
+ * Together with `skipped` slot entries in `segmentInfo`, this decides
62
+ * whether the payload can be *replayed*. A partial payload only renders
63
+ * merged over the tree on screen, and a `skip=true` outlet resolves against
64
+ * the slot content cache at render time — so a stored copy of either,
65
+ * replayed later, shows whatever the client holds *then*, not what was on
66
+ * screen when the entry committed. Such entries store a null payload and
67
+ * traversals to them fetch instead. Decided here, not at the fetch call
68
+ * sites, for the same reason as `status`: `performNavigationFetch` had the
69
+ * check and `fetchCommitAndRender` did not, so a back/forward fetch that
70
+ * was answered with skips stored the holes (TIM-1432).
71
+ */
72
+ skippedSegments?: string[] | null;
73
+ }
74
+
75
+ /** Whether a response skipped layouts — its payload merges over the tree on screen. */
76
+ export function isPartialNavigation(skippedSegments: string[] | null | undefined): boolean {
77
+ return skippedSegments != null && skippedSegments.length > 0;
78
+ }
79
+
80
+ /**
81
+ * Whether a payload can stand on its own when replayed from the history
82
+ * stack. See `NavigationCommitInput.skippedSegments`.
83
+ */
84
+ function isReplayable(opts: NavigationCommitInput): boolean {
85
+ return !isPartialNavigation(opts.skippedSegments) && !hasSkippedSlot(opts.segmentInfo);
86
+ }
87
+
88
+ function hasSkippedSlot(segmentInfo: SegmentInfo[] | null | undefined): boolean {
89
+ return segmentInfo?.some((s) => s.slot && s.skipped) ?? false;
90
+ }
91
+
92
+ /**
93
+ * The metadata a history entry keeps. `skipped` describes the *response*
94
+ * — "this slot's content was omitted" — and the entry stores no such
95
+ * payload (it stores null). Left on the entry, the flag outlives the
96
+ * response: `applyRevalidation()` reuses the entry's metadata beside a full
97
+ * re-render and would have that judged non-replayable too.
98
+ */
99
+ function storedSegmentInfo(segmentInfo: SegmentInfo[] | null | undefined) {
100
+ if (!hasSkippedSlot(segmentInfo)) return segmentInfo;
101
+ return segmentInfo!.map(({ skipped: _skipped, ...rest }) => rest);
57
102
  }
58
103
 
59
104
  /**
@@ -173,6 +218,7 @@ export function createNavigationCommitter(deps: {
173
218
  // traversal back to this URL would replay the metadata this drops.
174
219
  const cacheable = opts.status === undefined || opts.status < 400;
175
220
  const segmentInfo = cacheable ? opts.segmentInfo : [];
221
+ const payload = isReplayable(opts) ? opts.payload : null;
176
222
 
177
223
  return {
178
224
  navState,
@@ -186,9 +232,9 @@ export function createNavigationCommitter(deps: {
186
232
  setNavigationState(navState);
187
233
 
188
234
  historyStack.push(url, {
189
- payload: opts.payload,
235
+ payload,
190
236
  params: opts.params,
191
- segmentInfo,
237
+ segmentInfo: storedSegmentInfo(segmentInfo),
192
238
  });
193
239
  },
194
240
  };
@@ -18,14 +18,15 @@
18
18
  * All consumers (usePathname, useSearchParams) see the new values in the
19
19
  * same render pass as the new tree.
20
20
  *
21
- * During SSR, no NavigationProvider is mounted. Hooks fall back to
22
- * the ALS-backed getSsrData() for per-request isolation.
21
+ * During SSR, the wrapper chain mounts this same provider with the
22
+ * request's pathname and raw search string (TIM-1424), so the hooks
23
+ * resolve through the identical code path on both sides. Per-request
24
+ * isolation is by construction — the value is part of the request's
25
+ * element tree.
23
26
  *
24
- * IMPORTANT: createContext and useContext are NOT available in the RSC
25
- * environment (React Server Components use a stripped-down React).
26
- * The context is lazily initialized on first access, and all functions
27
- * that depend on these APIs are safe to call from any environment —
28
- * they return null or no-op when the APIs aren't available.
27
+ * This module is `'use client'` and is never evaluated in the RSC
28
+ * environment. The shims plugin resolves next/navigation to
29
+ * navigation-rsc.ts in RSC, which has throwing stubs (TIM-1420).
29
30
  *
30
31
  * SINGLETON GUARANTEE: All shared mutable state uses globalThis via
31
32
  * Symbol.for keys. The RSC client bundler can duplicate this module
@@ -41,7 +42,7 @@
41
42
  * See design/19-client-navigation.md §"NavigationContext"
42
43
  */
43
44
 
44
- import React, { createElement, type ReactNode } from 'react';
45
+ import { createContext, useContext, createElement, type ReactNode } from 'react';
45
46
  import type { SlotParamsRecord } from '../shared/slot-params.ts';
46
47
 
47
48
  // ---------------------------------------------------------------------------
@@ -68,10 +69,6 @@ export interface NavigationState {
68
69
  // ---------------------------------------------------------------------------
69
70
 
70
71
  /**
71
- * The context is created lazily to avoid calling createContext at module
72
- * level. In the RSC environment, React.createContext doesn't exist —
73
- * calling it at import time would crash the server.
74
- *
75
72
  * Context instances are stored on globalThis (NOT in module-level
76
73
  * variables) because the ESM bundler can duplicate this module across
77
74
  * chunks. Module-level variables would create separate instances per
@@ -79,38 +76,34 @@ export interface NavigationState {
79
76
  * context A while the consumer in usePendingNavigation (shared chunk)
80
77
  * reads from context B. globalThis guarantees a single instance.
81
78
  *
79
+ * This module is `'use client'` so it is never evaluated in the RSC
80
+ * environment — `createContext` and `useContext` are always available.
81
+ * The shims plugin resolves next/navigation to navigation-rsc.ts in RSC,
82
+ * which has throwing stubs for the hooks (TIM-1420).
83
+ *
82
84
  * See design/27-chunking-strategy.md §"Singleton Safety"
83
85
  */
84
86
 
85
- // Symbol keys for globalThis storage — prevents collisions with user code
86
87
  const NAV_CTX_KEY = Symbol.for('__timber_nav_ctx');
87
88
 
88
- function getOrCreateContext(): React.Context<NavigationState | null> | undefined {
89
+ function getOrCreateContext(): import('react').Context<NavigationState | null> {
89
90
  const existing = (globalThis as Record<symbol, unknown>)[NAV_CTX_KEY] as
90
- | React.Context<NavigationState | null>
91
+ | import('react').Context<NavigationState | null>
91
92
  | undefined;
92
93
  if (existing !== undefined) return existing;
93
- // createContext may not exist in the RSC environment
94
- if (typeof React.createContext === 'function') {
95
- const ctx = React.createContext<NavigationState | null>(null);
96
- (globalThis as Record<symbol, unknown>)[NAV_CTX_KEY] = ctx;
97
- return ctx;
98
- }
99
- return undefined;
94
+ const ctx = createContext<NavigationState | null>(null);
95
+ (globalThis as Record<symbol, unknown>)[NAV_CTX_KEY] = ctx;
96
+ return ctx;
100
97
  }
101
98
 
102
99
  /**
103
- * Read the navigation context. Returns null during SSR (no provider)
104
- * or in the RSC environment (no context available).
100
+ * Read the navigation context. Returns null only when no provider is above
101
+ * the caller — a component rendered outside the timber app.
102
+ *
105
103
  * Internal — used by usePathname() and useSearchParams().
106
104
  */
107
105
  export function useNavigationContext(): NavigationState | null {
108
- const ctx = getOrCreateContext();
109
- if (!ctx) return null;
110
- // useContext may not exist in the RSC environment — caller wraps in try/catch
111
- if (typeof React.useContext !== 'function') return null;
112
- // eslint-disable-next-line rules-of-hooks -- conditional on environment, not render path
113
- return React.useContext(ctx);
106
+ return useContext(getOrCreateContext());
114
107
  }
115
108
 
116
109
  // ---------------------------------------------------------------------------
@@ -131,13 +124,8 @@ export interface NavigationProviderProps {
131
124
  export function NavigationProvider({
132
125
  value,
133
126
  children,
134
- }: NavigationProviderProps): React.ReactElement {
135
- const ctx = getOrCreateContext();
136
- if (!ctx) {
137
- // RSC environment — no context available. Return children as-is.
138
- return children as React.ReactElement;
139
- }
140
- return createElement(ctx.Provider, { value }, children);
127
+ }: NavigationProviderProps): import('react').ReactElement {
128
+ return createElement(getOrCreateContext().Provider, { value }, children);
141
129
  }
142
130
 
143
131
  // ---------------------------------------------------------------------------