vinext 1.1.0 → 1.2.1

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 (288) hide show
  1. package/README.md +22 -10
  2. package/dist/build/client-build-config.d.ts +10 -0
  3. package/dist/build/client-build-config.js +18 -2
  4. package/dist/build/output-root-externals.d.ts +21 -0
  5. package/dist/build/output-root-externals.js +40 -0
  6. package/dist/build/pages-client-assets-module.d.ts +8 -0
  7. package/dist/build/pages-client-assets-module.js +21 -1
  8. package/dist/build/prerender-paths.js +6 -4
  9. package/dist/build/prerender.js +1 -1
  10. package/dist/build/run-prerender.js +1 -1
  11. package/dist/build/ssr-manifest.d.ts +2 -0
  12. package/dist/build/ssr-manifest.js +6 -2
  13. package/dist/check.js +12 -12
  14. package/dist/cli-dev-config.js +3 -3
  15. package/dist/cli.js +3 -2
  16. package/dist/client/client-redirect-matcher.d.ts +14 -0
  17. package/dist/client/client-redirect-matcher.js +37 -0
  18. package/dist/client/client-redirects.d.ts +27 -0
  19. package/dist/client/client-redirects.js +28 -0
  20. package/dist/client/client-rewrites.d.ts +2 -1
  21. package/dist/client/client-rewrites.js +1 -1
  22. package/dist/config/config-matchers.d.ts +26 -11
  23. package/dist/config/config-matchers.js +90 -88
  24. package/dist/config/destination-params.d.ts +47 -0
  25. package/dist/config/destination-params.js +116 -0
  26. package/dist/config/next-config.d.ts +7 -0
  27. package/dist/config/next-config.js +3 -0
  28. package/dist/entries/app-rsc-entry.d.ts +4 -0
  29. package/dist/entries/app-rsc-entry.js +20 -6
  30. package/dist/entries/pages-client-entry.js +3 -1
  31. package/dist/entries/pages-server-entry.js +13 -37
  32. package/dist/index.d.ts +3 -1
  33. package/dist/index.js +246 -136
  34. package/dist/init-cloudflare.d.ts +9 -0
  35. package/dist/init-cloudflare.js +107 -31
  36. package/dist/init-platform.d.ts +4 -0
  37. package/dist/init-platform.js +27 -2
  38. package/dist/init.d.ts +2 -1
  39. package/dist/init.js +153 -9
  40. package/dist/plugins/ast-scope.d.ts +7 -0
  41. package/dist/plugins/ast-scope.js +59 -3
  42. package/dist/plugins/ast-utils.d.ts +53 -2
  43. package/dist/plugins/ast-utils.js +90 -2
  44. package/dist/plugins/cloudflare-dev-module-urls.d.ts +41 -0
  45. package/dist/plugins/cloudflare-dev-module-urls.js +271 -0
  46. package/dist/plugins/commonjs-esm-facade.js +2 -2
  47. package/dist/plugins/css-module-imports.js +13 -20
  48. package/dist/plugins/dynamic-preload-metadata.d.ts +1 -1
  49. package/dist/plugins/dynamic-preload-metadata.js +11 -11
  50. package/dist/plugins/extensionless-dynamic-import.js +2 -3
  51. package/dist/plugins/fonts.d.ts +1 -1
  52. package/dist/plugins/fonts.js +3 -3
  53. package/dist/plugins/ignore-dynamic-requests.d.ts +1 -1
  54. package/dist/plugins/ignore-dynamic-requests.js +5 -7
  55. package/dist/plugins/import-meta-url.js +3 -4
  56. package/dist/plugins/instrumentation-client.js +3 -2
  57. package/dist/plugins/middleware-export-validation.js +2 -3
  58. package/dist/plugins/middleware-server-only.d.ts +5 -4
  59. package/dist/plugins/middleware-server-only.js +8 -5
  60. package/dist/plugins/og-harfbuzz.js +2 -3
  61. package/dist/plugins/optimize-imports.js +3 -4
  62. package/dist/plugins/pages-node-externals.js +3 -4
  63. package/dist/plugins/remove-console.js +2 -18
  64. package/dist/plugins/require-condition-resolution.js +3 -3
  65. package/dist/plugins/require-context.js +2 -3
  66. package/dist/plugins/rsc-scan-glob-imports.d.ts +19 -0
  67. package/dist/plugins/rsc-scan-glob-imports.js +50 -0
  68. package/dist/plugins/strip-server-exports.js +265 -102
  69. package/dist/plugins/styled-jsx.js +2 -3
  70. package/dist/plugins/transform-result.d.ts +9 -5
  71. package/dist/plugins/transitive-externals.js +1 -1
  72. package/dist/plugins/typeof-window.d.ts +2 -2
  73. package/dist/plugins/use-cache-callable.d.ts +21 -2
  74. package/dist/plugins/use-cache-callable.js +84 -35
  75. package/dist/plugins/worker-image-imports.js +30 -33
  76. package/dist/plugins/worker-server-only-imports.d.ts +20 -0
  77. package/dist/plugins/worker-server-only-imports.js +31 -0
  78. package/dist/plugins/worker-use-server.d.ts +25 -0
  79. package/dist/plugins/worker-use-server.js +153 -0
  80. package/dist/server/api-handler.d.ts +1 -0
  81. package/dist/server/api-handler.js +3 -2
  82. package/dist/server/app-action-forwarding.js +2 -2
  83. package/dist/server/app-browser-action-result.d.ts +1 -1
  84. package/dist/server/app-browser-action-result.js +9 -2
  85. package/dist/server/app-browser-entry.js +3 -3
  86. package/dist/server/app-browser-interception-context.js +1 -1
  87. package/dist/server/app-browser-navigation-controller.js +1 -1
  88. package/dist/server/app-browser-prefetch-response.js +2 -2
  89. package/dist/server/app-browser-server-action-client.js +1 -1
  90. package/dist/server/app-browser-state.js +1 -1
  91. package/dist/server/app-browser-supplemental-refresh.js +1 -1
  92. package/dist/server/app-middleware.d.ts +11 -0
  93. package/dist/server/app-middleware.js +17 -2
  94. package/dist/server/app-optimistic-routing.js +1 -1
  95. package/dist/server/app-page-cache.d.ts +1 -1
  96. package/dist/server/app-page-cache.js +1 -1
  97. package/dist/server/app-page-dispatch.d.ts +3 -1
  98. package/dist/server/app-page-dispatch.js +5 -4
  99. package/dist/server/app-page-ppr-runtime.js +1 -1
  100. package/dist/server/app-page-render-observation.js +1 -1
  101. package/dist/server/app-page-render.js +1 -1
  102. package/dist/server/app-page-request.js +53 -15
  103. package/dist/server/app-page-route-wiring.js +2 -2
  104. package/dist/server/app-pages-bridge.d.ts +1 -1
  105. package/dist/server/app-pages-bridge.js +3 -4
  106. package/dist/server/app-prerender-endpoints.js +9 -6
  107. package/dist/server/app-render-abort-error.d.ts +19 -0
  108. package/dist/server/app-render-abort-error.js +31 -1
  109. package/dist/server/app-request-stage-dispatch.js +6 -2
  110. package/dist/server/app-request-stage-independent-entry.js +29 -9
  111. package/dist/server/app-response-stage-entry.js +11 -3
  112. package/dist/server/app-route-handler-cache.d.ts +1 -0
  113. package/dist/server/app-route-handler-cache.js +1 -0
  114. package/dist/server/app-route-handler-dispatch.d.ts +1 -0
  115. package/dist/server/app-route-handler-dispatch.js +5 -2
  116. package/dist/server/app-route-handler-execution.d.ts +1 -0
  117. package/dist/server/app-route-handler-execution.js +1 -0
  118. package/dist/server/app-route-handler-runtime.d.ts +4 -1
  119. package/dist/server/app-route-handler-runtime.js +46 -25
  120. package/dist/server/app-router-entry.js +29 -10
  121. package/dist/server/app-rsc-cache-busting.d.ts +16 -0
  122. package/dist/server/app-rsc-cache-busting.js +30 -13
  123. package/dist/server/app-rsc-errors.js +20 -16
  124. package/dist/server/app-rsc-handler.d.ts +7 -0
  125. package/dist/server/app-rsc-handler.js +89 -39
  126. package/dist/server/app-rsc-request-normalization.js +3 -3
  127. package/dist/server/app-rsc-response-stage.js +5 -3
  128. package/dist/server/app-segment-config.d.ts +2 -1
  129. package/dist/server/app-server-action-execution.d.ts +2 -1
  130. package/dist/server/app-server-action-execution.js +62 -23
  131. package/dist/server/app-ssr-entry.js +7 -5
  132. package/dist/server/app-visited-response-cache.js +2 -2
  133. package/dist/server/app-worker-stages.d.ts +5 -0
  134. package/dist/server/app-worker-stages.js +1 -1
  135. package/dist/server/cacheability-request.js +2 -2
  136. package/dist/server/client-build-files.d.ts +16 -0
  137. package/dist/server/client-build-files.js +36 -0
  138. package/dist/server/client-reuse-manifest.js +1 -1
  139. package/dist/server/config-headers.js +1 -1
  140. package/dist/server/defer-until-stream-consumed.d.ts +2 -1
  141. package/dist/server/defer-until-stream-consumed.js +3 -1
  142. package/dist/server/dev-server.js +39 -28
  143. package/dist/server/dev-stack-sourcemap.js +18 -23
  144. package/dist/server/document-initial-head.d.ts +0 -11
  145. package/dist/server/document-initial-head.js +1 -1
  146. package/dist/server/forwarded-middleware-context.d.ts +5 -0
  147. package/dist/server/forwarded-middleware-context.js +22 -0
  148. package/dist/server/headers.d.ts +4 -1
  149. package/dist/server/headers.js +4 -1
  150. package/dist/server/http-conditional.d.ts +11 -0
  151. package/dist/server/http-conditional.js +10 -1
  152. package/dist/server/http-error-responses.d.ts +1 -1
  153. package/dist/server/image-optimization.d.ts +38 -0
  154. package/dist/server/image-optimization.js +94 -4
  155. package/dist/server/isr-cache.d.ts +10 -0
  156. package/dist/server/isr-cache.js +41 -6
  157. package/dist/server/metadata-route-response.js +11 -10
  158. package/dist/server/middleware-matcher.d.ts +2 -0
  159. package/dist/server/middleware-matcher.js +23 -2
  160. package/dist/server/middleware-path-to-regexp.js +1 -1
  161. package/dist/server/middleware-runtime.d.ts +13 -0
  162. package/dist/server/middleware-runtime.js +38 -25
  163. package/dist/server/middleware.d.ts +1 -1
  164. package/dist/server/middleware.js +3 -1
  165. package/dist/server/multi-stage.d.ts +11 -0
  166. package/dist/server/navigation-planner.js +1 -1
  167. package/dist/server/normalize-path.d.ts +0 -13
  168. package/dist/server/normalize-path.js +1 -34
  169. package/dist/server/open-redirect.d.ts +11 -4
  170. package/dist/server/open-redirect.js +11 -4
  171. package/dist/server/pages-api-route.d.ts +3 -2
  172. package/dist/server/pages-api-route.js +6 -5
  173. package/dist/server/pages-cache-tag.d.ts +12 -0
  174. package/dist/server/pages-cache-tag.js +19 -0
  175. package/dist/server/pages-client-assets.d.ts +2 -0
  176. package/dist/server/pages-data-route.d.ts +10 -0
  177. package/dist/server/pages-data-route.js +21 -3
  178. package/dist/server/pages-document-asset-props.d.ts +5 -1
  179. package/dist/server/pages-document-asset-props.js +13 -5
  180. package/dist/server/pages-document-splice.d.ts +19 -0
  181. package/dist/server/pages-document-splice.js +55 -0
  182. package/dist/server/pages-i18n.d.ts +12 -1
  183. package/dist/server/pages-i18n.js +18 -4
  184. package/dist/server/pages-node-compat.d.ts +16 -5
  185. package/dist/server/pages-node-compat.js +20 -12
  186. package/dist/server/pages-page-data.d.ts +32 -24
  187. package/dist/server/pages-page-data.js +51 -121
  188. package/dist/server/pages-page-handler.d.ts +0 -6
  189. package/dist/server/pages-page-handler.js +189 -127
  190. package/dist/server/pages-page-response.d.ts +12 -6
  191. package/dist/server/pages-page-response.js +42 -35
  192. package/dist/server/pages-request-pipeline.d.ts +10 -2
  193. package/dist/server/pages-request-pipeline.js +41 -22
  194. package/dist/server/pages-request-stage-entry.d.ts +6 -0
  195. package/dist/server/pages-request-stage-entry.js +75 -16
  196. package/dist/server/pages-response-stage-entry.js +4 -0
  197. package/dist/server/pages-revalidate.js +3 -3
  198. package/dist/server/pages-router-entry.js +5 -4
  199. package/dist/server/prerender-manifest.d.ts +3 -3
  200. package/dist/server/prerender-manifest.js +4 -3
  201. package/dist/server/prerender-work-unit-setup.js +4 -0
  202. package/dist/server/prod-server.d.ts +12 -0
  203. package/dist/server/prod-server.js +204 -74
  204. package/dist/server/proper-error.d.ts +4 -0
  205. package/dist/server/proper-error.js +31 -0
  206. package/dist/server/request-pipeline.d.ts +4 -1
  207. package/dist/server/request-pipeline.js +3 -2
  208. package/dist/server/revalidation-request.d.ts +1 -0
  209. package/dist/server/revalidation-request.js +1 -1
  210. package/dist/server/rsc-stream-hints.d.ts +1 -1
  211. package/dist/server/rsc-stream-hints.js +52 -5
  212. package/dist/server/seed-cache.d.ts +1 -0
  213. package/dist/server/seed-cache.js +5 -4
  214. package/dist/server/server-reference-loader.d.ts +4 -0
  215. package/dist/server/server-reference-loader.js +29 -0
  216. package/dist/server/socket-error-backstop.d.ts +9 -1
  217. package/dist/server/socket-error-backstop.js +46 -17
  218. package/dist/server/static-file-signal.d.ts +15 -4
  219. package/dist/server/static-file-signal.js +46 -12
  220. package/dist/server/worker-image-optimization.d.ts +34 -0
  221. package/dist/server/worker-image-optimization.js +99 -0
  222. package/dist/server/worker-stages.d.ts +1 -1
  223. package/dist/server/worker-stages.js +2 -2
  224. package/dist/server/worker-utils.d.ts +8 -3
  225. package/dist/server/worker-utils.js +12 -7
  226. package/dist/shims/app-router-scroll.js +4 -1
  227. package/dist/shims/app-router-tree-context.d.ts +5 -0
  228. package/dist/shims/app-router-tree-context.js +27 -0
  229. package/dist/shims/cache-callable-runtime.d.ts +4 -2
  230. package/dist/shims/cache-callable-runtime.js +21 -12
  231. package/dist/shims/cache-handler.js +1 -9
  232. package/dist/shims/cache-runtime.d.ts +6 -1
  233. package/dist/shims/cache-runtime.js +2 -1
  234. package/dist/shims/cache.d.ts +2 -2
  235. package/dist/shims/cache.js +40 -13
  236. package/dist/shims/cdn-cache.d.ts +8 -0
  237. package/dist/shims/cdn-cache.js +17 -1
  238. package/dist/shims/disable-smooth-scroll.d.ts +3 -0
  239. package/dist/shims/disable-smooth-scroll.js +36 -0
  240. package/dist/shims/document-placeholders.d.ts +19 -0
  241. package/dist/shims/document-placeholders.js +45 -0
  242. package/dist/shims/document.js +21 -5
  243. package/dist/shims/dynamic-preload-chunks.d.ts +1 -0
  244. package/dist/shims/dynamic-preload-chunks.js +13 -9
  245. package/dist/shims/dynamic.d.ts +1 -1
  246. package/dist/shims/dynamic.js +60 -8
  247. package/dist/shims/fetch-cache-state.d.ts +66 -0
  248. package/dist/shims/fetch-cache-state.js +92 -0
  249. package/dist/shims/fetch-cache.d.ts +3 -67
  250. package/dist/shims/fetch-cache.js +160 -125
  251. package/dist/shims/head.d.ts +10 -6
  252. package/dist/shims/head.js +16 -14
  253. package/dist/shims/headers.js +2 -2
  254. package/dist/shims/image.js +2 -1
  255. package/dist/shims/internal/hybrid-client-route-owner-direct.js +1 -1
  256. package/dist/shims/internal/hybrid-client-route-owner.js +1 -0
  257. package/dist/shims/internal/work-store-scope.d.ts +5 -0
  258. package/dist/shims/internal/work-store-scope.js +24 -0
  259. package/dist/shims/link-prefetch.js +5 -4
  260. package/dist/shims/link.js +2 -2
  261. package/dist/shims/navigation.js +34 -9
  262. package/dist/shims/request-context.d.ts +2 -0
  263. package/dist/shims/request-state-types.d.ts +2 -1
  264. package/dist/shims/router.js +21 -32
  265. package/dist/shims/server.d.ts +27 -4
  266. package/dist/shims/server.js +65 -9
  267. package/dist/shims/unified-request-context.d.ts +1 -1
  268. package/dist/shims/url-utils.d.ts +9 -0
  269. package/dist/shims/url-utils.js +15 -4
  270. package/dist/utils/client-runtime-metadata.js +1 -1
  271. package/dist/utils/domain-locale.js +1 -1
  272. package/dist/utils/hash.d.ts +7 -0
  273. package/dist/utils/hash.js +11 -1
  274. package/dist/utils/package-name.d.ts +6 -0
  275. package/dist/utils/package-name.js +12 -1
  276. package/dist/utils/plugin-rsc-version.d.ts +6 -0
  277. package/dist/utils/plugin-rsc-version.js +41 -0
  278. package/dist/utils/prerender-output-paths.js +15 -4
  279. package/dist/utils/public-file-path.d.ts +10 -2
  280. package/dist/utils/public-file-path.js +13 -9
  281. package/dist/utils/public-routes.js +2 -2
  282. package/dist/utils/react-version.d.ts +10 -0
  283. package/dist/utils/react-version.js +39 -16
  284. package/package.json +11 -7
  285. package/dist/build/nitro-route-rules.d.ts +0 -47
  286. package/dist/build/nitro-route-rules.js +0 -79
  287. package/dist/plugins/cache-flight-codec.d.ts +0 -19
  288. package/dist/plugins/cache-flight-codec.js +0 -68
@@ -0,0 +1,24 @@
1
+ import { getOrCreateAls } from "./als-registry.js";
2
+ //#region src/shims/internal/work-store-scope.ts
3
+ /**
4
+ * Marks the work that Next.js runs with a work store (`workAsyncStorage`):
5
+ * App Router renders, Server Actions, route handlers and metadata routes,
6
+ * `generateStaticParams`, and middleware. Pages Router renders, API routes
7
+ * and `getStaticPaths` run without one.
8
+ *
9
+ * Next.js only applies some rules inside a work store, such as skipping the
10
+ * read for an `unstable_cache` nested in another's callback.
11
+ * Source: https://github.com/vercel/next.js/blob/canary/packages/next/src/server/web/spec-extension/unstable-cache.ts
12
+ */
13
+ const _workStoreScopeAls = getOrCreateAls("vinext.workStoreScope.als");
14
+ function runInWorkStoreScope(fn) {
15
+ return _workStoreScopeAls.run(true, fn);
16
+ }
17
+ function runOutsideWorkStoreScope(fn) {
18
+ return _workStoreScopeAls.run(false, fn);
19
+ }
20
+ function isInsideWorkStoreScope() {
21
+ return _workStoreScopeAls.getStore() === true;
22
+ }
23
+ //#endregion
24
+ export { isInsideWorkStoreScope, runInWorkStoreScope, runOutsideWorkStoreScope };
@@ -1,5 +1,5 @@
1
1
  import { hasBasePath, stripBasePath } from "../utils/base-path.js";
2
- import { isAbsoluteOrProtocolRelativeUrl } from "./url-utils.js";
2
+ import { collapseLeadingSlashes, isAbsoluteOrProtocolRelativeUrl } from "./url-utils.js";
3
3
  //#region src/shims/link-prefetch.ts
4
4
  function canLinkPrefetch(input) {
5
5
  return input.nodeEnv === "production" && input.prefetch !== false && !input.isDangerous;
@@ -32,9 +32,10 @@ function getLinkPrefetchHref(input) {
32
32
  const current = new URL(currentOrigin);
33
33
  const parsed = href.startsWith("//") ? new URL(href, current.origin) : new URL(href);
34
34
  if (parsed.origin !== current.origin) return null;
35
- if (!basePath) return parsed.pathname + parsed.search + parsed.hash;
36
- if (!hasBasePath(parsed.pathname, basePath)) return null;
37
- return stripBasePath(parsed.pathname, basePath) + parsed.search + parsed.hash;
35
+ const pathname = collapseLeadingSlashes(parsed.pathname);
36
+ if (!basePath) return pathname + parsed.search + parsed.hash;
37
+ if (!hasBasePath(pathname, basePath)) return null;
38
+ return collapseLeadingSlashes(stripBasePath(pathname, basePath)) + parsed.search + parsed.hash;
38
39
  } catch {
39
40
  return null;
40
41
  }
@@ -317,7 +317,7 @@ function prefetchUrl(href, mode, priority = "low", pagesRouteHref, locale) {
317
317
  async function promotePrefetchEntriesForNavigation(href) {
318
318
  if (typeof window === "undefined") return;
319
319
  if (!hasAppNavigationRuntime()) return;
320
- const [{ getPrefetchCache }, { stripRscCacheBustingSearchParam, stripRscSuffix }] = await Promise.all([loadNavigationModule(), import("../server/app-rsc-cache-busting.js")]);
320
+ const [{ getPrefetchCache }, { parseRscRequestPath, stripRscCacheBustingSearchParam, stripRscSuffix }] = await Promise.all([loadNavigationModule(), import("../server/app-rsc-cache-busting.js")]);
321
321
  let target;
322
322
  try {
323
323
  target = new URL(toCanonicalBrowserNavigationHref(href, window.location.href, __basePath, __trailingSlash), window.location.href);
@@ -330,7 +330,7 @@ async function promotePrefetchEntriesForNavigation(href) {
330
330
  const [rscUrl] = cacheKey.split("\0", 1);
331
331
  let cached;
332
332
  try {
333
- cached = new URL(rscUrl, window.location.href);
333
+ cached = parseRscRequestPath(rscUrl);
334
334
  } catch {
335
335
  continue;
336
336
  }
@@ -9,14 +9,15 @@ import { getPprFallbackShellState, markPprFallbackShellDynamicBoundary } from ".
9
9
  import { AppRouterContext } from "./internal/app-router-context.js";
10
10
  import { resolveDirectHybridClientRouteOwner } from "./internal/hybrid-client-route-owner-direct.js";
11
11
  import { isHashOnlyBrowserUrlChange, toCanonicalBrowserNavigationHref, toSameOriginAppPath, withBasePath } from "./url-utils.js";
12
- import { retryScrollTo, scrollToHashTarget, scrollToHashTargetOnNextFrame } from "./hash-scroll.js";
12
+ import { disableSmoothScrollDuringRouteTransition } from "./disable-smooth-scroll.js";
13
+ import { retryScrollTo, scrollToHashTarget } from "./hash-scroll.js";
13
14
  import { AppElementsWire } from "../server/app-elements-wire.js";
14
15
  import "../server/app-elements.js";
15
16
  import { GLOBAL_ACCESSORS_KEY, ServerInsertedHTMLContext, _registerStateAccessors, clearClientHydrationContext, clearServerInsertedHTML, flushServerInsertedHTML, getBfcacheIdMapContext, getBfcacheSegmentIdContext, getLayoutSegmentContext, getNavigationContext, registerServerInsertedHTMLCallback, renderServerInsertedHTML, setNavigationContext } from "./navigation-context-state.js";
16
17
  import { BailoutToCSRError, DynamicServerError, HTTP_ERROR_FALLBACK_ERROR_CODE, RedirectType, decodeRedirectError, forbidden, getAccessFallbackHTTPStatus, isBailoutToCSRError, isDynamicServerError, isHTTPAccessFallbackError, isNextRouterError, isRedirectError, notFound, permanentRedirect, redirect, unauthorized, unstable_rethrow } from "./navigation-errors.js";
17
18
  import { clearAppNavigationFailureTarget, stageAppNavigationFailureTarget } from "../client/app-nav-failure-handler.js";
18
19
  import { beginAppRouterScrollIntent, clearAppRouterScrollIntent, consumeAppRouterScrollIntent, getPendingAppRouterScrollIntent, isLatestAppRouterScrollIntent } from "./app-router-scroll-state.js";
19
- import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, createRscRequestHeaders, createRscRequestUrl, stripRscCacheBustingSearchParam, stripRscSuffix } from "../server/app-rsc-cache-busting.js";
20
+ import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, createRscRequestHeaders, createRscRequestUrl, formatRscRequestPath, parseRscRequestPath, stripRscCacheBustingSearchParam, stripRscSuffix } from "../server/app-rsc-cache-busting.js";
20
21
  import { notifyAppRouterTransitionStart } from "../client/instrumentation-client-state.js";
21
22
  import { getNavigationRuntime, hasAppNavigationRuntime } from "../client/navigation-runtime.js";
22
23
  import { UnrecognizedActionError, unstable_isUnrecognizedActionError } from "./unrecognized-action-error.js";
@@ -235,7 +236,7 @@ function resolvePrefetchCacheEntryMountedSlotsHeader(entry) {
235
236
  }
236
237
  function normalizeRscCacheLookupUrl(rscUrl) {
237
238
  try {
238
- const url = new URL(rscUrl, "http://vinext.local");
239
+ const url = parseRscRequestPath(rscUrl);
239
240
  stripRscCacheBustingSearchParam(url);
240
241
  return `${url.pathname}${url.search}`;
241
242
  } catch {
@@ -244,7 +245,7 @@ function normalizeRscCacheLookupUrl(rscUrl) {
244
245
  }
245
246
  function normalizeRscCacheLookupPathname(rscUrl) {
246
247
  try {
247
- const url = new URL(rscUrl, "http://vinext.local");
248
+ const url = parseRscRequestPath(rscUrl);
248
249
  return stripRscSuffix(url.pathname);
249
250
  } catch {
250
251
  return null;
@@ -434,8 +435,7 @@ function toAppPrefetchDestination(href) {
434
435
  }
435
436
  const browserHref = toCanonicalBrowserNavigationHref(localHref, window.location.href, __basePath, __trailingSlash);
436
437
  try {
437
- const url = new URL(browserHref, window.location.href);
438
- return `${url.pathname}${url.search}`;
438
+ return formatRscRequestPath(new URL(browserHref, window.location.href));
439
439
  } catch {
440
440
  return browserHref.split("#", 1)[0];
441
441
  }
@@ -797,6 +797,22 @@ async function fetchRouteTreeGatedPrefetch(options) {
797
797
  return fetchFullRscPayload();
798
798
  }
799
799
  /**
800
+ * Whether a prefetch response may be decoded as Flight. A successful response
801
+ * that is not Flight (e.g. a Route Handler's text download) is a miss: decoding
802
+ * its bytes would let text shaped like Flight resource hints insert scripts.
803
+ * Navigation then fetches it and falls back to a document navigation.
804
+ *
805
+ * Ported from Next.js: fetchPrefetchResponse in
806
+ * packages/next/src/client/components/segment-cache/cache.ts, which skips the
807
+ * check in `output: "export"` because a static host, not Next.js, chooses the
808
+ * MIME type of the exported `.txt` payloads.
809
+ */
810
+ function isFlightPrefetchResponse(response) {
811
+ if (!response.ok) return false;
812
+ if (process.env.NODE_ENV === "production" && process.env.__NEXT_CONFIG_OUTPUT === "export") return true;
813
+ return response.headers.get("content-type")?.toLowerCase().startsWith(VINEXT_RSC_CONTENT_TYPE) === true;
814
+ }
815
+ /**
800
816
  * Prefetch an RSC response and snapshot it for later consumption.
801
817
  * Stores the in-flight promise so immediate clicks can await it instead
802
818
  * of firing a duplicate fetch.
@@ -824,7 +840,7 @@ function prefetchRscResponse(rscUrl, fetchPromise, interceptionContext = null, m
824
840
  addPrefetchInvalidationCallback(entry, options?.onInvalidate);
825
841
  entry.fetchPromise = fetchPromise;
826
842
  entry.pending = fetchPromise.then(async (response) => {
827
- if (response.ok) {
843
+ if (isFlightPrefetchResponse(response)) {
828
844
  const snapshot = await snapshotRscResponse(response);
829
845
  if (cache.get(cacheKey) !== entry) return;
830
846
  const previousSize = getPrefetchCacheEntrySize(entry);
@@ -841,6 +857,7 @@ function prefetchRscResponse(rscUrl, fetchPromise, interceptionContext = null, m
841
857
  evictPrefetchCacheIfNeeded();
842
858
  } else {
843
859
  releaseAppPrefetchFetchSlot(response);
860
+ response.body?.cancel().catch(() => {});
844
861
  deletePrefetchCacheEntry(cache, prefetched, cacheKey, entry, false);
845
862
  }
846
863
  }).catch(() => {
@@ -1333,11 +1350,19 @@ function commitHashOnlyHistoryState(href, mode, scroll) {
1333
1350
  function applyAppRouterScrollFallback(intent) {
1334
1351
  if (typeof document === "undefined" || typeof window === "undefined") return;
1335
1352
  if (intent.hash !== null) {
1336
- scrollToHashTargetOnNextFrame(intent.hash, () => isLatestAppRouterScrollIntent(intent));
1353
+ const hash = intent.hash;
1354
+ requestAnimationFrame(() => {
1355
+ if (!isLatestAppRouterScrollIntent(intent)) return;
1356
+ disableSmoothScrollDuringRouteTransition(() => {
1357
+ scrollToHashTarget(hash);
1358
+ });
1359
+ });
1337
1360
  return;
1338
1361
  }
1339
1362
  if (intent.targetHoistedInHead) return;
1340
- document.documentElement.scrollTop = 0;
1363
+ disableSmoothScrollDuringRouteTransition(() => {
1364
+ document.documentElement.scrollTop = 0;
1365
+ });
1341
1366
  }
1342
1367
  function scheduleAppRouterScrollFallback(intent) {
1343
1368
  queueMicrotask(() => {
@@ -44,6 +44,8 @@ export type ExecutionContextLike = {
44
44
  isInternalPagesRevalidation?: boolean;
45
45
  /** Marks a build-secret-authorized Worker request that may run prerender discovery hooks. */
46
46
  isPrerenderPathDiscovery?: boolean;
47
+ /** Marks an adapter-invoked regeneration of an entry stored for this build. */
48
+ isStoredEntryRegeneration?: boolean;
47
49
  /**
48
50
  * Installed only by the prerender server: records the page's special error
49
51
  * that escaped the shell, with the headers to store, for the build.
@@ -3,7 +3,8 @@ import { NavigationContext } from "./navigation-context-state.js";
3
3
  import "./navigation.js";
4
4
  import { CacheLifeConfig, CacheState } from "./cache-request-state.js";
5
5
  import { RootParamsState } from "./root-params.js";
6
- import { FetchCacheState } from "./fetch-cache.js";
6
+ import { FetchCacheState } from "./fetch-cache-state.js";
7
+ import "./fetch-cache.js";
7
8
  import { HeadersAccessPhase, VinextHeadersShimState } from "./internal/headers-state.js";
8
9
  import { HeadersContext } from "./headers.js";
9
10
  import { RouterState, SSRContext } from "./router-state.js";
@@ -1,8 +1,9 @@
1
1
  import { isUnknownRecord } from "../utils/record.js";
2
2
  import { parseCookieHeader } from "../utils/parse-cookie.js";
3
3
  import { isExternalUrl } from "../utils/external-url.js";
4
- import { sortRoutes, splitPathSegments } from "../routing/utils.js";
5
4
  import { hasBasePath, removeTrailingSlash, stripBasePath } from "../utils/base-path.js";
5
+ import { substituteDestinationParams } from "../config/destination-params.js";
6
+ import { sortRoutes, splitPathSegments } from "../routing/utils.js";
6
7
  import { assertSafeNavigationUrl } from "./url-safety.js";
7
8
  import { addLocalePrefix, getDomainLocaleUrl, getLocalePathPrefix } from "../utils/domain-locale.js";
8
9
  import { AppRouterContext } from "./internal/app-router-context.js";
@@ -19,6 +20,7 @@ import { resolveDirectHybridClientRouteOwner } from "./internal/hybrid-client-ro
19
20
  import { installWindowNext } from "../client/window-next.js";
20
21
  import { getWindowOrigin, isAbsoluteOrProtocolRelativeUrl, isAbsoluteUrl, isHashOnlyBrowserUrlChange, normalizePathTrailingSlash, toBrowserNavigationHref, toSameOriginAppPath, toSameOriginPath, withBasePath } from "./url-utils.js";
21
22
  import { addQueryParam, mergeRewriteQuery, mergeRouteParamsIntoQuery, parseQueryString } from "../utils/query.js";
23
+ import { disableSmoothScrollDuringRouteTransition } from "./disable-smooth-scroll.js";
22
24
  import { scrollToHashTarget } from "./hash-scroll.js";
23
25
  import { installPagesRouterRuntime, setPagesRouterPopStateHandler, setStampInitialHistoryState } from "./pages-router-runtime.js";
24
26
  import { interpolateDynamicRouteHref, resolveDynamicRouteHref } from "./internal/interpolate-as.js";
@@ -129,16 +131,9 @@ function scrollToPagesRouterPosition({ x, y }) {
129
131
  window.scrollTo(x, y);
130
132
  return;
131
133
  }
132
- const htmlElement = document.documentElement;
133
- if (!(htmlElement.dataset.scrollBehavior === "smooth")) {
134
+ disableSmoothScrollDuringRouteTransition(() => {
134
135
  window.scrollTo(x, y);
135
- return;
136
- }
137
- const previousScrollBehavior = htmlElement.style.scrollBehavior;
138
- htmlElement.style.scrollBehavior = "auto";
139
- htmlElement.getClientRects();
140
- window.scrollTo(x, y);
141
- htmlElement.style.scrollBehavior = previousScrollBehavior;
136
+ });
142
137
  }
143
138
  function isAtScrollPosition({ x, y }) {
144
139
  return Math.abs(window.scrollX - x) <= SCROLL_RESTORE_TOLERANCE_PX && Math.abs(window.scrollY - y) <= SCROLL_RESTORE_TOLERANCE_PX;
@@ -940,7 +935,6 @@ function isAppComponent(value) {
940
935
  function resolveSameOriginRedirectedUrl(responseUrl) {
941
936
  const appPath = toSameOriginAppPath(responseUrl, __basePath);
942
937
  if (appPath === null) return null;
943
- if (appPath.startsWith("//")) return normalizePathTrailingSlash(responseUrl, __trailingSlash);
944
938
  return normalizePathTrailingSlash(appPath, __trailingSlash);
945
939
  }
946
940
  function stripLocalePrefixForApiRedirect(appPath) {
@@ -962,19 +956,13 @@ function resolveLocalRedirectUrl(location) {
962
956
  let appPath;
963
957
  if (location.startsWith("/") && !location.startsWith("//")) try {
964
958
  const parsed = new URL(location, "http://vinext.local");
965
- appPath = stripBasePath(parsed.pathname, __basePath) + parsed.search + parsed.hash;
959
+ const pathname = stripBasePath(parsed.pathname, __basePath);
960
+ appPath = pathname.startsWith("//") ? toSameOriginAppPath(new URL(location, window.location.href).href, __basePath) : pathname + parsed.search + parsed.hash;
966
961
  } catch {
967
962
  appPath = location;
968
963
  }
969
964
  else appPath = toSameOriginAppPath(location, __basePath);
970
965
  if (appPath === null) return null;
971
- if (appPath.startsWith("//")) try {
972
- const browserUrl = new URL(location, window.location.href);
973
- if (browserUrl.origin !== getWindowOrigin()) return null;
974
- return normalizePathTrailingSlash(browserUrl.href, __trailingSlash);
975
- } catch {
976
- return null;
977
- }
978
966
  return normalizePathTrailingSlash(stripLocalePrefixForApiRedirect(appPath), __trailingSlash);
979
967
  }
980
968
  function hasClientRewriteRules() {
@@ -1020,10 +1008,10 @@ async function resolveClientConfigRedirect(href) {
1020
1008
  if (!redirects || redirects.length === 0) return null;
1021
1009
  const routeContext = getClientConfigRouteContext(href);
1022
1010
  if (!routeContext) return null;
1023
- const { matchRedirect, preserveRedirectDestinationQuery } = await import("../config/config-matchers.js");
1024
- const redirect = matchRedirect(routeContext.pathname, redirects, routeContext.context, routeContext.basePathState);
1011
+ const [{ matchClientRedirect }, { preserveRedirectDestinationQuery }] = await Promise.all([import("../client/client-redirect-matcher.js"), import("../config/config-matchers.js")]);
1012
+ const redirect = matchClientRedirect(routeContext.pathname, redirects, routeContext.context, routeContext.basePathState);
1025
1013
  if (!redirect) return null;
1026
- return preserveRedirectDestinationQuery(__basePath && routeContext.basePathState.hadBasePath && !isExternalUrl(redirect.destination) && !hasBasePath(redirect.destination, __basePath) ? __basePath + redirect.destination : redirect.destination, routeContext.search);
1014
+ return preserveRedirectDestinationQuery(__basePath && routeContext.basePathState.hadBasePath && !isExternalUrl(redirect.destination) && !hasBasePath(redirect.destination, __basePath) ? __basePath + redirect.destination : redirect.destination, routeContext.search, redirect.destinationQuery);
1027
1015
  }
1028
1016
  async function applyClientConfigRewrite(href, rewrite) {
1029
1017
  const routeContext = getClientConfigRouteContext(href);
@@ -1076,12 +1064,6 @@ function simpleClientConfigSourceCouldMatch(pathname, source) {
1076
1064
  const normalizedPathname = removeTrailingSlash(pathname);
1077
1065
  return normalizedPathname === normalizedPrefix || normalizedPathname.startsWith(`${normalizedPrefix}/`);
1078
1066
  }
1079
- function substituteSimpleClientConfigDestination(destination, params) {
1080
- const keys = Object.keys(params);
1081
- if (keys.length === 0) return destination;
1082
- const alternation = keys.sort((a, b) => b.length - a.length).map((key) => key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
1083
- return destination.replace(new RegExp(`:(${alternation})([+*])?(?![A-Za-z0-9_])`, "g"), (_token, key) => params[key] ?? _token);
1084
- }
1085
1067
  function isExternalClientConfigUrl(url) {
1086
1068
  return /^[a-z][a-z0-9+.-]*:/i.test(url) || url.startsWith("//");
1087
1069
  }
@@ -1111,7 +1093,7 @@ function resolveClientConfigRewriteSync(href) {
1111
1093
  const params = matchSimpleClientConfigPattern(routeContext.pathname, rewrite.source);
1112
1094
  if (params === void 0) return void 0;
1113
1095
  if (params === null) continue;
1114
- const rewritten = substituteSimpleClientConfigDestination(rewrite.destination, params);
1096
+ const rewritten = substituteDestinationParams(rewrite.destination, params, "rewrite");
1115
1097
  if (isExternalClientConfigUrl(rewritten)) return { kind: "document" };
1116
1098
  currentHref = mergeRewriteQuery(currentHref, rewritten);
1117
1099
  matched = true;
@@ -1827,6 +1809,10 @@ async function performNavigation(url, as, options, mode, onStateUpdate, redirect
1827
1809
  const absoluteHistoryUrl = sameOriginPath === null ? void 0 : origin && inputHref.startsWith(origin) ? inputHref.slice(origin.length) : sameOriginPath;
1828
1810
  const currentRoute = window.__NEXT_DATA__?.page ?? stripBasePath(window.location.pathname, __basePath);
1829
1811
  const currentAs = stripBasePath(window.location.pathname, __basePath) + window.location.search;
1812
+ const resolveRelativeRoutePath = (href) => href === "" || /^[/?#]/.test(href) || isAbsoluteUrl(href) ? href : resolvePagesRouterHref(href, {
1813
+ pathname: currentRoute,
1814
+ asPath: currentAs
1815
+ }, __basePath);
1830
1816
  const preparedHistoryHref = (isAbsoluteUrl(inputHref) ? toSameOriginAppPath(inputHref, __basePath) : null) ?? absoluteHistoryUrl ?? resolvePagesRouterHref(inputHref, {
1831
1817
  pathname: currentRoute,
1832
1818
  asPath: currentAs
@@ -1836,12 +1822,12 @@ async function performNavigation(url, as, options, mode, onStateUpdate, redirect
1836
1822
  const replaceInheritedLocale = options?.locale !== void 0 && (!as ? urlInheritsCurrentPath || typeof url !== "string" && inheritsVisiblePath(url) : typeof as !== "string" && inheritsVisiblePath(as));
1837
1823
  const credentialedHistoryHref = credentialedSameOriginHref(url);
1838
1824
  const credentialedDisplayHref = credentialedSameOriginHref(as) ?? (!as ? credentialedHistoryHref : void 0);
1839
- as = as ? resolveUrl(as, true) : void 0;
1825
+ as = as ? resolveRelativeRoutePath(resolveUrl(as, true)) : void 0;
1840
1826
  assertSafeNavigationUrl(resolveUrl(url));
1841
1827
  if (as !== void 0) assertSafeNavigationUrl(as);
1842
1828
  const isHydrationQueryUpdate = options?._h === 1;
1843
1829
  const navigationLocale = resolveTransitionLocale(options?.locale);
1844
- const unlocalizedRouteHref = resolveUrl(isQueryOnlyString ? `${stripBasePath(window.location.pathname, __basePath)}${url}` : url);
1830
+ const unlocalizedRouteHref = resolveRelativeRoutePath(resolveUrl(isQueryOnlyString ? `${stripBasePath(window.location.pathname, __basePath)}${url}` : url));
1845
1831
  const localRouteHref = isExternalUrl(unlocalizedRouteHref) ? toSameOriginAppPath(unlocalizedRouteHref, __basePath) : unlocalizedRouteHref;
1846
1832
  const implicitDynamicRoute = !isHydrationQueryUpdate && as === void 0 && localRouteHref !== null ? resolveDynamicRouteHref(localRouteHref) : null;
1847
1833
  const routeForNavigation = implicitDynamicRoute?.as ?? unlocalizedRouteHref;
@@ -2018,7 +2004,10 @@ See more info: https://nextjs.org/docs/messages/invalid-relative-url-external-as
2018
2004
  emitBeforeHistoryChange();
2019
2005
  if (doScroll) {
2020
2006
  if (hash) scrollToHashTarget(hash);
2021
- else window.scrollTo(0, 0);
2007
+ else scrollToPagesRouterPosition({
2008
+ x: 0,
2009
+ y: 0
2010
+ });
2022
2011
  }
2023
2012
  }
2024
2013
  onStateUpdate?.();
@@ -26,6 +26,7 @@ export type RequestInit = globalThis.RequestInit & {
26
26
  }>;
27
27
  } | null;
28
28
  trailingSlash?: boolean;
29
+ skipTrailingSlashRedirect?: boolean;
29
30
  };
30
31
  signal?: AbortSignal;
31
32
  duplex?: "half";
@@ -41,13 +42,20 @@ export declare class NextRequest extends Request {
41
42
  get page(): void;
42
43
  get ua(): void;
43
44
  /**
44
- * Client IP address. Prefers Cloudflare's trusted CF-Connecting-IP header
45
- * over the spoofable X-Forwarded-For. Returns undefined if unavailable.
45
+ * Client IP address. Next.js removed `ip` in v15; vinext keeps it as an
46
+ * extension. Returns undefined unless the value comes from a trusted source.
47
+ *
48
+ * On Cloudflare Workers (the request carries `cf` metadata), Cloudflare's
49
+ * edge sets `CF-Connecting-IP`. Elsewhere these headers are client-supplied
50
+ * and are only honored behind a trusted proxy (`VINEXT_TRUST_PROXY` /
51
+ * `VINEXT_TRUSTED_HOSTS`), matching the `X-Forwarded-*` policy.
46
52
  */
47
53
  get ip(): string | undefined;
48
54
  /**
49
- * Geolocation data. Platform-dependent (e.g., Cloudflare, Vercel).
50
- * Returns undefined if not available.
55
+ * Geolocation data. Next.js removed `geo` in v15; vinext keeps it as an
56
+ * extension. Read from Cloudflare's `request.cf` metadata on Workers, or
57
+ * from platform geo headers behind a trusted proxy. Returns undefined
58
+ * otherwise, because any client can send these headers.
51
59
  */
52
60
  get geo(): {
53
61
  city?: string;
@@ -109,6 +117,14 @@ export type NextURLConfig = {
109
117
  * honour the user's `trailingSlash` config.
110
118
  */
111
119
  trailingSlash?: boolean;
120
+ /**
121
+ * When true, the trailing slash follows the parsed URL instead, and
122
+ * `trailingSlash` only decides the root, like Next.js's
123
+ * getNextPathnameInfo. Without the trailing-slash redirect, requests
124
+ * arrive in either form, so the config can no longer be applied as a
125
+ * policy.
126
+ */
127
+ skipTrailingSlashRedirect?: boolean;
112
128
  };
113
129
  };
114
130
  export declare class NextURL {
@@ -121,6 +137,8 @@ export declare class NextURL {
121
137
  */
122
138
  private _configBasePath;
123
139
  private _basePath;
140
+ private _configTrailingSlash;
141
+ private _manualTrailingSlash;
124
142
  private _trailingSlash;
125
143
  private _locale;
126
144
  private _configDefaultLocale;
@@ -129,6 +147,11 @@ export declare class NextURL {
129
147
  private _domains;
130
148
  private _domainLocale;
131
149
  constructor(input: string | URL, base?: string | URL, config?: NextURLConfig);
150
+ /**
151
+ * With `skipTrailingSlashRedirect`, take the slash from the parsed pathname
152
+ * (before basePath and locale are stripped), as getNextPathnameInfo does.
153
+ */
154
+ private _analyzeTrailingSlash;
132
155
  /** Strip basePath prefix from the internal pathname.
133
156
  * Mirrors Next.js's getNextPathnameInfo (re-run by NextURL.analyze() on
134
157
  * every parse, including `href` reassignment): basePath is only considered
@@ -3,10 +3,11 @@ import { MIDDLEWARE_SET_COOKIE_HEADER } from "../utils/protocol-headers.js";
3
3
  import { encodeMiddlewareRequestHeaders } from "../utils/middleware-request-headers.js";
4
4
  import { parseEdgeRequestCookieHeader } from "../utils/parse-cookie.js";
5
5
  import { hasBasePath, removeTrailingSlash, stripBasePath } from "../utils/base-path.js";
6
- import { getRequestExecutionContext } from "./request-context.js";
7
6
  import { MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_REWRITE_HEADER } from "../server/headers.js";
7
+ import { getRequestExecutionContext } from "./request-context.js";
8
8
  import { validateCookieAttributeValue, validateCookieName } from "./internal/cookie-serialize.js";
9
9
  import { assertSafeNavigationUrl } from "./url-safety.js";
10
+ import { trustProxy } from "../server/proxy-trust.js";
10
11
  import { userAgent, userAgentFromString } from "./user-agent.js";
11
12
  //#region src/shims/server.ts
12
13
  /**
@@ -97,6 +98,19 @@ function initWithoutInputBody(input, init) {
97
98
  body: null
98
99
  };
99
100
  }
101
+ /**
102
+ * workerd's `request.cf` metadata. Only the Workers runtime sets it, so its
103
+ * presence means the request came through Cloudflare's edge rather than
104
+ * straight from a client.
105
+ */
106
+ function readCloudflareMetadata(request) {
107
+ const cf = Reflect.get(request, "cf");
108
+ return cf !== null && typeof cf === "object" ? cf : void 0;
109
+ }
110
+ function readStringField(source, key) {
111
+ const value = Reflect.get(source, key);
112
+ return typeof value === "string" && value !== "" ? value : void 0;
113
+ }
100
114
  var NextRequest = class extends Request {
101
115
  _nextUrl;
102
116
  _url;
@@ -131,7 +145,8 @@ var NextRequest = class extends Request {
131
145
  basePath: _nextConfig.basePath,
132
146
  nextConfig: {
133
147
  i18n,
134
- trailingSlash: _nextConfig.trailingSlash
148
+ trailingSlash: _nextConfig.trailingSlash,
149
+ skipTrailingSlashRedirect: _nextConfig.skipTrailingSlashRedirect
135
150
  }
136
151
  } : void 0;
137
152
  this._nextUrl = new NextURL(url, void 0, urlConfig);
@@ -154,17 +169,39 @@ var NextRequest = class extends Request {
154
169
  throw new Error("NextRequest.ua has been removed; use userAgent() instead");
155
170
  }
156
171
  /**
157
- * Client IP address. Prefers Cloudflare's trusted CF-Connecting-IP header
158
- * over the spoofable X-Forwarded-For. Returns undefined if unavailable.
172
+ * Client IP address. Next.js removed `ip` in v15; vinext keeps it as an
173
+ * extension. Returns undefined unless the value comes from a trusted source.
174
+ *
175
+ * On Cloudflare Workers (the request carries `cf` metadata), Cloudflare's
176
+ * edge sets `CF-Connecting-IP`. Elsewhere these headers are client-supplied
177
+ * and are only honored behind a trusted proxy (`VINEXT_TRUST_PROXY` /
178
+ * `VINEXT_TRUSTED_HOSTS`), matching the `X-Forwarded-*` policy.
159
179
  */
160
180
  get ip() {
181
+ if (readCloudflareMetadata(this)) return this.headers.get("cf-connecting-ip") ?? void 0;
182
+ if (!trustProxy) return void 0;
161
183
  return this.headers.get("cf-connecting-ip") ?? this.headers.get("x-real-ip") ?? this.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ?? void 0;
162
184
  }
163
185
  /**
164
- * Geolocation data. Platform-dependent (e.g., Cloudflare, Vercel).
165
- * Returns undefined if not available.
186
+ * Geolocation data. Next.js removed `geo` in v15; vinext keeps it as an
187
+ * extension. Read from Cloudflare's `request.cf` metadata on Workers, or
188
+ * from platform geo headers behind a trusted proxy. Returns undefined
189
+ * otherwise, because any client can send these headers.
166
190
  */
167
191
  get geo() {
192
+ const cf = readCloudflareMetadata(this);
193
+ if (cf) {
194
+ const country = readStringField(cf, "country");
195
+ if (!country) return void 0;
196
+ return {
197
+ country,
198
+ city: readStringField(cf, "city"),
199
+ region: readStringField(cf, "region"),
200
+ latitude: readStringField(cf, "latitude"),
201
+ longitude: readStringField(cf, "longitude")
202
+ };
203
+ }
204
+ if (!trustProxy) return void 0;
168
205
  const country = this.headers.get("cf-ipcountry") ?? this.headers.get("x-vercel-ip-country") ?? void 0;
169
206
  if (!country) return void 0;
170
207
  return {
@@ -266,6 +303,8 @@ var NextURL = class NextURL {
266
303
  */
267
304
  _configBasePath;
268
305
  _basePath;
306
+ _configTrailingSlash;
307
+ _manualTrailingSlash;
269
308
  _trailingSlash;
270
309
  _locale;
271
310
  _configDefaultLocale;
@@ -277,7 +316,10 @@ var NextURL = class NextURL {
277
316
  this._url = new URL(input.toString(), base);
278
317
  this._configBasePath = config?.basePath ?? "";
279
318
  this._basePath = this._configBasePath;
280
- this._trailingSlash = config?.nextConfig?.trailingSlash ?? false;
319
+ this._configTrailingSlash = config?.nextConfig?.trailingSlash ?? false;
320
+ this._manualTrailingSlash = config?.nextConfig?.skipTrailingSlashRedirect === true;
321
+ this._trailingSlash = this._configTrailingSlash;
322
+ this._analyzeTrailingSlash();
281
323
  this._stripBasePath();
282
324
  const i18n = config?.nextConfig?.i18n;
283
325
  if (i18n) {
@@ -290,6 +332,15 @@ var NextURL = class NextURL {
290
332
  this._analyzeI18n();
291
333
  }
292
334
  }
335
+ /**
336
+ * With `skipTrailingSlashRedirect`, take the slash from the parsed pathname
337
+ * (before basePath and locale are stripped), as getNextPathnameInfo does.
338
+ */
339
+ _analyzeTrailingSlash() {
340
+ if (!this._manualTrailingSlash) return;
341
+ const { pathname } = this._url;
342
+ this._trailingSlash = pathname !== "/" ? pathname.endsWith("/") : this._configTrailingSlash;
343
+ }
293
344
  /** Strip basePath prefix from the internal pathname.
294
345
  * Mirrors Next.js's getNextPathnameInfo (re-run by NextURL.analyze() on
295
346
  * every parse, including `href` reassignment): basePath is only considered
@@ -348,7 +399,8 @@ var NextURL = class NextURL {
348
399
  */
349
400
  _applyTrailingSlash(pathname) {
350
401
  if (pathname === "" || pathname === "/") return pathname;
351
- if ((this._basePath ? stripBasePath(pathname, this._basePath) : pathname).startsWith("/_next/")) return removeTrailingSlash(pathname);
402
+ const pathnameWithoutBasePath = this._basePath ? stripBasePath(pathname, this._basePath) : pathname;
403
+ if (!this._manualTrailingSlash && pathnameWithoutBasePath.startsWith("/_next/")) return removeTrailingSlash(pathname);
352
404
  if (this._trailingSlash) return pathname.endsWith("/") ? pathname : pathname + "/";
353
405
  return pathname.endsWith("/") ? pathname.slice(0, -1) : pathname;
354
406
  }
@@ -361,6 +413,7 @@ var NextURL = class NextURL {
361
413
  }
362
414
  set href(value) {
363
415
  this._url.href = value;
416
+ this._analyzeTrailingSlash();
364
417
  this._stripBasePath();
365
418
  this._analyzeI18n();
366
419
  }
@@ -467,7 +520,10 @@ var NextURL = class NextURL {
467
520
  locales: domain.locales ? [...domain.locales] : void 0
468
521
  }))
469
522
  };
470
- if (this._trailingSlash) nextConfig.trailingSlash = true;
523
+ if (this._manualTrailingSlash) {
524
+ nextConfig.skipTrailingSlashRedirect = true;
525
+ if (this._configTrailingSlash) nextConfig.trailingSlash = true;
526
+ } else if (this._trailingSlash) nextConfig.trailingSlash = true;
471
527
  const config = {
472
528
  basePath: this._configBasePath,
473
529
  nextConfig: Object.keys(nextConfig).length > 0 ? nextConfig : void 0
@@ -1,7 +1,7 @@
1
1
  import { ExecutionContextLike } from "./request-context.js";
2
2
  import { CacheState } from "./cache-request-state.js";
3
3
  import { RootParamsState } from "./root-params.js";
4
- import { FetchCacheState } from "./fetch-cache.js";
4
+ import { FetchCacheState } from "./fetch-cache-state.js";
5
5
  import { VinextHeadersShimState } from "./internal/headers-state.js";
6
6
  import { RouterState } from "./router-state.js";
7
7
  import { markFullyBufferedBody, preserveFullyBufferedBodyMetadata } from "../server/fully-buffered-response.js";
@@ -2,6 +2,13 @@
2
2
  export declare function isAbsoluteUrl(url: string): boolean;
3
3
  export declare function isAbsoluteOrProtocolRelativeUrl(url: string): boolean;
4
4
  export declare function getWindowOrigin(): string | null;
5
+ /**
6
+ * A same-origin pathname can start with "//" (e.g. `https://app.com//evil.com`),
7
+ * and as a local path that is a protocol-relative reference to another host.
8
+ * Collapse the leading slashes: Next.js does the same for these URLs, on the
9
+ * client (`resolveHref`) or with its server's repeated-slash 308.
10
+ */
11
+ export declare function collapseLeadingSlashes(pathname: string): string;
5
12
  /**
6
13
  * If `url` is an absolute same-origin URL, return the local path
7
14
  * (pathname + search + hash). Returns null for truly external URLs
@@ -30,6 +37,8 @@ export declare function toSameOriginAppPath(url: string, basePath: string): stri
30
37
  * `.well-known`-aware redirect rule shipped in `routes-manifest.json`.
31
38
  * - Query strings and hash fragments are preserved verbatim.
32
39
  * - Idempotent: already-canonical paths round-trip unchanged.
40
+ * - With `skipTrailingSlashRedirect`, every path is returned as written, like
41
+ * Next.js's `__NEXT_MANUAL_TRAILING_SLASH` short-circuit.
33
42
  */
34
43
  export declare function normalizePathTrailingSlash(path: string, trailingSlash: boolean): string;
35
44
  /**
@@ -25,6 +25,15 @@ function getWindowOrigin() {
25
25
  }
26
26
  }
27
27
  /**
28
+ * A same-origin pathname can start with "//" (e.g. `https://app.com//evil.com`),
29
+ * and as a local path that is a protocol-relative reference to another host.
30
+ * Collapse the leading slashes: Next.js does the same for these URLs, on the
31
+ * client (`resolveHref`) or with its server's repeated-slash 308.
32
+ */
33
+ function collapseLeadingSlashes(pathname) {
34
+ return pathname.replace(/^\/{2,}/, "/");
35
+ }
36
+ /**
28
37
  * If `url` is an absolute same-origin URL, return the local path
29
38
  * (pathname + search + hash). Returns null for truly external URLs
30
39
  * or on the server (where origin is unknown).
@@ -34,7 +43,7 @@ function toSameOriginPath(url) {
34
43
  if (!origin) return null;
35
44
  try {
36
45
  const parsed = url.startsWith("//") ? new URL(url, origin) : new URL(url);
37
- if (parsed.origin === origin) return parsed.pathname + parsed.search + parsed.hash;
46
+ if (parsed.origin === origin) return collapseLeadingSlashes(parsed.pathname) + parsed.search + parsed.hash;
38
47
  } catch {}
39
48
  return null;
40
49
  }
@@ -49,7 +58,7 @@ function toSameOriginAppPath(url, basePath) {
49
58
  try {
50
59
  const parsed = new URL(localPath, "http://vinext.local");
51
60
  if (!hasBasePath(parsed.pathname, basePath)) return null;
52
- return stripBasePath(parsed.pathname, basePath) + parsed.search + parsed.hash;
61
+ return collapseLeadingSlashes(stripBasePath(parsed.pathname, basePath)) + parsed.search + parsed.hash;
53
62
  } catch {
54
63
  return localPath;
55
64
  }
@@ -99,9 +108,11 @@ function removeRouteTrailingSlash(route) {
99
108
  * `.well-known`-aware redirect rule shipped in `routes-manifest.json`.
100
109
  * - Query strings and hash fragments are preserved verbatim.
101
110
  * - Idempotent: already-canonical paths round-trip unchanged.
111
+ * - With `skipTrailingSlashRedirect`, every path is returned as written, like
112
+ * Next.js's `__NEXT_MANUAL_TRAILING_SLASH` short-circuit.
102
113
  */
103
114
  function normalizePathTrailingSlash(path, trailingSlash) {
104
- if (!path.startsWith("/") || path.startsWith("//")) return path;
115
+ if (!path.startsWith("/") || path.startsWith("//") || process.env.__VINEXT_MANUAL_TRAILING_SLASH === "true") return path;
105
116
  const { pathname, query, hash } = parsePath(path);
106
117
  if (trailingSlash) {
107
118
  if (/\.[^/]+\/?$/.test(pathname)) return `${removeRouteTrailingSlash(pathname)}${query}${hash}`;
@@ -162,4 +173,4 @@ function isHashOnlyBrowserUrlChange(href, currentHref, basePath = "") {
162
173
  }
163
174
  }
164
175
  //#endregion
165
- export { getWindowOrigin, isAbsoluteOrProtocolRelativeUrl, isAbsoluteUrl, isHashOnlyBrowserUrlChange, normalizePathTrailingSlash, resolveRelativeHref, toBrowserNavigationHref, toCanonicalBrowserNavigationHref, toSameOriginAppPath, toSameOriginPath, withBasePath };
176
+ export { collapseLeadingSlashes, getWindowOrigin, isAbsoluteOrProtocolRelativeUrl, isAbsoluteUrl, isHashOnlyBrowserUrlChange, normalizePathTrailingSlash, resolveRelativeHref, toBrowserNavigationHref, toCanonicalBrowserNavigationHref, toSameOriginAppPath, toSameOriginPath, withBasePath };
@@ -1,8 +1,8 @@
1
1
  import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
2
  import { isAbsoluteAssetPrefix, resolveAssetsDir } from "./asset-prefix.js";
3
3
  import { manifestFileWithAssetPrefix, manifestFileWithBase } from "./manifest-paths.js";
4
- import { findClientEntryFile, findPagesClientEntryFile, readClientBuildManifest } from "./client-build-manifest.js";
5
4
  import { findClientEntryFileFromVinextManifest, findPagesClientEntryFileFromVinextManifest, readClientEntryManifest } from "./client-entry-manifest.js";
5
+ import { findClientEntryFile, findPagesClientEntryFile, readClientBuildManifest } from "./client-build-manifest.js";
6
6
  import { computeDynamicImportPreloads, computeLazyChunks, dynamicImportPreloadsWithBase } from "./lazy-chunks.js";
7
7
  //#region src/utils/client-runtime-metadata.ts
8
8
  function collectCssGraph(buildManifest, applyBase) {