@reckona/mreact-router 0.0.209 → 0.0.211

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 (46) hide show
  1. package/dist/adapters/cloudflare.d.ts.map +1 -1
  2. package/dist/adapters/cloudflare.js +17 -5
  3. package/dist/adapters/cloudflare.js.map +1 -1
  4. package/dist/build.d.ts.map +1 -1
  5. package/dist/build.js +43 -3
  6. package/dist/build.js.map +1 -1
  7. package/dist/built-runtime.d.ts +1 -0
  8. package/dist/built-runtime.d.ts.map +1 -1
  9. package/dist/built-runtime.js +4 -0
  10. package/dist/built-runtime.js.map +1 -1
  11. package/dist/client-manifest-assets.d.ts +2 -0
  12. package/dist/client-manifest-assets.d.ts.map +1 -1
  13. package/dist/client-manifest-assets.js +2 -1
  14. package/dist/client-manifest-assets.js.map +1 -1
  15. package/dist/client.d.ts +1 -0
  16. package/dist/client.d.ts.map +1 -1
  17. package/dist/client.js +367 -93
  18. package/dist/client.js.map +1 -1
  19. package/dist/module-preload-graph.d.ts +5 -0
  20. package/dist/module-preload-graph.d.ts.map +1 -0
  21. package/dist/module-preload-graph.js +48 -0
  22. package/dist/module-preload-graph.js.map +1 -0
  23. package/dist/reactive-devtools-stub.d.ts +3 -0
  24. package/dist/reactive-devtools-stub.d.ts.map +1 -0
  25. package/dist/reactive-devtools-stub.js +10 -0
  26. package/dist/reactive-devtools-stub.js.map +1 -0
  27. package/dist/render.d.ts +1 -0
  28. package/dist/render.d.ts.map +1 -1
  29. package/dist/render.js +47 -10
  30. package/dist/render.js.map +1 -1
  31. package/dist/serve.js +1 -0
  32. package/dist/serve.js.map +1 -1
  33. package/dist/vite.d.ts.map +1 -1
  34. package/dist/vite.js +2 -7
  35. package/dist/vite.js.map +1 -1
  36. package/package.json +11 -11
  37. package/src/adapters/cloudflare.ts +22 -5
  38. package/src/build.ts +50 -3
  39. package/src/built-runtime.ts +18 -6
  40. package/src/client-manifest-assets.ts +3 -1
  41. package/src/client.ts +367 -92
  42. package/src/module-preload-graph.ts +70 -0
  43. package/src/reactive-devtools-stub.ts +9 -0
  44. package/src/render.ts +74 -10
  45. package/src/serve.ts +1 -0
  46. package/src/vite.ts +2 -7
package/dist/client.js CHANGED
@@ -5,6 +5,7 @@ import { basename, dirname, extname, isAbsolute, join, relative, resolve, sep }
5
5
  import { analyzeBoundaryGraph, collectClientRouteModuleAnalysis, formatDiagnostic, } from "@reckona/mreact-compiler";
6
6
  import { collectClientRouteModuleAnalysisFromContext, createCompilerModuleContext, hasUnguardedBrowserGlobalReference, readTopLevelBooleanExport, readTopLevelBooleanExportFromContext, stripTypeScriptWithOxc, transformCompilerModuleContext, } from "@reckona/mreact-compiler/internal";
7
7
  import { assetPath } from "./assets.js";
8
+ import { reactiveDevtoolsStubSource } from "./reactive-devtools-stub.js";
8
9
  import { bundleRouterModule, bundleRouterModules, } from "./bundle-pipeline.js";
9
10
  import { resolveClientConsolePureFunctions } from "./config.js";
10
11
  import { existingRouteShellCandidates } from "./route-shells.js";
@@ -1923,6 +1924,7 @@ export async function buildClientRouteEntrySource(options) {
1923
1924
  const navigationStateDeclaration = inlineClientNavigation
1924
1925
  ? `const __mreactNavigationState = __mreactGlobal.__mreactNavigationState ??= {
1925
1926
  cache: new Map(),
1927
+ cacheTokens: new Map(),
1926
1928
  current: {
1927
1929
  from: null,
1928
1930
  pending: false,
@@ -1931,6 +1933,7 @@ export async function buildClientRouteEntrySource(options) {
1931
1933
  },
1932
1934
  fetchRevalidationInstalled: false,
1933
1935
  installed: false,
1936
+ navigationFetchInits: new WeakSet(),
1934
1937
  pendingHtmlFetches: new Map(),
1935
1938
  prefetchedUrls: new Set(),
1936
1939
  prefetchedScripts: new Set(),
@@ -1939,7 +1942,9 @@ export async function buildClientRouteEntrySource(options) {
1939
1942
  routePrefetchManifestText: undefined,
1940
1943
  viewportAnchors: new WeakSet(),
1941
1944
  viewportObserver: undefined,
1942
- };`
1945
+ };
1946
+ __mreactNavigationState.cacheTokens ??= new Map();
1947
+ __mreactNavigationState.navigationFetchInits ??= new WeakSet();`
1943
1948
  : "";
1944
1949
  const deferredNavigationRuntime = deferredClientNavigation
1945
1950
  ? `
@@ -2102,10 +2107,10 @@ export async function __mreactNavigate(url, options = {}) {
2102
2107
  : false;
2103
2108
  }
2104
2109
 
2105
- export async function __mreactPrefetch(url) {
2110
+ export async function __mreactPrefetch(url, options = {}) {
2106
2111
  const runtime = await __mreactLoadNavigationRuntime();
2107
2112
  return typeof runtime?.__mreactPrefetch === "function"
2108
- ? runtime.__mreactPrefetch(url)
2113
+ ? runtime.__mreactPrefetch(url, options)
2109
2114
  : false;
2110
2115
  }
2111
2116
 
@@ -2151,13 +2156,16 @@ __mreactGlobal.__mreactRouteCell = (nativeCell, initial) => {
2151
2156
  const stateCell = nativeCell(record.value);
2152
2157
  const setStateCell = stateCell.set;
2153
2158
 
2154
- stateCell.set = (next) => {
2159
+ const setRouteStateCell = (next) => {
2155
2160
  setStateCell((previous) => {
2156
2161
  const resolved = typeof next === "function" ? next(previous) : next;
2157
2162
  record.value = resolved;
2158
2163
  return resolved;
2159
2164
  });
2160
2165
  };
2166
+ stateCell.set = setRouteStateCell;
2167
+ stateCell.setValue = (next) => stateCell.set(() => next);
2168
+ stateCell.update = (updater) => stateCell.set(updater);
2161
2169
 
2162
2170
  __mreactActiveCellRecords.set(cellKey, record);
2163
2171
  return stateCell;
@@ -2578,7 +2586,7 @@ function __mreactRouteAnnouncementElement() {
2578
2586
  return announcement;
2579
2587
  }
2580
2588
 
2581
- export async function __mreactPrefetch(url) {
2589
+ export async function __mreactPrefetch(url, options = {}) {
2582
2590
  if (!__mreactCanPrefetch()) {
2583
2591
  return false;
2584
2592
  }
@@ -2597,10 +2605,6 @@ export async function __mreactPrefetch(url) {
2597
2605
  return false;
2598
2606
  }
2599
2607
 
2600
- if (__mreactNavigationState.prefetchedUrls.has(href)) {
2601
- return true;
2602
- }
2603
-
2604
2608
  __mreactRememberPrefetchedUrl(href);
2605
2609
 
2606
2610
  const script = __mreactRouteScriptForNavigationUrl(href);
@@ -2609,21 +2613,30 @@ export async function __mreactPrefetch(url) {
2609
2613
  return __mreactPrefetchNavigationHtml(href);
2610
2614
  }
2611
2615
 
2612
- return __mreactPrefetchRouteScript(script);
2616
+ const scriptPrefetched = __mreactPrefetchRouteScript(script);
2617
+
2618
+ if (options?.trigger === "viewport") {
2619
+ return scriptPrefetched;
2620
+ }
2621
+
2622
+ return Promise.resolve(__mreactPrefetchNavigationHtml(href))
2623
+ .then(() => scriptPrefetched)
2624
+ .catch(() => scriptPrefetched);
2613
2625
  }
2614
2626
 
2615
2627
  function __mreactPrefetchNavigationHtml(href) {
2616
- if (__mreactNavigationState.cache.has(href)) {
2628
+ if (__mreactCachedNavigationHtml(href) !== undefined) {
2617
2629
  return true;
2618
2630
  }
2619
2631
 
2620
- return __mreactFetchNavigationHtmlOnce(href)
2621
- .then((html) => typeof html === "string")
2632
+ return __mreactFetchNavigationHtmlOnce(href, { speculative: true })
2633
+ .then((result) => result.cacheable && typeof result.html === "string")
2622
2634
  .catch(() => false);
2623
2635
  }
2624
2636
 
2625
2637
  const __mreactNavigationHtmlCacheMaxEntries = 64;
2626
2638
  const __mreactNavigationPrefetchHistoryMaxEntries = 64;
2639
+ const __mreactNavigationHtmlCacheTtlMs = 30_000;
2627
2640
 
2628
2641
  function __mreactRememberPrefetchedUrl(href) {
2629
2642
  __mreactRememberPrefetchHistory(__mreactNavigationState.prefetchedUrls, href);
@@ -2652,23 +2665,49 @@ function __mreactRememberPrefetchHistory(history, href) {
2652
2665
  }
2653
2666
 
2654
2667
  function __mreactCachedNavigationHtml(href) {
2655
- const html = __mreactNavigationState.cache.get(href);
2668
+ const entry = __mreactNavigationState.cache.get(href);
2656
2669
 
2657
- if (html === undefined) {
2670
+ if (entry === undefined) {
2671
+ return undefined;
2672
+ }
2673
+
2674
+ if (
2675
+ entry.token !== __mreactNavigationState.cacheTokens.get(href) ||
2676
+ entry.token.valid !== true ||
2677
+ entry.cacheContext !== __mreactNavigationCacheContext() ||
2678
+ Date.now() - entry.fetchedAt >= __mreactNavigationHtmlCacheTtlMs
2679
+ ) {
2680
+ __mreactNavigationState.cache.delete(href);
2681
+ __mreactNavigationState.prefetchedUrls.delete(href);
2682
+ __mreactReleaseNavigationCacheToken(href, entry.token);
2658
2683
  return undefined;
2659
2684
  }
2660
2685
 
2661
2686
  __mreactNavigationState.cache.delete(href);
2662
- __mreactNavigationState.cache.set(href, html);
2663
- return html;
2687
+ __mreactNavigationState.cache.set(href, entry);
2688
+ return entry.html;
2664
2689
  }
2665
2690
 
2666
- function __mreactRememberNavigationHtml(href, html) {
2691
+ function __mreactRememberNavigationHtml(href, html, token, cacheContext) {
2692
+ if (
2693
+ token === undefined ||
2694
+ token.valid !== true ||
2695
+ __mreactNavigationState.cacheTokens.get(href) !== token ||
2696
+ cacheContext !== __mreactNavigationCacheContext()
2697
+ ) {
2698
+ return;
2699
+ }
2700
+
2667
2701
  if (__mreactNavigationState.cache.has(href)) {
2668
2702
  __mreactNavigationState.cache.delete(href);
2669
2703
  }
2670
2704
 
2671
- __mreactNavigationState.cache.set(href, html);
2705
+ __mreactNavigationState.cache.set(href, {
2706
+ cacheContext,
2707
+ fetchedAt: Date.now(),
2708
+ html,
2709
+ token,
2710
+ });
2672
2711
 
2673
2712
  while (__mreactNavigationState.cache.size > __mreactNavigationHtmlCacheMaxEntries) {
2674
2713
  const oldestHref = __mreactNavigationState.cache.keys().next().value;
@@ -2679,37 +2718,100 @@ function __mreactRememberNavigationHtml(href, html) {
2679
2718
 
2680
2719
  __mreactNavigationState.cache.delete(oldestHref);
2681
2720
  __mreactNavigationState.prefetchedUrls.delete(oldestHref);
2721
+ __mreactReleaseNavigationCacheToken(oldestHref);
2682
2722
  }
2683
2723
  }
2684
2724
 
2685
- function __mreactPrefetchRouteScript(script) {
2686
- if (typeof document === "undefined") {
2687
- return false;
2725
+ function __mreactNavigationCacheContext() {
2726
+ const authSession = typeof document === "undefined"
2727
+ ? undefined
2728
+ : document.getElementById("__mreact_auth_session")?.textContent ?? undefined;
2729
+ const cookies = __mreactNavigationCookies();
2730
+
2731
+ return JSON.stringify([authSession, cookies]);
2732
+ }
2733
+
2734
+ function __mreactNavigationCookies() {
2735
+ return typeof document === "undefined" ? "" : document.cookie;
2736
+ }
2737
+
2738
+ function __mreactNavigationResponseCacheContext(html, requestCookies) {
2739
+ let authSession;
2740
+
2741
+ if (typeof document !== "undefined") {
2742
+ const template = document.createElement("template");
2743
+ template.innerHTML = html;
2744
+ authSession = template.content.querySelector("#__mreact_auth_session")?.textContent ?? undefined;
2745
+ }
2746
+
2747
+ return JSON.stringify([authSession, requestCookies]);
2748
+ }
2749
+
2750
+ function __mreactReleaseNavigationCacheToken(href, token) {
2751
+ if (
2752
+ __mreactNavigationState.cache.get(href) === undefined &&
2753
+ __mreactNavigationState.pendingHtmlFetches.get(href) === undefined &&
2754
+ (token === undefined || __mreactNavigationState.cacheTokens.get(href) === token)
2755
+ ) {
2756
+ __mreactNavigationState.cacheTokens.delete(href);
2688
2757
  }
2758
+ }
2689
2759
 
2690
- const href = __mreactNormalizeAssetUrl(script);
2760
+ function __mreactNavigationCacheToken(href) {
2761
+ const existing = __mreactNavigationState.cacheTokens.get(href);
2691
2762
 
2692
- if (href === undefined) {
2693
- return false;
2763
+ if (existing !== undefined) {
2764
+ return existing;
2694
2765
  }
2695
2766
 
2696
- if (__mreactNavigationState.prefetchedScripts.has(href)) {
2697
- return true;
2767
+ const token = { valid: true };
2768
+ __mreactNavigationState.cacheTokens.set(href, token);
2769
+ return token;
2770
+ }
2771
+
2772
+ function __mreactPrefetchRouteScript(route) {
2773
+ if (typeof document === "undefined") {
2774
+ return false;
2698
2775
  }
2699
2776
 
2700
- for (const link of Array.from(document.querySelectorAll('link[rel="modulepreload"][href]'))) {
2701
- if (link.href === href) {
2702
- __mreactRememberPrefetchedScript(href);
2703
- return true;
2777
+ const files = [route.script, ...(route.modulePreloads ?? [])];
2778
+ let valid = true;
2779
+
2780
+ for (const file of files) {
2781
+ const href = __mreactNormalizeAssetUrl(file);
2782
+
2783
+ if (
2784
+ href === undefined ||
2785
+ !__mreactIsSupportedModulePreloadUrl(file) ||
2786
+ !__mreactModulePreloadMatchesRouteScript(file, route.script)
2787
+ ) {
2788
+ valid = false;
2789
+ continue;
2704
2790
  }
2791
+
2792
+ if (__mreactNavigationState.prefetchedScripts.has(href)) {
2793
+ continue;
2794
+ }
2795
+
2796
+ let found = false;
2797
+ for (const link of Array.from(document.querySelectorAll('link[rel="modulepreload"][href]'))) {
2798
+ if (link.href === href) {
2799
+ found = true;
2800
+ break;
2801
+ }
2802
+ }
2803
+
2804
+ if (!found) {
2805
+ const link = document.createElement("link");
2806
+ link.rel = "modulepreload";
2807
+ link.href = href;
2808
+ document.head.appendChild(link);
2809
+ }
2810
+
2811
+ __mreactRememberPrefetchedScript(href);
2705
2812
  }
2706
2813
 
2707
- const link = document.createElement("link");
2708
- link.rel = "modulepreload";
2709
- link.href = href;
2710
- document.head.appendChild(link);
2711
- __mreactRememberPrefetchedScript(href);
2712
- return true;
2814
+ return valid;
2713
2815
  }
2714
2816
 
2715
2817
  export async function __mreactNavigate(url, options = {}) {
@@ -2722,19 +2824,46 @@ export async function __mreactNavigate(url, options = {}) {
2722
2824
  __mreactSetNavigationState(__mreactPendingNavigationState(href, options.type ?? "push"));
2723
2825
 
2724
2826
  try {
2725
- const cachedHtml = __mreactCachedNavigationHtml(href);
2726
- const script = __mreactRouteScriptForNavigationUrl(href);
2727
- if (script !== undefined) {
2728
- void __mreactPrefetchRouteScript(script);
2729
- }
2730
- const html = cachedHtml ?? await __mreactFetchNavigationHtmlOnce(href);
2827
+ let forceReload = __mreactNavigationState.reloadNextNavigationFetch === true;
2731
2828
 
2732
- if (typeof html !== "string") {
2733
- return false;
2734
- }
2829
+ for (;;) {
2830
+ const cachedHtml = forceReload ? undefined : __mreactCachedNavigationHtml(href);
2831
+ const script = __mreactRouteScriptForNavigationUrl(href);
2832
+ if (script !== undefined) {
2833
+ void __mreactPrefetchRouteScript(script);
2834
+ }
2835
+ const result = cachedHtml === undefined
2836
+ ? await __mreactFetchNavigationHtmlOnce(href, { force: forceReload })
2837
+ : {
2838
+ cacheable: true,
2839
+ cacheContext: __mreactNavigationCacheContext(),
2840
+ cacheContextMatches: true,
2841
+ html: cachedHtml,
2842
+ reusable: true,
2843
+ speculative: false,
2844
+ token: undefined,
2845
+ };
2846
+
2847
+ if (result.token !== undefined && result.token.valid !== true) {
2848
+ forceReload = __mreactNavigationState.reloadNextNavigationFetch === true;
2849
+ continue;
2850
+ }
2735
2851
 
2736
- __mreactRememberNavigationHtml(href, html);
2737
- return await __mreactApplyNavigationHtmlWithOptionalTransition(html, href, options);
2852
+ if (result.speculative === true && result.cacheable !== true) {
2853
+ forceReload = true;
2854
+ continue;
2855
+ }
2856
+
2857
+ if (result.reusable === true && result.cacheContextMatches !== true) {
2858
+ return false;
2859
+ }
2860
+
2861
+ if (typeof result.html !== "string") {
2862
+ return false;
2863
+ }
2864
+
2865
+ return await __mreactApplyNavigationHtmlWithOptionalTransition(result.html, href, options);
2866
+ }
2738
2867
  } finally {
2739
2868
  __mreactSetNavigationState(__mreactIdleNavigationState());
2740
2869
  }
@@ -2869,7 +2998,7 @@ function __mreactViewTransitionsAllowed(transition) {
2869
2998
  }
2870
2999
  }
2871
3000
 
2872
- export function __mreactInvalidateNavigationCache(path) {
3001
+ export function __mreactInvalidateNavigationCache(path, excludedRequest) {
2873
3002
  const normalizedPath = __mreactNormalizeNavigationPath(path);
2874
3003
 
2875
3004
  if (normalizedPath === undefined) {
@@ -2883,64 +3012,158 @@ export function __mreactInvalidateNavigationCache(path) {
2883
3012
 
2884
3013
  for (const href of hrefs) {
2885
3014
  if (__mreactNormalizeNavigationPath(href) === normalizedPath) {
3015
+ const token = __mreactNavigationState.cacheTokens.get(href);
3016
+ if (
3017
+ excludedRequest?.href === href &&
3018
+ excludedRequest.token === token &&
3019
+ token?.valid === true
3020
+ ) {
3021
+ continue;
3022
+ }
3023
+ if (token !== undefined) {
3024
+ token.valid = false;
3025
+ }
2886
3026
  __mreactNavigationState.cache.delete(href);
2887
3027
  __mreactNavigationState.prefetchedUrls.delete(href);
2888
3028
  __mreactNavigationState.pendingHtmlFetches.delete(href);
3029
+ __mreactNavigationState.cacheTokens.delete(href);
2889
3030
  }
2890
3031
  }
2891
3032
  }
2892
3033
 
2893
3034
  function __mreactInvalidateAllNavigationCache() {
3035
+ for (const entry of __mreactNavigationState.cache.values()) {
3036
+ entry.token.valid = false;
3037
+ }
3038
+ for (const request of __mreactNavigationState.pendingHtmlFetches.values()) {
3039
+ request.token.valid = false;
3040
+ }
2894
3041
  __mreactNavigationState.cache.clear();
3042
+ __mreactNavigationState.cacheTokens.clear();
2895
3043
  __mreactNavigationState.prefetchedUrls.clear();
2896
3044
  __mreactNavigationState.pendingHtmlFetches.clear();
2897
3045
  }
2898
3046
 
2899
- function __mreactFetchNavigationHtmlOnce(href) {
3047
+ function __mreactFetchNavigationHtmlOnce(href, options = {}) {
2900
3048
  const pending = __mreactNavigationState.pendingHtmlFetches.get(href);
2901
3049
 
3050
+ if (pending !== undefined && options.force !== true) {
3051
+ return pending.promise;
3052
+ }
3053
+
2902
3054
  if (pending !== undefined) {
2903
- return pending;
3055
+ pending.token.valid = false;
3056
+ __mreactNavigationState.pendingHtmlFetches.delete(href);
3057
+ __mreactNavigationState.cacheTokens.delete(href);
2904
3058
  }
2905
3059
 
2906
- const request = __mreactFetchNavigationHtml(href)
2907
- .then((html) => {
3060
+ const token = __mreactNavigationCacheToken(href);
3061
+ const pendingRequest = {
3062
+ promise: undefined,
3063
+ token,
3064
+ };
3065
+ const request = __mreactFetchNavigationHtml(href, options, pendingRequest)
3066
+ .then((result) => {
3067
+ const enriched = {
3068
+ ...result,
3069
+ speculative: options.speculative === true,
3070
+ token,
3071
+ };
2908
3072
  if (
2909
- typeof html === "string" &&
2910
- __mreactNavigationState.pendingHtmlFetches.get(href) === request
3073
+ enriched.cacheable === true &&
3074
+ typeof enriched.html === "string" &&
3075
+ __mreactNavigationState.pendingHtmlFetches.get(href) === pendingRequest &&
3076
+ token.valid === true &&
3077
+ __mreactNavigationState.cacheTokens.get(href) === token
2911
3078
  ) {
2912
- __mreactRememberNavigationHtml(href, html);
3079
+ __mreactRememberNavigationHtml(href, enriched.html, token, enriched.cacheContext);
2913
3080
  }
2914
3081
 
2915
- return html;
3082
+ return enriched;
2916
3083
  })
2917
3084
  .finally(() => {
2918
- if (__mreactNavigationState.pendingHtmlFetches.get(href) === request) {
3085
+ if (__mreactNavigationState.pendingHtmlFetches.get(href) === pendingRequest) {
2919
3086
  __mreactNavigationState.pendingHtmlFetches.delete(href);
3087
+ __mreactReleaseNavigationCacheToken(href, token);
2920
3088
  }
2921
3089
  });
2922
3090
 
2923
- __mreactNavigationState.pendingHtmlFetches.set(href, request);
3091
+ pendingRequest.promise = request;
3092
+ __mreactNavigationState.pendingHtmlFetches.set(href, pendingRequest);
2924
3093
  return request;
2925
3094
  }
2926
3095
 
2927
- function __mreactFetchNavigationHtml(href) {
2928
- const reloadRouteCache = __mreactNavigationState.reloadNextNavigationFetch === true;
2929
- __mreactNavigationState.reloadNextNavigationFetch = false;
3096
+ function __mreactFetchNavigationHtml(href, options = {}, pendingRequest) {
3097
+ const speculative = options.speculative === true;
3098
+ const reloadRouteCache = !speculative && __mreactNavigationState.reloadNextNavigationFetch === true;
3099
+ if (!speculative) {
3100
+ __mreactNavigationState.reloadNextNavigationFetch = false;
3101
+ }
2930
3102
  const headers = reloadRouteCache
2931
3103
  ? { "x-mreact-navigation": "1", "x-mreact-navigation-cache": "reload" }
2932
3104
  : { "x-mreact-navigation": "1" };
3105
+ const requestCacheContext = __mreactNavigationCacheContext();
3106
+ const requestCookies = __mreactNavigationCookies();
3107
+ const navigationRequestInit = { headers };
3108
+ __mreactNavigationState.navigationFetchInits.add(navigationRequestInit);
3109
+
3110
+ return fetch(__mreactStaticNavigationRequestHref(href), navigationRequestInit).then((response) => {
3111
+ const currentRequest =
3112
+ pendingRequest !== undefined &&
3113
+ __mreactNavigationState.pendingHtmlFetches.get(href) === pendingRequest &&
3114
+ __mreactNavigationState.cacheTokens.get(href) === pendingRequest.token &&
3115
+ pendingRequest.token.valid === true
3116
+ ? { href, token: pendingRequest.token }
3117
+ : undefined;
3118
+ if (pendingRequest === undefined || currentRequest !== undefined) {
3119
+ __mreactApplyRevalidationHeader(response, currentRequest);
3120
+ }
3121
+ const requiresDocumentReload = __mreactNavigationResponseRequiresDocumentReload(response);
3122
+ const responseIsSuccessful =
3123
+ response.ok === true ||
3124
+ (response.ok === undefined && response.status >= 200 && response.status < 300);
3125
+ const responseIsRedirected = response.redirected === true;
3126
+ const cacheControl = (response.headers.get("cache-control") ?? "").toLowerCase();
3127
+ const responseDisallowsCache = /(?:^|,)\\s*(?:no-store|no-cache)(?:\\s*=|\\s*(?:,|$))/.test(
3128
+ cacheControl,
3129
+ );
3130
+ const reusable =
3131
+ !requiresDocumentReload &&
3132
+ responseIsSuccessful &&
3133
+ !responseIsRedirected &&
3134
+ !responseDisallowsCache;
3135
+
3136
+ if (requiresDocumentReload || responseIsRedirected || speculative && !reusable) {
3137
+ __mreactDiscardNavigationResponse(response);
3138
+ return { cacheable: false, html: undefined };
3139
+ }
3140
+
3141
+ return response.text().then((html) => {
3142
+ const cacheContext = __mreactNavigationResponseCacheContext(html, requestCookies);
3143
+ const currentCacheContext = __mreactNavigationCacheContext();
3144
+ const cacheContextMatches =
3145
+ cacheContext === currentCacheContext && requestCacheContext === currentCacheContext;
3146
+
3147
+ return {
3148
+ cacheContext,
3149
+ cacheContextMatches,
3150
+ cacheable: reusable && cacheContextMatches,
3151
+ html,
3152
+ reusable,
3153
+ };
3154
+ });
3155
+ });
3156
+ }
2933
3157
 
2934
- return fetch(__mreactStaticNavigationRequestHref(href), {
2935
- headers,
2936
- }).then((response) => {
2937
- __mreactApplyRevalidationHeader(response);
2938
- if (__mreactNavigationResponseRequiresDocumentReload(response)) {
2939
- return undefined;
3158
+ function __mreactDiscardNavigationResponse(response) {
3159
+ try {
3160
+ const result = response?.body?.cancel?.();
3161
+ if (result !== undefined && typeof result.catch === "function") {
3162
+ void result.catch(() => {});
2940
3163
  }
2941
-
2942
- return response.text();
2943
- });
3164
+ } catch {
3165
+ // Ignore body cancellation failures while falling back to a document navigation.
3166
+ }
2944
3167
  }
2945
3168
 
2946
3169
  function __mreactStaticNavigationRequestHref(href) {
@@ -2978,7 +3201,7 @@ function __mreactNavigationResponseRequiresDocumentReload(response) {
2978
3201
  return response.status === 204 || response.headers.get("x-mreact-navigation") === "reload";
2979
3202
  }
2980
3203
 
2981
- function __mreactApplyRevalidationHeader(response) {
3204
+ function __mreactApplyRevalidationHeader(response, currentRequest) {
2982
3205
  const header = typeof response?.headers?.get === "function"
2983
3206
  ? response.headers.get("x-mreact-revalidate")
2984
3207
  : null;
@@ -2987,18 +3210,34 @@ function __mreactApplyRevalidationHeader(response) {
2987
3210
  return;
2988
3211
  }
2989
3212
 
3213
+ const currentPath = __mreactNormalizeNavigationPath(currentRequest?.href);
3214
+
2990
3215
  for (const path of header.split(",")) {
2991
- __mreactInvalidateNavigationCache(path.trim());
3216
+ const trimmedPath = path.trim();
3217
+ const normalizedPath = __mreactNormalizeNavigationPath(trimmedPath);
3218
+ const excludedRequest =
3219
+ currentRequest !== undefined && normalizedPath === currentPath ? currentRequest : undefined;
3220
+ __mreactInvalidateNavigationCache(trimmedPath, excludedRequest);
2992
3221
  }
2993
3222
  }
2994
3223
 
3224
+ function __mreactIsNavigationFetchRequest(_input, init) {
3225
+ return init !== null && typeof init === "object" && __mreactNavigationState.navigationFetchInits.has(init);
3226
+ }
3227
+
2995
3228
  function __mreactNormalizeNavigationPath(path) {
3229
+ if (typeof path !== "string" || path.trim() === "") {
3230
+ return undefined;
3231
+ }
3232
+
3233
+ const normalizedInput = path.trim();
3234
+
2996
3235
  if (typeof location === "undefined") {
2997
- return typeof path === "string" && path.length > 0 ? path : undefined;
3236
+ return normalizedInput;
2998
3237
  }
2999
3238
 
3000
3239
  try {
3001
- const url = new URL(path, location.href);
3240
+ const url = new URL(normalizedInput, location.href);
3002
3241
  const pathname = url.pathname.replace(/\\/+$/, "");
3003
3242
 
3004
3243
  return pathname === "" ? "/" : pathname;
@@ -3014,7 +3253,7 @@ export function __mreactRestoreHistoryState(state) {
3014
3253
 
3015
3254
  const html = typeof state.html === "string"
3016
3255
  ? state.html
3017
- : typeof state.url === "string" ? __mreactNavigationState.cache.get(state.url) : undefined;
3256
+ : typeof state.url === "string" ? __mreactCachedNavigationHtml(state.url) : undefined;
3018
3257
 
3019
3258
  if (typeof html !== "string") {
3020
3259
  return false;
@@ -3390,7 +3629,7 @@ function __mreactRouteScriptForNavigationUrl(url) {
3390
3629
 
3391
3630
  for (const route of __mreactClientRoutePrefetchManifest()) {
3392
3631
  if (__mreactRoutePathMatches(route.path, nextUrl.pathname)) {
3393
- return route.script;
3632
+ return route;
3394
3633
  }
3395
3634
  }
3396
3635
 
@@ -3420,12 +3659,28 @@ function __mreactClientRoutePrefetchManifest() {
3420
3659
  try {
3421
3660
  const parsed = JSON.parse(text);
3422
3661
  __mreactNavigationState.routePrefetchManifest = Array.isArray(parsed)
3423
- ? parsed.filter((route) =>
3424
- route !== null &&
3425
- typeof route === "object" &&
3426
- typeof route.path === "string" &&
3427
- typeof route.script === "string"
3428
- )
3662
+ ? parsed.flatMap((route) => {
3663
+ if (
3664
+ route === null ||
3665
+ typeof route !== "object" ||
3666
+ typeof route.path !== "string" ||
3667
+ typeof route.script !== "string" ||
3668
+ !__mreactIsSupportedModulePreloadUrl(route.script)
3669
+ ) {
3670
+ return [];
3671
+ }
3672
+
3673
+ const modulePreloads = Array.isArray(route.modulePreloads)
3674
+ ? route.modulePreloads.filter(
3675
+ (file) =>
3676
+ typeof file === "string" &&
3677
+ __mreactIsSupportedModulePreloadUrl(file) &&
3678
+ __mreactModulePreloadMatchesRouteScript(file, route.script),
3679
+ )
3680
+ : [];
3681
+
3682
+ return [{ ...route, modulePreloads }];
3683
+ })
3429
3684
  : [];
3430
3685
  } catch {
3431
3686
  __mreactNavigationState.routePrefetchManifest = [];
@@ -3434,6 +3689,29 @@ function __mreactClientRoutePrefetchManifest() {
3434
3689
  return __mreactNavigationState.routePrefetchManifest;
3435
3690
  }
3436
3691
 
3692
+ function __mreactIsSupportedModulePreloadUrl(value) {
3693
+ if (typeof value !== "string" || value === "" || value.startsWith("javascript:")) {
3694
+ return false;
3695
+ }
3696
+
3697
+ try {
3698
+ const resolved = new URL(value, typeof location === "undefined" ? undefined : location.href);
3699
+ return resolved.protocol === "http:" || resolved.protocol === "https:";
3700
+ } catch {
3701
+ return false;
3702
+ }
3703
+ }
3704
+
3705
+ function __mreactModulePreloadMatchesRouteScript(value, script) {
3706
+ try {
3707
+ const scriptUrl = new URL(__mreactNormalizeAssetUrl(script), location.href);
3708
+ const dependencyUrl = new URL(__mreactNormalizeAssetUrl(value), location.href);
3709
+ return scriptUrl.origin === dependencyUrl.origin;
3710
+ } catch {
3711
+ return false;
3712
+ }
3713
+ }
3714
+
3437
3715
  function __mreactRoutePathMatches(routePath, pathname) {
3438
3716
  const routeSegments = __mreactNormalizeRoutePath(routePath);
3439
3717
  const pathSegments = __mreactNormalizeRoutePath(pathname);
@@ -3731,9 +4009,12 @@ function __mreactInstallNavigationFetchRevalidation() {
3731
4009
  const fetchImpl = globalThis.fetch;
3732
4010
  globalThis.fetch = function(input, init) {
3733
4011
  const mutating = __mreactIsMutatingFetchRequest(input, init);
4012
+ const navigation = __mreactIsNavigationFetchRequest(input, init);
3734
4013
 
3735
4014
  return Promise.resolve(fetchImpl.call(this, input, init)).then((response) => {
3736
- __mreactApplyRevalidationHeader(response);
4015
+ if (!navigation) {
4016
+ __mreactApplyRevalidationHeader(response);
4017
+ }
3737
4018
 
3738
4019
  if (mutating) {
3739
4020
  __mreactInvalidateAllNavigationCache();
@@ -3805,7 +4086,7 @@ function __mreactObserveViewportPrefetchAnchors(root) {
3805
4086
  }
3806
4087
 
3807
4088
  __mreactNavigationState.viewportObserver?.unobserve(entry.target);
3808
- void __mreactPrefetch(entry.target.href);
4089
+ void __mreactPrefetch(entry.target.href, { trigger: "viewport" });
3809
4090
  }
3810
4091
  });
3811
4092
  }
@@ -4425,14 +4706,7 @@ export function cell(initial) {
4425
4706
  resolveDir: reactiveCoreDir,
4426
4707
  }));
4427
4708
  buildApi.onLoad({ filter: /^devtools$/, namespace: "mreact-devtools-stub" }, () => ({
4428
- contents: `export function emitReactiveDevtoolsEvent() {}
4429
- export function emitReactiveEffectRunDevtoolsEvent() {}
4430
- export function hasReactiveDevtoolsEmitter() { return false; }
4431
- export function currentDevtoolsEmitter() { return undefined; }
4432
- export function currentReactiveDevtools() { return undefined; }
4433
- export function registerReactiveDevtoolsResource() { return { dispose() {}, update() {} }; }
4434
- export function invalidateReactiveDevtoolsCache() {}
4435
- export function prepareReactiveEffectRunDevtoolsEvent() { return undefined; }`,
4709
+ contents: reactiveDevtoolsStubSource,
4436
4710
  loader: "ts",
4437
4711
  }));
4438
4712
  if (options.sourceRegionModulePaths !== undefined) {