vinext 1.4.0 → 1.4.2

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 (156) hide show
  1. package/README.md +21 -21
  2. package/dist/build/action-owner-manifest.js +1 -0
  3. package/dist/build/prerender.js +3 -2
  4. package/dist/check.js +4 -7
  5. package/dist/client/dev-error-overlay-store.d.ts +1 -0
  6. package/dist/client/dev-error-overlay.d.ts +2 -0
  7. package/dist/client/dev-error-overlay.js +19 -3
  8. package/dist/config/config-matchers.js +4 -2
  9. package/dist/config/destination-params.d.ts +2 -1
  10. package/dist/config/destination-params.js +55 -2
  11. package/dist/config/next-config.d.ts +22 -1
  12. package/dist/config/next-config.js +49 -4
  13. package/dist/entries/app-rsc-entry.js +41 -19
  14. package/dist/entries/app-rsc-manifest.js +4 -0
  15. package/dist/entries/pages-server-entry.js +5 -1
  16. package/dist/index.js +44 -32
  17. package/dist/init-cloudflare.js +18 -2
  18. package/dist/plugins/css-target.d.ts +18 -0
  19. package/dist/plugins/css-target.js +93 -0
  20. package/dist/plugins/strip-server-exports.js +2 -1
  21. package/dist/routing/app-route-graph.d.ts +10 -0
  22. package/dist/routing/app-route-graph.js +15 -1
  23. package/dist/server/api-handler.d.ts +2 -0
  24. package/dist/server/api-handler.js +5 -6
  25. package/dist/server/app-action-forwarding.js +3 -1
  26. package/dist/server/app-browser-entry.js +28 -7
  27. package/dist/server/app-browser-rsc-redirect.js +3 -3
  28. package/dist/server/app-browser-server-action-client.js +2 -2
  29. package/dist/server/app-browser-visible-commit.js +2 -2
  30. package/dist/server/app-elements.d.ts +3 -0
  31. package/dist/server/app-elements.js +14 -1
  32. package/dist/server/app-fallback-renderer.js +2 -2
  33. package/dist/server/app-optimistic-routing.d.ts +12 -1
  34. package/dist/server/app-optimistic-routing.js +40 -19
  35. package/dist/server/app-page-boundary-render.js +7 -12
  36. package/dist/server/app-page-cache-render.js +1 -1
  37. package/dist/server/app-page-dispatch.d.ts +10 -2
  38. package/dist/server/app-page-dispatch.js +73 -31
  39. package/dist/server/app-page-element-builder.d.ts +9 -20
  40. package/dist/server/app-page-element-builder.js +40 -52
  41. package/dist/server/app-page-execution.js +9 -6
  42. package/dist/server/app-page-intercept-segment-config.d.ts +56 -0
  43. package/dist/server/app-page-intercept-segment-config.js +90 -0
  44. package/dist/server/app-page-navigation-params.d.ts +23 -0
  45. package/dist/server/app-page-navigation-params.js +49 -0
  46. package/dist/server/app-page-render.d.ts +5 -3
  47. package/dist/server/app-page-render.js +31 -17
  48. package/dist/server/app-page-request.d.ts +24 -2
  49. package/dist/server/app-page-request.js +10 -10
  50. package/dist/server/app-page-response.js +1 -1
  51. package/dist/server/app-page-route-wiring.d.ts +10 -0
  52. package/dist/server/app-page-route-wiring.js +34 -13
  53. package/dist/server/app-page-segment-plan.js +9 -1
  54. package/dist/server/app-page-segment-state.d.ts +9 -0
  55. package/dist/server/app-page-segment-state.js +54 -1
  56. package/dist/server/app-page-stream.d.ts +10 -8
  57. package/dist/server/app-page-stream.js +2 -1
  58. package/dist/server/app-request-stage-independent-entry.js +1 -1
  59. package/dist/server/app-response-stage-entry.js +1 -0
  60. package/dist/server/app-route-handler-execution.js +3 -2
  61. package/dist/server/app-route-handler-runtime.d.ts +1 -1
  62. package/dist/server/app-route-handler-runtime.js +11 -7
  63. package/dist/server/app-route-module-loader.d.ts +2 -0
  64. package/dist/server/app-route-module-loader.js +1 -0
  65. package/dist/server/app-router-entry.js +1 -1
  66. package/dist/server/app-rsc-cache-busting.d.ts +10 -10
  67. package/dist/server/app-rsc-cache-busting.js +27 -68
  68. package/dist/server/app-rsc-error-handler.js +1 -1
  69. package/dist/server/app-rsc-errors.js +4 -1
  70. package/dist/server/app-rsc-handler.d.ts +10 -1
  71. package/dist/server/app-rsc-handler.js +126 -103
  72. package/dist/server/app-rsc-request-normalization.d.ts +10 -12
  73. package/dist/server/app-rsc-request-normalization.js +11 -14
  74. package/dist/server/app-rsc-response-finalizer.d.ts +3 -0
  75. package/dist/server/app-rsc-response-finalizer.js +11 -1
  76. package/dist/server/app-rsc-response-stage.js +4 -12
  77. package/dist/server/app-rsc-route-matching.d.ts +14 -0
  78. package/dist/server/app-rsc-route-matching.js +42 -18
  79. package/dist/server/app-server-action-execution.js +24 -14
  80. package/dist/server/app-ssr-entry.d.ts +6 -0
  81. package/dist/server/app-ssr-entry.js +13 -8
  82. package/dist/server/app-worker-stages.d.ts +12 -3
  83. package/dist/server/app-worker-stages.js +37 -4
  84. package/dist/server/bfcache-identity.d.ts +7 -0
  85. package/dist/server/bfcache-identity.js +10 -1
  86. package/dist/server/cache-control.d.ts +2 -0
  87. package/dist/server/cache-control.js +3 -1
  88. package/dist/server/cacheability-manifest.js +1 -2
  89. package/dist/server/dev-origin-check.d.ts +2 -1
  90. package/dist/server/dev-origin-check.js +2 -1
  91. package/dist/server/dev-server.js +2 -1
  92. package/dist/server/image-local-patterns.d.ts +21 -0
  93. package/dist/server/image-local-patterns.js +59 -0
  94. package/dist/server/image-optimization.d.ts +28 -22
  95. package/dist/server/image-optimization.js +17 -2
  96. package/dist/server/isr-cache.js +4 -1
  97. package/dist/server/metadata-route-response.js +25 -7
  98. package/dist/server/middleware-matcher.js +19 -6
  99. package/dist/server/middleware-runtime.js +6 -2
  100. package/dist/server/multi-stage.d.ts +7 -3
  101. package/dist/server/nitro-public-files-plugin.js +20 -8
  102. package/dist/server/pages-data-route.d.ts +5 -1
  103. package/dist/server/pages-data-route.js +14 -6
  104. package/dist/server/pages-node-compat.d.ts +1 -1
  105. package/dist/server/pages-node-compat.js +2 -0
  106. package/dist/server/pages-page-data.js +2 -1
  107. package/dist/server/pages-page-handler.js +17 -6
  108. package/dist/server/pages-page-response.d.ts +9 -0
  109. package/dist/server/pages-page-response.js +18 -4
  110. package/dist/server/pages-request-pipeline.js +6 -5
  111. package/dist/server/pages-request-stage-entry.js +25 -4
  112. package/dist/server/pages-router-entry.js +3 -2
  113. package/dist/server/prod-server.d.ts +2 -2
  114. package/dist/server/prod-server.js +59 -17
  115. package/dist/server/request-error-log.d.ts +9 -0
  116. package/dist/server/request-error-log.js +13 -0
  117. package/dist/server/request-pipeline.d.ts +14 -4
  118. package/dist/server/request-pipeline.js +33 -15
  119. package/dist/server/response-stage-cacheability.d.ts +7 -0
  120. package/dist/server/response-stage-cacheability.js +6 -5
  121. package/dist/server/rsc-completion-metadata.js +3 -0
  122. package/dist/server/static-file-cache.d.ts +9 -0
  123. package/dist/server/static-file-cache.js +13 -2
  124. package/dist/server/worker-image-optimization.d.ts +2 -2
  125. package/dist/server/worker-image-optimization.js +18 -3
  126. package/dist/server/worker-utils.d.ts +8 -0
  127. package/dist/server/worker-utils.js +19 -2
  128. package/dist/shims/cache-request-state.d.ts +6 -6
  129. package/dist/shims/cache-request-state.js +6 -36
  130. package/dist/shims/cache-runtime.d.ts +3 -1
  131. package/dist/shims/cache-runtime.js +77 -47
  132. package/dist/shims/cache.d.ts +10 -5
  133. package/dist/shims/cache.js +35 -22
  134. package/dist/shims/client-hook-error.d.ts +2 -0
  135. package/dist/shims/client-hook-error.js +16 -1
  136. package/dist/shims/fetch-cache.js +6 -2
  137. package/dist/shims/headers.d.ts +2 -1
  138. package/dist/shims/headers.js +5 -3
  139. package/dist/shims/image-props.d.ts +6 -0
  140. package/dist/shims/image-props.js +39 -3
  141. package/dist/shims/image.js +3 -3
  142. package/dist/shims/internal/cache-context-accessor.d.ts +1 -1
  143. package/dist/shims/link.js +5 -10
  144. package/dist/shims/navigation.js +2 -3
  145. package/dist/shims/request-state-types.d.ts +2 -1
  146. package/dist/shims/slot.d.ts +9 -2
  147. package/dist/shims/slot.js +76 -11
  148. package/dist/shims/unified-request-context.d.ts +6 -0
  149. package/dist/shims/unified-request-context.js +1 -0
  150. package/dist/utils/base-path.d.ts +7 -0
  151. package/dist/utils/base-path.js +11 -1
  152. package/dist/utils/cache-life-profiles.d.ts +33 -0
  153. package/dist/utils/cache-life-profiles.js +111 -0
  154. package/dist/utils/middleware-request-headers.d.ts +14 -3
  155. package/dist/utils/middleware-request-headers.js +54 -7
  156. package/package.json +1 -1
@@ -86,7 +86,8 @@ async function renderAppPageHtmlStream(options) {
86
86
  initialDevServerError: options.initialDevServerError,
87
87
  onSsrError: options.onSsrError,
88
88
  mirrorNextFlight: options.mirrorNextFlight,
89
- fallbackToErrorDocumentOnShellError: options.fallbackToErrorDocumentOnShellError ?? (options.waitForAllReady !== true && options.hasCustomGlobalError === false),
89
+ fallbackToErrorDocumentOnShellError: options.fallbackToErrorDocumentOnShellError,
90
+ isServerComponentError: options.isServerComponentError,
90
91
  resolveCaughtNotFoundBoundaries: options.resolveCaughtNotFoundBoundaries,
91
92
  dynamicStaleTimeSeconds: options.dynamicStaleTimeSeconds,
92
93
  getInitialNavigationCacheMetadata: options.getInitialNavigationCacheMetadata
@@ -32,7 +32,7 @@ function handleRequestStage(request, env, ctx, dispatchResponseStage) {
32
32
  callback: () => handleRequest(request, env, ctx, dispatchResponseStage, ctx?.assets).then((response) => applyCdnResponseIdentityHeaders(response, originalRequest)),
33
33
  getStatus: (response) => response?.status,
34
34
  headers: request.headers,
35
- isRsc: url.pathname.endsWith(".rsc") || request.headers.get("RSC") === "1",
35
+ isRsc: request.headers.get("RSC") === "1",
36
36
  method: request.method,
37
37
  target: url.pathname + url.search
38
38
  });
@@ -58,6 +58,7 @@ async function handleResponseStage(request, env, platformCtx, props, dispatchReq
58
58
  probeMode: props.cacheability.probeMode,
59
59
  rawManifest: __cacheabilityManifest,
60
60
  recomposesRequestScopedHeaders: props.kind === "app-page" && props.isRscRequest,
61
+ keepsParamsHeader: props.kind === "app-page" && props.interceptionContext !== null,
61
62
  registerCacheAdapters: () => registerConfiguredCacheAdapters(env),
62
63
  request,
63
64
  representation: props.cacheability.representation,
@@ -1,6 +1,7 @@
1
1
  import { isFullyBufferedBody } from "./fully-buffered-response.js";
2
2
  import { patternToNextFormat } from "../routing/route-validation.js";
3
3
  import { frameworkTracer } from "./tracer.js";
4
+ import { logRequestError } from "./request-error-log.js";
4
5
  import { getRouteCacheabilityCaptureOptions, getRouteCacheabilityDynamicReason, isRouteCacheabilityEvaluation, markRouteCacheabilityExplicitResponsePolicy, markRouteCacheabilityResponseBodyComplete, recordRouteCacheability } from "../shims/cacheability-classification.js";
5
6
  import { NEVER_CACHE_CONTROL, applyCdnResponseHeaders, hasCdnResponsePolicy, hasExplicitNonCacheableResponsePolicy } from "./cache-control.js";
6
7
  import { buildAppRouteMissIsrCacheControl } from "./isr-decision.js";
@@ -117,7 +118,7 @@ async function runAppRouteHandler(options) {
117
118
  onDynamicAccess() {
118
119
  options.markDynamicUsage();
119
120
  },
120
- requestMode: options.dynamicConfig === "force-static" || options.dynamicConfig === "error" ? options.dynamicConfig : "auto",
121
+ requestMode: options.dynamicConfig === "force-dynamic" || options.dynamicConfig === "force-static" || options.dynamicConfig === "error" ? options.dynamicConfig : "auto",
121
122
  signal: options.requestSignal,
122
123
  staticGenerationErrorMessage(expression) {
123
124
  return getAppRouteStaticGenerationErrorMessage(options.routePattern, expression);
@@ -339,7 +340,7 @@ async function executeAppRouteHandlerImpl(options) {
339
340
  }), options.middlewareContext, middlewareMergeOptions), shouldApplyDraftPolicy);
340
341
  return applyDraftModeCachePolicy(applyRouteHandlerMiddlewareContext(new Response(null, { status: specialError.statusCode }), options.middlewareContext, middlewareMergeOptions), shouldApplyDraftPolicy);
341
342
  }
342
- console.error("[vinext] Route handler error:", error);
343
+ logRequestError("[vinext] Route handler error:", error);
343
344
  await options.reportRequestError(error, {
344
345
  path: options.cleanPathname,
345
346
  method: options.request.method,
@@ -21,7 +21,7 @@ type NextUrlDynamicAccess = "nextUrl.search" | "nextUrl.searchParams" | "nextUrl
21
21
  type HostDerivedNextUrlDynamicAccess = "nextUrl.protocol" | "nextUrl.username" | "nextUrl.password" | "nextUrl.host" | "nextUrl.hostname" | "nextUrl.port" | DomainLocaleNextUrlDynamicAccess;
22
22
  type DomainLocaleNextUrlDynamicAccess = "nextUrl.locale" | "nextUrl.defaultLocale" | "nextUrl.domainLocale";
23
23
  type AppRouteDynamicRequestAccess = RequestDynamicAccess | NextUrlDynamicAccess;
24
- type AppRouteRequestMode = "auto" | "force-static" | "error";
24
+ type AppRouteRequestMode = "auto" | "force-dynamic" | "force-static" | "error";
25
25
  type TrackedAppRouteRequestOptions = {
26
26
  basePath?: string;
27
27
  i18n?: NextI18nConfig | null;
@@ -2,6 +2,7 @@ import { buildRequestHeadersFromMiddlewareResponse } from "../utils/middleware-r
2
2
  import { addBasePathToPathname } from "../utils/base-path.js";
3
3
  import { cloneRequestWithHeaders, cloneRequestWithUrl } from "./request-pipeline.js";
4
4
  import { NextRequest, RequestCookies, sealRequestCookies, sealRequestHeaders } from "../shims/server.js";
5
+ import { throwIfInsideCacheScope } from "../shims/internal/headers-state.js";
5
6
  import { BUILT_IN_REQUEST_PROPERTIES } from "./app-route-request-built-ins.js";
6
7
  //#region src/server/app-route-handler-runtime.ts
7
8
  const BUILT_IN_REQUEST_PROPERTY_MAP = new Map(BUILT_IN_REQUEST_PROPERTIES.map((property) => [property.key, property]));
@@ -150,7 +151,8 @@ function createTrackedAppRouteRequest(request, options = {}) {
150
151
  let didAccessDynamicRequest = false;
151
152
  const requestMode = options.requestMode ?? "auto";
152
153
  const nextConfig = buildNextConfig(options);
153
- const markDynamicAccess = (access) => {
154
+ const markDynamicAccess = (access, trackedByNext) => {
155
+ if (trackedByNext && requestMode !== "force-dynamic") throwIfInsideCacheScope(access);
154
156
  didAccessDynamicRequest = true;
155
157
  options.onDynamicAccess?.(access);
156
158
  };
@@ -184,7 +186,7 @@ function createTrackedAppRouteRequest(request, options = {}) {
184
186
  const wrapNextUrl = (nextUrl) => {
185
187
  return new Proxy(nextUrl, { get(target, prop) {
186
188
  if (isNextDynamicNextUrlProperty(prop) || isHostDerivedNextUrlProperty(prop)) {
187
- markDynamicAccess(`nextUrl.${String(prop)}`);
189
+ markDynamicAccess(`nextUrl.${String(prop)}`, isNextDynamicNextUrlProperty(prop));
188
190
  return bindMethodIfNeeded(Reflect.get(target, prop, target), target);
189
191
  }
190
192
  if (prop === "clone") return () => wrapNextUrl(target.clone());
@@ -252,7 +254,7 @@ function createTrackedAppRouteRequest(request, options = {}) {
252
254
  const accessCf = (read, forceStaticValue) => {
253
255
  if (requestMode === "force-static") return forceStaticValue;
254
256
  if (requestMode === "error") return throwStaticGenerationError("request.cf");
255
- markDynamicAccess("request.cf");
257
+ markDynamicAccess("request.cf", false);
256
258
  return read();
257
259
  };
258
260
  const controlledCfGetter = () => accessCf(sourceCfAccessor ? () => Reflect.apply(sourceCfAccessor, requestWithOverrides, []) : () => rawCfMetadata, void 0);
@@ -354,17 +356,19 @@ function createTrackedAppRouteRequest(request, options = {}) {
354
356
  return proxiedNextUrl;
355
357
  case "headers":
356
358
  case "cookies":
357
- case "ip":
358
- case "geo":
359
359
  case "url":
360
360
  case "body":
361
361
  case "blob":
362
- case "bytes":
363
362
  case "json":
364
363
  case "text":
365
364
  case "arrayBuffer":
366
365
  case "formData":
367
- markDynamicAccess(`request.${String(prop)}`);
366
+ markDynamicAccess(`request.${String(prop)}`, true);
367
+ return bindMethodIfNeeded(Reflect.get(target, prop, target), target);
368
+ case "ip":
369
+ case "geo":
370
+ case "bytes":
371
+ markDynamicAccess(`request.${String(prop)}`, false);
368
372
  return bindMethodIfNeeded(Reflect.get(target, prop, target), target);
369
373
  case "clone": return cloneTrackedRequest;
370
374
  default: return getTrackedRequestProperty(target, prop, receiver);
@@ -67,6 +67,7 @@ type LazyLoadableSlot = {
67
67
  loading?: unknown;
68
68
  loadings?: readonly unknown[];
69
69
  error?: unknown;
70
+ errors?: readonly unknown[];
70
71
  notFound?: unknown;
71
72
  notFounds?: readonly unknown[];
72
73
  __loadPage?: LazyModuleThunk | null;
@@ -76,6 +77,7 @@ type LazyLoadableSlot = {
76
77
  __loadLoading?: LazyModuleThunk | null;
77
78
  __loadLoadings?: LazyModuleLoaderArray | null;
78
79
  __loadError?: LazyModuleThunk | null;
80
+ __loadErrors?: LazyModuleLoaderArray | null;
79
81
  __loadNotFound?: LazyModuleThunk | null;
80
82
  __loadNotFounds?: LazyModuleLoaderArray | null;
81
83
  /** Hydrated only after an intercept matches, not with the slot's base modules. */
@@ -145,6 +145,7 @@ function ensureAppRouteModulesLoaded(route) {
145
145
  pushFieldLoad(loads, slot, "loading", slot.__loadLoading);
146
146
  pushArrayLoads(loads, slot.loadings, slot.__loadLoadings);
147
147
  pushFieldLoad(loads, slot, "error", slot.__loadError);
148
+ pushArrayLoads(loads, slot.errors, slot.__loadErrors);
148
149
  pushFieldLoad(loads, slot, "notFound", slot.__loadNotFound);
149
150
  pushArrayLoads(loads, slot.notFounds, slot.__loadNotFounds);
150
151
  }
@@ -37,7 +37,7 @@ var app_router_entry_default = {
37
37
  callback: async () => applyCdnResponseIdentityHeaders(await handleRequest(request, env, ctx), request),
38
38
  getStatus: (response) => response?.status,
39
39
  headers: request.headers,
40
- isRsc: url.pathname.endsWith(".rsc") || request.headers.get("RSC") === "1",
40
+ isRsc: request.headers.get("RSC") === "1",
41
41
  method: request.method,
42
42
  target: url.pathname + url.search
43
43
  });
@@ -68,11 +68,6 @@ export declare function stripRscCacheBustingSearchParam(url: URL): void;
68
68
  * are dropped. Other requests keep no `_rsc`, because it selects nothing there.
69
69
  */
70
70
  export declare function keepOnlyValidatedRscCacheBustingSearchParam(url: URL, isRscRequest: boolean): void;
71
- /**
72
- * Remove a trailing `.rsc` suffix from a pathname. Returns the pathname
73
- * unchanged when the suffix is absent.
74
- */
75
- export declare function stripRscSuffix(pathname: string): string;
76
71
  export declare function createCanonicalRscRequestHeaders(deploymentId?: string | null | undefined): Headers;
77
72
  /** Headers for the deterministic loading-boundary prefetch representation. */
78
73
  export declare function createCanonicalLoadingShellRscRequestHeaders(deploymentId?: string | null | undefined): Headers;
@@ -96,13 +91,18 @@ export declare function canonicalizeLoadingShellRscRequestHeaders(headers: Heade
96
91
  * appended to the base instead of resolved against it.
97
92
  */
98
93
  export declare function parseRscRequestPath(href: string): URL;
94
+ /**
95
+ * A pathname as the start of a path-absolute reference. One that begins with
96
+ * "//" would serialize as a protocol-relative URL naming another host, so it
97
+ * keeps a "/." prefix, as the WHATWG URL serializer does. URL resolution drops
98
+ * the dot segment, so the reference still targets the same pathname on the
99
+ * origin it is resolved against.
100
+ */
101
+ export declare function toPathAbsolutePathname(pathname: string): string;
99
102
  /**
100
103
  * Serialize the path-absolute reference callers fetch against the app origin.
101
- * A pathname that begins with "//" would serialize as a protocol-relative URL
102
- * and send the request to another host, so it keeps a "/." prefix, as the
103
- * WHATWG URL serializer does. URL resolution drops the dot segment, so the
104
- * request still targets the same pathname on the app origin, as Next.js's
105
- * full-URL fetch does.
104
+ * A "//" pathname keeps its "/." prefix so the request still targets the same
105
+ * pathname on the app origin, as Next.js's full-URL fetch does.
106
106
  */
107
107
  export declare function formatRscRequestPath(url: URL): string;
108
108
  export declare function createRscRequestUrl(href: string, headers: Headers): Promise<string>;
@@ -22,8 +22,10 @@ function encodeBase64Url(bytes) {
22
22
  for (const byte of bytes) binary += String.fromCharCode(byte);
23
23
  return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
24
24
  }
25
- function normalizeHeaderValue(value) {
26
- return value ?? "0";
25
+ function serializeHeaderValue(value) {
26
+ if (value === null) return "0";
27
+ if (value === "0") return "\\0";
28
+ return value.replaceAll("\\", "\\\\").replaceAll(",", "\\,");
27
29
  }
28
30
  function normalizeCompatibilityId(value) {
29
31
  return value && value.length > 0 ? value : null;
@@ -57,9 +59,9 @@ function resolveHardNavigationTargetFromRscResponse(responseUrl, currentHref, or
57
59
  const parsed = new URL(responseUrl, origin);
58
60
  stripRscCacheBustingSearchParam(parsed);
59
61
  const origUrl = new URL(currentHref, origin);
60
- let pathname = stripRscSuffix(parsed.pathname);
62
+ let pathname = parsed.pathname;
61
63
  if (origUrl.pathname.length > 1 && origUrl.pathname.endsWith("/") && !pathname.endsWith("/")) pathname += "/";
62
- let hardNavigationTarget = pathname + parsed.search;
64
+ let hardNavigationTarget = toPathAbsolutePathname(pathname) + parsed.search;
63
65
  if (origUrl.hash) hardNavigationTarget += origUrl.hash;
64
66
  return hardNavigationTarget;
65
67
  }
@@ -74,21 +76,21 @@ function normalizeRenderModeHeaderValue(value) {
74
76
  const renderMode = parseAppRscRenderMode(value);
75
77
  return renderMode === "navigation" ? null : renderMode;
76
78
  }
77
- function createCacheBustingInput(headers, options = {}) {
79
+ function createCacheBustingInput(headers) {
78
80
  const values = [
79
81
  headers.get(NEXT_ROUTER_PREFETCH_HEADER),
80
82
  headers.get(NEXT_ROUTER_SEGMENT_PREFETCH_HEADER),
81
83
  headers.get(NEXT_ROUTER_STATE_TREE_HEADER),
82
84
  headers.get(NEXT_URL_HEADER),
83
85
  headers.get(VINEXT_INTERCEPTION_CONTEXT_HEADER),
84
- ...options.includeInterceptionIdHeader === false ? [] : [headers.get(VINEXT_INTERCEPTION_ID_HEADER)],
86
+ headers.get(VINEXT_INTERCEPTION_ID_HEADER),
85
87
  headers.get(VINEXT_MOUNTED_SLOTS_HEADER),
86
- ...options.includeRenderModeHeader === false ? [] : [normalizeRenderModeHeaderValue(headers.get(VINEXT_RSC_RENDER_MODE_HEADER))]
88
+ normalizeRenderModeHeaderValue(headers.get(VINEXT_RSC_RENDER_MODE_HEADER))
87
89
  ];
88
90
  const stateFingerprint = headers.get(VINEXT_RSC_STATE_FINGERPRINT_HEADER);
89
- if (options.includeStateFingerprintHeader !== false && stateFingerprint !== null) values.push(stateFingerprint);
91
+ if (stateFingerprint !== null) values.push(stateFingerprint);
90
92
  if (values.every((value) => value === null)) return null;
91
- return values.map(normalizeHeaderValue).join(",");
93
+ return values.map(serializeHeaderValue).join(",");
92
94
  }
93
95
  async function sha256CacheBustingHash(input) {
94
96
  const subtle = globalThis.crypto?.subtle;
@@ -147,13 +149,6 @@ function keepOnlyValidatedRscCacheBustingSearchParam(url, isRscRequest) {
147
149
  url.search = "";
148
150
  if (hash !== null) setRscCacheBustingSearchParam(url, hash);
149
151
  }
150
- /**
151
- * Remove a trailing `.rsc` suffix from a pathname. Returns the pathname
152
- * unchanged when the suffix is absent.
153
- */
154
- function stripRscSuffix(pathname) {
155
- return pathname.endsWith(".rsc") ? pathname.slice(0, -4) : pathname;
156
- }
157
152
  function createCanonicalRscRequestHeaders(deploymentId = getDeploymentId()) {
158
153
  const headers = new Headers({
159
154
  Accept: VINEXT_RSC_CONTENT_TYPE,
@@ -232,15 +227,22 @@ function parseRscRequestPath(href) {
232
227
  return new URL(beforeHash, RSC_REQUEST_URL_BASE);
233
228
  }
234
229
  /**
230
+ * A pathname as the start of a path-absolute reference. One that begins with
231
+ * "//" would serialize as a protocol-relative URL naming another host, so it
232
+ * keeps a "/." prefix, as the WHATWG URL serializer does. URL resolution drops
233
+ * the dot segment, so the reference still targets the same pathname on the
234
+ * origin it is resolved against.
235
+ */
236
+ function toPathAbsolutePathname(pathname) {
237
+ return pathname.startsWith("//") ? `/.${pathname}` : pathname;
238
+ }
239
+ /**
235
240
  * Serialize the path-absolute reference callers fetch against the app origin.
236
- * A pathname that begins with "//" would serialize as a protocol-relative URL
237
- * and send the request to another host, so it keeps a "/." prefix, as the
238
- * WHATWG URL serializer does. URL resolution drops the dot segment, so the
239
- * request still targets the same pathname on the app origin, as Next.js's
240
- * full-URL fetch does.
241
+ * A "//" pathname keeps its "/." prefix so the request still targets the same
242
+ * pathname on the app origin, as Next.js's full-URL fetch does.
241
243
  */
242
244
  function formatRscRequestPath(url) {
243
- return `${url.pathname.startsWith("//") ? `/.${url.pathname}` : url.pathname}${url.search}`;
245
+ return `${toPathAbsolutePathname(url.pathname)}${url.search}`;
244
246
  }
245
247
  async function createRscRequestUrl(href, headers) {
246
248
  const url = parseRscRequestPath(href);
@@ -275,55 +277,12 @@ async function resolveInvalidRscCacheBustingRequest(options) {
275
277
  const url = new URL(options.request.url);
276
278
  const actualHash = url.searchParams.get(VINEXT_RSC_CACHE_BUSTING_SEARCH_PARAM);
277
279
  const expectedHash = await computeRscCacheBustingSearchParam(options.request.headers);
278
- if (actualHash === null && expectedHash === "" && url.pathname.endsWith(".rsc")) return null;
279
- const acceptedHashes = /* @__PURE__ */ new Set([expectedHash]);
280
- if (actualHash !== null && actualHash !== expectedHash) {
281
- acceptedHashes.add(computeLegacyRscCacheBustingSearchParam(options.request.headers));
282
- const compatibilityInputs = [];
283
- const hasInterceptionId = options.request.headers.has(VINEXT_INTERCEPTION_ID_HEADER);
284
- const hasStateFingerprint = options.request.headers.has(VINEXT_RSC_STATE_FINGERPRINT_HEADER);
285
- const hasNormalRenderMode = normalizeRenderModeHeaderValue(options.request.headers.get(VINEXT_RSC_RENDER_MODE_HEADER)) === null;
286
- if (!hasInterceptionId) compatibilityInputs.push({ includeInterceptionIdHeader: false });
287
- if (hasStateFingerprint) {
288
- compatibilityInputs.push({ includeStateFingerprintHeader: false });
289
- if (!hasInterceptionId) compatibilityInputs.push({
290
- includeInterceptionIdHeader: false,
291
- includeStateFingerprintHeader: false
292
- });
293
- }
294
- if (hasNormalRenderMode) {
295
- compatibilityInputs.push({ includeRenderModeHeader: false });
296
- if (!hasInterceptionId) compatibilityInputs.push({
297
- includeInterceptionIdHeader: false,
298
- includeRenderModeHeader: false
299
- });
300
- }
301
- if (hasStateFingerprint && hasNormalRenderMode) {
302
- compatibilityInputs.push({
303
- includeRenderModeHeader: false,
304
- includeStateFingerprintHeader: false
305
- });
306
- if (!hasInterceptionId) compatibilityInputs.push({
307
- includeInterceptionIdHeader: false,
308
- includeRenderModeHeader: false,
309
- includeStateFingerprintHeader: false
310
- });
311
- }
312
- for (const compatibilityOptions of compatibilityInputs) {
313
- const input = createCacheBustingInput(options.request.headers, compatibilityOptions);
314
- if (input === null) acceptedHashes.add("");
315
- else {
316
- acceptedHashes.add(await sha256CacheBustingHash(input));
317
- acceptedHashes.add(fnv1a64(input));
318
- }
319
- }
320
- }
321
- if (actualHash !== null && acceptedHashes.has(actualHash)) return null;
280
+ if (actualHash !== null && (actualHash === expectedHash || actualHash === computeLegacyRscCacheBustingSearchParam(options.request.headers))) return null;
322
281
  setRscCacheBustingSearchParam(url, expectedHash);
323
282
  return new Response(null, {
324
283
  status: 307,
325
- headers: { Location: `${url.pathname}${url.search}` }
284
+ headers: { Location: formatRscRequestPath(url) }
326
285
  });
327
286
  }
328
287
  //#endregion
329
- export { VINEXT_RSC_BUILD_ID_HEADER, VINEXT_RSC_CACHE_BUSTING_SEARCH_PARAM, VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_RSC_VARY_HEADER, applyRscCompatibilityIdHeader, applyRscDeploymentIdHeader, canonicalizeLoadingShellRscRequestHeaders, canonicalizePrewarmableRscRequestHeaders, computeRscCacheBustingSearchParam, createCanonicalLoadingShellRscRequestHeaders, createCanonicalRscRequestHeaders, createCanonicalRscRequestUrl, createRscRedirectLocation, createRscRequestHeaders, createRscRequestUrl, createServerActionRequestUrl, formatRscRequestPath, getVinextRscCompatibilityId, hasRscCacheBustingSearchParam, isRscCompatibilityIdCompatible, keepOnlyValidatedRscCacheBustingSearchParam, parseRscRequestPath, resolveHardNavigationTargetFromRscResponse, resolveInvalidRscCacheBustingRequest, resolveRscCompatibilityNavigationDecision, setRscCacheBustingSearchParam, stripRscCacheBustingSearchParam, stripRscSuffix };
288
+ export { VINEXT_RSC_BUILD_ID_HEADER, VINEXT_RSC_CACHE_BUSTING_SEARCH_PARAM, VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_RSC_VARY_HEADER, applyRscCompatibilityIdHeader, applyRscDeploymentIdHeader, canonicalizeLoadingShellRscRequestHeaders, canonicalizePrewarmableRscRequestHeaders, computeRscCacheBustingSearchParam, createCanonicalLoadingShellRscRequestHeaders, createCanonicalRscRequestHeaders, createCanonicalRscRequestUrl, createRscRedirectLocation, createRscRequestHeaders, createRscRequestUrl, createServerActionRequestUrl, formatRscRequestPath, getVinextRscCompatibilityId, hasRscCacheBustingSearchParam, isRscCompatibilityIdCompatible, keepOnlyValidatedRscCacheBustingSearchParam, parseRscRequestPath, resolveHardNavigationTargetFromRscResponse, resolveInvalidRscCacheBustingRequest, resolveRscCompatibilityNavigationDecision, setRscCacheBustingSearchParam, stripRscCacheBustingSearchParam, toPathAbsolutePathname };
@@ -23,7 +23,7 @@ function createAppRscOnErrorHandler(reportRequestError, request, pathname, route
23
23
  routerKind: "App Router",
24
24
  routePath: routePath || pathname,
25
25
  routeType: overrides?.routeType ?? "render",
26
- renderSource: overrides?.renderSource ?? (request.headers.get("RSC") === "1" || new URL(request.url).pathname.endsWith(".rsc") ? "react-server-components-payload" : "react-server-components"),
26
+ renderSource: overrides?.renderSource ?? (request.headers.get("RSC") === "1" ? "react-server-components-payload" : "react-server-components"),
27
27
  revalidateReason: isOnDemandRevalidateRequest(request.headers.get("x-prerender-revalidate")) ? "on-demand" : overrides?.revalidateReason
28
28
  };
29
29
  let loggedErrors = loggedErrorsByRequest.get(request);
@@ -1,4 +1,6 @@
1
1
  import { isNavigationSignalError } from "../utils/navigation-signal.js";
2
+ import { logRequestError } from "./request-error-log.js";
3
+ import { rewriteClientHookError } from "../shims/client-hook-error.js";
2
4
  import { resolveAppPageSpecialError } from "./app-page-execution.js";
3
5
  import { isAppRenderAbortError } from "./app-render-abort-error.js";
4
6
  import { getProperError } from "./proper-error.js";
@@ -87,6 +89,7 @@ function createRscOnErrorHandler(options) {
87
89
  if (!hasDigest(properError) && (properError !== reportableError || options.attachDigest !== false)) try {
88
90
  Object.assign(properError, { digest });
89
91
  } catch {}
92
+ if (nodeEnv !== "production") rewriteClientHookError(properError);
90
93
  if (options.requestInfo && options.errorContext) options.reportRequestError(properError, options.requestInfo, options.errorContext);
91
94
  if (process.env.VINEXT_PRERENDER === "1" && error) {
92
95
  if (!loggedPrerenderErrors?.has(reportableError)) {
@@ -94,7 +97,7 @@ function createRscOnErrorHandler(options) {
94
97
  const route = options.requestInfo?.path ?? options.errorContext?.routePath ?? "<unknown>";
95
98
  console.error(`[vinext] Error prerendering route ${JSON.stringify(route)}:`, reportableError);
96
99
  }
97
- } else if (!(reportableError !== error && thrownHasDigest) && !wasLogged(reportableError)) console.error("[vinext] Server render error:", properError);
100
+ } else if (!(reportableError !== error && thrownHasDigest) && !wasLogged(reportableError)) logRequestError("[vinext] Server render error:", properError);
98
101
  return digest;
99
102
  };
100
103
  }
@@ -6,6 +6,7 @@ import { ExecutionContextLike } from "../shims/request-context.js";
6
6
  import { AppMiddlewareContext, ApplyAppMiddlewareResult } from "./app-middleware.js";
7
7
  import { RootParams } from "../shims/root-params.js";
8
8
  import { AppPagePprFallbackCacheShell } from "./app-ppr-fallback-shell.js";
9
+ import { AppPageNavigationParamSlot } from "./app-page-navigation-params.js";
9
10
  import { PagesRouteDataKind } from "./pages-route-data-kind.js";
10
11
  import { AppPrerenderRootParamNamesMap, AppPrerenderStaticParamsMap } from "./app-prerender-endpoints.js";
11
12
  import { TrustedPrerenderState } from "./prerender-route-params.js";
@@ -41,9 +42,17 @@ export type AppRscHandlerRoute = {
41
42
  rootParamNames?: readonly string[];
42
43
  routeHandler?: unknown;
43
44
  routeSegments: readonly string[];
44
- slots?: AppRouteTreePrefetchRoute["slots"];
45
+ slots?: Readonly<Record<string, NonNullable<AppRouteTreePrefetchRoute["slots"]>[string] & AppPageNavigationParamSlot>> | null;
45
46
  };
46
47
  type AppRscRouteMatch<TRoute> = {
48
+ /**
49
+ * For an interception match, the source route and params the interception
50
+ * context names, when they differ from the rendered `route` and `params`:
51
+ * a sibling intercept opened from a deeper source renders the intercepting
52
+ * route itself, while the deeper source is what gets authorized.
53
+ */
54
+ authorizationParams?: AppPageParams;
55
+ authorizationRoute?: TRoute;
47
56
  interceptionSourceIsConcrete?: boolean;
48
57
  params: AppPageParams;
49
58
  route: TRoute;