@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/src/client.ts CHANGED
@@ -26,6 +26,7 @@ import {
26
26
  type CompilerModuleContext,
27
27
  } from "@reckona/mreact-compiler/internal";
28
28
  import { assetPath } from "./assets.js";
29
+ import { reactiveDevtoolsStubSource } from "./reactive-devtools-stub.js";
29
30
  import {
30
31
  bundleRouterModule,
31
32
  bundleRouterModules,
@@ -60,6 +61,7 @@ export interface ClientRouteManifestEntry {
60
61
  clientReferenceManifest?: readonly ClientReferenceMetadata[] | undefined;
61
62
  devScript?: string;
62
63
  imports?: readonly string[];
64
+ modulePreloads?: readonly string[];
63
65
  navigation?: boolean;
64
66
  navigationScript?: string;
65
67
  routeId?: string;
@@ -3012,6 +3014,7 @@ export async function buildClientRouteEntrySource(
3012
3014
  const navigationStateDeclaration = inlineClientNavigation
3013
3015
  ? `const __mreactNavigationState = __mreactGlobal.__mreactNavigationState ??= {
3014
3016
  cache: new Map(),
3017
+ cacheTokens: new Map(),
3015
3018
  current: {
3016
3019
  from: null,
3017
3020
  pending: false,
@@ -3020,6 +3023,7 @@ export async function buildClientRouteEntrySource(
3020
3023
  },
3021
3024
  fetchRevalidationInstalled: false,
3022
3025
  installed: false,
3026
+ navigationFetchInits: new WeakSet(),
3023
3027
  pendingHtmlFetches: new Map(),
3024
3028
  prefetchedUrls: new Set(),
3025
3029
  prefetchedScripts: new Set(),
@@ -3028,7 +3032,9 @@ export async function buildClientRouteEntrySource(
3028
3032
  routePrefetchManifestText: undefined,
3029
3033
  viewportAnchors: new WeakSet(),
3030
3034
  viewportObserver: undefined,
3031
- };`
3035
+ };
3036
+ __mreactNavigationState.cacheTokens ??= new Map();
3037
+ __mreactNavigationState.navigationFetchInits ??= new WeakSet();`
3032
3038
  : "";
3033
3039
  const deferredNavigationRuntime = deferredClientNavigation
3034
3040
  ? `
@@ -3191,10 +3197,10 @@ export async function __mreactNavigate(url, options = {}) {
3191
3197
  : false;
3192
3198
  }
3193
3199
 
3194
- export async function __mreactPrefetch(url) {
3200
+ export async function __mreactPrefetch(url, options = {}) {
3195
3201
  const runtime = await __mreactLoadNavigationRuntime();
3196
3202
  return typeof runtime?.__mreactPrefetch === "function"
3197
- ? runtime.__mreactPrefetch(url)
3203
+ ? runtime.__mreactPrefetch(url, options)
3198
3204
  : false;
3199
3205
  }
3200
3206
 
@@ -3240,13 +3246,16 @@ __mreactGlobal.__mreactRouteCell = (nativeCell, initial) => {
3240
3246
  const stateCell = nativeCell(record.value);
3241
3247
  const setStateCell = stateCell.set;
3242
3248
 
3243
- stateCell.set = (next) => {
3249
+ const setRouteStateCell = (next) => {
3244
3250
  setStateCell((previous) => {
3245
3251
  const resolved = typeof next === "function" ? next(previous) : next;
3246
3252
  record.value = resolved;
3247
3253
  return resolved;
3248
3254
  });
3249
3255
  };
3256
+ stateCell.set = setRouteStateCell;
3257
+ stateCell.setValue = (next) => stateCell.set(() => next);
3258
+ stateCell.update = (updater) => stateCell.set(updater);
3250
3259
 
3251
3260
  __mreactActiveCellRecords.set(cellKey, record);
3252
3261
  return stateCell;
@@ -3673,7 +3682,7 @@ function __mreactRouteAnnouncementElement() {
3673
3682
  return announcement;
3674
3683
  }
3675
3684
 
3676
- export async function __mreactPrefetch(url) {
3685
+ export async function __mreactPrefetch(url, options = {}) {
3677
3686
  if (!__mreactCanPrefetch()) {
3678
3687
  return false;
3679
3688
  }
@@ -3692,10 +3701,6 @@ export async function __mreactPrefetch(url) {
3692
3701
  return false;
3693
3702
  }
3694
3703
 
3695
- if (__mreactNavigationState.prefetchedUrls.has(href)) {
3696
- return true;
3697
- }
3698
-
3699
3704
  __mreactRememberPrefetchedUrl(href);
3700
3705
 
3701
3706
  const script = __mreactRouteScriptForNavigationUrl(href);
@@ -3704,21 +3709,30 @@ export async function __mreactPrefetch(url) {
3704
3709
  return __mreactPrefetchNavigationHtml(href);
3705
3710
  }
3706
3711
 
3707
- return __mreactPrefetchRouteScript(script);
3712
+ const scriptPrefetched = __mreactPrefetchRouteScript(script);
3713
+
3714
+ if (options?.trigger === "viewport") {
3715
+ return scriptPrefetched;
3716
+ }
3717
+
3718
+ return Promise.resolve(__mreactPrefetchNavigationHtml(href))
3719
+ .then(() => scriptPrefetched)
3720
+ .catch(() => scriptPrefetched);
3708
3721
  }
3709
3722
 
3710
3723
  function __mreactPrefetchNavigationHtml(href) {
3711
- if (__mreactNavigationState.cache.has(href)) {
3724
+ if (__mreactCachedNavigationHtml(href) !== undefined) {
3712
3725
  return true;
3713
3726
  }
3714
3727
 
3715
- return __mreactFetchNavigationHtmlOnce(href)
3716
- .then((html) => typeof html === "string")
3728
+ return __mreactFetchNavigationHtmlOnce(href, { speculative: true })
3729
+ .then((result) => result.cacheable && typeof result.html === "string")
3717
3730
  .catch(() => false);
3718
3731
  }
3719
3732
 
3720
3733
  const __mreactNavigationHtmlCacheMaxEntries = 64;
3721
3734
  const __mreactNavigationPrefetchHistoryMaxEntries = 64;
3735
+ const __mreactNavigationHtmlCacheTtlMs = 30_000;
3722
3736
 
3723
3737
  function __mreactRememberPrefetchedUrl(href) {
3724
3738
  __mreactRememberPrefetchHistory(__mreactNavigationState.prefetchedUrls, href);
@@ -3747,23 +3761,49 @@ function __mreactRememberPrefetchHistory(history, href) {
3747
3761
  }
3748
3762
 
3749
3763
  function __mreactCachedNavigationHtml(href) {
3750
- const html = __mreactNavigationState.cache.get(href);
3764
+ const entry = __mreactNavigationState.cache.get(href);
3751
3765
 
3752
- if (html === undefined) {
3766
+ if (entry === undefined) {
3767
+ return undefined;
3768
+ }
3769
+
3770
+ if (
3771
+ entry.token !== __mreactNavigationState.cacheTokens.get(href) ||
3772
+ entry.token.valid !== true ||
3773
+ entry.cacheContext !== __mreactNavigationCacheContext() ||
3774
+ Date.now() - entry.fetchedAt >= __mreactNavigationHtmlCacheTtlMs
3775
+ ) {
3776
+ __mreactNavigationState.cache.delete(href);
3777
+ __mreactNavigationState.prefetchedUrls.delete(href);
3778
+ __mreactReleaseNavigationCacheToken(href, entry.token);
3753
3779
  return undefined;
3754
3780
  }
3755
3781
 
3756
3782
  __mreactNavigationState.cache.delete(href);
3757
- __mreactNavigationState.cache.set(href, html);
3758
- return html;
3783
+ __mreactNavigationState.cache.set(href, entry);
3784
+ return entry.html;
3759
3785
  }
3760
3786
 
3761
- function __mreactRememberNavigationHtml(href, html) {
3787
+ function __mreactRememberNavigationHtml(href, html, token, cacheContext) {
3788
+ if (
3789
+ token === undefined ||
3790
+ token.valid !== true ||
3791
+ __mreactNavigationState.cacheTokens.get(href) !== token ||
3792
+ cacheContext !== __mreactNavigationCacheContext()
3793
+ ) {
3794
+ return;
3795
+ }
3796
+
3762
3797
  if (__mreactNavigationState.cache.has(href)) {
3763
3798
  __mreactNavigationState.cache.delete(href);
3764
3799
  }
3765
3800
 
3766
- __mreactNavigationState.cache.set(href, html);
3801
+ __mreactNavigationState.cache.set(href, {
3802
+ cacheContext,
3803
+ fetchedAt: Date.now(),
3804
+ html,
3805
+ token,
3806
+ });
3767
3807
 
3768
3808
  while (__mreactNavigationState.cache.size > __mreactNavigationHtmlCacheMaxEntries) {
3769
3809
  const oldestHref = __mreactNavigationState.cache.keys().next().value;
@@ -3774,37 +3814,100 @@ function __mreactRememberNavigationHtml(href, html) {
3774
3814
 
3775
3815
  __mreactNavigationState.cache.delete(oldestHref);
3776
3816
  __mreactNavigationState.prefetchedUrls.delete(oldestHref);
3817
+ __mreactReleaseNavigationCacheToken(oldestHref);
3777
3818
  }
3778
3819
  }
3779
3820
 
3780
- function __mreactPrefetchRouteScript(script) {
3781
- if (typeof document === "undefined") {
3782
- return false;
3821
+ function __mreactNavigationCacheContext() {
3822
+ const authSession = typeof document === "undefined"
3823
+ ? undefined
3824
+ : document.getElementById("__mreact_auth_session")?.textContent ?? undefined;
3825
+ const cookies = __mreactNavigationCookies();
3826
+
3827
+ return JSON.stringify([authSession, cookies]);
3828
+ }
3829
+
3830
+ function __mreactNavigationCookies() {
3831
+ return typeof document === "undefined" ? "" : document.cookie;
3832
+ }
3833
+
3834
+ function __mreactNavigationResponseCacheContext(html, requestCookies) {
3835
+ let authSession;
3836
+
3837
+ if (typeof document !== "undefined") {
3838
+ const template = document.createElement("template");
3839
+ template.innerHTML = html;
3840
+ authSession = template.content.querySelector("#__mreact_auth_session")?.textContent ?? undefined;
3783
3841
  }
3784
3842
 
3785
- const href = __mreactNormalizeAssetUrl(script);
3843
+ return JSON.stringify([authSession, requestCookies]);
3844
+ }
3786
3845
 
3787
- if (href === undefined) {
3788
- return false;
3846
+ function __mreactReleaseNavigationCacheToken(href, token) {
3847
+ if (
3848
+ __mreactNavigationState.cache.get(href) === undefined &&
3849
+ __mreactNavigationState.pendingHtmlFetches.get(href) === undefined &&
3850
+ (token === undefined || __mreactNavigationState.cacheTokens.get(href) === token)
3851
+ ) {
3852
+ __mreactNavigationState.cacheTokens.delete(href);
3789
3853
  }
3854
+ }
3790
3855
 
3791
- if (__mreactNavigationState.prefetchedScripts.has(href)) {
3792
- return true;
3856
+ function __mreactNavigationCacheToken(href) {
3857
+ const existing = __mreactNavigationState.cacheTokens.get(href);
3858
+
3859
+ if (existing !== undefined) {
3860
+ return existing;
3793
3861
  }
3794
3862
 
3795
- for (const link of Array.from(document.querySelectorAll('link[rel="modulepreload"][href]'))) {
3796
- if (link.href === href) {
3797
- __mreactRememberPrefetchedScript(href);
3798
- return true;
3863
+ const token = { valid: true };
3864
+ __mreactNavigationState.cacheTokens.set(href, token);
3865
+ return token;
3866
+ }
3867
+
3868
+ function __mreactPrefetchRouteScript(route) {
3869
+ if (typeof document === "undefined") {
3870
+ return false;
3871
+ }
3872
+
3873
+ const files = [route.script, ...(route.modulePreloads ?? [])];
3874
+ let valid = true;
3875
+
3876
+ for (const file of files) {
3877
+ const href = __mreactNormalizeAssetUrl(file);
3878
+
3879
+ if (
3880
+ href === undefined ||
3881
+ !__mreactIsSupportedModulePreloadUrl(file) ||
3882
+ !__mreactModulePreloadMatchesRouteScript(file, route.script)
3883
+ ) {
3884
+ valid = false;
3885
+ continue;
3886
+ }
3887
+
3888
+ if (__mreactNavigationState.prefetchedScripts.has(href)) {
3889
+ continue;
3890
+ }
3891
+
3892
+ let found = false;
3893
+ for (const link of Array.from(document.querySelectorAll('link[rel="modulepreload"][href]'))) {
3894
+ if (link.href === href) {
3895
+ found = true;
3896
+ break;
3897
+ }
3898
+ }
3899
+
3900
+ if (!found) {
3901
+ const link = document.createElement("link");
3902
+ link.rel = "modulepreload";
3903
+ link.href = href;
3904
+ document.head.appendChild(link);
3799
3905
  }
3906
+
3907
+ __mreactRememberPrefetchedScript(href);
3800
3908
  }
3801
3909
 
3802
- const link = document.createElement("link");
3803
- link.rel = "modulepreload";
3804
- link.href = href;
3805
- document.head.appendChild(link);
3806
- __mreactRememberPrefetchedScript(href);
3807
- return true;
3910
+ return valid;
3808
3911
  }
3809
3912
 
3810
3913
  export async function __mreactNavigate(url, options = {}) {
@@ -3817,19 +3920,46 @@ export async function __mreactNavigate(url, options = {}) {
3817
3920
  __mreactSetNavigationState(__mreactPendingNavigationState(href, options.type ?? "push"));
3818
3921
 
3819
3922
  try {
3820
- const cachedHtml = __mreactCachedNavigationHtml(href);
3821
- const script = __mreactRouteScriptForNavigationUrl(href);
3822
- if (script !== undefined) {
3823
- void __mreactPrefetchRouteScript(script);
3824
- }
3825
- const html = cachedHtml ?? await __mreactFetchNavigationHtmlOnce(href);
3923
+ let forceReload = __mreactNavigationState.reloadNextNavigationFetch === true;
3826
3924
 
3827
- if (typeof html !== "string") {
3828
- return false;
3829
- }
3925
+ for (;;) {
3926
+ const cachedHtml = forceReload ? undefined : __mreactCachedNavigationHtml(href);
3927
+ const script = __mreactRouteScriptForNavigationUrl(href);
3928
+ if (script !== undefined) {
3929
+ void __mreactPrefetchRouteScript(script);
3930
+ }
3931
+ const result = cachedHtml === undefined
3932
+ ? await __mreactFetchNavigationHtmlOnce(href, { force: forceReload })
3933
+ : {
3934
+ cacheable: true,
3935
+ cacheContext: __mreactNavigationCacheContext(),
3936
+ cacheContextMatches: true,
3937
+ html: cachedHtml,
3938
+ reusable: true,
3939
+ speculative: false,
3940
+ token: undefined,
3941
+ };
3942
+
3943
+ if (result.token !== undefined && result.token.valid !== true) {
3944
+ forceReload = __mreactNavigationState.reloadNextNavigationFetch === true;
3945
+ continue;
3946
+ }
3830
3947
 
3831
- __mreactRememberNavigationHtml(href, html);
3832
- return await __mreactApplyNavigationHtmlWithOptionalTransition(html, href, options);
3948
+ if (result.speculative === true && result.cacheable !== true) {
3949
+ forceReload = true;
3950
+ continue;
3951
+ }
3952
+
3953
+ if (result.reusable === true && result.cacheContextMatches !== true) {
3954
+ return false;
3955
+ }
3956
+
3957
+ if (typeof result.html !== "string") {
3958
+ return false;
3959
+ }
3960
+
3961
+ return await __mreactApplyNavigationHtmlWithOptionalTransition(result.html, href, options);
3962
+ }
3833
3963
  } finally {
3834
3964
  __mreactSetNavigationState(__mreactIdleNavigationState());
3835
3965
  }
@@ -3964,7 +4094,7 @@ function __mreactViewTransitionsAllowed(transition) {
3964
4094
  }
3965
4095
  }
3966
4096
 
3967
- export function __mreactInvalidateNavigationCache(path) {
4097
+ export function __mreactInvalidateNavigationCache(path, excludedRequest) {
3968
4098
  const normalizedPath = __mreactNormalizeNavigationPath(path);
3969
4099
 
3970
4100
  if (normalizedPath === undefined) {
@@ -3978,66 +4108,160 @@ export function __mreactInvalidateNavigationCache(path) {
3978
4108
 
3979
4109
  for (const href of hrefs) {
3980
4110
  if (__mreactNormalizeNavigationPath(href) === normalizedPath) {
4111
+ const token = __mreactNavigationState.cacheTokens.get(href);
4112
+ if (
4113
+ excludedRequest?.href === href &&
4114
+ excludedRequest.token === token &&
4115
+ token?.valid === true
4116
+ ) {
4117
+ continue;
4118
+ }
4119
+ if (token !== undefined) {
4120
+ token.valid = false;
4121
+ }
3981
4122
  __mreactNavigationState.cache.delete(href);
3982
4123
  __mreactNavigationState.prefetchedUrls.delete(href);
3983
4124
  __mreactNavigationState.pendingHtmlFetches.delete(href);
4125
+ __mreactNavigationState.cacheTokens.delete(href);
3984
4126
  }
3985
4127
  }
3986
4128
  }
3987
4129
 
3988
4130
  function __mreactInvalidateAllNavigationCache() {
4131
+ for (const entry of __mreactNavigationState.cache.values()) {
4132
+ entry.token.valid = false;
4133
+ }
4134
+ for (const request of __mreactNavigationState.pendingHtmlFetches.values()) {
4135
+ request.token.valid = false;
4136
+ }
3989
4137
  __mreactNavigationState.cache.clear();
4138
+ __mreactNavigationState.cacheTokens.clear();
3990
4139
  __mreactNavigationState.prefetchedUrls.clear();
3991
4140
  __mreactNavigationState.pendingHtmlFetches.clear();
3992
4141
  }
3993
4142
 
3994
- function __mreactFetchNavigationHtmlOnce(href) {
4143
+ function __mreactFetchNavigationHtmlOnce(href, options = {}) {
3995
4144
  const pending = __mreactNavigationState.pendingHtmlFetches.get(href);
3996
4145
 
4146
+ if (pending !== undefined && options.force !== true) {
4147
+ return pending.promise;
4148
+ }
4149
+
3997
4150
  if (pending !== undefined) {
3998
- return pending;
4151
+ pending.token.valid = false;
4152
+ __mreactNavigationState.pendingHtmlFetches.delete(href);
4153
+ __mreactNavigationState.cacheTokens.delete(href);
3999
4154
  }
4000
4155
 
4001
- const request = __mreactFetchNavigationHtml(href)
4002
- .then((html) => {
4156
+ const token = __mreactNavigationCacheToken(href);
4157
+ const pendingRequest = {
4158
+ promise: undefined,
4159
+ token,
4160
+ };
4161
+ const request = __mreactFetchNavigationHtml(href, options, pendingRequest)
4162
+ .then((result) => {
4163
+ const enriched = {
4164
+ ...result,
4165
+ speculative: options.speculative === true,
4166
+ token,
4167
+ };
4003
4168
  if (
4004
- typeof html === "string" &&
4005
- __mreactNavigationState.pendingHtmlFetches.get(href) === request
4169
+ enriched.cacheable === true &&
4170
+ typeof enriched.html === "string" &&
4171
+ __mreactNavigationState.pendingHtmlFetches.get(href) === pendingRequest &&
4172
+ token.valid === true &&
4173
+ __mreactNavigationState.cacheTokens.get(href) === token
4006
4174
  ) {
4007
- __mreactRememberNavigationHtml(href, html);
4175
+ __mreactRememberNavigationHtml(href, enriched.html, token, enriched.cacheContext);
4008
4176
  }
4009
4177
 
4010
- return html;
4178
+ return enriched;
4011
4179
  })
4012
4180
  .finally(() => {
4013
- if (__mreactNavigationState.pendingHtmlFetches.get(href) === request) {
4181
+ if (__mreactNavigationState.pendingHtmlFetches.get(href) === pendingRequest) {
4014
4182
  __mreactNavigationState.pendingHtmlFetches.delete(href);
4183
+ __mreactReleaseNavigationCacheToken(href, token);
4015
4184
  }
4016
4185
  });
4017
4186
 
4018
- __mreactNavigationState.pendingHtmlFetches.set(href, request);
4187
+ pendingRequest.promise = request;
4188
+ __mreactNavigationState.pendingHtmlFetches.set(href, pendingRequest);
4019
4189
  return request;
4020
4190
  }
4021
4191
 
4022
- function __mreactFetchNavigationHtml(href) {
4023
- const reloadRouteCache = __mreactNavigationState.reloadNextNavigationFetch === true;
4024
- __mreactNavigationState.reloadNextNavigationFetch = false;
4192
+ function __mreactFetchNavigationHtml(href, options = {}, pendingRequest) {
4193
+ const speculative = options.speculative === true;
4194
+ const reloadRouteCache = !speculative && __mreactNavigationState.reloadNextNavigationFetch === true;
4195
+ if (!speculative) {
4196
+ __mreactNavigationState.reloadNextNavigationFetch = false;
4197
+ }
4025
4198
  const headers = reloadRouteCache
4026
4199
  ? { "x-mreact-navigation": "1", "x-mreact-navigation-cache": "reload" }
4027
4200
  : { "x-mreact-navigation": "1" };
4028
-
4029
- return fetch(__mreactStaticNavigationRequestHref(href), {
4030
- headers,
4031
- }).then((response) => {
4032
- __mreactApplyRevalidationHeader(response);
4033
- if (__mreactNavigationResponseRequiresDocumentReload(response)) {
4034
- return undefined;
4201
+ const requestCacheContext = __mreactNavigationCacheContext();
4202
+ const requestCookies = __mreactNavigationCookies();
4203
+ const navigationRequestInit = { headers };
4204
+ __mreactNavigationState.navigationFetchInits.add(navigationRequestInit);
4205
+
4206
+ return fetch(__mreactStaticNavigationRequestHref(href), navigationRequestInit).then((response) => {
4207
+ const currentRequest =
4208
+ pendingRequest !== undefined &&
4209
+ __mreactNavigationState.pendingHtmlFetches.get(href) === pendingRequest &&
4210
+ __mreactNavigationState.cacheTokens.get(href) === pendingRequest.token &&
4211
+ pendingRequest.token.valid === true
4212
+ ? { href, token: pendingRequest.token }
4213
+ : undefined;
4214
+ if (pendingRequest === undefined || currentRequest !== undefined) {
4215
+ __mreactApplyRevalidationHeader(response, currentRequest);
4216
+ }
4217
+ const requiresDocumentReload = __mreactNavigationResponseRequiresDocumentReload(response);
4218
+ const responseIsSuccessful =
4219
+ response.ok === true ||
4220
+ (response.ok === undefined && response.status >= 200 && response.status < 300);
4221
+ const responseIsRedirected = response.redirected === true;
4222
+ const cacheControl = (response.headers.get("cache-control") ?? "").toLowerCase();
4223
+ const responseDisallowsCache = /(?:^|,)\\s*(?:no-store|no-cache)(?:\\s*=|\\s*(?:,|$))/.test(
4224
+ cacheControl,
4225
+ );
4226
+ const reusable =
4227
+ !requiresDocumentReload &&
4228
+ responseIsSuccessful &&
4229
+ !responseIsRedirected &&
4230
+ !responseDisallowsCache;
4231
+
4232
+ if (requiresDocumentReload || responseIsRedirected || speculative && !reusable) {
4233
+ __mreactDiscardNavigationResponse(response);
4234
+ return { cacheable: false, html: undefined };
4035
4235
  }
4036
4236
 
4037
- return response.text();
4237
+ return response.text().then((html) => {
4238
+ const cacheContext = __mreactNavigationResponseCacheContext(html, requestCookies);
4239
+ const currentCacheContext = __mreactNavigationCacheContext();
4240
+ const cacheContextMatches =
4241
+ cacheContext === currentCacheContext && requestCacheContext === currentCacheContext;
4242
+
4243
+ return {
4244
+ cacheContext,
4245
+ cacheContextMatches,
4246
+ cacheable: reusable && cacheContextMatches,
4247
+ html,
4248
+ reusable,
4249
+ };
4250
+ });
4038
4251
  });
4039
4252
  }
4040
4253
 
4254
+ function __mreactDiscardNavigationResponse(response) {
4255
+ try {
4256
+ const result = response?.body?.cancel?.();
4257
+ if (result !== undefined && typeof result.catch === "function") {
4258
+ void result.catch(() => {});
4259
+ }
4260
+ } catch {
4261
+ // Ignore body cancellation failures while falling back to a document navigation.
4262
+ }
4263
+ }
4264
+
4041
4265
  function __mreactStaticNavigationRequestHref(href) {
4042
4266
  if (typeof document === "undefined" || typeof location === "undefined") {
4043
4267
  return href;
@@ -4073,7 +4297,7 @@ function __mreactNavigationResponseRequiresDocumentReload(response) {
4073
4297
  return response.status === 204 || response.headers.get("x-mreact-navigation") === "reload";
4074
4298
  }
4075
4299
 
4076
- function __mreactApplyRevalidationHeader(response) {
4300
+ function __mreactApplyRevalidationHeader(response, currentRequest) {
4077
4301
  const header = typeof response?.headers?.get === "function"
4078
4302
  ? response.headers.get("x-mreact-revalidate")
4079
4303
  : null;
@@ -4082,18 +4306,34 @@ function __mreactApplyRevalidationHeader(response) {
4082
4306
  return;
4083
4307
  }
4084
4308
 
4309
+ const currentPath = __mreactNormalizeNavigationPath(currentRequest?.href);
4310
+
4085
4311
  for (const path of header.split(",")) {
4086
- __mreactInvalidateNavigationCache(path.trim());
4312
+ const trimmedPath = path.trim();
4313
+ const normalizedPath = __mreactNormalizeNavigationPath(trimmedPath);
4314
+ const excludedRequest =
4315
+ currentRequest !== undefined && normalizedPath === currentPath ? currentRequest : undefined;
4316
+ __mreactInvalidateNavigationCache(trimmedPath, excludedRequest);
4087
4317
  }
4088
4318
  }
4089
4319
 
4320
+ function __mreactIsNavigationFetchRequest(_input, init) {
4321
+ return init !== null && typeof init === "object" && __mreactNavigationState.navigationFetchInits.has(init);
4322
+ }
4323
+
4090
4324
  function __mreactNormalizeNavigationPath(path) {
4325
+ if (typeof path !== "string" || path.trim() === "") {
4326
+ return undefined;
4327
+ }
4328
+
4329
+ const normalizedInput = path.trim();
4330
+
4091
4331
  if (typeof location === "undefined") {
4092
- return typeof path === "string" && path.length > 0 ? path : undefined;
4332
+ return normalizedInput;
4093
4333
  }
4094
4334
 
4095
4335
  try {
4096
- const url = new URL(path, location.href);
4336
+ const url = new URL(normalizedInput, location.href);
4097
4337
  const pathname = url.pathname.replace(/\\/+$/, "");
4098
4338
 
4099
4339
  return pathname === "" ? "/" : pathname;
@@ -4109,7 +4349,7 @@ export function __mreactRestoreHistoryState(state) {
4109
4349
 
4110
4350
  const html = typeof state.html === "string"
4111
4351
  ? state.html
4112
- : typeof state.url === "string" ? __mreactNavigationState.cache.get(state.url) : undefined;
4352
+ : typeof state.url === "string" ? __mreactCachedNavigationHtml(state.url) : undefined;
4113
4353
 
4114
4354
  if (typeof html !== "string") {
4115
4355
  return false;
@@ -4485,7 +4725,7 @@ function __mreactRouteScriptForNavigationUrl(url) {
4485
4725
 
4486
4726
  for (const route of __mreactClientRoutePrefetchManifest()) {
4487
4727
  if (__mreactRoutePathMatches(route.path, nextUrl.pathname)) {
4488
- return route.script;
4728
+ return route;
4489
4729
  }
4490
4730
  }
4491
4731
 
@@ -4515,12 +4755,28 @@ function __mreactClientRoutePrefetchManifest() {
4515
4755
  try {
4516
4756
  const parsed = JSON.parse(text);
4517
4757
  __mreactNavigationState.routePrefetchManifest = Array.isArray(parsed)
4518
- ? parsed.filter((route) =>
4519
- route !== null &&
4520
- typeof route === "object" &&
4521
- typeof route.path === "string" &&
4522
- typeof route.script === "string"
4523
- )
4758
+ ? parsed.flatMap((route) => {
4759
+ if (
4760
+ route === null ||
4761
+ typeof route !== "object" ||
4762
+ typeof route.path !== "string" ||
4763
+ typeof route.script !== "string" ||
4764
+ !__mreactIsSupportedModulePreloadUrl(route.script)
4765
+ ) {
4766
+ return [];
4767
+ }
4768
+
4769
+ const modulePreloads = Array.isArray(route.modulePreloads)
4770
+ ? route.modulePreloads.filter(
4771
+ (file) =>
4772
+ typeof file === "string" &&
4773
+ __mreactIsSupportedModulePreloadUrl(file) &&
4774
+ __mreactModulePreloadMatchesRouteScript(file, route.script),
4775
+ )
4776
+ : [];
4777
+
4778
+ return [{ ...route, modulePreloads }];
4779
+ })
4524
4780
  : [];
4525
4781
  } catch {
4526
4782
  __mreactNavigationState.routePrefetchManifest = [];
@@ -4529,6 +4785,29 @@ function __mreactClientRoutePrefetchManifest() {
4529
4785
  return __mreactNavigationState.routePrefetchManifest;
4530
4786
  }
4531
4787
 
4788
+ function __mreactIsSupportedModulePreloadUrl(value) {
4789
+ if (typeof value !== "string" || value === "" || value.startsWith("javascript:")) {
4790
+ return false;
4791
+ }
4792
+
4793
+ try {
4794
+ const resolved = new URL(value, typeof location === "undefined" ? undefined : location.href);
4795
+ return resolved.protocol === "http:" || resolved.protocol === "https:";
4796
+ } catch {
4797
+ return false;
4798
+ }
4799
+ }
4800
+
4801
+ function __mreactModulePreloadMatchesRouteScript(value, script) {
4802
+ try {
4803
+ const scriptUrl = new URL(__mreactNormalizeAssetUrl(script), location.href);
4804
+ const dependencyUrl = new URL(__mreactNormalizeAssetUrl(value), location.href);
4805
+ return scriptUrl.origin === dependencyUrl.origin;
4806
+ } catch {
4807
+ return false;
4808
+ }
4809
+ }
4810
+
4532
4811
  function __mreactRoutePathMatches(routePath, pathname) {
4533
4812
  const routeSegments = __mreactNormalizeRoutePath(routePath);
4534
4813
  const pathSegments = __mreactNormalizeRoutePath(pathname);
@@ -4826,9 +5105,12 @@ function __mreactInstallNavigationFetchRevalidation() {
4826
5105
  const fetchImpl = globalThis.fetch;
4827
5106
  globalThis.fetch = function(input, init) {
4828
5107
  const mutating = __mreactIsMutatingFetchRequest(input, init);
5108
+ const navigation = __mreactIsNavigationFetchRequest(input, init);
4829
5109
 
4830
5110
  return Promise.resolve(fetchImpl.call(this, input, init)).then((response) => {
4831
- __mreactApplyRevalidationHeader(response);
5111
+ if (!navigation) {
5112
+ __mreactApplyRevalidationHeader(response);
5113
+ }
4832
5114
 
4833
5115
  if (mutating) {
4834
5116
  __mreactInvalidateAllNavigationCache();
@@ -4900,7 +5182,7 @@ function __mreactObserveViewportPrefetchAnchors(root) {
4900
5182
  }
4901
5183
 
4902
5184
  __mreactNavigationState.viewportObserver?.unobserve(entry.target);
4903
- void __mreactPrefetch(entry.target.href);
5185
+ void __mreactPrefetch(entry.target.href, { trigger: "viewport" });
4904
5186
  }
4905
5187
  });
4906
5188
  }
@@ -5540,14 +5822,7 @@ export function cell(initial) {
5540
5822
  resolveDir: reactiveCoreDir,
5541
5823
  }));
5542
5824
  buildApi.onLoad({ filter: /^devtools$/, namespace: "mreact-devtools-stub" }, () => ({
5543
- contents: `export function emitReactiveDevtoolsEvent() {}
5544
- export function emitReactiveEffectRunDevtoolsEvent() {}
5545
- export function hasReactiveDevtoolsEmitter() { return false; }
5546
- export function currentDevtoolsEmitter() { return undefined; }
5547
- export function currentReactiveDevtools() { return undefined; }
5548
- export function registerReactiveDevtoolsResource() { return { dispose() {}, update() {} }; }
5549
- export function invalidateReactiveDevtoolsCache() {}
5550
- export function prepareReactiveEffectRunDevtoolsEvent() { return undefined; }`,
5825
+ contents: reactiveDevtoolsStubSource,
5551
5826
  loader: "ts",
5552
5827
  }));
5553
5828
  if (options.sourceRegionModulePaths !== undefined) {