vinext 0.1.2 → 0.1.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 (199) hide show
  1. package/dist/build/prerender.d.ts +9 -1
  2. package/dist/build/prerender.js +41 -12
  3. package/dist/build/run-prerender.d.ts +10 -2
  4. package/dist/build/run-prerender.js +15 -1
  5. package/dist/client/app-nav-failure-handler.d.ts +8 -0
  6. package/dist/client/app-nav-failure-handler.js +44 -0
  7. package/dist/client/vinext-next-data.d.ts +18 -1
  8. package/dist/client/window-next.d.ts +2 -1
  9. package/dist/client/window-next.js +12 -1
  10. package/dist/cloudflare/src/cache/cdn-adapter.runtime.js +6 -1
  11. package/dist/config/config-matchers.js +73 -14
  12. package/dist/config/next-config.d.ts +46 -4
  13. package/dist/config/next-config.js +147 -48
  14. package/dist/deploy.d.ts +30 -11
  15. package/dist/deploy.js +180 -99
  16. package/dist/entries/app-browser-entry.d.ts +9 -3
  17. package/dist/entries/app-browser-entry.js +21 -3
  18. package/dist/entries/app-rsc-entry.d.ts +2 -0
  19. package/dist/entries/app-rsc-entry.js +64 -5
  20. package/dist/entries/app-rsc-manifest.js +2 -0
  21. package/dist/entries/app-ssr-entry.js +1 -1
  22. package/dist/entries/pages-client-entry.js +53 -8
  23. package/dist/entries/pages-server-entry.js +41 -5
  24. package/dist/index.js +200 -62
  25. package/dist/plugins/extensionless-dynamic-import.d.ts +6 -0
  26. package/dist/plugins/extensionless-dynamic-import.js +152 -0
  27. package/dist/plugins/optimize-imports.d.ts +2 -1
  28. package/dist/plugins/optimize-imports.js +11 -9
  29. package/dist/plugins/postcss.js +7 -7
  30. package/dist/plugins/typeof-window.d.ts +14 -0
  31. package/dist/plugins/typeof-window.js +150 -0
  32. package/dist/routing/app-route-graph.d.ts +2 -1
  33. package/dist/routing/app-route-graph.js +44 -14
  34. package/dist/routing/file-matcher.d.ts +10 -1
  35. package/dist/routing/file-matcher.js +22 -1
  36. package/dist/routing/pages-router.js +3 -3
  37. package/dist/routing/utils.d.ts +35 -6
  38. package/dist/routing/utils.js +59 -7
  39. package/dist/server/api-handler.d.ts +6 -1
  40. package/dist/server/api-handler.js +21 -15
  41. package/dist/server/app-browser-action-result.d.ts +19 -6
  42. package/dist/server/app-browser-action-result.js +19 -10
  43. package/dist/server/app-browser-entry.js +167 -90
  44. package/dist/server/app-browser-error.d.ts +10 -6
  45. package/dist/server/app-browser-error.js +43 -8
  46. package/dist/server/app-browser-hydration.d.ts +2 -0
  47. package/dist/server/app-browser-hydration.js +1 -0
  48. package/dist/server/app-browser-navigation-controller.d.ts +4 -2
  49. package/dist/server/app-browser-navigation-controller.js +23 -2
  50. package/dist/server/app-browser-server-action-navigation.d.ts +6 -0
  51. package/dist/server/app-browser-server-action-navigation.js +9 -0
  52. package/dist/server/app-browser-stream.js +86 -43
  53. package/dist/server/app-elements-wire.d.ts +6 -1
  54. package/dist/server/app-elements-wire.js +14 -4
  55. package/dist/server/app-elements.d.ts +2 -2
  56. package/dist/server/app-elements.js +2 -2
  57. package/dist/server/app-fallback-renderer.d.ts +1 -0
  58. package/dist/server/app-fallback-renderer.js +3 -1
  59. package/dist/server/app-optimistic-routing.js +2 -2
  60. package/dist/server/app-page-boundary-render.d.ts +1 -0
  61. package/dist/server/app-page-boundary-render.js +27 -14
  62. package/dist/server/app-page-cache-render.d.ts +53 -0
  63. package/dist/server/app-page-cache-render.js +91 -0
  64. package/dist/server/app-page-cache.d.ts +16 -2
  65. package/dist/server/app-page-cache.js +62 -1
  66. package/dist/server/app-page-dispatch.d.ts +26 -0
  67. package/dist/server/app-page-dispatch.js +149 -92
  68. package/dist/server/app-page-element-builder.d.ts +1 -0
  69. package/dist/server/app-page-element-builder.js +5 -2
  70. package/dist/server/app-page-execution.d.ts +6 -1
  71. package/dist/server/app-page-execution.js +21 -1
  72. package/dist/server/app-page-probe.d.ts +1 -0
  73. package/dist/server/app-page-probe.js +4 -0
  74. package/dist/server/app-page-render-observation.d.ts +3 -1
  75. package/dist/server/app-page-render-observation.js +17 -1
  76. package/dist/server/app-page-render.d.ts +12 -1
  77. package/dist/server/app-page-render.js +42 -4
  78. package/dist/server/app-page-request.d.ts +2 -0
  79. package/dist/server/app-page-request.js +2 -1
  80. package/dist/server/app-page-route-wiring.d.ts +3 -1
  81. package/dist/server/app-page-route-wiring.js +14 -5
  82. package/dist/server/app-page-stream.d.ts +15 -3
  83. package/dist/server/app-page-stream.js +11 -5
  84. package/dist/server/app-pages-bridge.d.ts +18 -0
  85. package/dist/server/app-pages-bridge.js +22 -5
  86. package/dist/server/app-ppr-fallback-shell-render.d.ts +17 -0
  87. package/dist/server/app-ppr-fallback-shell-render.js +26 -0
  88. package/dist/server/app-ppr-fallback-shell.d.ts +13 -1
  89. package/dist/server/app-ppr-fallback-shell.js +8 -1
  90. package/dist/server/app-route-handler-dispatch.js +9 -2
  91. package/dist/server/app-route-handler-policy.d.ts +1 -0
  92. package/dist/server/app-router-entry.js +5 -0
  93. package/dist/server/app-rsc-cache-busting.js +2 -0
  94. package/dist/server/app-rsc-handler.d.ts +25 -0
  95. package/dist/server/app-rsc-handler.js +154 -54
  96. package/dist/server/app-rsc-route-matching.d.ts +3 -0
  97. package/dist/server/app-rsc-route-matching.js +2 -0
  98. package/dist/server/app-segment-config.d.ts +9 -1
  99. package/dist/server/app-segment-config.js +12 -3
  100. package/dist/server/app-server-action-execution.d.ts +1 -0
  101. package/dist/server/app-server-action-execution.js +42 -13
  102. package/dist/server/app-ssr-entry.d.ts +2 -0
  103. package/dist/server/app-ssr-entry.js +83 -10
  104. package/dist/server/cache-control.js +4 -0
  105. package/dist/server/dev-server.d.ts +2 -2
  106. package/dist/server/dev-server.js +244 -51
  107. package/dist/server/hybrid-route-priority.d.ts +22 -0
  108. package/dist/server/hybrid-route-priority.js +33 -0
  109. package/dist/server/image-optimization.d.ts +18 -9
  110. package/dist/server/image-optimization.js +37 -23
  111. package/dist/server/implicit-tags.d.ts +2 -1
  112. package/dist/server/implicit-tags.js +4 -1
  113. package/dist/server/navigation-planner.d.ts +133 -30
  114. package/dist/server/navigation-planner.js +114 -0
  115. package/dist/server/navigation-trace.d.ts +8 -1
  116. package/dist/server/navigation-trace.js +8 -1
  117. package/dist/server/pages-api-route.d.ts +6 -0
  118. package/dist/server/pages-api-route.js +13 -2
  119. package/dist/server/pages-asset-tags.d.ts +2 -1
  120. package/dist/server/pages-asset-tags.js +6 -2
  121. package/dist/server/pages-data-route.d.ts +8 -1
  122. package/dist/server/pages-data-route.js +11 -2
  123. package/dist/server/pages-get-initial-props.d.ts +54 -4
  124. package/dist/server/pages-get-initial-props.js +43 -1
  125. package/dist/server/pages-node-compat.js +2 -2
  126. package/dist/server/pages-page-data.d.ts +11 -2
  127. package/dist/server/pages-page-data.js +204 -33
  128. package/dist/server/pages-page-handler.d.ts +4 -2
  129. package/dist/server/pages-page-handler.js +59 -22
  130. package/dist/server/pages-page-response.d.ts +2 -1
  131. package/dist/server/pages-page-response.js +7 -4
  132. package/dist/server/pages-request-pipeline.d.ts +1 -0
  133. package/dist/server/pages-request-pipeline.js +73 -36
  134. package/dist/server/pregenerated-concrete-paths.d.ts +1 -17
  135. package/dist/server/pregenerated-concrete-paths.js +2 -19
  136. package/dist/server/prerender-manifest.d.ts +33 -0
  137. package/dist/server/prerender-manifest.js +54 -0
  138. package/dist/server/prerender-route-params.d.ts +1 -2
  139. package/dist/server/prod-server.js +9 -3
  140. package/dist/server/request-pipeline.d.ts +3 -15
  141. package/dist/server/request-pipeline.js +58 -47
  142. package/dist/server/rsc-stream-hints.d.ts +5 -1
  143. package/dist/server/rsc-stream-hints.js +6 -1
  144. package/dist/server/seed-cache.js +10 -18
  145. package/dist/shims/app-router-scroll-state.d.ts +3 -1
  146. package/dist/shims/app-router-scroll-state.js +14 -2
  147. package/dist/shims/app-router-scroll.d.ts +3 -0
  148. package/dist/shims/app-router-scroll.js +28 -18
  149. package/dist/shims/cache-runtime.js +3 -2
  150. package/dist/shims/cache.d.ts +1 -0
  151. package/dist/shims/cache.js +1 -1
  152. package/dist/shims/cdn-cache.d.ts +5 -5
  153. package/dist/shims/dynamic-preload-chunks.js +6 -4
  154. package/dist/shims/error-boundary.d.ts +2 -0
  155. package/dist/shims/error-boundary.js +7 -0
  156. package/dist/shims/error.js +3 -2
  157. package/dist/shims/error.react-server.d.ts +9 -0
  158. package/dist/shims/error.react-server.js +6 -0
  159. package/dist/shims/fetch-cache.d.ts +3 -1
  160. package/dist/shims/fetch-cache.js +45 -20
  161. package/dist/shims/hash-scroll.js +6 -1
  162. package/dist/shims/headers.js +29 -4
  163. package/dist/shims/internal/als-registry.js +28 -1
  164. package/dist/shims/internal/app-route-detection.js +8 -17
  165. package/dist/shims/internal/hybrid-client-route-owner.d.ts +31 -0
  166. package/dist/shims/internal/hybrid-client-route-owner.js +143 -0
  167. package/dist/shims/internal/navigation-untracked.d.ts +35 -0
  168. package/dist/shims/internal/navigation-untracked.js +55 -0
  169. package/dist/shims/internal/pages-data-target.d.ts +7 -2
  170. package/dist/shims/internal/pages-data-target.js +17 -8
  171. package/dist/shims/internal/pages-router-accessor.d.ts +19 -0
  172. package/dist/shims/internal/pages-router-accessor.js +13 -0
  173. package/dist/shims/internal/router-context.d.ts +2 -1
  174. package/dist/shims/internal/router-context.js +3 -1
  175. package/dist/shims/link.js +12 -5
  176. package/dist/shims/navigation.d.ts +8 -2
  177. package/dist/shims/navigation.js +61 -31
  178. package/dist/shims/ppr-fallback-shell.d.ts +5 -1
  179. package/dist/shims/ppr-fallback-shell.js +28 -7
  180. package/dist/shims/router.d.ts +13 -2
  181. package/dist/shims/router.js +419 -128
  182. package/dist/shims/server.d.ts +16 -1
  183. package/dist/shims/server.js +44 -12
  184. package/dist/shims/unified-request-context.js +1 -0
  185. package/dist/utils/built-asset-url.d.ts +4 -0
  186. package/dist/utils/built-asset-url.js +11 -0
  187. package/dist/utils/commonjs-loader.d.ts +16 -0
  188. package/dist/utils/commonjs-loader.js +100 -0
  189. package/dist/utils/deployment-id.d.ts +8 -0
  190. package/dist/utils/deployment-id.js +22 -0
  191. package/dist/utils/html-limited-bots.d.ts +18 -1
  192. package/dist/utils/html-limited-bots.js +23 -1
  193. package/dist/utils/parse-cookie.d.ts +13 -0
  194. package/dist/utils/parse-cookie.js +52 -0
  195. package/dist/utils/path.d.ts +7 -1
  196. package/dist/utils/path.js +9 -1
  197. package/package.json +2 -2
  198. package/dist/shims/internal/parse-cookie-header.d.ts +0 -14
  199. package/dist/shims/internal/parse-cookie-header.js +0 -30
@@ -201,6 +201,7 @@ function toWebHeaders(headersRecord) {
201
201
  }
202
202
  function appendWebHeader(headers, key, value) {
203
203
  if (value === void 0) return;
204
+ if (key.startsWith(":")) return;
204
205
  if (Array.isArray(value)) {
205
206
  for (const item of value) headers.append(key, item);
206
207
  return;
@@ -783,7 +784,7 @@ async function startAppRouterServer(options) {
783
784
  }
784
785
  }
785
786
  if (isImageOptimizationPath(pathname)) {
786
- const params = parseImageParams(new URL(rawUrl, "http://localhost"), [...DEFAULT_DEVICE_SIZES, ...DEFAULT_IMAGE_SIZES]);
787
+ const params = parseImageParams(new URL(rawUrl, "http://localhost"), [...imageConfig?.deviceSizes ?? DEFAULT_DEVICE_SIZES, ...imageConfig?.imageSizes ?? DEFAULT_IMAGE_SIZES], imageConfig?.qualities);
787
788
  if (!params) {
788
789
  res.writeHead(400);
789
790
  res.end("Bad Request");
@@ -891,6 +892,7 @@ async function startPagesRouterServer(options) {
891
892
  const pagesImageConfig = vinextConfig?.images ? {
892
893
  dangerouslyAllowSVG: vinextConfig.images.dangerouslyAllowSVG,
893
894
  dangerouslyAllowLocalIP: vinextConfig.images.dangerouslyAllowLocalIP,
895
+ qualities: vinextConfig.images.qualities,
894
896
  contentDispositionType: vinextConfig.images.contentDispositionType,
895
897
  contentSecurityPolicy: vinextConfig.images.contentSecurityPolicy
896
898
  } : void 0;
@@ -960,7 +962,7 @@ async function startPagesRouterServer(options) {
960
962
  return;
961
963
  }
962
964
  if (isImageOptimizationPath(pathname) || isImageOptimizationPath(staticLookupPath)) {
963
- const params = parseImageParams(new URL(rawUrl, "http://localhost"), allowedImageWidths);
965
+ const params = parseImageParams(new URL(rawUrl, "http://localhost"), allowedImageWidths, pagesImageConfig?.qualities);
964
966
  if (!params) {
965
967
  res.writeHead(400);
966
968
  res.end("Bad Request");
@@ -991,6 +993,7 @@ async function startPagesRouterServer(options) {
991
993
  }
992
994
  }
993
995
  let isDataReq = false;
996
+ const originalRenderUrl = url;
994
997
  if (isNextDataPathname(pathname)) {
995
998
  const dataMatch = pagesBuildId ? parseNextDataPathname(pathname, pagesBuildId) : null;
996
999
  if (!dataMatch) {
@@ -1028,7 +1031,10 @@ async function startPagesRouterServer(options) {
1028
1031
  rawSearch: rawQs,
1029
1032
  matchPageRoute: matchPageRoute ?? null,
1030
1033
  runMiddleware: typeof runMiddleware === "function" ? wrapMiddlewareWithBasePath(runMiddleware, basePath, hadBasePath) : null,
1031
- renderPage: typeof renderPage === "function" ? (request, resolvedUrl, options, stagedHeaders) => renderPage(request, resolvedUrl, ssrManifest, void 0, stagedHeaders, options) : null,
1034
+ renderPage: typeof renderPage === "function" ? (request, resolvedUrl, options, stagedHeaders) => renderPage(request, resolvedUrl, ssrManifest, void 0, stagedHeaders, {
1035
+ ...options,
1036
+ originalUrl: originalRenderUrl
1037
+ }) : null,
1032
1038
  handleApi: typeof handleApi === "function" ? (request, apiUrl) => handleApi(request, apiUrl, createNodeExecutionContext()) : null,
1033
1039
  serveStaticFile: async (requestPathname, stagedHeaders) => {
1034
1040
  if (requestPathname === "/" || requestPathname.startsWith("/api/") || requestPathname.startsWith(`/_next/static/`)) return false;
@@ -76,7 +76,8 @@ type ApplyConfigHeadersOptions = {
76
76
  * (basePath: true) rule for backward compatibility — callers that need to
77
77
  * support `basePath: false` headers must pass this in.
78
78
  */
79
- basePathState?: BasePathMatchState;
79
+ basePathState?: BasePathMatchState; /** Existing framework-generated headers that matching config rules may replace. */
80
+ overwriteExisting?: ReadonlySet<string>;
80
81
  };
81
82
  type StaticFileSignalContext = {
82
83
  headers: Headers | null;
@@ -157,19 +158,6 @@ declare function validateCsrfOrigin(request: Request, allowedOrigins?: string[])
157
158
  */
158
159
  declare function validateServerActionPayload(body: string | FormData): Promise<Response | null>;
159
160
  declare function isOriginAllowed(origin: string, allowed: string[]): boolean;
160
- /**
161
- * Validate an image optimization URL parameter.
162
- *
163
- * Ensures the URL is a relative path that doesn't escape the origin:
164
- * - Must start with "/" but not "//"
165
- * - Backslashes are normalized (browsers treat `\` as `/`)
166
- * - Origin validation as defense-in-depth
167
- *
168
- * @param rawUrl - The raw `url` query parameter value
169
- * @param requestUrl - The full request URL for origin comparison
170
- * @returns An error Response if validation fails, or the normalized image URL
171
- */
172
- declare function validateImageUrl(rawUrl: string | null, requestUrl: string): Response | string;
173
161
  /**
174
162
  * Strip internal `x-middleware-*` headers from a Headers object.
175
163
  *
@@ -219,4 +207,4 @@ declare function cloneRequestWithHeaders(request: Request, headers: Headers): Re
219
207
  */
220
208
  declare function cloneRequestWithUrl(request: Request, url: string): Request;
221
209
  //#endregion
222
- export { HeaderRecord, INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, applyConfigHeadersToHeaderRecord, applyConfigHeadersToResponse, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, stripBasePath, validateCsrfOrigin, validateImageUrl, validateServerActionPayload };
210
+ export { HeaderRecord, INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, applyConfigHeadersToHeaderRecord, applyConfigHeadersToResponse, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, stripBasePath, validateCsrfOrigin, validateServerActionPayload };
@@ -122,7 +122,7 @@ function applyConfigHeadersToResponse(responseHeaders, options) {
122
122
  for (const header of matched) {
123
123
  const lowerName = header.key.toLowerCase();
124
124
  if (lowerName === "vary" || lowerName === "set-cookie") responseHeaders.append(header.key, header.value);
125
- else if (!responseHeaders.has(lowerName)) responseHeaders.set(header.key, header.value);
125
+ else if (options.overwriteExisting?.has(lowerName) || !responseHeaders.has(lowerName)) responseHeaders.set(header.key, header.value);
126
126
  }
127
127
  }
128
128
  /**
@@ -197,9 +197,10 @@ function normalizeTrailingSlash(pathname, basePath, trailingSlash, search) {
197
197
  if (isOpenRedirectShaped(pathname)) return notFoundResponse();
198
198
  const normalizedPathname = normalizeTrailingSlashPathname(pathname, trailingSlash);
199
199
  if (normalizedPathname === null) return null;
200
+ const encodedPathname = normalizedPathname.replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@/%]/gu, encodeURIComponent);
200
201
  return new Response(null, {
201
202
  status: 308,
202
- headers: { Location: basePath + normalizedPathname + search }
203
+ headers: { Location: basePath + encodedPathname + search }
203
204
  });
204
205
  }
205
206
  /**
@@ -246,45 +247,74 @@ function validateCsrfOrigin(request, allowedOrigins = []) {
246
247
  * Regular user form fields are ignored entirely.
247
248
  */
248
249
  async function validateServerActionPayload(body) {
249
- const containerRefRe = /"\$([QWi])(\d+)"/g;
250
+ const maxNumericFields = 4096;
251
+ const maxContainerReferences = 16384;
252
+ const maxContainerDepth = 1024;
253
+ const containerRefRe = /"\$([QWi])([0-9a-f]+)(?=[:"])/gi;
250
254
  const fieldRefs = /* @__PURE__ */ new Map();
255
+ let referenceCount = 0;
256
+ const invalidPayloadResponse = () => new Response("Invalid server action payload", {
257
+ status: 400,
258
+ headers: { "Content-Type": "text/plain" }
259
+ });
251
260
  const collectRefs = (fieldKey, text) => {
261
+ if (fieldRefs.has(fieldKey)) return true;
262
+ if (fieldRefs.size >= maxNumericFields) return false;
252
263
  const refs = /* @__PURE__ */ new Set();
253
264
  let match;
254
265
  containerRefRe.lastIndex = 0;
255
- while ((match = containerRefRe.exec(text)) !== null) refs.add(match[2]);
266
+ while ((match = containerRefRe.exec(text)) !== null) {
267
+ const previousSize = refs.size;
268
+ refs.add(String(Number.parseInt(match[2], 16)));
269
+ if (refs.size !== previousSize && ++referenceCount > maxContainerReferences) return false;
270
+ }
256
271
  fieldRefs.set(fieldKey, refs);
272
+ return true;
257
273
  };
258
- if (typeof body === "string") collectRefs("0", body);
259
- else for (const [key, value] of body.entries()) {
274
+ if (typeof body === "string") {
275
+ if (!collectRefs("0", body)) return invalidPayloadResponse();
276
+ } else for (const [key, value] of body.entries()) {
260
277
  if (!/^\d+$/.test(key)) continue;
261
278
  if (typeof value === "string") {
262
- collectRefs(key, value);
279
+ if (!collectRefs(key, value)) return invalidPayloadResponse();
263
280
  continue;
264
281
  }
265
- if (typeof value?.text === "function") collectRefs(key, await value.text());
282
+ if (typeof value?.text === "function") {
283
+ if (!collectRefs(key, await value.text())) return invalidPayloadResponse();
284
+ }
266
285
  }
267
286
  if (fieldRefs.size === 0) return null;
268
287
  const knownFields = new Set(fieldRefs.keys());
269
- for (const refs of fieldRefs.values()) for (const ref of refs) if (!knownFields.has(ref)) return new Response("Invalid server action payload", {
270
- status: 400,
271
- headers: { "Content-Type": "text/plain" }
272
- });
273
- const visited = /* @__PURE__ */ new Set();
274
- const stack = /* @__PURE__ */ new Set();
275
- const hasCycle = (node) => {
276
- if (stack.has(node)) return true;
277
- if (visited.has(node)) return false;
278
- visited.add(node);
279
- stack.add(node);
280
- for (const ref of fieldRefs.get(node) ?? []) if (hasCycle(ref)) return true;
281
- stack.delete(node);
282
- return false;
283
- };
284
- for (const node of fieldRefs.keys()) if (hasCycle(node)) return new Response("Invalid server action payload", {
285
- status: 400,
286
- headers: { "Content-Type": "text/plain" }
287
- });
288
+ for (const refs of fieldRefs.values()) for (const ref of refs) if (!knownFields.has(ref)) return invalidPayloadResponse();
289
+ const state = /* @__PURE__ */ new Map();
290
+ for (const root of fieldRefs.keys()) {
291
+ if (state.has(root)) continue;
292
+ const stack = [{
293
+ node: root,
294
+ refs: [...fieldRefs.get(root) ?? []],
295
+ nextRef: 0
296
+ }];
297
+ state.set(root, "visiting");
298
+ while (stack.length > 0) {
299
+ if (stack.length > maxContainerDepth) return invalidPayloadResponse();
300
+ const frame = stack[stack.length - 1];
301
+ const ref = frame.refs[frame.nextRef++];
302
+ if (ref === void 0) {
303
+ state.set(frame.node, "visited");
304
+ stack.pop();
305
+ continue;
306
+ }
307
+ const refState = state.get(ref);
308
+ if (refState === "visiting") return invalidPayloadResponse();
309
+ if (refState === "visited") continue;
310
+ state.set(ref, "visiting");
311
+ stack.push({
312
+ node: ref,
313
+ refs: [...fieldRefs.get(ref) ?? []],
314
+ nextRef: 0
315
+ });
316
+ }
317
+ }
288
318
  return null;
289
319
  }
290
320
  /**
@@ -329,25 +359,6 @@ function isOriginAllowed(origin, allowed) {
329
359
  return false;
330
360
  }
331
361
  /**
332
- * Validate an image optimization URL parameter.
333
- *
334
- * Ensures the URL is a relative path that doesn't escape the origin:
335
- * - Must start with "/" but not "//"
336
- * - Backslashes are normalized (browsers treat `\` as `/`)
337
- * - Origin validation as defense-in-depth
338
- *
339
- * @param rawUrl - The raw `url` query parameter value
340
- * @param requestUrl - The full request URL for origin comparison
341
- * @returns An error Response if validation fails, or the normalized image URL
342
- */
343
- function validateImageUrl(rawUrl, requestUrl) {
344
- const imgUrl = rawUrl?.replaceAll("\\", "/") ?? null;
345
- if (!imgUrl || !imgUrl.startsWith("/") || imgUrl.startsWith("//")) return new Response(!rawUrl ? "Missing url parameter" : "Only relative URLs allowed", { status: 400 });
346
- const url = new URL(requestUrl);
347
- if (new URL(imgUrl, url.origin).origin !== url.origin) return new Response("Only relative URLs allowed", { status: 400 });
348
- return imgUrl;
349
- }
350
- /**
351
362
  * Strip internal `x-middleware-*` headers from a Headers object.
352
363
  *
353
364
  * Middleware uses `x-middleware-*` headers as internal signals (e.g.
@@ -465,4 +476,4 @@ function cloneRequestWithUrl(request, url) {
465
476
  return cloned;
466
477
  }
467
478
  //#endregion
468
- export { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, applyConfigHeadersToHeaderRecord, applyConfigHeadersToResponse, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, stripBasePath, validateCsrfOrigin, validateImageUrl, validateServerActionPayload };
479
+ export { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, applyConfigHeadersToHeaderRecord, applyConfigHeadersToResponse, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, stripBasePath, validateCsrfOrigin, validateServerActionPayload };
@@ -1,6 +1,10 @@
1
1
  //#region src/server/rsc-stream-hints.d.ts
2
2
  declare function normalizeReactFlightPreloadHints(stream: ReadableStream<Uint8Array>): ReadableStream<Uint8Array>;
3
3
  type RscRawRenderer = (model: unknown, options?: unknown) => ReadableStream<Uint8Array>;
4
+ type RscRawPrerenderer = (model: unknown, options?: unknown) => Promise<{
5
+ prelude: ReadableStream<Uint8Array>;
6
+ }>;
4
7
  declare function createRscRenderer(render: RscRawRenderer): RscRawRenderer;
8
+ declare function createRscPrerenderer(prerender: RscRawPrerenderer): RscRawPrerenderer;
5
9
  //#endregion
6
- export { createRscRenderer, normalizeReactFlightPreloadHints };
10
+ export { RscRawPrerenderer, RscRawRenderer, createRscPrerenderer, createRscRenderer, normalizeReactFlightPreloadHints };
@@ -32,5 +32,10 @@ function normalizeReactFlightPreloadHints(stream) {
32
32
  function createRscRenderer(render) {
33
33
  return (model, options) => normalizeReactFlightPreloadHints(render(model, options));
34
34
  }
35
+ function createRscPrerenderer(prerender) {
36
+ return async (model, options) => {
37
+ return { prelude: normalizeReactFlightPreloadHints((await prerender(model, options)).prelude) };
38
+ };
39
+ }
35
40
  //#endregion
36
- export { createRscRenderer, normalizeReactFlightPreloadHints };
41
+ export { createRscPrerenderer, createRscRenderer, normalizeReactFlightPreloadHints };
@@ -1,8 +1,8 @@
1
- import { normalizePathnameForRouteMatch } from "../routing/utils.js";
2
- import { normalizePath } from "./normalize-path.js";
3
1
  import { isrCacheKey, isrSetPrerenderedAppPage } from "./isr-cache.js";
4
2
  import { buildAppPageCacheTags } from "./app-page-cache.js";
5
3
  import { getOutputPath, getRscOutputPath } from "../utils/prerender-output-paths.js";
4
+ import { addPregeneratedConcretePath, clearPregeneratedConcretePaths, normalizePregeneratedPathname } from "./pregenerated-concrete-paths.js";
5
+ import { getRenderedAppRoutes, isFallbackShellArtifactPath, readPrerenderManifest } from "./prerender-manifest.js";
6
6
  import fs from "node:fs";
7
7
  import path from "node:path";
8
8
  //#region src/server/seed-cache.ts
@@ -46,26 +46,21 @@ import path from "node:path";
46
46
  * @returns The number of routes seeded (0 if no manifest or no renderable routes).
47
47
  */
48
48
  async function seedMemoryCacheFromPrerender(serverDir, options) {
49
- const manifestPath = path.join(serverDir, "vinext-prerender.json");
50
- if (!fs.existsSync(manifestPath)) return 0;
51
- let manifest;
52
- try {
53
- manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
54
- } catch (err) {
55
- console.warn("[vinext] Failed to parse vinext-prerender.json, skipping cache seeding:", err);
56
- return 0;
57
- }
49
+ clearPregeneratedConcretePaths();
50
+ const manifest = readPrerenderManifest(path.join(serverDir, "vinext-prerender.json"));
51
+ if (!manifest) return 0;
58
52
  const { buildId, routes } = manifest;
59
53
  if (!buildId || !Array.isArray(routes)) return 0;
60
54
  const trailingSlash = manifest.trailingSlash ?? false;
61
55
  const prerenderDir = path.join(serverDir, "prerendered-routes");
62
56
  const writeAppPageEntry = options?.writeAppPageEntry ?? createDefaultAppPageEntryWriter();
63
57
  let seeded = 0;
64
- for (const route of routes) {
65
- if (route.status !== "rendered") continue;
66
- if (route.router !== "app") continue;
58
+ const appRoutes = getRenderedAppRoutes(routes);
59
+ for (const route of appRoutes) {
60
+ const concretePathname = route.path ?? route.route;
61
+ if (!isFallbackShellArtifactPath(concretePathname, route)) addPregeneratedConcretePath(route.route, concretePathname);
67
62
  const artifactPathname = route.path ?? route.route;
68
- const cachePathname = normalizePrerenderCachePathname(artifactPathname);
63
+ const cachePathname = normalizePregeneratedPathname(artifactPathname);
69
64
  const baseKey = isrCacheKey("app", cachePathname, buildId);
70
65
  const htmlKey = options?.buildAppPageHtmlKey?.(cachePathname) ?? baseKey + ":html";
71
66
  const rscKey = options?.buildAppPageRscKey?.(cachePathname) ?? baseKey + ":rsc";
@@ -79,9 +74,6 @@ async function seedMemoryCacheFromPrerender(serverDir, options) {
79
74
  }
80
75
  return seeded;
81
76
  }
82
- function normalizePrerenderCachePathname(pathname) {
83
- return normalizePath(normalizePathnameForRouteMatch(pathname));
84
- }
85
77
  function createDefaultAppPageEntryWriter() {
86
78
  return (key, data, metadata) => isrSetPrerenderedAppPage(key, data, metadata);
87
79
  }
@@ -3,11 +3,13 @@ type AppRouterScrollIntent = Readonly<{
3
3
  commitId: number | null;
4
4
  hash: string | null;
5
5
  id: number;
6
+ targetHoistedInHead: boolean;
6
7
  }>;
7
8
  declare function beginAppRouterScrollIntent(hash: string | null): AppRouterScrollIntent;
8
9
  declare function clearAppRouterScrollIntent(): void;
9
10
  declare function getPendingAppRouterScrollIntent(): AppRouterScrollIntent | null;
10
11
  declare function claimAppRouterScrollIntentForCommit(expected: AppRouterScrollIntent | null | undefined, commitId: number): void;
12
+ declare function markAppRouterScrollIntentHeadHoisted(expected: AppRouterScrollIntent | null | undefined, commitId: number): void;
11
13
  declare function consumeAppRouterScrollIntent(expected: AppRouterScrollIntent | null | undefined, commitId?: number): AppRouterScrollIntent | null;
12
14
  //#endregion
13
- export { AppRouterScrollIntent, beginAppRouterScrollIntent, claimAppRouterScrollIntentForCommit, clearAppRouterScrollIntent, consumeAppRouterScrollIntent, getPendingAppRouterScrollIntent };
15
+ export { AppRouterScrollIntent, beginAppRouterScrollIntent, claimAppRouterScrollIntentForCommit, clearAppRouterScrollIntent, consumeAppRouterScrollIntent, getPendingAppRouterScrollIntent, markAppRouterScrollIntentHeadHoisted };
@@ -14,7 +14,8 @@ function beginAppRouterScrollIntent(hash) {
14
14
  const intent = {
15
15
  commitId: null,
16
16
  hash,
17
- id: store.nextId
17
+ id: store.nextId,
18
+ targetHoistedInHead: false
18
19
  };
19
20
  store.pending = intent;
20
21
  return intent;
@@ -35,6 +36,17 @@ function claimAppRouterScrollIntentForCommit(expected, commitId) {
35
36
  commitId
36
37
  };
37
38
  }
39
+ function markAppRouterScrollIntentHeadHoisted(expected, commitId) {
40
+ const store = getScrollIntentStore();
41
+ const intent = store.pending;
42
+ if (expected === null || expected === void 0 || intent === null) return;
43
+ if (intent.id !== expected.id) return;
44
+ if (intent.commitId !== commitId) return;
45
+ store.pending = {
46
+ ...intent,
47
+ targetHoistedInHead: true
48
+ };
49
+ }
38
50
  function consumeAppRouterScrollIntent(expected, commitId) {
39
51
  if (expected === null || expected === void 0) return null;
40
52
  const store = getScrollIntentStore();
@@ -46,4 +58,4 @@ function consumeAppRouterScrollIntent(expected, commitId) {
46
58
  return intent;
47
59
  }
48
60
  //#endregion
49
- export { beginAppRouterScrollIntent, claimAppRouterScrollIntentForCommit, clearAppRouterScrollIntent, consumeAppRouterScrollIntent, getPendingAppRouterScrollIntent };
61
+ export { beginAppRouterScrollIntent, claimAppRouterScrollIntentForCommit, clearAppRouterScrollIntent, consumeAppRouterScrollIntent, getPendingAppRouterScrollIntent, markAppRouterScrollIntentHeadHoisted };
@@ -5,9 +5,12 @@ declare class AppRouterScrollTargetInner extends React$1.Component<{
5
5
  children: React$1.ReactNode;
6
6
  commitId: number | null;
7
7
  }> {
8
+ scheduledCommitId: number | null;
9
+ schedulePotentialScroll: () => void;
8
10
  handlePotentialScroll: () => void;
9
11
  componentDidMount(): void;
10
12
  componentDidUpdate(): void;
13
+ componentWillUnmount(): void;
11
14
  render(): React$1.ReactNode;
12
15
  }
13
16
  declare function AppRouterScrollCommitProvider({
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { decodeHashFragment } from "./hash-scroll.js";
3
- import { consumeAppRouterScrollIntent, getPendingAppRouterScrollIntent } from "./app-router-scroll-state.js";
3
+ import { consumeAppRouterScrollIntent, getPendingAppRouterScrollIntent, markAppRouterScrollIntentHeadHoisted } from "./app-router-scroll-state.js";
4
4
  import * as React$1 from "react";
5
5
  import { jsx } from "react/jsx-runtime";
6
6
  import * as ReactDOM from "react-dom";
@@ -46,8 +46,7 @@ function topOfElementInViewport(element, viewportHeight) {
46
46
  function getHashFragmentDomNode(hash) {
47
47
  const fragment = decodeHashFragment(hash.startsWith("#") ? hash.slice(1) : hash);
48
48
  if (fragment === "top") return document.body;
49
- const element = document.getElementById(fragment) ?? document.getElementsByName(fragment)[0];
50
- return element instanceof HTMLElement ? element : null;
49
+ return document.getElementById(fragment) ?? document.getElementsByName(fragment)[0] ?? null;
51
50
  }
52
51
  function isInDocumentHead(node) {
53
52
  const head = node.ownerDocument?.head;
@@ -55,7 +54,7 @@ function isInDocumentHead(node) {
55
54
  }
56
55
  function findNextScrollTarget(node) {
57
56
  if (!(node instanceof Element)) return null;
58
- if (isInDocumentHead(node)) return { kind: "document-top" };
57
+ if (isInDocumentHead(node)) return null;
59
58
  let target = node;
60
59
  while (!(target instanceof HTMLElement) || shouldSkipElement(target)) {
61
60
  if (target.nextElementSibling === null) return null;
@@ -82,34 +81,45 @@ function scrollToElement(target, hash) {
82
81
  });
83
82
  }
84
83
  var AppRouterScrollTargetInner = class extends React$1.Component {
84
+ scheduledCommitId = null;
85
+ schedulePotentialScroll = () => {
86
+ const commitId = this.props.commitId;
87
+ this.scheduledCommitId = commitId;
88
+ queueMicrotask(() => {
89
+ if (this.scheduledCommitId !== commitId) return;
90
+ this.handlePotentialScroll();
91
+ });
92
+ };
85
93
  handlePotentialScroll = () => {
86
94
  const intent = getPendingAppRouterScrollIntent();
87
95
  if (intent === null) return;
88
96
  if (this.props.commitId === null || intent.commitId !== this.props.commitId) return;
89
- let target;
90
- if (intent.hash !== null) {
91
- target = getHashFragmentDomNode(intent.hash);
92
- if (target === null) return;
93
- } else {
94
- const next = findNextScrollTarget(findDOMNode(this));
95
- if (next === null) return;
96
- if (next.kind === "document-top") {
97
- if (consumeAppRouterScrollIntent(intent, this.props.commitId) === null) return;
98
- document.documentElement.scrollTop = 0;
97
+ let node;
98
+ if (intent.hash !== null) node = getHashFragmentDomNode(intent.hash);
99
+ else node = null;
100
+ if (node === null) {
101
+ node = findDOMNode(this);
102
+ if (node !== null && isInDocumentHead(node)) {
103
+ markAppRouterScrollIntentHeadHoisted(intent, this.props.commitId);
99
104
  return;
100
105
  }
101
- target = next.element;
102
106
  }
107
+ const next = findNextScrollTarget(node);
108
+ if (next === null) return;
109
+ const target = next.element;
103
110
  const consumed = consumeAppRouterScrollIntent(intent, this.props.commitId);
104
111
  if (consumed === null) return;
105
112
  scrollToElement(target, consumed.hash);
106
- target.focus({ preventScroll: true });
113
+ target.focus();
107
114
  };
108
115
  componentDidMount() {
109
- this.handlePotentialScroll();
116
+ this.schedulePotentialScroll();
110
117
  }
111
118
  componentDidUpdate() {
112
- this.handlePotentialScroll();
119
+ this.schedulePotentialScroll();
120
+ }
121
+ componentWillUnmount() {
122
+ this.scheduledCommitId = null;
113
123
  }
114
124
  render() {
115
125
  return this.props.children;
@@ -1,6 +1,7 @@
1
1
  import { getOrCreateAls } from "./internal/als-registry.js";
2
2
  import { getRequestContext, isInsideUnifiedScope, runWithUnifiedStateMutation } from "./unified-request-context.js";
3
3
  import { VINEXT_RSC_MARKER_HEADER } from "../server/headers.js";
4
+ import { trackPprFallbackShellCacheTask } from "./ppr-fallback-shell.js";
4
5
  import { markDynamicUsage } from "./headers.js";
5
6
  import { _registerCacheContextAccessor, _setRequestScopedCacheLife, cacheLifeProfiles, getDataCacheHandler } from "./cache.js";
6
7
  import { addCollectedRequestTags, getCurrentFetchSoftTags } from "./fetch-cache.js";
@@ -243,7 +244,7 @@ function registerCachedFunction(fn, id, variant, options = {}) {
243
244
  const cacheVariant = variant ?? "";
244
245
  const omitAppPageSearchParamsFromFirstArg = options.appPageDefaultExport === true;
245
246
  const isDev = typeof process !== "undefined" && process.env.NODE_ENV === "development";
246
- const cachedFn = async (...args) => {
247
+ const cachedFn = (...args) => trackPprFallbackShellCacheTask(async () => {
247
248
  const rsc = await getRscModule();
248
249
  const keySeed = getUseCacheKeySeed();
249
250
  let cacheKey;
@@ -320,7 +321,7 @@ function registerCachedFunction(fn, id, variant, options = {}) {
320
321
  });
321
322
  } catch {}
322
323
  return result;
323
- };
324
+ }, cacheVariant);
324
325
  Object.defineProperty(cachedFn, "length", {
325
326
  value: fn.length,
326
327
  configurable: true
@@ -53,6 +53,7 @@ type CachedPagesValue = {
53
53
  kind: "PAGES";
54
54
  html: string;
55
55
  pageData: object;
56
+ generatedFromDataRequest?: boolean;
56
57
  headers: Record<string, string | string[]> | undefined;
57
58
  status: number | undefined;
58
59
  };
@@ -1,10 +1,10 @@
1
1
  import { getOrCreateAls } from "./internal/als-registry.js";
2
2
  import { getRequestContext, isInsideUnifiedScope, runWithUnifiedStateMutation } from "./unified-request-context.js";
3
3
  import { getRequestExecutionContext, runWithExecutionContext } from "./request-context.js";
4
+ import { makeHangingPromise } from "./internal/make-hanging-promise.js";
4
5
  import { getHeadersAccessPhase, markDynamicUsage } from "./headers.js";
5
6
  import { fnv1a64 } from "../utils/hash.js";
6
7
  import { workUnitAsyncStorage } from "./internal/work-unit-async-storage.js";
7
- import { makeHangingPromise } from "./internal/make-hanging-promise.js";
8
8
  import { readCacheControlNumberField } from "../utils/cache-control-metadata.js";
9
9
  import { encodeCacheTag, encodeCacheTags } from "../utils/encode-cache-tag.js";
10
10
  import { getCdnCacheAdapter } from "./cdn-cache.js";
@@ -1,8 +1,8 @@
1
1
  import { CacheHandlerValue, IncrementalCacheValue } from "./cache.js";
2
2
 
3
3
  //#region src/shims/cdn-cache.d.ts
4
- /** A map of response header name -> value the adapter wants applied. */
5
- type CdnResponseHeaders = Record<string, string>;
4
+ /** A map of response header name -> value the adapter wants applied or removed. */
5
+ type CdnResponseHeaders = Record<string, string | null>;
6
6
  type CdnCacheableHeaderInput = {
7
7
  /**
8
8
  * The cacheable `Cache-Control` value the framework computed for shared
@@ -50,9 +50,9 @@ type CdnCacheAdapter = {
50
50
  */
51
51
  set(key: string, data: IncrementalCacheValue | null, ctx?: Record<string, unknown>): Promise<void>;
52
52
  /**
53
- * Build the response cache headers for a given cacheable policy. Returns a map
54
- * so an adapter can emit more than one header (e.g. `Cache-Control` +
55
- * `CDN-Cache-Control`).
53
+ * Build the response cache headers for a given policy. Returns a map so an
54
+ * adapter can emit more than one header (e.g. `Cache-Control` +
55
+ * `CDN-Cache-Control`) and remove stale adapter-owned headers with `null`.
56
56
  */
57
57
  buildResponseHeaders(input: CdnCacheableHeaderInput): CdnResponseHeaders;
58
58
  /**
@@ -1,4 +1,5 @@
1
1
  "use client";
2
+ import { appendAssetDeploymentIdQuery } from "../utils/deployment-id.js";
2
3
  import { useScriptNonce } from "./script-nonce-context.js";
3
4
  import React from "react";
4
5
  import * as ReactDOM from "react-dom";
@@ -51,8 +52,9 @@ function DynamicPreloadChunks(props) {
51
52
  if (files.length === 0) return null;
52
53
  const stylesheets = [];
53
54
  for (const file of files) {
54
- const href = dynamicPreloadHref(file);
55
- if (href.endsWith(".css")) {
55
+ const assetHref = dynamicPreloadHref(file);
56
+ if (assetHref.endsWith(".css")) {
57
+ const href = appendAssetDeploymentIdQuery(assetHref);
56
58
  stylesheets.push(React.createElement("link", {
57
59
  key: href,
58
60
  rel: "stylesheet",
@@ -62,13 +64,13 @@ function DynamicPreloadChunks(props) {
62
64
  }));
63
65
  continue;
64
66
  }
65
- if (href.endsWith(".js") && typeof ReactDOM.preload === "function") {
67
+ if (assetHref.endsWith(".js") && typeof ReactDOM.preload === "function") {
66
68
  const preloadOptions = {
67
69
  as: "script",
68
70
  fetchPriority: "low",
69
71
  nonce
70
72
  };
71
- ReactDOM.preload(href, preloadOptions);
73
+ ReactDOM.preload(assetHref, preloadOptions);
72
74
  }
73
75
  }
74
76
  return stylesheets.length > 0 ? React.createElement(React.Fragment, null, ...stylesheets) : null;
@@ -17,6 +17,7 @@ type RedirectBoundaryState = {
17
17
  redirectType: "push" | "replace" | null;
18
18
  };
19
19
  type ErrorBoundaryInnerProps = {
20
+ isImplicitRootErrorBoundary?: boolean;
20
21
  pathname: string | null;
21
22
  } & ErrorBoundaryProps;
22
23
  type ErrorBoundaryState = {
@@ -131,6 +132,7 @@ declare function UnauthorizedBoundary({
131
132
  resetKey
132
133
  }: UnauthorizedBoundaryProps): React.JSX.Element;
133
134
  type DevRecoveryBoundaryProps = {
135
+ isImplicitRootErrorBoundary?: boolean;
134
136
  resetKey: number;
135
137
  onCatch?: (resetKey: number) => void;
136
138
  children?: React.ReactNode;
@@ -1,5 +1,7 @@
1
1
  "use client";
2
+ import { handleAppNavigationFailure } from "../client/app-nav-failure-handler.js";
2
3
  import { decodeRedirectError, isRedirectError, usePathname, useRouter } from "./navigation.js";
4
+ import DefaultGlobalError from "./default-global-error.js";
3
5
  import { VINEXT_DEV_ERROR_RECOVERY_EVENT } from "../utils/dev-error-recovery-event.js";
4
6
  import { isNavigationSignalError } from "../utils/navigation-signal.js";
5
7
  import React from "react";
@@ -98,6 +100,10 @@ var ErrorBoundaryInner = class extends React.Component {
98
100
  }
99
101
  static getDerivedStateFromProps(props, state) {
100
102
  const nextResetState = readBoundaryResetState(props);
103
+ if (state.error && handleAppNavigationFailure(state.error.thrownValue)) return {
104
+ error: null,
105
+ ...nextResetState
106
+ };
101
107
  if (state.error && shouldResetBoundary(nextResetState, state)) return {
102
108
  error: null,
103
109
  ...nextResetState
@@ -150,6 +156,7 @@ function GlobalErrorBoundary({ fallback, children }) {
150
156
  return /* @__PURE__ */ jsx(ErrorBoundaryInner, {
151
157
  pathname: usePathname(),
152
158
  fallback,
159
+ isImplicitRootErrorBoundary: fallback === DefaultGlobalError,
153
160
  children
154
161
  });
155
162
  }
@@ -1,6 +1,7 @@
1
1
  import { AppRouterContext } from "./internal/app-router-context.js";
2
2
  import { RouterContext } from "./internal/router-context.js";
3
- import { isNextRouterError, usePathname } from "./navigation.js";
3
+ import { isNextRouterError } from "./navigation.js";
4
+ import { useUntrackedPathname } from "./internal/navigation-untracked.js";
4
5
  import React from "react";
5
6
  //#region src/shims/error.tsx
6
7
  /**
@@ -104,7 +105,7 @@ function unstable_catchError(fallback) {
104
105
  Fallback.displayName = fallback.name || "CatchErrorFallback";
105
106
  function CatchErrorBoundary(allProps) {
106
107
  const { children, ...rest } = allProps;
107
- const pathname = usePathname();
108
+ const pathname = useUntrackedPathname();
108
109
  const isPagesRouter = React.useContext(RouterContext) !== null;
109
110
  const forwardedProps = rest;
110
111
  return React.createElement(_CatchError, {