@timber-js/app 0.2.0-alpha.166 → 0.2.0-alpha.168

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 (188) hide show
  1. package/dist/_chunks/{actions-CDPfMp_I.js → actions-O_LsyCE4.js} +3 -3
  2. package/dist/_chunks/{actions-CDPfMp_I.js.map → actions-O_LsyCE4.js.map} +1 -1
  3. package/dist/_chunks/{cache-api-DygSeKCB.js → cache-api-B-lhk9p4.js} +107 -58
  4. package/dist/_chunks/cache-api-B-lhk9p4.js.map +1 -0
  5. package/dist/_chunks/{cli-schema-sync-EXGYPhI2.js → cli-schema-sync-B73L6pMq.js} +5 -3
  6. package/dist/_chunks/cli-schema-sync-B73L6pMq.js.map +1 -0
  7. package/dist/_chunks/cloudflare-AHoWYTYr.js +1188 -0
  8. package/dist/_chunks/cloudflare-AHoWYTYr.js.map +1 -0
  9. package/dist/_chunks/json-lossy-check-ClNvBM_3.js +63 -0
  10. package/dist/_chunks/json-lossy-check-ClNvBM_3.js.map +1 -0
  11. package/dist/_chunks/{logger-t3uxAmbX.js → logger-AWfuX-KJ.js} +2 -19
  12. package/dist/_chunks/logger-AWfuX-KJ.js.map +1 -0
  13. package/dist/_chunks/{walkers-DBVzXuWc.js → walkers-CoOC8Hga.js} +2 -2
  14. package/dist/_chunks/{walkers-DBVzXuWc.js.map → walkers-CoOC8Hga.js.map} +1 -1
  15. package/dist/adapters/cloudflare-dev.js +1 -1
  16. package/dist/adapters/cloudflare-kv-cache.d.ts.map +1 -1
  17. package/dist/adapters/cloudflare-kv-cache.js +10 -3
  18. package/dist/adapters/cloudflare-kv-cache.js.map +1 -1
  19. package/dist/adapters/cloudflare.d.ts +12 -1
  20. package/dist/adapters/cloudflare.d.ts.map +1 -1
  21. package/dist/adapters/cloudflare.js +2 -461
  22. package/dist/adapters/types.d.ts +2 -0
  23. package/dist/adapters/types.d.ts.map +1 -1
  24. package/dist/cache/cache-api.d.ts +33 -11
  25. package/dist/cache/cache-api.d.ts.map +1 -1
  26. package/dist/cache/index.d.ts +1 -1
  27. package/dist/cache/index.d.ts.map +1 -1
  28. package/dist/cache/index.js +1 -1
  29. package/dist/cache/json-lossy-check.d.ts +11 -0
  30. package/dist/cache/json-lossy-check.d.ts.map +1 -0
  31. package/dist/cache/redis-handler.d.ts +42 -0
  32. package/dist/cache/redis-handler.d.ts.map +1 -1
  33. package/dist/cache/singleflight.d.ts.map +1 -1
  34. package/dist/cache/stores/cloudflare-kv.d.ts +1 -1
  35. package/dist/cache/stores/cloudflare-kv.d.ts.map +1 -1
  36. package/dist/cache/stores/memory.d.ts +1 -1
  37. package/dist/cache/stores/memory.d.ts.map +1 -1
  38. package/dist/cache/stores/redis.d.ts +1 -1
  39. package/dist/cache/stores/redis.d.ts.map +1 -1
  40. package/dist/cache/stores/vercel.d.ts +1 -1
  41. package/dist/cache/stores/vercel.d.ts.map +1 -1
  42. package/dist/cache/tag-aware-handler.d.ts.map +1 -1
  43. package/dist/cache/timber-cache.d.ts.map +1 -1
  44. package/dist/cdn/cloudflare-purge.d.ts.map +1 -1
  45. package/dist/cdn/fastly-purge.d.ts.map +1 -1
  46. package/dist/cdn/workers-cache-purge.d.ts.map +1 -1
  47. package/dist/cli.d.ts +1 -1
  48. package/dist/cli.d.ts.map +1 -1
  49. package/dist/cli.js +3 -2
  50. package/dist/cli.js.map +1 -1
  51. package/dist/client/browser-entry/post-hydration.d.ts.map +1 -1
  52. package/dist/client/browser-entry/router-init.d.ts +1 -0
  53. package/dist/client/browser-entry/router-init.d.ts.map +1 -1
  54. package/dist/client/child-segment-context.d.ts +2 -2
  55. package/dist/client/child-segment-context.d.ts.map +1 -1
  56. package/dist/client/error-boundary.d.ts.map +1 -1
  57. package/dist/client/history.d.ts +10 -0
  58. package/dist/client/history.d.ts.map +1 -1
  59. package/dist/client/internal.js +141 -116
  60. package/dist/client/internal.js.map +1 -1
  61. package/dist/client/router.d.ts +6 -0
  62. package/dist/client/router.d.ts.map +1 -1
  63. package/dist/client/rsc-fetch.d.ts.map +1 -1
  64. package/dist/client/segment-cache.d.ts.map +1 -1
  65. package/dist/client/segment-update-context.d.ts +3 -3
  66. package/dist/client/segment-update-context.d.ts.map +1 -1
  67. package/dist/client/slot-outlet.d.ts +1 -1
  68. package/dist/codec.d.ts.map +1 -1
  69. package/dist/codec.js +2 -1
  70. package/dist/codec.js.map +1 -1
  71. package/dist/config-types.d.ts +16 -37
  72. package/dist/config-types.d.ts.map +1 -1
  73. package/dist/config-validation.d.ts.map +1 -1
  74. package/dist/dev-tools/instrumentation.d.ts.map +1 -1
  75. package/dist/fonts/pipeline.d.ts +19 -0
  76. package/dist/fonts/pipeline.d.ts.map +1 -1
  77. package/dist/fonts/transform.d.ts.map +1 -1
  78. package/dist/fonts/virtual-modules.d.ts.map +1 -1
  79. package/dist/index.d.ts.map +1 -1
  80. package/dist/index.js +378 -109
  81. package/dist/index.js.map +1 -1
  82. package/dist/plugin-context.d.ts.map +1 -1
  83. package/dist/plugins/adapter-build.d.ts +1 -0
  84. package/dist/plugins/adapter-build.d.ts.map +1 -1
  85. package/dist/plugins/cache.d.ts +8 -8
  86. package/dist/plugins/cache.d.ts.map +1 -1
  87. package/dist/plugins/entries.d.ts +27 -3
  88. package/dist/plugins/entries.d.ts.map +1 -1
  89. package/dist/plugins/fonts.d.ts.map +1 -1
  90. package/dist/plugins/server-bundle.d.ts.map +1 -1
  91. package/dist/routing/index.js +2 -2
  92. package/dist/server/action-client.d.ts +1 -1
  93. package/dist/server/action-client.d.ts.map +1 -1
  94. package/dist/server/als-registry.d.ts +7 -0
  95. package/dist/server/als-registry.d.ts.map +1 -1
  96. package/dist/server/body-limits.d.ts.map +1 -1
  97. package/dist/server/deny-boundary.d.ts +3 -1
  98. package/dist/server/deny-boundary.d.ts.map +1 -1
  99. package/dist/server/form-data.d.ts.map +1 -1
  100. package/dist/server/html-injector-core.d.ts.map +1 -1
  101. package/dist/server/index.js +20 -3
  102. package/dist/server/index.js.map +1 -1
  103. package/dist/server/internal.js +28 -41
  104. package/dist/server/internal.js.map +1 -1
  105. package/dist/server/middleware-runner.d.ts +0 -17
  106. package/dist/server/middleware-runner.d.ts.map +1 -1
  107. package/dist/server/param-coercion.d.ts.map +1 -1
  108. package/dist/server/pipeline-phases.d.ts +6 -3
  109. package/dist/server/pipeline-phases.d.ts.map +1 -1
  110. package/dist/server/pipeline.d.ts +18 -0
  111. package/dist/server/pipeline.d.ts.map +1 -1
  112. package/dist/server/prebuilt/capture-state.d.ts.map +1 -1
  113. package/dist/server/prebuilt/synthetic-store.d.ts.map +1 -1
  114. package/dist/server/primitives.d.ts.map +1 -1
  115. package/dist/server/render-timeout.d.ts.map +1 -1
  116. package/dist/server/route-element-builder.d.ts +9 -0
  117. package/dist/server/route-element-builder.d.ts.map +1 -1
  118. package/dist/server/rsc-entry/action-middleware-runner.d.ts +14 -14
  119. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  120. package/dist/server/rsc-entry/render-route.d.ts +1 -0
  121. package/dist/server/rsc-entry/render-route.d.ts.map +1 -1
  122. package/dist/server/rsc-entry/revalidate-renderer.d.ts.map +1 -1
  123. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  124. package/dist/server/rsc-entry/rsc-stream.d.ts +8 -0
  125. package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
  126. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts +44 -74
  127. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts.map +1 -1
  128. package/dist/server/safe-load.d.ts.map +1 -1
  129. package/dist/server/ssr-entry.d.ts.map +1 -1
  130. package/dist/shared/redirect-type.d.ts +2 -2
  131. package/dist/shared/redirect-type.d.ts.map +1 -1
  132. package/dist/shims/font-google.d.ts.map +1 -1
  133. package/dist/shims/image.d.ts +120 -120
  134. package/dist/shims/image.d.ts.map +1 -1
  135. package/docs/api/32-api-cache.mdx +116 -4
  136. package/docs/api/34-api-config.mdx +0 -26
  137. package/docs/learn/09-caching.mdx +126 -10
  138. package/docs/learn/12-client-navigation.mdx +9 -1
  139. package/docs/learn/13-configuration.mdx +6 -8
  140. package/docs/learn/14-deploying.mdx +18 -18
  141. package/docs/more/03-coming-from-nextjs.mdx +2 -2
  142. package/docs/more/04-metadata-and-fonts.mdx +1 -1
  143. package/docs/more/50-ai-agent-instructions.mdx +5 -5
  144. package/package.json +6 -5
  145. package/src/adapters/cloudflare-kv-cache.ts +27 -6
  146. package/src/adapters/cloudflare.ts +63 -25
  147. package/src/adapters/types.ts +2 -0
  148. package/src/cache/cache-api.ts +84 -84
  149. package/src/cache/index.ts +1 -1
  150. package/src/cache/json-lossy-check.ts +75 -0
  151. package/src/cache/redis-handler.ts +65 -14
  152. package/src/cache/timber-cache.ts +10 -2
  153. package/src/client/browser-entry/index.ts +2 -0
  154. package/src/client/browser-entry/post-hydration.ts +16 -9
  155. package/src/client/browser-entry/router-init.ts +2 -0
  156. package/src/client/history.ts +11 -0
  157. package/src/client/router.ts +224 -208
  158. package/src/codec.ts +3 -1
  159. package/src/config-types.ts +16 -37
  160. package/src/config-validation.ts +7 -5
  161. package/src/fonts/pipeline.ts +32 -0
  162. package/src/fonts/transform.ts +30 -24
  163. package/src/plugins/adapter-build.ts +132 -13
  164. package/src/plugins/cache.ts +45 -30
  165. package/src/plugins/entries.ts +40 -43
  166. package/src/plugins/fonts.ts +30 -0
  167. package/src/plugins/server-bundle.ts +7 -15
  168. package/src/routing/scanner.ts +7 -0
  169. package/src/server/action-client.ts +1 -1
  170. package/src/server/als-registry.ts +7 -0
  171. package/src/server/deny-boundary.ts +6 -3
  172. package/src/server/middleware-runner.ts +0 -44
  173. package/src/server/param-coercion.ts +1 -0
  174. package/src/server/pipeline-phases.ts +11 -13
  175. package/src/server/pipeline.ts +51 -3
  176. package/src/server/route-element-builder.ts +59 -9
  177. package/src/server/rsc-entry/action-middleware-runner.ts +14 -14
  178. package/src/server/rsc-entry/index.ts +30 -40
  179. package/src/server/rsc-entry/render-route.ts +6 -2
  180. package/src/server/rsc-entry/rsc-payload.ts +21 -4
  181. package/src/server/rsc-entry/rsc-stream.ts +8 -0
  182. package/src/server/rsc-entry/wrap-action-dispatch.ts +109 -372
  183. package/dist/_chunks/cache-api-DygSeKCB.js.map +0 -1
  184. package/dist/_chunks/cli-schema-sync-EXGYPhI2.js.map +0 -1
  185. package/dist/_chunks/logger-t3uxAmbX.js.map +0 -1
  186. package/dist/_chunks/tree-match-D2l830j2.js +0 -102
  187. package/dist/_chunks/tree-match-D2l830j2.js.map +0 -1
  188. package/dist/adapters/cloudflare.js.map +0 -1
@@ -450,6 +450,7 @@ function createRouter(deps) {
450
450
  }
451
451
  /** Update the segment cache from server-provided segment metadata. */
452
452
  function updateSegmentCache(segmentInfo) {
453
+ if (!deps.clientSegmentCache) return;
453
454
  if (!segmentInfo || segmentInfo.length === 0) return;
454
455
  const tree = buildSegmentTree(segmentInfo);
455
456
  if (tree) segmentCache.set("/", tree);
@@ -458,6 +459,59 @@ function createRouter(deps) {
458
459
  function renderPayload(payload, navState) {
459
460
  if (deps.renderRoot) deps.renderRoot(payload, navState);
460
461
  }
462
+ /**
463
+ * Atomically update all navigation-owned state for a new page. Every
464
+ * code path that changes the "current page" must go through this
465
+ * function — making "forgot a field" impossible by construction.
466
+ *
467
+ * The three operations:
468
+ * 1. Segment cache — update from server-provided segment metadata
469
+ * 2. Navigation state — params + pathname for useSegmentParams/usePathname
470
+ * 3. History stack — store the payload for instant back/forward replay
471
+ */
472
+ function commitNavigation(url, opts) {
473
+ if (opts.segmentInfo && opts.segmentInfo.length > 0) updateSegmentCache(opts.segmentInfo);
474
+ else if (opts.clearSegmentCacheOnEmpty) segmentCache.clear();
475
+ const navState = updateNavigationState(opts.params, url);
476
+ historyStack.push(url, {
477
+ payload: opts.payload,
478
+ params: navState.params,
479
+ segmentInfo: opts.segmentInfo
480
+ });
481
+ return navState;
482
+ }
483
+ /**
484
+ * Wrap a navigation in the standard abort/pending/cleanup lifecycle.
485
+ * Consolidates the createNavAbort + setPending + staleness-guarded
486
+ * finally that was duplicated across navigate, refresh, and both
487
+ * handlePopState paths. AbortErrors are swallowed (not application
488
+ * errors); all other errors propagate to the caller.
489
+ */
490
+ async function runNavigation(url, fn, externalSignal) {
491
+ const navAbort = createNavAbort(externalSignal);
492
+ setPending(true, url);
493
+ try {
494
+ await fn(navAbort);
495
+ } catch (error) {
496
+ if (isAbortError(error)) return;
497
+ throw error;
498
+ } finally {
499
+ if (currentNavAbort === navAbort) {
500
+ currentNavAbort = null;
501
+ setPending(false);
502
+ deps.completeRouterNavigation?.();
503
+ }
504
+ }
505
+ }
506
+ /**
507
+ * Resolve thenable payloads in the test/fallback path (no navigateTransition).
508
+ * In production, React handles thenables from createFromFetch directly via
509
+ * Suspense. In tests, renderRoot is a plain mock that expects resolved values.
510
+ */
511
+ async function resolveForFallback(payload) {
512
+ if (!deps.navigateTransition && payload != null && typeof payload === "object" && "then" in payload) return await payload;
513
+ return payload;
514
+ }
461
515
  function isPartialNavigation(skippedSegments) {
462
516
  return skippedSegments != null && skippedSegments.length > 0;
463
517
  }
@@ -499,9 +553,11 @@ function createRouter(deps) {
499
553
  }
500
554
  /**
501
555
  * Render a payload via navigateTransition (production) or renderRoot (tests).
502
- * The perform callback should fetch data, update state, and return the
503
- * FetchResult plus the NavigationState (so it can be passed explicitly
504
- * to wrapPayload/renderRoot without temporal coupling).
556
+ * The perform callback should fetch data, call commitNavigation, and return
557
+ * the FetchResult plus the NavigationState.
558
+ *
559
+ * State management (segmentCache, navState, historyStack) is handled by
560
+ * commitNavigation inside perform — this function only handles rendering.
505
561
  */
506
562
  async function renderViaTransition(url, perform) {
507
563
  if (deps.navigateTransition) {
@@ -509,19 +565,11 @@ function createRouter(deps) {
509
565
  const result = await perform();
510
566
  if (isPartialNavigation(result.skippedSegments)) {
511
567
  const segmentUpdates = buildSegmentUpdates(result);
512
- historyStack.push(url, {
513
- payload: null,
514
- params: result.params
515
- });
516
568
  return {
517
569
  element: wrapPayload(deps._getCurrentPayload?.() ?? result.payload, result.navState, segmentUpdates),
518
570
  decodePromise: result.decodePromise
519
571
  };
520
572
  }
521
- historyStack.push(url, {
522
- payload: result.payload,
523
- params: result.params
524
- });
525
573
  return {
526
574
  element: wrapPayload(result.payload, result.navState),
527
575
  decodePromise: result.decodePromise
@@ -530,18 +578,7 @@ function createRouter(deps) {
530
578
  return;
531
579
  }
532
580
  const result = await perform();
533
- if (isPartialNavigation(result.skippedSegments)) historyStack.push(url, {
534
- payload: null,
535
- params: result.params
536
- });
537
- else {
538
- const payload = result.payload != null && typeof result.payload === "object" && "then" in result.payload ? await result.payload : result.payload;
539
- historyStack.push(url, {
540
- payload,
541
- params: result.params
542
- });
543
- renderPayload(payload, result.navState);
544
- }
581
+ if (!isPartialNavigation(result.skippedSegments)) renderPayload(result.payload, result.navState);
545
582
  }
546
583
  /** Run a callback after the next paint (after React commit). */
547
584
  function afterPaint(callback) {
@@ -584,7 +621,7 @@ function createRouter(deps) {
584
621
  skippedSegments: prefetched.skippedSegments ?? null
585
622
  } : void 0;
586
623
  if (result === void 0) {
587
- const stateTree = segmentCache.serializeStateTree();
624
+ const stateTree = deps.clientSegmentCache ? segmentCache.serializeStateTree() : void 0;
588
625
  const rawCurrentUrl = deps.getCurrentUrl();
589
626
  result = await fetchRscPayload(url, deps, stateTree, rawCurrentUrl.startsWith("http") ? new URL(rawCurrentUrl).pathname : new URL(rawCurrentUrl, "http://localhost").pathname, options.signal);
590
627
  }
@@ -601,10 +638,15 @@ function createRouter(deps) {
601
638
  }, "", commitUrl);
602
639
  deps.setRouterNavigating?.(false);
603
640
  }
604
- updateSegmentCache(result.segmentInfo);
605
- const navState = updateNavigationState(result.params, url);
641
+ const payload = await resolveForFallback(result.payload);
642
+ const navState = commitNavigation(url, {
643
+ payload: isPartialNavigation(result.skippedSegments) ? null : payload,
644
+ params: result.params,
645
+ segmentInfo: result.segmentInfo
646
+ });
606
647
  return {
607
648
  ...result,
649
+ payload,
608
650
  navState
609
651
  };
610
652
  }
@@ -616,7 +658,6 @@ function createRouter(deps) {
616
658
  const hashIndex = url.indexOf("#");
617
659
  const hash = hashIndex === -1 ? "" : url.slice(hashIndex);
618
660
  const fetchUrl = hashIndex === -1 ? url : url.slice(0, hashIndex);
619
- const navAbort = createNavAbort(externalSignal);
620
661
  const currentScrollY = deps.getScrollY();
621
662
  if (deps.saveNavigationEntryScroll) deps.saveNavigationEntryScroll(currentScrollY);
622
663
  else deps.replaceState({
@@ -624,107 +665,91 @@ function createRouter(deps) {
624
665
  scrollY: currentScrollY
625
666
  }, "", deps.getCurrentUrl());
626
667
  let effectiveSkipHistory = skipHistory;
627
- if (!skipHistory && deps.navigationNavigate) {
628
- deps.setRouterNavigating?.(true);
629
- deps.navigationNavigate(url, replace);
630
- deps.setRouterNavigating?.(false);
631
- effectiveSkipHistory = true;
632
- }
633
- setPending(true, url);
634
- try {
635
- await renderViaTransition(fetchUrl, () => performNavigationFetch(fetchUrl, {
636
- replace,
637
- commitUrl: url,
638
- signal: navAbort.signal,
639
- skipHistory: effectiveSkipHistory
640
- }));
641
- window.dispatchEvent(new Event("timber:navigation-end"));
642
- if (scroll && hash) scrollToHashAfterPaint(hash);
643
- else restoreScrollAfterPaint(scroll ? 0 : currentScrollY);
644
- } catch (error) {
645
- if (error instanceof VersionSkewError) {
646
- setHardNavigating(true);
647
- window.location.reload();
648
- return new Promise(() => {});
649
- }
650
- if (error instanceof RedirectError) {
651
- if (currentNavAbort !== navAbort) return;
652
- await navigate(error.redirectUrl, { replace: true });
653
- return;
668
+ await runNavigation(url, async (navAbort) => {
669
+ if (!effectiveSkipHistory && deps.navigationNavigate) {
670
+ deps.setRouterNavigating?.(true);
671
+ deps.navigationNavigate(url, replace);
672
+ deps.setRouterNavigating?.(false);
673
+ effectiveSkipHistory = true;
654
674
  }
655
- if (error instanceof ServerErrorResponse) {
656
- setHardNavigating(true);
657
- window.location.href = error.url;
658
- return new Promise(() => {});
659
- }
660
- if (isAbortError(error)) return;
661
- throw error;
662
- } finally {
663
- if (currentNavAbort === navAbort) {
664
- currentNavAbort = null;
665
- setPending(false);
666
- deps.completeRouterNavigation?.();
675
+ try {
676
+ await renderViaTransition(fetchUrl, () => performNavigationFetch(fetchUrl, {
677
+ replace,
678
+ commitUrl: url,
679
+ signal: navAbort.signal,
680
+ skipHistory: effectiveSkipHistory
681
+ }));
682
+ window.dispatchEvent(new Event("timber:navigation-end"));
683
+ if (scroll && hash) scrollToHashAfterPaint(hash);
684
+ else restoreScrollAfterPaint(scroll ? 0 : currentScrollY);
685
+ } catch (error) {
686
+ if (error instanceof VersionSkewError) {
687
+ setHardNavigating(true);
688
+ window.location.reload();
689
+ await new Promise(() => {});
690
+ }
691
+ if (error instanceof RedirectError) {
692
+ if (currentNavAbort !== navAbort) return;
693
+ await navigate(error.redirectUrl, { replace: true });
694
+ return;
695
+ }
696
+ if (error instanceof ServerErrorResponse) {
697
+ setHardNavigating(true);
698
+ window.location.href = error.url;
699
+ await new Promise(() => {});
700
+ }
701
+ throw error;
667
702
  }
668
- }
703
+ }, externalSignal);
669
704
  }
670
705
  async function refresh() {
671
706
  const currentUrl = deps.getCurrentUrl();
672
- const navAbort = createNavAbort();
673
- setPending(true, currentUrl);
674
- try {
707
+ await runNavigation(currentUrl, async (navAbort) => {
675
708
  await renderViaTransition(currentUrl, async () => {
676
709
  const result = await fetchRscPayload(currentUrl, deps, void 0, void 0, navAbort.signal);
677
- updateSegmentCache(result.segmentInfo);
678
- const navState = updateNavigationState(result.params, currentUrl);
710
+ const payload = await resolveForFallback(result.payload);
711
+ const navState = commitNavigation(currentUrl, {
712
+ payload,
713
+ params: result.params,
714
+ segmentInfo: result.segmentInfo
715
+ });
679
716
  return {
680
717
  ...result,
718
+ payload,
681
719
  navState
682
720
  };
683
721
  });
684
- } catch (error) {
685
- if (isAbortError(error)) return;
686
- throw error;
687
- } finally {
688
- if (currentNavAbort === navAbort) {
689
- currentNavAbort = null;
690
- setPending(false);
691
- deps.completeRouterNavigation?.();
692
- }
693
- }
722
+ });
694
723
  }
695
724
  async function handlePopState(url, scrollY = 0, externalSignal) {
696
725
  const entry = historyStack.get(url);
697
- if (entry && entry.payload !== null) {
698
- createNavAbort(externalSignal);
699
- currentNavAbort = null;
700
- setPending(false);
701
- const navState = updateNavigationState(entry.params, url);
726
+ if (entry && entry.payload !== null) await runNavigation(url, async () => {
727
+ const navState = commitNavigation(url, {
728
+ payload: entry.payload,
729
+ params: entry.params,
730
+ segmentInfo: entry.segmentInfo,
731
+ clearSegmentCacheOnEmpty: true
732
+ });
702
733
  renderPayload(entry.payload, navState);
703
734
  restoreScrollAfterPaint(scrollY);
704
- } else {
705
- const navAbort = createNavAbort(externalSignal);
706
- setPending(true, url);
707
- try {
708
- await renderViaTransition(url, async () => {
709
- const result = await fetchRscPayload(url, deps, segmentCache.serializeStateTree(), void 0, navAbort.signal);
710
- updateSegmentCache(result.segmentInfo);
711
- const navState = updateNavigationState(result.params, url);
712
- return {
713
- ...result,
714
- navState
715
- };
735
+ }, externalSignal);
736
+ else await runNavigation(url, async (navAbort) => {
737
+ await renderViaTransition(url, async () => {
738
+ const result = await fetchRscPayload(url, deps, deps.clientSegmentCache ? segmentCache.serializeStateTree() : void 0, void 0, navAbort.signal);
739
+ const payload = await resolveForFallback(result.payload);
740
+ const navState = commitNavigation(url, {
741
+ payload,
742
+ params: result.params,
743
+ segmentInfo: result.segmentInfo
716
744
  });
717
- restoreScrollAfterPaint(scrollY);
718
- } catch (error) {
719
- if (isAbortError(error)) return;
720
- throw error;
721
- } finally {
722
- if (currentNavAbort === navAbort) {
723
- currentNavAbort = null;
724
- setPending(false);
725
- }
726
- }
727
- }
745
+ return {
746
+ ...result,
747
+ payload,
748
+ navState
749
+ };
750
+ });
751
+ restoreScrollAfterPaint(scrollY);
752
+ }, externalSignal);
728
753
  }
729
754
  /**
730
755
  * Prefetch an RSC payload for a URL and store it in the prefetch cache.
@@ -735,7 +760,7 @@ function createRouter(deps) {
735
760
  const fetchUrl = hashIndex === -1 ? url : url.slice(0, hashIndex);
736
761
  if (prefetchCache.get(fetchUrl) !== void 0) return;
737
762
  if (historyStack.has(fetchUrl)) return;
738
- fetchRscPayload(fetchUrl, deps, segmentCache.serializeStateTree()).then((result) => {
763
+ fetchRscPayload(fetchUrl, deps, deps.clientSegmentCache ? segmentCache.serializeStateTree() : void 0).then((result) => {
739
764
  result.decodePromise?.catch(() => {});
740
765
  prefetchCache.set(fetchUrl, result);
741
766
  }, () => {});
@@ -753,12 +778,12 @@ function createRouter(deps) {
753
778
  prefetch,
754
779
  applyRevalidation(element) {
755
780
  const currentUrl = deps.getCurrentUrl();
756
- const navState = getNavigationState();
757
- historyStack.push(currentUrl, {
781
+ const existingEntry = historyStack.get(currentUrl);
782
+ renderPayload(element, commitNavigation(currentUrl, {
758
783
  payload: element,
759
- params: navState.params
760
- });
761
- renderPayload(element, navState);
784
+ params: getNavigationState().params,
785
+ segmentInfo: existingEntry?.segmentInfo
786
+ }));
762
787
  },
763
788
  initSegmentCache: (segments) => updateSegmentCache(segments),
764
789
  segmentCache,