vinext 1.4.2 → 1.4.3

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 (88) hide show
  1. package/README.md +4 -1
  2. package/dist/build/prerender-paths.d.ts +11 -1
  3. package/dist/build/prerender-paths.js +36 -10
  4. package/dist/cli.js +1 -1
  5. package/dist/client/dev-error-overlay.d.ts +2 -1
  6. package/dist/client/dev-error-overlay.js +8 -3
  7. package/dist/config/config-matchers.js +6 -2
  8. package/dist/config/next-config.d.ts +9 -0
  9. package/dist/entries/app-rsc-entry.js +24 -12
  10. package/dist/entries/pages-server-entry.js +17 -4
  11. package/dist/index.js +22 -25
  12. package/dist/init-platform.js +2 -2
  13. package/dist/init.js +42 -5
  14. package/dist/server/api-handler.js +7 -3
  15. package/dist/server/app-browser-entry.js +16 -37
  16. package/dist/server/app-browser-error.d.ts +1 -1
  17. package/dist/server/app-browser-error.js +13 -4
  18. package/dist/server/app-browser-supplemental-refresh.d.ts +13 -0
  19. package/dist/server/app-browser-supplemental-refresh.js +36 -4
  20. package/dist/server/app-middleware.d.ts +2 -0
  21. package/dist/server/app-middleware.js +1 -0
  22. package/dist/server/app-page-boundary-render.js +2 -0
  23. package/dist/server/app-page-dispatch.d.ts +1 -0
  24. package/dist/server/app-page-dispatch.js +5 -1
  25. package/dist/server/app-page-element-builder.d.ts +2 -0
  26. package/dist/server/app-page-element-builder.js +2 -1
  27. package/dist/server/app-page-route-wiring.d.ts +7 -5
  28. package/dist/server/app-page-route-wiring.js +3 -1
  29. package/dist/server/app-pages-bridge.d.ts +6 -0
  30. package/dist/server/app-pages-bridge.js +8 -3
  31. package/dist/server/app-request-stage-independent-entry.js +2 -3
  32. package/dist/server/app-router-entry.js +2 -3
  33. package/dist/server/app-rsc-cache-busting.d.ts +8 -0
  34. package/dist/server/app-rsc-cache-busting.js +13 -1
  35. package/dist/server/app-rsc-combined-handler.js +2 -1
  36. package/dist/server/app-rsc-handler.d.ts +4 -0
  37. package/dist/server/app-rsc-handler.js +39 -22
  38. package/dist/server/app-rsc-response-finalizer.d.ts +15 -3
  39. package/dist/server/app-rsc-response-finalizer.js +51 -7
  40. package/dist/server/app-ssr-entry.js +8 -0
  41. package/dist/server/config-headers.d.ts +7 -0
  42. package/dist/server/config-headers.js +20 -1
  43. package/dist/server/image-animation.d.ts +9 -0
  44. package/dist/server/image-animation.js +118 -0
  45. package/dist/server/image-optimization.d.ts +25 -5
  46. package/dist/server/image-optimization.js +305 -21
  47. package/dist/server/image-source-reader.d.ts +34 -0
  48. package/dist/server/image-source-reader.js +126 -0
  49. package/dist/server/metadata-route-response.d.ts +6 -1
  50. package/dist/server/metadata-route-response.js +22 -4
  51. package/dist/server/middleware-matcher.d.ts +1 -0
  52. package/dist/server/middleware-matcher.js +1 -1
  53. package/dist/server/middleware-runtime.d.ts +5 -0
  54. package/dist/server/middleware-runtime.js +48 -15
  55. package/dist/server/middleware.d.ts +1 -1
  56. package/dist/server/middleware.js +2 -1
  57. package/dist/server/nitro-request-target-plugin.d.ts +17 -0
  58. package/dist/server/nitro-request-target-plugin.js +22 -0
  59. package/dist/server/open-redirect.d.ts +2 -1
  60. package/dist/server/open-redirect.js +2 -1
  61. package/dist/server/pages-client-assets.d.ts +1 -0
  62. package/dist/server/pages-i18n.d.ts +7 -0
  63. package/dist/server/pages-i18n.js +14 -1
  64. package/dist/server/pages-node-compat.js +7 -3
  65. package/dist/server/pages-page-handler.js +4 -4
  66. package/dist/server/pages-request-pipeline.d.ts +9 -0
  67. package/dist/server/pages-request-pipeline.js +29 -10
  68. package/dist/server/pages-request-stage-entry.js +7 -2
  69. package/dist/server/pages-router-entry.js +3 -0
  70. package/dist/server/prod-server.d.ts +1 -1
  71. package/dist/server/prod-server.js +48 -54
  72. package/dist/server/srvx-middleware-origin.d.ts +13 -0
  73. package/dist/server/srvx-middleware-origin.js +19 -0
  74. package/dist/server/srvx-request-target.d.ts +12 -0
  75. package/dist/server/srvx-request-target.js +20 -0
  76. package/dist/server/static-file-cache.js +10 -22
  77. package/dist/server/worker-image-optimization.d.ts +8 -1
  78. package/dist/server/worker-image-optimization.js +25 -6
  79. package/dist/server/worker-revalidation-context.js +1 -0
  80. package/dist/shims/error-boundary.d.ts +6 -2
  81. package/dist/shims/error-boundary.js +4 -2
  82. package/dist/shims/request-context.d.ts +3 -1
  83. package/dist/shims/url-utils.js +6 -1
  84. package/dist/utils/client-entry-manifest.d.ts +5 -0
  85. package/dist/utils/client-entry-manifest.js +1 -0
  86. package/dist/utils/client-runtime-metadata.d.ts +5 -0
  87. package/dist/utils/client-runtime-metadata.js +33 -7
  88. package/package.json +1 -1
package/dist/init.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import path, { toSlash } from "./deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
2
  import { detectPackageManager, detectPackageManagerName, detectProject, ensureESModule, findViteConfigPath, hasViteConfig, renameCJSConfigs } from "./utils/project.js";
3
3
  import { compactResourceName, cssModulesConfigSource, findConfigKeepingWorkerName, setupCloudflarePlatform, updateViteConfigForCssModules, usesCommonJsViteConfig, validateCloudflarePlatformSetup } from "./init-cloudflare.js";
4
- import { getReactUpgradeDeps } from "./utils/react-version.js";
4
+ import { findPackageVersion, getReactUpgradeDeps } from "./utils/react-version.js";
5
5
  import { getPluginReactUpgradeDeps } from "./utils/plugin-react-version.js";
6
+ import { createRequire } from "node:module";
6
7
  import fs from "node:fs";
7
8
  import { spawn, spawnSync } from "node:child_process";
8
9
  //#region src/init.ts
@@ -297,6 +298,26 @@ function addDependencyEntries(root, deps, { dev }) {
297
298
  if (added.length > 0) fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
298
299
  return added;
299
300
  }
301
+ /** The spec in the manifest sections `isDepInstalled` reads. */
302
+ function readDeclaredDependency(root, name) {
303
+ try {
304
+ const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf-8"));
305
+ return pkg.dependencies?.[name] ?? pkg.devDependencies?.[name] ?? pkg.peerDependencies?.[name];
306
+ } catch {
307
+ return;
308
+ }
309
+ }
310
+ /** The declared spec, or the installed version of a package hoisted from elsewhere. */
311
+ function readCurrentDependencyVersion(root, name) {
312
+ const declared = readDeclaredDependency(root, name);
313
+ if (declared !== void 0) return declared;
314
+ try {
315
+ const resolved = createRequire(path.join(root, "package.json")).resolve(name);
316
+ return findPackageVersion(resolved, name) ?? void 0;
317
+ } catch {
318
+ return;
319
+ }
320
+ }
300
321
  /**
301
322
  * Check if react/react-dom need upgrading for react-server-dom-webpack compatibility.
302
323
  *
@@ -578,12 +599,20 @@ async function init(options) {
578
599
  let dependencyInstallNeedsApproval = false;
579
600
  const dependencyEntriesAdded = [];
580
601
  const devDependencyEntriesAdded = [];
602
+ const upgradedFrom = /* @__PURE__ */ new Map();
603
+ const recordUpgrades = (deps) => {
604
+ for (const dep of deps) {
605
+ const { name } = parseDependencySpecifier(dep);
606
+ upgradedFrom.set(name, readCurrentDependencyVersion(root, name));
607
+ }
608
+ };
581
609
  if (isApp) {
582
610
  const reactUpgrade = shouldInstall ? [.../* @__PURE__ */ new Set([...getReactUpgradeDeps(root), ...getReactUpgradeDeps(root, {
583
611
  fromManifest: true,
584
612
  installing: true
585
613
  })])] : getReactUpgradeDeps(root, { fromManifest: true });
586
614
  if (reactUpgrade.length > 0) {
615
+ recordUpgrades(reactUpgrade);
587
616
  console.log(` ${terminalStyle.cyan(terminalStyle.bold("Upgrading dependencies:"))}`);
588
617
  console.log(formatList(reactUpgrade.map((dep) => dep.replace(/@latest$/, "")), " "));
589
618
  try {
@@ -601,6 +630,7 @@ async function init(options) {
601
630
  }
602
631
  const pluginReactUpgrade = getPluginReactUpgradeDeps(root);
603
632
  if (pluginReactUpgrade.length > 0) {
633
+ recordUpgrades(pluginReactUpgrade);
604
634
  console.log(` ${terminalStyle.cyan(terminalStyle.bold("Upgrading devDependencies:"))}`);
605
635
  console.log(formatList(pluginReactUpgrade.map((dep) => dep.replace(/@latest$/, "")), " "));
606
636
  try {
@@ -654,13 +684,20 @@ async function init(options) {
654
684
  if (pmName === "pnpm" && shouldInstall && !dependencyInstallNeedsApproval && !options._exec && hasAutomaticallyIgnoredBuilds(inspectPnpmIgnoredBuilds(root))) dependencyInstallNeedsApproval = true;
655
685
  else if (pmName === "pnpm" && shouldInstall && !dependencyInstallNeedsApproval && options._inspectPnpmIgnoredBuilds && hasAutomaticallyIgnoredBuilds(options._inspectPnpmIgnoredBuilds(root))) dependencyInstallNeedsApproval = true;
656
686
  console.log(` ${terminalStyle.green(terminalStyle.bold("vinext init complete!"))}\n`);
657
- if (dependencyEntriesAdded.length > 0) {
687
+ const addedDependencies = dependencyEntriesAdded.filter((name) => !upgradedFrom.has(name));
688
+ const addedDevDependencies = devDependencyEntriesAdded.filter((name) => !upgradedFrom.has(name));
689
+ if (addedDependencies.length > 0) {
658
690
  console.log(` ${terminalStyle.green("✓")} Added dependencies to dependencies:`);
659
- console.log(formatList(dependencyEntriesAdded, " "));
691
+ console.log(formatList(addedDependencies, " "));
660
692
  }
661
- if (devDependencyEntriesAdded.length > 0) {
693
+ if (addedDevDependencies.length > 0) {
662
694
  console.log(` ${terminalStyle.green("✓")} Added dependencies to devDependencies:`);
663
- console.log(formatList(devDependencyEntriesAdded, " "));
695
+ console.log(formatList(addedDevDependencies, " "));
696
+ }
697
+ for (const [name, from] of upgradedFrom) {
698
+ const to = readDeclaredDependency(root, name);
699
+ if (to === void 0 || to === from) continue;
700
+ console.log(` ${terminalStyle.green("✓")} Upgraded ${name} ${from ?? "(not installed)"} \u2192 ${to}`);
664
701
  }
665
702
  if (dependencyInstallNeedsApproval) console.log(` ${terminalStyle.yellow("!")} Dependency installation is waiting for build-script approval`);
666
703
  if (addedTypeModule) console.log(` ${terminalStyle.green("✓")} Added "type": "module" to package.json`);
@@ -186,10 +186,14 @@ function enhanceApiObjects(req, res, query, body, trustedRevalidateOrigin, allow
186
186
  return this;
187
187
  },
188
188
  json(data) {
189
- this.setHeader("Content-Type", "application/json");
189
+ this.setHeader("Content-Type", "application/json; charset=utf-8");
190
190
  this.end(JSON.stringify(data));
191
191
  },
192
192
  send(data) {
193
+ if (data === null || data === void 0) {
194
+ this.end();
195
+ return;
196
+ }
193
197
  if (this.statusCode === 204 || this.statusCode === 304) {
194
198
  this.removeHeader("Content-Type");
195
199
  this.removeHeader("Content-Length");
@@ -208,8 +212,8 @@ function enhanceApiObjects(req, res, query, body, trustedRevalidateOrigin, allow
208
212
  this.end(data);
209
213
  return;
210
214
  }
211
- if (typeof data === "object" && data !== null) {
212
- this.setHeader("Content-Type", "application/json");
215
+ if (typeof data === "object" || typeof data === "number" || typeof data === "boolean") {
216
+ this.setHeader("Content-Type", "application/json; charset=utf-8");
213
217
  this.end(JSON.stringify(data));
214
218
  } else {
215
219
  if (!this.getHeader("Content-Type")) this.setHeader("Content-Type", "text/plain");
@@ -1,8 +1,5 @@
1
- import { addBasePathToPathname, stripBasePath } from "../utils/base-path.js";
2
1
  import { VINEXT_MOUNTED_LAYOUTS_HEADER, VINEXT_PARAMS_HEADER, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_REDIRECT_TYPE_HEADER } from "./headers.js";
3
- import { splitPathnameForRouteMatch } from "../routing/utils.js";
4
2
  import { AppRouterContext } from "../shims/internal/app-router-context.js";
5
- import { matchRoutePattern } from "../routing/route-pattern.js";
6
3
  import { installWindowNext, setWindowNextInternalSourcePage } from "../client/window-next.js";
7
4
  import { retryScrollTo, scrollToHashTargetOnNextFrame } from "../shims/hash-scroll.js";
8
5
  import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, createRscRequestHeaders, createRscRequestUrl, formatRscRequestPath, isRscCompatibilityIdCompatible } from "./app-rsc-cache-busting.js";
@@ -32,7 +29,7 @@ import { COMMITTED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN, FRESH_APP_NAVIGATION_PAY
32
29
  import { clearHardNavigationLoopGuard, createAppBrowserNavigationController, createBasePathStrippedPathAndSearch, navigateDocumentDirectly } from "./app-browser-navigation-controller.js";
33
30
  import { createAppRouteTree } from "./app-route-tree.js";
34
31
  import { shouldRecoverSamePathSearchCommitOnResponseCompletion } from "./app-browser-navigation-response.js";
35
- import { createSupplementalRefreshCoordinator, mergeRefreshedParallelSlot, requireCompleteSupplementalRefresh, resolvePersistedSourcePageRefreshes, resolveServerActionSupplementalRefresh, resolveSupplementalRefreshes } from "./app-browser-supplemental-refresh.js";
32
+ import { createSupplementalRefreshCoordinator, mergeRefreshedParallelSlot, requireCompleteSupplementalRefresh, resolvePersistedRefreshInterceptions, resolvePersistedSourcePageRefreshes, resolveServerActionSupplementalRefresh, resolveSupplementalRefreshes } from "./app-browser-supplemental-refresh.js";
36
33
  import { createAppBrowserNavigationAbortCoordinator } from "./app-browser-navigation-abort.js";
37
34
  import { consumeInitialFormState, createVinextHydrateRootOptions, hydrateRootInTransition, resolveFetchedHydrationLocation } from "./app-browser-hydration.js";
38
35
  import { AppBrowserHistoryController } from "./app-browser-history-controller.js";
@@ -278,36 +275,6 @@ function clearDynamicClientNavigationCaches() {
278
275
  optimisticRouteTemplates.clear();
279
276
  historyController.invalidateRestorableClientState();
280
277
  }
281
- function getMatchedUrlPathname(matchedUrl) {
282
- return new URL(matchedUrl, "https://vinext.local").pathname;
283
- }
284
- function resolvePersistedRefreshInterceptions(state, routeManifest, refreshUrl, requestInterceptionContext) {
285
- if (routeManifest === null) return [];
286
- const refreshPathname = stripBasePath(refreshUrl.pathname, __basePath);
287
- const refreshPathParts = splitPathnameForRouteMatch(refreshPathname);
288
- const resolved = [];
289
- for (const binding of state.slotBindings) {
290
- if (binding.state !== "active" || !binding.activeRouteId) continue;
291
- if (!binding.interceptionId || !binding.interceptionSourceMatchedUrl) continue;
292
- const activeRoute = AppElementsWire.parseElementKey(binding.activeRouteId);
293
- if (activeRoute?.kind !== "route") continue;
294
- const match = routeManifest.segmentGraph.interceptions.get(binding.interceptionId);
295
- if (!match) continue;
296
- const retainedInterception = state.interception?.slotId === binding.slotId && matchRoutePattern(splitPathnameForRouteMatch(getMatchedUrlPathname(state.interception.targetMatchedUrl)), match.targetPatternParts) !== null ? state.interception : null;
297
- const targetPathname = retainedInterception?.targetMatchedUrl ?? activeRoute.path;
298
- const targetParts = splitPathnameForRouteMatch(getMatchedUrlPathname(targetPathname));
299
- if (requestInterceptionContext !== null && state.interception?.slotId === binding.slotId && matchRoutePattern(refreshPathParts, match.targetPatternParts) !== null && matchRoutePattern(targetParts, match.targetPatternParts) !== null) continue;
300
- const targetUrl = new URL(targetPathname, refreshUrl);
301
- targetUrl.pathname = addBasePathToPathname(targetUrl.pathname, __basePath);
302
- targetUrl.search = refreshUrl.search;
303
- resolved.push({
304
- interception: match,
305
- interceptionContext: retainedInterception?.sourceMatchedUrl ?? binding.interceptionSourceMatchedUrl,
306
- targetPathname: `${targetUrl.pathname}${targetUrl.search}`
307
- });
308
- }
309
- return resolved;
310
- }
311
278
  async function fetchPersistedInterceptedSlotRefresh(options) {
312
279
  const headers = createRscRequestHeaders({
313
280
  interceptionContext: options.interceptionContext,
@@ -482,7 +449,13 @@ async function commitSameUrlNavigatePayload(nextElements, actionInitiation, retu
482
449
  if (revalidation !== "none" && routeManifestLoaded) {
483
450
  const refreshUrl = new URL(actionInitiation.href);
484
451
  const requestInterceptionContext = resolveInterceptionContextFromPreviousNextUrl(actionInitiation.routerState.previousNextUrl, __basePath);
485
- const persistedInterceptions = resolvePersistedRefreshInterceptions(actionInitiation.routerState, getBrowserRouteManifest(), refreshUrl, requestInterceptionContext);
452
+ const persistedInterceptions = resolvePersistedRefreshInterceptions({
453
+ basePath: __basePath,
454
+ refreshUrl,
455
+ requestInterceptionContext,
456
+ routeManifest: getBrowserRouteManifest(),
457
+ state: actionInitiation.routerState
458
+ });
486
459
  const sourcePageRefreshes = resolvePersistedSourcePageRefreshes({
487
460
  basePath: __basePath,
488
461
  refreshUrl,
@@ -1094,7 +1067,7 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
1094
1067
  const formState = consumeInitialFormState(getVinextBrowserGlobal());
1095
1068
  const hydrateRootOptions = import.meta.env.DEV && devErrorOverlay ? createVinextHydrateRootOptions({
1096
1069
  formState,
1097
- onCaughtError: invalidateOnCaughtError(createDevOnCaughtError(devErrorOverlay.devOnCaughtError, onUncaughtError)),
1070
+ onCaughtError: invalidateOnCaughtError(createDevOnCaughtError(devErrorOverlay.devOnCaughtError, onUncaughtError, devErrorOverlay.reportRootErrorBoundaryCatch)),
1098
1071
  onUncaughtError
1099
1072
  }) : createVinextHydrateRootOptions({
1100
1073
  formState,
@@ -1195,7 +1168,13 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
1195
1168
  const requestInterceptionContext = requestState.interceptionContext;
1196
1169
  const requestPreviousNextUrl = requestState.previousNextUrl;
1197
1170
  const shouldSupplementVisibleBranches = navigationKind === "refresh" || navigationKind === "traverse" && !reuseCurrentBfcacheIds;
1198
- const persistedRefreshInterceptions = navigationKind === "refresh" ? resolvePersistedRefreshInterceptions(navigationInitiationState, getBrowserRouteManifest(), url, requestInterceptionContext) : [];
1171
+ const persistedRefreshInterceptions = navigationKind === "refresh" ? resolvePersistedRefreshInterceptions({
1172
+ basePath: __basePath,
1173
+ refreshUrl: url,
1174
+ requestInterceptionContext,
1175
+ routeManifest: getBrowserRouteManifest(),
1176
+ state: navigationInitiationState
1177
+ }) : [];
1199
1178
  const persistedSourcePageRefreshes = shouldSupplementVisibleBranches ? resolvePersistedSourcePageRefreshes({
1200
1179
  activeRoutePaths: navigationKind === "traverse" ? readHistoryStateActiveRoutePaths(activeTraversalIntent?.historyState) ?? [] : void 0,
1201
1180
  basePath: __basePath,
@@ -8,7 +8,7 @@ export declare function createOnUncaughtError(options?: {
8
8
  coerceToError?: boolean;
9
9
  }): HydrateRootErrorHandler;
10
10
  export declare function createProdOnCaughtError(onImplicitRootError: HydrateRootErrorHandler): HydrateRootErrorHandler;
11
- export declare function createDevOnCaughtError(onCaughtError: HydrateRootErrorHandler, onImplicitRootError: HydrateRootErrorHandler): HydrateRootErrorHandler;
11
+ export declare function createDevOnCaughtError(onCaughtError: HydrateRootErrorHandler, onImplicitRootError: HydrateRootErrorHandler, onRootErrorBoundaryCatch?: () => void): HydrateRootErrorHandler;
12
12
  export declare function createProdOnRecoverableError(onReportedError: () => void): (error: unknown) => void;
13
13
  //#endregion
14
14
  export {};
@@ -2,10 +2,18 @@ import { isUnknownRecord } from "../utils/record.js";
2
2
  import { isNavigationSignalError } from "../utils/navigation-signal.js";
3
3
  import { isBailoutToCSRError } from "../shims/navigation-errors.js";
4
4
  //#region src/server/app-browser-error.ts
5
- function isImplicitRootErrorBoundary(errorInfo) {
6
- if (!isUnknownRecord(errorInfo.errorBoundary)) return false;
5
+ function getErrorBoundaryProps(errorInfo) {
6
+ if (!isUnknownRecord(errorInfo.errorBoundary)) return {};
7
7
  const props = errorInfo.errorBoundary.props;
8
- return isUnknownRecord(props) && props.isImplicitRootErrorBoundary === true;
8
+ return isUnknownRecord(props) ? props : {};
9
+ }
10
+ function isImplicitRootErrorBoundary(errorInfo) {
11
+ return getErrorBoundaryProps(errorInfo).isImplicitRootErrorBoundary === true;
12
+ }
13
+ function isRootErrorBoundary(errorInfo) {
14
+ const props = getErrorBoundaryProps(errorInfo);
15
+ if (props.isGlobalErrorBoundary === true) return true;
16
+ return props.isImplicitRootErrorBoundary === true && props.wrapsGlobalErrorBoundary !== true;
9
17
  }
10
18
  function logCaughtError(error, errorInfo) {
11
19
  console.error(error);
@@ -38,8 +46,9 @@ function createProdOnCaughtError(onImplicitRootError) {
38
46
  logCaughtError(error, errorInfo);
39
47
  };
40
48
  }
41
- function createDevOnCaughtError(onCaughtError, onImplicitRootError) {
49
+ function createDevOnCaughtError(onCaughtError, onImplicitRootError, onRootErrorBoundaryCatch) {
42
50
  return (error, errorInfo) => {
51
+ if (!isNavigationSignalError(error) && isRootErrorBoundary(errorInfo)) onRootErrorBoundaryCatch?.();
43
52
  if (isImplicitRootErrorBoundary(errorInfo)) {
44
53
  onImplicitRootError(error, errorInfo);
45
54
  return;
@@ -1,3 +1,4 @@
1
+ import { BrowserRouteManifest, BrowserRouteManifestInterception } from "../routing/browser-route-manifest.js";
1
2
  import { AppElements } from "./app-elements-wire.js";
2
3
  import "./app-elements.js";
3
4
  import { AppRouterState } from "./app-browser-state.js";
@@ -31,6 +32,18 @@ export declare function resolvePersistedSourcePageRefreshes(options: {
31
32
  refreshUrl: URL;
32
33
  state: Pick<AppRouterState, "previousNextUrl" | "slotBindings">;
33
34
  }): string[];
35
+ export type PersistedRefreshInterception = {
36
+ interception: BrowserRouteManifestInterception;
37
+ interceptionContext: string;
38
+ targetPathname: string;
39
+ };
40
+ export declare function resolvePersistedRefreshInterceptions(options: {
41
+ basePath: string;
42
+ refreshUrl: URL;
43
+ requestInterceptionContext: string | null;
44
+ routeManifest: BrowserRouteManifest | null;
45
+ state: Pick<AppRouterState, "interception" | "slotBindings">;
46
+ }): PersistedRefreshInterception[];
34
47
  export declare function createSupplementalRefreshCoordinator(): {
35
48
  abortAll(): void;
36
49
  begin(options: {
@@ -1,6 +1,7 @@
1
- import { addBasePathToPathname, removeTrailingSlash, stripBasePath } from "../utils/base-path.js";
2
- import { normalizePathnameForRouteMatch } from "../routing/utils.js";
1
+ import { removeTrailingSlash, restoreBasePath, stripBasePath } from "../utils/base-path.js";
2
+ import { normalizePathnameForRouteMatch, splitPathnameForRouteMatch } from "../routing/utils.js";
3
3
  import { normalizePath } from "./normalize-path.js";
4
+ import { matchRoutePattern } from "../routing/route-pattern.js";
4
5
  import { APP_BFCACHE_SEGMENT_IDENTITIES_KEY, APP_INTERCEPTION_CONTEXT_KEY, APP_INTERCEPTION_KEY, APP_SLOT_BINDINGS_KEY, AppElementsWire, normalizeAppElementsSlotBindings } from "./app-elements-wire.js";
5
6
  import "./app-elements.js";
6
7
  import { isNestedBfcacheSlotSegmentIdFor } from "./bfcache-identity.js";
@@ -131,7 +132,7 @@ function resolvePersistedSourcePageRefreshes(options) {
131
132
  });
132
133
  for (const activeRoutePath of activeRoutePaths) {
133
134
  if (activeRoutePath !== refreshRoutePathname && (refreshRoutePathname === "/" || activeRoutePath.startsWith(refreshRoutePathname + "/"))) continue;
134
- const pathname = addBasePathToPathname(activeRoutePath, options.basePath);
135
+ const pathname = restoreBasePath(activeRoutePath, options.basePath);
135
136
  if (sourceUrlsByPathname.has(pathname)) continue;
136
137
  const sourceUrl = new URL(pathname, options.refreshUrl);
137
138
  sourceUrl.search = options.refreshUrl.search;
@@ -145,6 +146,37 @@ function resolvePersistedSourcePageRefreshes(options) {
145
146
  }
146
147
  return resolved;
147
148
  }
149
+ function getMatchedUrlPathname(matchedUrl) {
150
+ return new URL(matchedUrl, "https://vinext.local").pathname;
151
+ }
152
+ function resolvePersistedRefreshInterceptions(options) {
153
+ const { basePath, refreshUrl, requestInterceptionContext, routeManifest, state } = options;
154
+ if (routeManifest === null) return [];
155
+ const refreshPathname = stripBasePath(refreshUrl.pathname, basePath);
156
+ const refreshPathParts = splitPathnameForRouteMatch(refreshPathname);
157
+ const resolved = [];
158
+ for (const binding of state.slotBindings) {
159
+ if (binding.state !== "active" || !binding.activeRouteId) continue;
160
+ if (!binding.interceptionId || !binding.interceptionSourceMatchedUrl) continue;
161
+ const activeRoute = AppElementsWire.parseElementKey(binding.activeRouteId);
162
+ if (activeRoute?.kind !== "route") continue;
163
+ const match = routeManifest.segmentGraph.interceptions.get(binding.interceptionId);
164
+ if (!match) continue;
165
+ const retainedInterception = state.interception?.slotId === binding.slotId && matchRoutePattern(splitPathnameForRouteMatch(getMatchedUrlPathname(state.interception.targetMatchedUrl)), match.targetPatternParts) !== null ? state.interception : null;
166
+ const targetPathname = retainedInterception?.targetMatchedUrl ?? activeRoute.path;
167
+ const targetParts = splitPathnameForRouteMatch(getMatchedUrlPathname(targetPathname));
168
+ if (requestInterceptionContext !== null && state.interception?.slotId === binding.slotId && matchRoutePattern(refreshPathParts, match.targetPatternParts) !== null && matchRoutePattern(targetParts, match.targetPatternParts) !== null) continue;
169
+ const targetUrl = new URL(targetPathname, refreshUrl);
170
+ targetUrl.pathname = restoreBasePath(targetUrl.pathname, basePath);
171
+ targetUrl.search = refreshUrl.search;
172
+ resolved.push({
173
+ interception: match,
174
+ interceptionContext: retainedInterception?.sourceMatchedUrl ?? binding.interceptionSourceMatchedUrl,
175
+ targetPathname: `${targetUrl.pathname}${targetUrl.search}`
176
+ });
177
+ }
178
+ return resolved;
179
+ }
148
180
  function createSupplementalRefreshCoordinator() {
149
181
  const controllers = /* @__PURE__ */ new Set();
150
182
  return {
@@ -202,4 +234,4 @@ async function resolveSupplementalRefreshes(options) {
202
234
  }
203
235
  }
204
236
  //#endregion
205
- export { SupplementalRefreshError, createSupplementalRefreshCoordinator, mergeRefreshedParallelSlot, requireCompleteSupplementalRefresh, resolvePersistedSourcePageRefreshes, resolveServerActionSupplementalRefresh, resolveSupplementalRefreshes };
237
+ export { SupplementalRefreshError, createSupplementalRefreshCoordinator, mergeRefreshedParallelSlot, requireCompleteSupplementalRefresh, resolvePersistedRefreshInterceptions, resolvePersistedSourcePageRefreshes, resolveServerActionSupplementalRefresh, resolveSupplementalRefreshes };
@@ -26,6 +26,8 @@ export type ApplyAppMiddlewareOptions = {
26
26
  middlewareRequest?: Request;
27
27
  module: MiddlewareModule;
28
28
  request: Request;
29
+ /** Forwarded to `executeMiddleware`. */
30
+ restoreLocaleRootTrailingSlash?: boolean;
29
31
  validateExternalRewriteRequest?: () => Promise<Response | null>;
30
32
  /**
31
33
  * Forwarded to `executeMiddleware` so the NextRequest exposes a NextURL with
@@ -203,6 +203,7 @@ async function applyAppMiddleware(options) {
203
203
  },
204
204
  requestBodyAlreadyIsolated: true,
205
205
  request: middlewareRequest,
206
+ restoreLocaleRootTrailingSlash: options.restoreLocaleRootTrailingSlash,
206
207
  skipTrailingSlashRedirect: options.skipTrailingSlashRedirect,
207
208
  trailingSlash: options.trailingSlash
208
209
  });
@@ -85,8 +85,10 @@ function wrapRenderedBoundaryElement(options) {
85
85
  renderErrorBoundary(GlobalErrorComponent, children) {
86
86
  return createElement(GlobalErrorBoundary, {
87
87
  fallback: DEFAULT_GLOBAL_ERROR_COMPONENT,
88
+ wrapsGlobalErrorBoundary: true,
88
89
  children: createElement(ErrorBoundary, {
89
90
  fallback: GlobalErrorComponent,
91
+ isGlobalErrorBoundary: true,
90
92
  children
91
93
  })
92
94
  });
@@ -166,6 +166,7 @@ export type DispatchAppPageOptions<TRoute extends AppPageDispatchRoute> = {
166
166
  buildPageElement: (route: TRoute, params: AppPageParams, opts: AppPageDispatchInterceptOptions | undefined, searchParams: URLSearchParams, options?: {
167
167
  isForceStatic?: boolean;
168
168
  isUncacheableRender?: boolean;
169
+ routeTreeOnlyPrefetchShell?: boolean;
169
170
  /** Layouts the navigating client holds, which the render may leave out. */
170
171
  mountedLayouts?: ReadonlySet<string> | null;
171
172
  observeMetadataSearchParamsAccess?: boolean;
@@ -293,7 +293,8 @@ async function dispatchAppPageInner(options) {
293
293
  const isOnDemandRevalidate = _isOnDemandRevalidate();
294
294
  const shouldReadCache = isStoredRender && !isOnDemandRevalidate;
295
295
  const isCacheCandidate = isCacheEligibleRender && isStaticEligible && options.pprFallbackShell === void 0 && !isNeverStoredPath;
296
- const isUncacheableRender = !(isCacheEligibleRender && isStaticEligible) && options.pprFallbackShell === void 0 && (options.pprRuntime === void 0 || !options.isProduction);
296
+ const routeTreeOnlyPrefetchShell = options.pprFallbackShell === void 0 && (options.pprRuntime === void 0 || !options.isProduction);
297
+ const isUncacheableRender = !(isCacheEligibleRender && isStaticEligible) && routeTreeOnlyPrefetchShell;
297
298
  const mountedLayouts = isUncacheableRender && options.isRscRequest && (options.renderMode ?? "navigation") === "navigation" && options.interceptionContext === null && options.isProgressiveActionRender !== true ? parseMountedLayoutsHeader(options.request.headers.get(VINEXT_MOUNTED_LAYOUTS_HEADER)) : null;
298
299
  if (shouldReadCache && isStaticEligible) {
299
300
  traceOperation = resolveAppPageTraceOperation({
@@ -387,6 +388,7 @@ async function dispatchAppPageInner(options) {
387
388
  isForceStatic: revalidationDynamicConfig === "force-static",
388
389
  observeMetadataSearchParamsAccess: revalidationDynamicConfig !== "force-static",
389
390
  observePageSearchParamsAccess: revalidationDynamicConfig !== "force-static",
391
+ routeTreeOnlyPrefetchShell,
390
392
  serveStreamingMetadata: placeGeneratedMetadataInBody,
391
393
  placeStreamedMetadataInHead: true,
392
394
  onHeadPrepared(head) {
@@ -552,6 +554,7 @@ async function dispatchAppPageInner(options) {
552
554
  observeMetadataSearchParamsAccess: interceptDynamicConfig !== "force-static",
553
555
  observePageSearchParamsAccess: interceptDynamicConfig !== "force-static",
554
556
  isUncacheableRender,
557
+ routeTreeOnlyPrefetchShell,
555
558
  serveStreamingMetadata: placeGeneratedMetadataInBody,
556
559
  placeStreamedMetadataInHead: options.renderWholeDocument === true,
557
560
  onHeadPrepared
@@ -611,6 +614,7 @@ async function dispatchAppPageInner(options) {
611
614
  observeMetadataSearchParamsAccess: !isForceStatic,
612
615
  observePageSearchParamsAccess: !isForceStatic,
613
616
  isUncacheableRender,
617
+ routeTreeOnlyPrefetchShell,
614
618
  mountedLayouts: interceptResult.interceptOpts ? null : mountedLayouts,
615
619
  serveStreamingMetadata: placeGeneratedMetadataInBody,
616
620
  placeStreamedMetadataInHead: options.renderWholeDocument === true,
@@ -66,6 +66,8 @@ export type AppPagePageRequest<TModule extends AppPageModule = AppPageModule> =
66
66
  isForceStatic?: boolean;
67
67
  /** The render can never be stored (see buildAppPageElements). */
68
68
  isUncacheableRender?: boolean;
69
+ /** A loading shell without a loading boundary is the route tree (see buildAppPageElements). */
70
+ routeTreeOnlyPrefetchShell?: boolean;
69
71
  /** Layouts the navigating client holds (see buildAppPageElements). */
70
72
  mountedLayouts?: ReadonlySet<string> | null;
71
73
  /** Observe page metadata `searchParams` access for cache-safety classification. */
@@ -107,7 +107,7 @@ function traceAppPageInterceptLayoutModules(modules, branchSegments, layoutSegme
107
107
  async function buildPageElements(options) {
108
108
  const { route, params, routePath, displayPathname = routePath, pageRequest, globalErrorModule, rootNotFoundModule, rootForbiddenModule, rootUnauthorizedModule, metadataRoutes } = options;
109
109
  const slotParamOverrides = resolveSlotParamOverrides(route, routePath);
110
- const { opts, searchParams, isRscRequest, mountedSlotsHeader, renderMode = APP_RSC_RENDER_MODE_NAVIGATION, observeMetadataSearchParamsAccess = false, observePageSearchParamsAccess = false, isForceStatic = false, isUncacheableRender = false, mountedLayouts = null, serveStreamingMetadata, placeStreamedMetadataInHead = false, isProduction = process.env.NODE_ENV === "production", onHeadPrepared } = pageRequest;
110
+ const { opts, searchParams, isRscRequest, mountedSlotsHeader, renderMode = APP_RSC_RENDER_MODE_NAVIGATION, observeMetadataSearchParamsAccess = false, observePageSearchParamsAccess = false, isForceStatic = false, isUncacheableRender = false, routeTreeOnlyPrefetchShell = false, mountedLayouts = null, serveStreamingMetadata, placeStreamedMetadataInHead = false, isProduction = process.env.NODE_ENV === "production", onHeadPrepared } = pageRequest;
111
111
  const pageModule = route.page;
112
112
  const isSiblingIntercept = opts?.interceptSlotKey === "__vinext_page_intercept" && !!opts?.interceptPage;
113
113
  const effectivePageModule = isSiblingIntercept ? opts.interceptPage : pageModule;
@@ -414,6 +414,7 @@ async function buildPageElements(options) {
414
414
  globalErrorModule: globalErrorModule ?? DEFAULT_GLOBAL_ERROR_MODULE,
415
415
  isRscRequest,
416
416
  isUncacheableRender,
417
+ routeTreeOnlyPrefetchShell,
417
418
  interceptionId: opts?.interceptionId ?? null,
418
419
  mountedLayouts,
419
420
  mountedSlotIds,
@@ -220,11 +220,7 @@ type BuildAppPageElementsOptions<TModule extends AppPageModule = AppPageModule,
220
220
  isRscRequest?: boolean;
221
221
  /**
222
222
  * The render can never be stored and, in production, is never sent with
223
- * static cache headers (see app-page-dispatch.ts), so a loading-shell
224
- * prefetch without a loading boundary may skip its layouts. A render that
225
- * may be stored keeps them, so a layout's dynamic API read still keeps it
226
- * out of the cache. An explicit CDN cache policy on a dynamic route can
227
- * still store the route tree, as a CDN would store Next.js's.
223
+ * static cache headers (see app-page-dispatch.ts).
228
224
  */
229
225
  isUncacheableRender?: boolean;
230
226
  /**
@@ -236,6 +232,12 @@ type BuildAppPageElementsOptions<TModule extends AppPageModule = AppPageModule,
236
232
  mountedLayouts?: ReadonlySet<string> | null;
237
233
  mountedSlotIds?: ReadonlySet<string> | null;
238
234
  renderIdentity?: AppPageRenderIdentity;
235
+ /**
236
+ * The render has no PPR (see app-page-dispatch.ts), so a loading-shell
237
+ * prefetch without a loading boundary sends only the route tree. Without it,
238
+ * the layouts render, as Next.js renders them with isRoutePPREnabled.
239
+ */
240
+ routeTreeOnlyPrefetchShell?: boolean;
239
241
  renderMode?: AppRscRenderMode;
240
242
  routePath: string;
241
243
  semanticPageIdentity?: Readonly<{
@@ -514,7 +514,7 @@ function buildAppPageElements(options) {
514
514
  [pageElementId]: null,
515
515
  [routeId]: null
516
516
  };
517
- if (isPrefetchLoadingShell && !shouldRenderPrefetchLoadingShell && options.isUncacheableRender === true) return {
517
+ if (isPrefetchLoadingShell && !shouldRenderPrefetchLoadingShell && options.routeTreeOnlyPrefetchShell === true) return {
518
518
  ...metadataEntries,
519
519
  ...options.route.staticSiblings && options.route.staticSiblings.length > 0 ? { [APP_STATIC_SIBLINGS_KEY]: options.route.staticSiblings } : {},
520
520
  [pageElementId]: null,
@@ -981,8 +981,10 @@ function buildAppPageElements(options) {
981
981
  const globalErrorComponent = getErrorBoundaryExport(options.globalErrorModule);
982
982
  routeChildren = /* @__PURE__ */ jsx(GlobalErrorBoundary, {
983
983
  fallback: DEFAULT_GLOBAL_ERROR_COMPONENT,
984
+ wrapsGlobalErrorBoundary: globalErrorComponent !== null,
984
985
  children: globalErrorComponent ? /* @__PURE__ */ jsx(ErrorBoundary, {
985
986
  fallback: globalErrorComponent,
987
+ isGlobalErrorBoundary: true,
986
988
  children: routeChildren
987
989
  }) : routeChildren
988
990
  });
@@ -57,6 +57,12 @@ type RenderPagesFallbackOptions = {
57
57
  pathname?: string;
58
58
  pagesDataRequest?: Request | null;
59
59
  request: Request;
60
+ /**
61
+ * The basePath `request.url` still carries. The page renderer receives the
62
+ * URL without it, as in the Pages Router pipeline, so `req.url` and `asPath`
63
+ * leave it out like Next.js.
64
+ */
65
+ requestBasePath?: string;
60
66
  /** The shared cache entry answers every host; a request-time render is not stored. */
61
67
  sharedAcrossHosts?: boolean;
62
68
  url: URL;
@@ -1,5 +1,7 @@
1
+ import { stripBasePath } from "../utils/base-path.js";
1
2
  import { cloneRequestWithHeaders, cloneRequestWithUrl } from "./request-pipeline.js";
2
3
  import { getRequestExecutionContext } from "../shims/request-context.js";
4
+ import { isLocaleRedirect, markLocaleRedirect } from "./pages-i18n.js";
3
5
  import { beginRouteCacheability, markRouteCacheabilityDynamic } from "../shims/cacheability-classification.js";
4
6
  import { mergeHeaders } from "./worker-utils.js";
5
7
  import { pagesRouteHasPriorityOverAppRoute } from "./hybrid-route-priority.js";
@@ -18,7 +20,7 @@ function applyPagesMiddlewareContext(response, middlewareContext) {
18
20
  * Fallback handler to route App Router requests to the Pages Router when no App Router route matches.
19
21
  */
20
22
  async function renderPagesFallback(options, dependencies) {
21
- const { allowRscDocumentFallback = false, appRouteMatch = null, isDataRequest = false, isRscRequest, initialResponseHeaders, matchKind, middlewareContext, pathname = options.url.pathname, pagesDataRequest = null, request, sharedAcrossHosts = false, url } = options;
23
+ const { allowRscDocumentFallback = false, appRouteMatch = null, isDataRequest = false, isRscRequest, initialResponseHeaders, matchKind, middlewareContext, pathname = options.url.pathname, pagesDataRequest = null, request, requestBasePath = "", sharedAcrossHosts = false, url } = options;
22
24
  const { loadPagesEntry, buildRequestHeaders, applyRouteHandlerMiddlewareContext, getDraftModeCookieHeader } = dependencies;
23
25
  if (isRscRequest && !allowRscDocumentFallback) return null;
24
26
  const pagesEntry = await loadPagesEntry();
@@ -67,8 +69,10 @@ async function renderPagesFallback(options, dependencies) {
67
69
  if (runtimeDataKind === "server" || runtimeDataKind === "initial") markRouteCacheabilityDynamic("getInitialProps render shares a cache entry across hosts");
68
70
  }
69
71
  const pagesRequest = claimPagesRequest();
72
+ const renderRequestUrl = new URL(pagesDataRequest?.url ?? pagesRequest.url);
73
+ if (!pagesDataRequest) renderRequestUrl.pathname = stripBasePath(renderRequestUrl.pathname, requestBasePath);
70
74
  const renderArgs = [
71
- pagesDataRequest ? cloneRequestWithUrl(pagesRequest, pagesDataRequest.url) : pagesRequest,
75
+ renderRequestUrl.href === pagesRequest.url ? pagesRequest : cloneRequestWithUrl(pagesRequest, renderRequestUrl.href),
72
76
  pagesUrl,
73
77
  {},
74
78
  getRequestExecutionContext() ?? void 0,
@@ -76,7 +80,8 @@ async function renderPagesFallback(options, dependencies) {
76
80
  ];
77
81
  const pagesRes = isDataRequest ? await (initialResponseHeaders ? pagesEntry.renderPage(...renderArgs, { isDataReq: true }, initialResponseHeaders) : pagesEntry.renderPage(...renderArgs, { isDataReq: true })) : await (initialResponseHeaders ? pagesEntry.renderPage(...renderArgs, void 0, initialResponseHeaders) : pagesEntry.renderPage(...renderArgs));
78
82
  if (pagesRes.status === 404 && pageMatch === null) return null;
79
- return applyDraftModeCookie(applyPagesMiddlewareContext(pagesRes, middlewareContext), getDraftModeCookieHeader());
83
+ const response = applyDraftModeCookie(applyPagesMiddlewareContext(pagesRes, middlewareContext), getDraftModeCookieHeader());
84
+ return isLocaleRedirect(pagesRes) ? markLocaleRedirect(response) : response;
80
85
  }
81
86
  /**
82
87
  * Append a middleware-emitted `__prerender_bypass` Set-Cookie header to a Pages
@@ -9,7 +9,7 @@ import "./server-globals.js";
9
9
  import { getExplicitCdnCacheAdapter } from "../shims/cdn-cache-state.js";
10
10
  import { applyCdnResponseIdentityHeaders, validateCdnRequest } from "./cache-control.js";
11
11
  import { registerLazyDataCacheHandler } from "../shims/cache-handler.js";
12
- import { getImageOptimizer, readImageOptimizationSignal } from "./image-optimization.js";
12
+ import { readImageOptimizationSignal } from "./image-optimization.js";
13
13
  import { consumeFrameworkRequestRoute, traceFrameworkRequest } from "./request-tracing.js";
14
14
  import { createStaticAssetRequest, resolveStaticAssetSignal } from "./worker-utils.js";
15
15
  import { readTrustedPrerenderStateFromHeaders } from "./prerender-route-params.js";
@@ -75,7 +75,6 @@ async function handleRequest(request, env, platformCtx, dispatchResponseStage, a
75
75
  if (cdnValidationResponse) return cdnValidationResponse;
76
76
  }
77
77
  const url = new URL(request.url);
78
- const servesImages = Boolean(assets) && getImageOptimizer() !== null;
79
78
  const protocolRelativeGuard = guardProtocolRelativeUrl(url.pathname, url.search);
80
79
  if (protocolRelativeGuard) return protocolRelativeGuard;
81
80
  try {
@@ -104,7 +103,7 @@ async function handleRequest(request, env, platformCtx, dispatchResponseStage, a
104
103
  };
105
104
  const handle = () => requestRscHandler(request, ctx, trackedDispatchResponseStage, probeMode, ctx.isPrerenderPathDiscovery === true, trustedPrerenderState);
106
105
  let response = await runWithExecutionContext(ctx, handle);
107
- const imageSignal = servesImages ? readImageOptimizationSignal(response) : null;
106
+ const imageSignal = assets ? readImageOptimizationSignal(response) : null;
108
107
  if (imageSignal && assets) {
109
108
  const { fetchSignalledPublicFile, serveWorkerImageOptimization } = await import("./worker-image-optimization.js");
110
109
  response = await serveWorkerImageOptimization(request, imageSignal, __imageAllowedWidths, __imageConfig, {
@@ -8,7 +8,7 @@ import { runWithExecutionContext } from "../shims/request-context.js";
8
8
  import "./server-globals.js";
9
9
  import { getCdnCacheAdapter } from "../shims/cdn-cache.js";
10
10
  import { applyCdnResponseIdentityHeaders, validateCdnRequest } from "./cache-control.js";
11
- import { getImageOptimizer, readImageOptimizationSignal } from "./image-optimization.js";
11
+ import { readImageOptimizationSignal } from "./image-optimization.js";
12
12
  import { traceFrameworkRequest } from "./request-tracing.js";
13
13
  import { createStaticAssetRequest, resolveStaticAssetSignal } from "./worker-utils.js";
14
14
  import { readTrustedPrerenderRouteParams, serializePrerenderRouteParamsHeader } from "./prerender-route-params.js";
@@ -83,7 +83,6 @@ async function handleRequest(request, env, platformCtx) {
83
83
  const cdnValidationResponse = await validateCdnRequest(request);
84
84
  if (cdnValidationResponse) return cdnValidationResponse;
85
85
  const url = new URL(request.url);
86
- const servesImages = Boolean(assets) && getImageOptimizer() !== null;
87
86
  const protocolRelativeGuard = guardProtocolRelativeUrl(url.pathname, url.search);
88
87
  if (protocolRelativeGuard) return protocolRelativeGuard;
89
88
  try {
@@ -109,7 +108,7 @@ async function handleRequest(request, env, platformCtx) {
109
108
  const result = await (ctx ? runWithExecutionContext(ctx, handleFn) : handleFn());
110
109
  if (result instanceof Response) {
111
110
  let response = result;
112
- const imageSignal = servesImages ? readImageOptimizationSignal(response) : null;
111
+ const imageSignal = assets ? readImageOptimizationSignal(response) : null;
113
112
  if (imageSignal && assets) {
114
113
  const { fetchSignalledPublicFile, serveWorkerImageOptimization } = await import("./worker-image-optimization.js");
115
114
  response = await serveWorkerImageOptimization(request, imageSignal, __imageAllowedWidths, __imageConfig, {
@@ -78,6 +78,14 @@ export declare function createRscRequestHeaders(options?: CreateRscRequestHeader
78
78
  * their bytes can legitimately differ from the destination's full route.
79
79
  */
80
80
  export declare function canonicalizePrewarmableRscRequestHeaders(headers: Headers): boolean;
81
+ /**
82
+ * Convert a search-param shell prefetch to the canonical navigation of its
83
+ * page. The shell renders the page with an empty query (app-page-dispatch.ts),
84
+ * which is what the query-free navigation renders, as Next.js's static
85
+ * prefetch of a page segment never varies on search params. The caller drops
86
+ * the query from the URL.
87
+ */
88
+ export declare function canonicalizeDynamicShellRscRequestHeaders(headers: Headers): boolean;
81
89
  /**
82
90
  * Convert an ordinary loading-boundary prefetch to the shared loading-shell
83
91
  * variant. The payload is selected by the three retained mode headers; visible