@reckona/mreact-router 0.0.201 → 0.0.202

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 (72) hide show
  1. package/README.md +1 -1
  2. package/dist/adapters/cloudflare.d.ts.map +1 -1
  3. package/dist/adapters/cloudflare.js +51 -4
  4. package/dist/adapters/cloudflare.js.map +1 -1
  5. package/dist/adapters/node.d.ts +7 -0
  6. package/dist/adapters/node.d.ts.map +1 -1
  7. package/dist/adapters/node.js +7 -1
  8. package/dist/adapters/node.js.map +1 -1
  9. package/dist/adapters/static.d.ts.map +1 -1
  10. package/dist/adapters/static.js +14 -3
  11. package/dist/adapters/static.js.map +1 -1
  12. package/dist/build.d.ts +3 -0
  13. package/dist/build.d.ts.map +1 -1
  14. package/dist/build.js +42 -5
  15. package/dist/build.js.map +1 -1
  16. package/dist/built-runtime.d.ts +4 -1
  17. package/dist/built-runtime.d.ts.map +1 -1
  18. package/dist/built-runtime.js.map +1 -1
  19. package/dist/cache.d.ts +26 -0
  20. package/dist/cache.d.ts.map +1 -1
  21. package/dist/cache.js +78 -12
  22. package/dist/cache.js.map +1 -1
  23. package/dist/cli-options.d.ts +4 -0
  24. package/dist/cli-options.d.ts.map +1 -1
  25. package/dist/cli-options.js +17 -0
  26. package/dist/cli-options.js.map +1 -1
  27. package/dist/cli.js +2 -1
  28. package/dist/cli.js.map +1 -1
  29. package/dist/node-server.d.ts +6 -0
  30. package/dist/node-server.d.ts.map +1 -1
  31. package/dist/node-server.js +19 -1
  32. package/dist/node-server.js.map +1 -1
  33. package/dist/prerender-entry.d.ts +5 -0
  34. package/dist/prerender-entry.d.ts.map +1 -0
  35. package/dist/prerender-entry.js +41 -0
  36. package/dist/prerender-entry.js.map +1 -0
  37. package/dist/render.d.ts +13 -0
  38. package/dist/render.d.ts.map +1 -1
  39. package/dist/render.js +139 -87
  40. package/dist/render.js.map +1 -1
  41. package/dist/request-header-tracking.d.ts +44 -0
  42. package/dist/request-header-tracking.d.ts.map +1 -0
  43. package/dist/request-header-tracking.js +116 -0
  44. package/dist/request-header-tracking.js.map +1 -0
  45. package/dist/route-source.d.ts +16 -0
  46. package/dist/route-source.d.ts.map +1 -1
  47. package/dist/route-source.js +55 -1
  48. package/dist/route-source.js.map +1 -1
  49. package/dist/security-headers.d.ts +16 -0
  50. package/dist/security-headers.d.ts.map +1 -1
  51. package/dist/security-headers.js +37 -0
  52. package/dist/security-headers.js.map +1 -1
  53. package/dist/serve.d.ts +7 -0
  54. package/dist/serve.d.ts.map +1 -1
  55. package/dist/serve.js +94 -28
  56. package/dist/serve.js.map +1 -1
  57. package/package.json +11 -11
  58. package/src/adapters/cloudflare.ts +81 -9
  59. package/src/adapters/node.ts +14 -1
  60. package/src/adapters/static.ts +18 -5
  61. package/src/build.ts +71 -4
  62. package/src/built-runtime.ts +2 -2
  63. package/src/cache.ts +117 -11
  64. package/src/cli-options.ts +24 -0
  65. package/src/cli.ts +5 -0
  66. package/src/node-server.ts +27 -1
  67. package/src/prerender-entry.ts +56 -0
  68. package/src/render.ts +188 -98
  69. package/src/request-header-tracking.ts +161 -0
  70. package/src/route-source.ts +71 -0
  71. package/src/security-headers.ts +41 -0
  72. package/src/serve.ts +133 -30
package/dist/render.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { AsyncLocalStorage } from "node:async_hooks";
3
+ import { readFileSync } from "node:fs";
3
4
  import { access, readFile, stat } from "node:fs/promises";
4
5
  import { dirname, join, relative, resolve, sep } from "node:path";
5
6
  import { formatDiagnostic, transform } from "@reckona/mreact-compiler";
@@ -14,13 +15,15 @@ import { appFileConventionContentType } from "./file-conventions.js";
14
15
  import { dispatchServerActionRequest, prepareRouteServerActionPlaceholders, prepareRouteServerActions, } from "./actions.js";
15
16
  import { serverActionCookie } from "./csrf.js";
16
17
  import { activeRouteCacheContext, withRouteCacheContext, cachedRouteResponse, cacheRouteResponse, routeCacheKey, routeCachePolicyFromSource, } from "./cache.js";
18
+ import { trackRequestHeaderReads, withTrackedRequest, } from "./request-header-tracking.js";
19
+ import { configuredHstsHeader, hasInvalidConfiguredHsts, } from "./security-headers.js";
17
20
  import { resolveRouterCacheLimit } from "./cache-config.js";
18
21
  import { importAppRouterBuiltFileModule, importAppRouterFileModule, importAppRouterSourceModule, fileImportMetaUrlPlugin, rewriteCompatVendorPlaceholderImportsForRunner, } from "./module-runner.js";
19
22
  import { bytesResponse, htmlResponse } from "./http.js";
20
23
  import { isNotFoundError, isRedirectError, rewriteLocation } from "./navigation.js";
21
24
  import { createAppRouterImportPolicyPlugin } from "./import-policy.js";
22
25
  import { existingRouteShellCandidates } from "./route-shells.js";
23
- import { hasLoaderExport, isStreamRouteSource, routeClosureMayUseAwaitBoundary, stripRouteBuildExports, stripRouteModuleExports, stripRouteClientOnlyExports, stripRouteClientSource, stripRouteLoaderOnlyExports, stripRouteMetadataOnlyExports, } from "./route-source.js";
26
+ import { hasLoaderExport, isStreamRouteSource, routeClosureMayUseAwaitBoundary, routeClosureMayUseRequestInput, stripRouteBuildExports, stripRouteModuleExports, stripRouteClientOnlyExports, stripRouteClientSource, stripRouteLoaderOnlyExports, stripRouteMetadataOnlyExports, } from "./route-source.js";
24
27
  import { emitRouterLog, logDurationMs, logNow } from "./logger.js";
25
28
  import { createRouterRuntimeCacheCounters, readRouterRuntimeCacheEntry, routerRuntimeCacheStat, } from "./cache-stats.js";
26
29
  import { bundleRouterModule } from "./bundle-pipeline.js";
@@ -240,23 +243,17 @@ export function routerRenderRuntimeCacheStats() {
240
243
  routerRuntimeCacheStat("middleware-module", middlewareModuleCache, maxMiddlewareModuleCacheEntries, middlewareModuleCacheCounters),
241
244
  routerRuntimeCacheStat("server-route-module", serverRouteModuleCache, maxServerRouteModuleCacheEntries, serverRouteModuleCacheCounters),
242
245
  routerRuntimeCacheStat("composed-route-metadata", composedRouteMetadataCache, maxComposedRouteMetadataCacheEntries, composedRouteMetadataCacheCounters),
243
- routerRuntimeCacheStat("rendered-shell", renderedShellCache, MAX_RENDERED_SHELL_CACHE_ENTRIES, renderedShellCacheCounters),
246
+ // Preserve the public diagnostics entry while making explicit that rendered HTML reuse is disabled.
247
+ {
248
+ evictions: 0,
249
+ hits: 0,
250
+ maxEntries: 0,
251
+ misses: 0,
252
+ name: "rendered-shell",
253
+ size: 0,
254
+ },
244
255
  ];
245
256
  }
246
- // Issue 086: per-shell prefix/suffix cache. Pure layouts (whose
247
- // exported component takes zero arguments and therefore cannot
248
- // depend on the request props) produce the same HTML for every
249
- // request, so we cache the already-split { prefix, suffix } strings
250
- // keyed by appDir + shellFile + serverModuleCacheVersion. Impure
251
- // layouts (function.length > 0) are tagged "impure" so we skip the
252
- // detection on subsequent requests but still render per-request.
253
- //
254
- // The cache is only active when a version is present (production
255
- // builds); dev mode keeps the previous behaviour so reloads pick up
256
- // edits without server restart.
257
- const renderedShellCache = new Map();
258
- const MAX_RENDERED_SHELL_CACHE_ENTRIES = 1024;
259
- const renderedShellCacheCounters = createRouterRuntimeCacheCounters();
260
257
  /**
261
258
  * Renders a source app-router request into a `Response`.
262
259
  */
@@ -449,6 +446,9 @@ async function renderAppRequestInternal(options) {
449
446
  renderSingleFlightNavigation: async (singleFlight) => renderAppRequestInternal({
450
447
  ...options,
451
448
  matchedRoute: undefined,
449
+ // This renders a different route than the caller asked about, so its
450
+ // header dependence says nothing about the caller's request.
451
+ renderSignals: undefined,
452
452
  request: singleFlightNavigationRequest({
453
453
  path: singleFlight.path,
454
454
  request: singleFlight.request,
@@ -490,6 +490,9 @@ async function renderAppRequestInternal(options) {
490
490
  }
491
491
  const queryClient = options.queryClient ?? createQueryClient();
492
492
  let recoveryRoute;
493
+ let trackedRequest;
494
+ let sourceUsesRequestInput = true;
495
+ let invalidConfiguredHsts = false;
493
496
  return (await withRouteCacheContext(options.routeCache, async () => {
494
497
  try {
495
498
  if (matched.route.kind === "asset") {
@@ -554,13 +557,15 @@ async function renderAppRequestInternal(options) {
554
557
  timing,
555
558
  vitePlugins: options.vitePlugins,
556
559
  });
560
+ sourceUsesRequestInput = originalAnalysis.usesRequestInput;
557
561
  finishRenderTimingPhase(timing, phaseStartedAt, "sourceAnalysisMs");
558
562
  const cachePolicy = originalAnalysis.cachePolicy;
559
563
  const navigationScript = options.navigationScripts?.get(matched.route.path);
560
564
  const cacheKey = routeCacheKey(options.appDir, matched.route.path, url);
561
- const mayUseRouteCache = cachePolicy === undefined
562
- ? originalAnalysis.usesRuntimeCacheControl
563
- : cachePolicy.revalidateSeconds !== 0;
565
+ const mayUseRouteCache = !sourceUsesRequestInput &&
566
+ (cachePolicy === undefined
567
+ ? originalAnalysis.usesRuntimeCacheControl
568
+ : cachePolicy.revalidateSeconds !== 0);
564
569
  const reloadRouteCache = isNavigationRouteCacheReloadRequest(options.request);
565
570
  phaseStartedAt = renderTimingPhaseStartedAt(timing);
566
571
  const cachedResponse = !mayUseRouteCache || reloadRouteCache
@@ -574,6 +579,17 @@ async function renderAppRequestInternal(options) {
574
579
  if (cachedResponse !== undefined) {
575
580
  return cachedResponse;
576
581
  }
582
+ // Only a route whose result may be stored pays for header-read tracking;
583
+ // cache hits returned above never construct it. Application code reads
584
+ // headers through this request so that a render depending on one is never
585
+ // stored under a key that ignores it. Callers that store by path alone ask
586
+ // for tracking through `renderSignals` even when the route itself declares
587
+ // no cache policy.
588
+ trackedRequest =
589
+ mayUseRouteCache || options.renderSignals !== undefined
590
+ ? trackRequestHeaderReads(options.request)
591
+ : undefined;
592
+ const appRequest = trackedRequest?.request ?? options.request;
577
593
  phaseStartedAt = renderTimingPhaseStartedAt(timing);
578
594
  const preparedActions = await prepareRouteServerActions({
579
595
  appDir: options.appDir,
@@ -614,12 +630,11 @@ async function renderAppRequestInternal(options) {
614
630
  ? loadRouteDataWithInstrumentation({
615
631
  appDir: options.appDir,
616
632
  code: originalCode,
617
- context: {
633
+ context: withTrackedRequest({
618
634
  env: options.env,
619
635
  params: matched.params,
620
636
  queryClient,
621
- request: options.request,
622
- },
637
+ }, appRequest, trackedRequest),
623
638
  filename: matched.route.file,
624
639
  importPolicy: options.importPolicy,
625
640
  instrumentation: options.instrumentation,
@@ -698,12 +713,11 @@ async function renderAppRequestInternal(options) {
698
713
  headers: { "content-type": "text/plain; charset=utf-8" },
699
714
  });
700
715
  }
701
- const renderedPage = await runWithQueryClient(queryClient, () => runServerModuleWithSlots(stringOutput.code, {
716
+ const renderedPage = await runWithQueryClient(queryClient, () => runServerModuleWithSlots(stringOutput.code, withTrackedRequest({
702
717
  data,
703
718
  params: matched.params,
704
719
  queryClient,
705
- request: options.request,
706
- }, matched.route.file, options.serverModules, options.serverModuleCacheVersion, options.define, options.vitePlugins, undefined, options.importPolicy, options.devServerModuleCacheVersion));
720
+ }, appRequest, trackedRequest), matched.route.file, options.serverModules, options.serverModuleCacheVersion, options.define, options.vitePlugins, undefined, options.importPolicy, options.devServerModuleCacheVersion));
707
721
  const pageHtml = renderedPage.html;
708
722
  const pageHtmlForLayout = clientRoute
709
723
  ? withHydrationMarkers({
@@ -728,12 +742,11 @@ async function renderAppRequestInternal(options) {
728
742
  appDir: options.appDir,
729
743
  pageFile: matched.route.file,
730
744
  html: pageHtmlForLayout,
731
- props: {
745
+ props: withTrackedRequest({
732
746
  data,
733
747
  params: matched.params,
734
748
  queryClient,
735
- request: options.request,
736
- },
749
+ }, appRequest, trackedRequest),
737
750
  slots: renderedPage.slots,
738
751
  serverModules: options.serverModules,
739
752
  serverModuleCacheVersion: options.serverModuleCacheVersion,
@@ -746,11 +759,10 @@ async function renderAppRequestInternal(options) {
746
759
  const metadata = await loadComposedRouteMetadata({
747
760
  appDir: options.appDir,
748
761
  code: originalCode,
749
- context: {
762
+ context: withTrackedRequest({
750
763
  data,
751
764
  params: matched.params,
752
- request: options.request,
753
- },
765
+ }, appRequest, trackedRequest),
754
766
  filename: matched.route.file,
755
767
  importPolicy: options.importPolicy,
756
768
  routes,
@@ -851,7 +863,8 @@ async function renderAppRequestInternal(options) {
851
863
  pageFile: matched.route.file,
852
864
  params: matched.params,
853
865
  queryClient,
854
- request: options.request,
866
+ request: appRequest,
867
+ trackedRequest,
855
868
  routePath: matched.route.path,
856
869
  routeScripts: options.clientScripts,
857
870
  serverModules: options.serverModules,
@@ -871,12 +884,11 @@ async function renderAppRequestInternal(options) {
871
884
  return response;
872
885
  }
873
886
  const data = streamData;
874
- const props = {
887
+ const props = withTrackedRequest({
875
888
  data,
876
889
  params: matched.params,
877
890
  queryClient,
878
- request: options.request,
879
- };
891
+ }, appRequest, trackedRequest);
880
892
  phaseStartedAt = renderTimingPhaseStartedAt(timing);
881
893
  const stream = runServerStreamModule(output.code, {
882
894
  appDir: options.appDir,
@@ -915,6 +927,12 @@ async function renderAppRequestInternal(options) {
915
927
  emitRenderTiming(options, timing, data.status);
916
928
  return data;
917
929
  }
930
+ sourceUsesRequestInput ||= await routeShellsMayUseRequestInput({
931
+ appDir: options.appDir,
932
+ pageFile: matched.route.file,
933
+ serverModuleCacheVersion: options.serverModuleCacheVersion,
934
+ serverSourceFiles: options.serverSourceFiles,
935
+ });
918
936
  await waitForRenderPreload(options, timing);
919
937
  await loadServerRenderArtifacts(options, matched.route.file, timing);
920
938
  phaseStartedAt = renderTimingPhaseStartedAt(timing);
@@ -935,12 +953,11 @@ async function renderAppRequestInternal(options) {
935
953
  });
936
954
  }
937
955
  phaseStartedAt = renderTimingPhaseStartedAt(timing);
938
- const renderedPage = await runWithQueryClient(queryClient, () => runServerModuleWithSlots(output.code, {
956
+ const renderedPage = await runWithQueryClient(queryClient, () => runServerModuleWithSlots(output.code, withTrackedRequest({
939
957
  data,
940
958
  params: matched.params,
941
959
  queryClient,
942
- request: options.request,
943
- }, matched.route.file, options.serverModules, options.serverModuleCacheVersion, options.define, options.vitePlugins, timing, options.importPolicy, options.devServerModuleCacheVersion));
960
+ }, appRequest, trackedRequest), matched.route.file, options.serverModules, options.serverModuleCacheVersion, options.define, options.vitePlugins, timing, options.importPolicy, options.devServerModuleCacheVersion));
944
961
  finishRenderTimingPhase(timing, phaseStartedAt, "pageRenderMs");
945
962
  const pageHtml = renderedPage.html;
946
963
  // Wrap the page (not the full document) with the hydration marker so
@@ -972,12 +989,11 @@ async function renderAppRequestInternal(options) {
972
989
  appDir: options.appDir,
973
990
  pageFile: matched.route.file,
974
991
  html: pageHtmlForLayout,
975
- props: {
992
+ props: withTrackedRequest({
976
993
  data,
977
994
  params: matched.params,
978
995
  queryClient,
979
- request: options.request,
980
- },
996
+ }, appRequest, trackedRequest),
981
997
  slots: renderedPage.slots,
982
998
  serverModules: options.serverModules,
983
999
  serverModuleCacheVersion: options.serverModuleCacheVersion,
@@ -991,11 +1007,10 @@ async function renderAppRequestInternal(options) {
991
1007
  const metadata = await loadComposedRouteMetadata({
992
1008
  appDir: options.appDir,
993
1009
  code: originalCode,
994
- context: {
1010
+ context: withTrackedRequest({
995
1011
  data,
996
1012
  params: matched.params,
997
- request: options.request,
998
- },
1013
+ }, appRequest, trackedRequest),
999
1014
  filename: matched.route.file,
1000
1015
  importPolicy: options.importPolicy,
1001
1016
  routes,
@@ -1028,11 +1043,21 @@ async function renderAppRequestInternal(options) {
1028
1043
  headers: responseHeadersForMetadata(metadata, options.request),
1029
1044
  }), preparedActions.csrfToken, preparedActions.csrfTokenIsNew === true);
1030
1045
  const effectiveCachePolicy = cachePolicy ?? activeRouteCacheContext()?.cachePolicy;
1046
+ const configuredHsts = configuredHstsHeader(metadata?.security);
1047
+ invalidConfiguredHsts = hasInvalidConfiguredHsts(metadata?.security);
1031
1048
  const finalResponse = preparedActions.hasFormActions
1032
1049
  ? withRouteCacheHeader(response, effectiveCachePolicy)
1033
1050
  : await cacheRouteResponse({
1034
1051
  key: cacheKey,
1035
1052
  cache: options.routeCache,
1053
+ // A policy can still arrive from a runtime cacheControl() call made
1054
+ // outside the page source, which is what `mayUseRouteCache` scans.
1055
+ // Without a tracker there is no evidence the render ignored request
1056
+ // headers, so the entry is treated as header dependent.
1057
+ headerDependent: sourceUsesRequestInput ||
1058
+ invalidConfiguredHsts ||
1059
+ (trackedRequest?.requestDependent() ?? true),
1060
+ ...(configuredHsts === undefined ? {} : { strictTransportSecurity: configuredHsts }),
1036
1061
  path: matched.route.path,
1037
1062
  policy: effectiveCachePolicy,
1038
1063
  request: options.request,
@@ -1103,13 +1128,31 @@ async function renderAppRequestInternal(options) {
1103
1128
  emitRenderTiming(options, timing, response.status);
1104
1129
  return response;
1105
1130
  }
1131
+ finally {
1132
+ // Reported from every return path, including the streaming ones, so that a
1133
+ // caller storing by path alone never mistakes an unreported render for a
1134
+ // shareable one. The value stays lazy because a streamed render can still
1135
+ // read request headers while its deferred parts resolve; callers read it
1136
+ // after draining the body.
1137
+ if (options.renderSignals !== undefined) {
1138
+ const tracked = trackedRequest;
1139
+ options.renderSignals.headerDependent = () => sourceUsesRequestInput || invalidConfiguredHsts || (tracked?.requestDependent() ?? true);
1140
+ }
1141
+ }
1106
1142
  })).value;
1107
1143
  }
1108
1144
  async function applyAppRouterResponseHook(response, options) {
1109
1145
  const onResponse = await resolveAppRouterResponseHook(options);
1110
- const hooked = await onResponse?.(response, {
1111
- request: options.request,
1112
- });
1146
+ if (onResponse === undefined) {
1147
+ return response;
1148
+ }
1149
+ const renderSignals = options.renderSignals;
1150
+ const trackedRequest = renderSignals === undefined ? undefined : trackRequestHeaderReads(options.request);
1151
+ const hooked = await onResponse(response, withTrackedRequest({}, trackedRequest?.request ?? options.request, trackedRequest));
1152
+ if (renderSignals !== undefined && trackedRequest !== undefined) {
1153
+ const renderHeaderDependent = renderSignals.headerDependent;
1154
+ renderSignals.headerDependent = () => renderHeaderDependent() || trackedRequest.requestDependent();
1155
+ }
1113
1156
  return hooked instanceof Response ? hooked : response;
1114
1157
  }
1115
1158
  export async function resolveAppRouterResponseHook(options) {
@@ -1934,6 +1977,9 @@ function routeSourceAnalysisFromArtifact(artifact) {
1934
1977
  hasLoader: artifact.hasLoader,
1935
1978
  routeCode: artifact.routeCode,
1936
1979
  streamRoute: artifact.streamRoute,
1980
+ // Older built artifacts predate request-input safety analysis. Treat them
1981
+ // as dependent until rebuilt rather than trusting an absent safety signal.
1982
+ usesRequestInput: artifact.usesRequestInput ?? true,
1937
1983
  usesRuntimeCacheControl: artifact.usesRuntimeCacheControl,
1938
1984
  };
1939
1985
  }
@@ -1962,11 +2008,52 @@ async function analyzeRouteSourceUncached(options) {
1962
2008
  projectRoot: options.appDir,
1963
2009
  source: options.code,
1964
2010
  }),
2011
+ usesRequestInput: routeClosureMayUseRequestInput({
2012
+ filename: options.filename,
2013
+ files: routeSourceFilesForAnalysis(options),
2014
+ projectRoot: options.appDir,
2015
+ source: options.code,
2016
+ }),
1965
2017
  usesRuntimeCacheControl: usesRuntimeCacheControl(options.code),
1966
2018
  };
1967
2019
  }
1968
2020
  function routeSourceFilesForAnalysis(options) {
1969
- return (file) => file === options.filename ? options.code : options.serverSourceFiles?.get(file);
2021
+ return (file) => {
2022
+ const known = file === options.filename ? options.code : options.serverSourceFiles?.get(file);
2023
+ if (known !== undefined) {
2024
+ return known;
2025
+ }
2026
+ const relativeFile = relative(resolve(options.appDir), resolve(file));
2027
+ if (relativeFile === "" || relativeFile === ".." || relativeFile.startsWith(`..${sep}`)) {
2028
+ return undefined;
2029
+ }
2030
+ try {
2031
+ return readFileSync(file, "utf8");
2032
+ }
2033
+ catch {
2034
+ return undefined;
2035
+ }
2036
+ };
2037
+ }
2038
+ async function routeShellsMayUseRequestInput(options) {
2039
+ const shells = await shellFilesForPage(options.appDir, options.pageFile, options.serverModuleCacheVersion);
2040
+ for (const shell of shells) {
2041
+ const source = await readServerSourceFile(shell.file, options.serverModuleCacheVersion, options.serverSourceFiles);
2042
+ if (routeClosureMayUseRequestInput({
2043
+ filename: shell.file,
2044
+ files: routeSourceFilesForAnalysis({
2045
+ appDir: options.appDir,
2046
+ code: source,
2047
+ filename: shell.file,
2048
+ serverSourceFiles: options.serverSourceFiles,
2049
+ }),
2050
+ projectRoot: options.appDir,
2051
+ source,
2052
+ })) {
2053
+ return true;
2054
+ }
2055
+ }
2056
+ return false;
1970
2057
  }
1971
2058
  async function runServerModule(code, props, sourcefile, serverModules, serverModuleCacheVersion, define, vitePlugins) {
1972
2059
  const component = await loadServerComponent(code, sourcefile, serverModules, serverModuleCacheVersion, define, vitePlugins);
@@ -2215,12 +2302,11 @@ function localServerSourceImportCandidates(base) {
2215
2302
  return candidates;
2216
2303
  }
2217
2304
  async function runServerStreamModuleWithLoading(code, options) {
2218
- const loadingProps = {
2305
+ const loadingProps = withTrackedRequest({
2219
2306
  data: undefined,
2220
2307
  params: options.params,
2221
2308
  queryClient: options.queryClient,
2222
- request: options.request,
2223
- };
2309
+ }, options.request, options.trackedRequest);
2224
2310
  const layoutShells = await layoutShellsForPage(options.appDir, options.pageFile, loadingProps, {}, options.serverModules, options.serverModuleCacheVersion, options.serverSourceFiles, options.clientRouteInferenceCache, options.define, options.vitePlugins, options.importPolicy);
2225
2311
  const loadingHtml = await renderServerFileToHtml(options.loadingFile, loadingProps, options.serverModules, options.serverModuleCacheVersion, options.serverSourceFiles, options.define, options.vitePlugins);
2226
2312
  const marker = options.clientRoute
@@ -2247,12 +2333,11 @@ async function runServerStreamModuleWithLoading(code, options) {
2247
2333
  }
2248
2334
  sink.append(marker?.prefix ?? "");
2249
2335
  renderVisibleOutOfOrderBoundary(sink, "mreact-route", options.data, async (boundarySink, data) => {
2250
- await appendServerStreamModule(code, boundarySink, {
2336
+ await appendServerStreamModule(code, boundarySink, withTrackedRequest({
2251
2337
  data,
2252
2338
  params: options.params,
2253
2339
  queryClient: options.queryClient,
2254
- request: options.request,
2255
- }, options.pageFile, options.serverModules, options.serverModuleCacheVersion, options.define, options.vitePlugins, options.importPolicy);
2340
+ }, options.request, options.trackedRequest), options.pageFile, options.serverModules, options.serverModuleCacheVersion, options.define, options.vitePlugins, options.importPolicy);
2256
2341
  }, {
2257
2342
  placeholder(boundarySink) {
2258
2343
  boundarySink.append(loadingHtml);
@@ -2441,12 +2526,6 @@ async function renderShellPrefixSuffix(appDir, shell, props, slotContext, server
2441
2526
  const cacheKey = serverModuleCacheVersion === undefined || hasNamedSlots || shell.kind === "template"
2442
2527
  ? undefined
2443
2528
  : `${appDir}\0${shell.file}\0${serverModuleCacheVersion}\0${importPolicyCacheKey(importPolicy)}\0${viteDefineCacheKey(define)}\0${vitePluginsCacheKey(vitePlugins)}`;
2444
- if (cacheKey !== undefined) {
2445
- const cached = readRouterRuntimeCacheEntry(renderedShellCache, cacheKey, renderedShellCacheCounters);
2446
- if (cached !== undefined && cached !== "impure") {
2447
- return cached;
2448
- }
2449
- }
2450
2529
  const staticEntry = cacheKey === undefined ? undefined : shellStaticRenderCache.get(cacheKey);
2451
2530
  const loadedStaticEntry = staticEntry ??
2452
2531
  (await loadShellStaticRenderEntry({
@@ -2472,33 +2551,6 @@ async function renderShellPrefixSuffix(appDir, shell, props, slotContext, server
2472
2551
  hasOutOfOrderBoundary: loadedStaticEntry.hasOutOfOrderBoundary,
2473
2552
  };
2474
2553
  addRenderTimingPhaseDuration(timing, phaseStartedAt, "layoutSlotSplitMs");
2475
- const shellCacheKey = loadedStaticEntry.shellUsesAwait ? undefined : cacheKey;
2476
- const cached = shellCacheKey !== undefined
2477
- ? readRouterRuntimeCacheEntry(renderedShellCache, shellCacheKey, renderedShellCacheCounters)
2478
- : undefined;
2479
- // Detect purity: a zero-arg component cannot depend on props. The
2480
- // markShellBoundary + splitLayoutSlot output is then constant for
2481
- // the (appDir, shellFile, version) tuple. We only set the cache
2482
- // entry on the first request that observes the function arity; on
2483
- // an "impure" tag we never overwrite it.
2484
- if (shellCacheKey !== undefined && cached !== "impure") {
2485
- if (loadedStaticEntry.component.length === 0) {
2486
- if (renderedShellCache.size >= MAX_RENDERED_SHELL_CACHE_ENTRIES) {
2487
- const oldestKey = renderedShellCache.keys().next().value;
2488
- if (oldestKey !== undefined) {
2489
- renderedShellCache.delete(oldestKey);
2490
- renderedShellCacheCounters.evictions += 1;
2491
- }
2492
- }
2493
- renderedShellCache.set(shellCacheKey, rendered);
2494
- }
2495
- else {
2496
- // Impure — stamp the cache so subsequent lookups short-circuit
2497
- // without re-checking arity. We still run the per-request
2498
- // render path above so the props are honoured.
2499
- renderedShellCache.set(shellCacheKey, "impure");
2500
- }
2501
- }
2502
2554
  return rendered;
2503
2555
  }
2504
2556
  async function loadShellStaticRenderEntry(options) {