vinext 1.2.0 → 1.3.0

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 (283) hide show
  1. package/README.md +13 -10
  2. package/dist/build/client-build-config.d.ts +64 -3
  3. package/dist/build/client-build-config.js +131 -4
  4. package/dist/build/client-reference-groups.d.ts +163 -0
  5. package/dist/build/client-reference-groups.js +534 -0
  6. package/dist/build/module-dependency-cache.d.ts +31 -0
  7. package/dist/build/module-dependency-cache.js +51 -1
  8. package/dist/build/next-client-runtime-manifests.d.ts +10 -1
  9. package/dist/build/next-client-runtime-manifests.js +19 -4
  10. package/dist/build/nitro-public-files.d.ts +24 -0
  11. package/dist/build/nitro-public-files.js +60 -0
  12. package/dist/build/output-root-externals.d.ts +21 -0
  13. package/dist/build/output-root-externals.js +40 -0
  14. package/dist/build/pages-client-assets-module.js +1 -1
  15. package/dist/build/prerender-paths.js +6 -3
  16. package/dist/build/prerender.js +1 -1
  17. package/dist/build/server-actions-encryption-key.d.ts +23 -0
  18. package/dist/build/server-actions-encryption-key.js +101 -0
  19. package/dist/build/ssr-manifest.d.ts +3 -0
  20. package/dist/build/ssr-manifest.js +10 -1
  21. package/dist/check.js +5 -6
  22. package/dist/cli-dev-config.js +3 -3
  23. package/dist/cli.js +1 -1
  24. package/dist/client/client-rewrite-params.d.ts +9 -0
  25. package/dist/client/client-rewrite-params.js +20 -0
  26. package/dist/client/dev-error-overlay.js +2 -1
  27. package/dist/client/navigation-runtime.d.ts +25 -2
  28. package/dist/client/navigation-runtime.js +42 -11
  29. package/dist/client/vinext-next-data.d.ts +0 -1
  30. package/dist/config/config-matchers.d.ts +15 -40
  31. package/dist/config/config-matchers.js +195 -262
  32. package/dist/config/config-source.d.ts +34 -0
  33. package/dist/config/config-source.js +141 -0
  34. package/dist/config/header-params.d.ts +29 -0
  35. package/dist/config/header-params.js +106 -0
  36. package/dist/config/next-config.d.ts +7 -5
  37. package/dist/config/next-config.js +120 -7
  38. package/dist/entries/app-browser-entry.d.ts +14 -1
  39. package/dist/entries/app-browser-entry.js +26 -36
  40. package/dist/entries/app-rsc-entry.d.ts +2 -0
  41. package/dist/entries/app-rsc-entry.js +92 -57
  42. package/dist/entries/app-rsc-manifest.d.ts +10 -0
  43. package/dist/entries/app-rsc-manifest.js +20 -1
  44. package/dist/entries/app-ssr-entry.d.ts +4 -1
  45. package/dist/entries/app-ssr-entry.js +9 -2
  46. package/dist/entries/pages-client-entry.d.ts +1 -0
  47. package/dist/entries/pages-client-entry.js +6 -6
  48. package/dist/entries/pages-server-entry.js +17 -14
  49. package/dist/index.d.ts +11 -0
  50. package/dist/index.js +350 -175
  51. package/dist/init.d.ts +1 -1
  52. package/dist/init.js +103 -6
  53. package/dist/plugins/ast-utils.d.ts +53 -2
  54. package/dist/plugins/ast-utils.js +90 -2
  55. package/dist/plugins/cloudflare-dev-module-urls.d.ts +41 -0
  56. package/dist/plugins/cloudflare-dev-module-urls.js +271 -0
  57. package/dist/plugins/commonjs-esm-facade.js +2 -2
  58. package/dist/plugins/css-module-imports.js +13 -20
  59. package/dist/plugins/dynamic-preload-metadata.js +2 -3
  60. package/dist/plugins/extensionless-dynamic-import.js +2 -3
  61. package/dist/plugins/fonts.d.ts +1 -1
  62. package/dist/plugins/fonts.js +3 -3
  63. package/dist/plugins/ignore-dynamic-requests.js +2 -3
  64. package/dist/plugins/import-meta-url.js +3 -4
  65. package/dist/plugins/instrumentation-client.js +3 -2
  66. package/dist/plugins/middleware-export-validation.js +2 -3
  67. package/dist/plugins/og-harfbuzz.js +2 -3
  68. package/dist/plugins/optimize-imports.d.ts +25 -2
  69. package/dist/plugins/optimize-imports.js +109 -24
  70. package/dist/plugins/pages-node-externals.js +2 -3
  71. package/dist/plugins/remove-console.js +2 -18
  72. package/dist/plugins/require-condition-resolution.js +3 -3
  73. package/dist/plugins/require-context.js +2 -3
  74. package/dist/plugins/rsc-client-reference-loaders.d.ts +45 -1
  75. package/dist/plugins/rsc-client-reference-loaders.js +336 -18
  76. package/dist/plugins/strip-server-exports.js +3 -4
  77. package/dist/plugins/styled-jsx.js +2 -3
  78. package/dist/plugins/use-cache-callable.d.ts +26 -2
  79. package/dist/plugins/use-cache-callable.js +101 -36
  80. package/dist/plugins/worker-image-imports.d.ts +1 -0
  81. package/dist/plugins/worker-image-imports.js +31 -34
  82. package/dist/plugins/worker-use-server.js +38 -14
  83. package/dist/routing/app-route-graph.d.ts +1 -1
  84. package/dist/routing/browser-route-manifest.d.ts +43 -0
  85. package/dist/routing/browser-route-manifest.js +195 -0
  86. package/dist/routing/hashed-route-patterns.d.ts +4 -0
  87. package/dist/routing/hashed-route-patterns.js +22 -0
  88. package/dist/routing/optimistic-route-trie.d.ts +27 -0
  89. package/dist/routing/optimistic-route-trie.js +102 -0
  90. package/dist/routing/route-validation.d.ts +6 -1
  91. package/dist/routing/route-validation.js +26 -10
  92. package/dist/server/api-handler.js +1 -1
  93. package/dist/server/app-action-forwarding.d.ts +8 -0
  94. package/dist/server/app-action-forwarding.js +17 -3
  95. package/dist/server/app-browser-entry.js +149 -62
  96. package/dist/server/app-browser-error.d.ts +3 -1
  97. package/dist/server/app-browser-error.js +7 -2
  98. package/dist/server/app-browser-interception-context.d.ts +2 -2
  99. package/dist/server/app-browser-interception-context.js +1 -1
  100. package/dist/server/app-browser-mpa-navigation.d.ts +1 -0
  101. package/dist/server/app-browser-mpa-navigation.js +6 -5
  102. package/dist/server/app-browser-navigation-controller.d.ts +17 -6
  103. package/dist/server/app-browser-navigation-controller.js +88 -26
  104. package/dist/server/app-browser-prefetch-response.js +2 -2
  105. package/dist/server/app-browser-state.d.ts +2 -2
  106. package/dist/server/app-browser-state.js +1 -1
  107. package/dist/server/app-browser-supplemental-refresh.js +1 -1
  108. package/dist/server/app-browser-visible-commit.d.ts +4 -4
  109. package/dist/server/app-client-reference-preloader.d.ts +23 -0
  110. package/dist/server/app-client-reference-preloader.js +30 -7
  111. package/dist/server/app-fallback-renderer.js +2 -1
  112. package/dist/server/app-middleware.d.ts +6 -0
  113. package/dist/server/app-middleware.js +8 -10
  114. package/dist/server/app-optimistic-routing.d.ts +8 -10
  115. package/dist/server/app-optimistic-routing.js +4 -95
  116. package/dist/server/app-page-boundary-render.js +8 -1
  117. package/dist/server/app-page-cache-render.js +1 -0
  118. package/dist/server/app-page-dispatch.js +3 -1
  119. package/dist/server/app-page-render.js +4 -2
  120. package/dist/server/app-page-rsc-render-status.d.ts +1 -0
  121. package/dist/server/app-page-rsc-render-status.js +1 -0
  122. package/dist/server/app-page-search-params-observation.d.ts +1 -4
  123. package/dist/server/app-page-search-params-observation.js +8 -10
  124. package/dist/server/app-page-stream.d.ts +7 -0
  125. package/dist/server/app-page-stream.js +1 -0
  126. package/dist/server/app-pages-bridge.d.ts +2 -1
  127. package/dist/server/app-pages-bridge.js +12 -8
  128. package/dist/server/app-prerender-endpoints.js +1 -1
  129. package/dist/server/app-prerender-static-params.js +3 -2
  130. package/dist/server/app-render-abort-error.d.ts +19 -0
  131. package/dist/server/app-render-abort-error.js +31 -1
  132. package/dist/server/app-render-dependency.js +42 -2
  133. package/dist/server/app-request-stage-independent-entry.js +7 -7
  134. package/dist/server/app-response-stage-entry.js +5 -5
  135. package/dist/server/app-route-handler-dispatch.js +3 -1
  136. package/dist/server/app-route-handler-execution.d.ts +2 -0
  137. package/dist/server/app-route-handler-execution.js +9 -2
  138. package/dist/server/app-route-handler-middleware-context.d.ts +2 -1
  139. package/dist/server/app-route-handler-middleware-context.js +3 -3
  140. package/dist/server/app-route-handler-response.d.ts +2 -1
  141. package/dist/server/app-route-handler-response.js +2 -2
  142. package/dist/server/app-route-handler-runtime.d.ts +2 -0
  143. package/dist/server/app-route-handler-runtime.js +21 -4
  144. package/dist/server/app-route-module-loader.js +7 -6
  145. package/dist/server/app-route-tree.d.ts +20 -0
  146. package/dist/server/app-route-tree.js +18 -0
  147. package/dist/server/app-router-entry.js +7 -7
  148. package/dist/server/app-rsc-cache-busting.d.ts +16 -0
  149. package/dist/server/app-rsc-cache-busting.js +29 -12
  150. package/dist/server/app-rsc-combined-handler.js +8 -1
  151. package/dist/server/app-rsc-errors.d.ts +7 -0
  152. package/dist/server/app-rsc-errors.js +9 -2
  153. package/dist/server/app-rsc-handler.d.ts +4 -1
  154. package/dist/server/app-rsc-handler.js +151 -135
  155. package/dist/server/app-rsc-request-normalization.js +3 -3
  156. package/dist/server/app-rsc-response-finalizer.d.ts +5 -0
  157. package/dist/server/app-rsc-response-finalizer.js +4 -1
  158. package/dist/server/app-rsc-response-stage.js +5 -3
  159. package/dist/server/app-rsc-route-matching.d.ts +1 -0
  160. package/dist/server/app-rsc-route-matching.js +1 -1
  161. package/dist/server/app-server-action-execution.d.ts +23 -6
  162. package/dist/server/app-server-action-execution.js +51 -57
  163. package/dist/server/app-ssr-entry.d.ts +6 -0
  164. package/dist/server/app-ssr-entry.js +21 -11
  165. package/dist/server/app-ssr-stream.d.ts +7 -0
  166. package/dist/server/app-ssr-stream.js +13 -1
  167. package/dist/server/app-visited-response-cache.js +2 -2
  168. package/dist/server/config-headers.js +13 -4
  169. package/dist/server/defer-until-stream-consumed.d.ts +2 -1
  170. package/dist/server/defer-until-stream-consumed.js +3 -1
  171. package/dist/server/dev-server.js +1 -1
  172. package/dist/server/image-optimization.js +1 -0
  173. package/dist/server/isolate-shared-promise.d.ts +10 -0
  174. package/dist/server/isolate-shared-promise.js +33 -0
  175. package/dist/server/isr-cache.js +3 -2
  176. package/dist/server/metadata-route-response.js +2 -2
  177. package/dist/server/middleware-matcher-pattern.d.ts +0 -1
  178. package/dist/server/middleware-matcher-pattern.js +11 -140
  179. package/dist/server/middleware-matcher.js +5 -6
  180. package/dist/server/middleware-response-headers.d.ts +31 -0
  181. package/dist/server/middleware-response-headers.js +57 -1
  182. package/dist/server/middleware-runtime.js +30 -17
  183. package/dist/server/navigation-planner.d.ts +2 -2
  184. package/dist/server/navigation-planner.js +1 -1
  185. package/dist/server/nitro-public-files-plugin.d.ts +38 -0
  186. package/dist/server/nitro-public-files-plugin.js +106 -0
  187. package/dist/server/nitro-public-files.d.ts +21 -0
  188. package/dist/server/nitro-public-files.js +39 -0
  189. package/dist/server/open-redirect.d.ts +11 -4
  190. package/dist/server/open-redirect.js +11 -4
  191. package/dist/server/pages-api-route.js +2 -2
  192. package/dist/server/pages-asset-tags.d.ts +5 -0
  193. package/dist/server/pages-asset-tags.js +9 -2
  194. package/dist/server/pages-client-assets.d.ts +4 -1
  195. package/dist/server/pages-data-route.js +2 -1
  196. package/dist/server/pages-i18n.d.ts +18 -0
  197. package/dist/server/pages-i18n.js +18 -2
  198. package/dist/server/pages-node-compat.d.ts +5 -3
  199. package/dist/server/pages-node-compat.js +35 -16
  200. package/dist/server/pages-page-data.js +1 -1
  201. package/dist/server/pages-page-handler.js +1 -1
  202. package/dist/server/pages-page-response.js +1 -1
  203. package/dist/server/pages-request-pipeline.d.ts +6 -1
  204. package/dist/server/pages-request-pipeline.js +35 -39
  205. package/dist/server/pages-request-stage-entry.js +20 -10
  206. package/dist/server/pages-response-stage-entry.js +1 -1
  207. package/dist/server/pages-revalidate.js +3 -2
  208. package/dist/server/pages-router-entry.js +2 -2
  209. package/dist/server/prod-server.d.ts +2 -2
  210. package/dist/server/prod-server.js +74 -36
  211. package/dist/server/proxy-trust.d.ts +6 -0
  212. package/dist/server/proxy-trust.js +12 -6
  213. package/dist/server/request-pipeline.d.ts +5 -0
  214. package/dist/server/request-pipeline.js +12 -1
  215. package/dist/server/rsc-stream-hints.d.ts +1 -1
  216. package/dist/server/rsc-stream-hints.js +21 -3
  217. package/dist/server/server-reference-loader.js +10 -1
  218. package/dist/server/static-file-cache.js +15 -3
  219. package/dist/server/worker-image-optimization.js +19 -6
  220. package/dist/server/worker-utils.js +1 -1
  221. package/dist/shims/cache-handler.js +3 -2
  222. package/dist/shims/cache-request-state.d.ts +40 -3
  223. package/dist/shims/cache-request-state.js +70 -5
  224. package/dist/shims/cache-runtime.js +47 -19
  225. package/dist/shims/cache.d.ts +2 -2
  226. package/dist/shims/cache.js +17 -8
  227. package/dist/shims/cdn-purge-routes.d.ts +24 -0
  228. package/dist/shims/cdn-purge-routes.js +77 -0
  229. package/dist/shims/dynamic.js +17 -4
  230. package/dist/shims/form.js +8 -3
  231. package/dist/shims/head.js +1 -1
  232. package/dist/shims/headers.js +2 -2
  233. package/dist/shims/image-props.d.ts +93 -0
  234. package/dist/shims/image-props.js +323 -0
  235. package/dist/shims/image.d.ts +4 -18
  236. package/dist/shims/image.js +4 -319
  237. package/dist/shims/internal/headers-state.d.ts +7 -0
  238. package/dist/shims/internal/headers-state.js +14 -2
  239. package/dist/shims/internal/hybrid-client-route-owner-direct.d.ts +4 -1
  240. package/dist/shims/internal/hybrid-client-route-owner-direct.js +32 -15
  241. package/dist/shims/internal/hybrid-client-route-owner.js +9 -8
  242. package/dist/shims/link-prefetch.js +5 -4
  243. package/dist/shims/link.js +7 -5
  244. package/dist/shims/navigation.d.ts +1 -1
  245. package/dist/shims/navigation.js +21 -10
  246. package/dist/shims/og.js +35 -10
  247. package/dist/shims/request-context.d.ts +13 -0
  248. package/dist/shims/router.js +28 -52
  249. package/dist/shims/server.d.ts +42 -8
  250. package/dist/shims/server.js +99 -15
  251. package/dist/shims/thenable-params.d.ts +6 -1
  252. package/dist/shims/thenable-params.js +11 -2
  253. package/dist/shims/unified-request-context.d.ts +5 -0
  254. package/dist/shims/unified-request-context.js +2 -0
  255. package/dist/shims/url-utils.d.ts +7 -0
  256. package/dist/shims/url-utils.js +12 -3
  257. package/dist/utils/asset-prefix.d.ts +7 -0
  258. package/dist/utils/asset-prefix.js +10 -1
  259. package/dist/utils/client-build-manifest.js +12 -5
  260. package/dist/utils/client-runtime-metadata.d.ts +1 -0
  261. package/dist/utils/client-runtime-metadata.js +15 -2
  262. package/dist/utils/domain-locale.js +1 -1
  263. package/dist/utils/lazy-chunks.d.ts +4 -0
  264. package/dist/utils/manifest-paths.d.ts +14 -0
  265. package/dist/utils/manifest-paths.js +22 -1
  266. package/dist/utils/path-to-regexp.d.ts +21 -0
  267. package/dist/{server/middleware-path-to-regexp.js → utils/path-to-regexp.js} +14 -11
  268. package/dist/utils/plugin-react-version.d.ts +15 -0
  269. package/dist/utils/plugin-react-version.js +64 -0
  270. package/dist/utils/plugin-rsc-version.d.ts +2 -0
  271. package/dist/utils/plugin-rsc-version.js +28 -2
  272. package/dist/utils/public-routes.d.ts +2 -0
  273. package/dist/utils/public-routes.js +18 -7
  274. package/dist/utils/react-version.d.ts +7 -0
  275. package/dist/utils/react-version.js +39 -16
  276. package/package.json +6 -6
  277. package/dist/build/nitro-route-rules.d.ts +0 -47
  278. package/dist/build/nitro-route-rules.js +0 -79
  279. package/dist/plugins/cache-flight-codec.d.ts +0 -19
  280. package/dist/plugins/cache-flight-codec.js +0 -68
  281. package/dist/server/middleware-path-to-regexp.d.ts +0 -13
  282. package/dist/utils/regex-safety.d.ts +0 -17
  283. package/dist/utils/regex-safety.js +0 -789
@@ -1,7 +1,6 @@
1
1
  "use client";
2
- import { getDeploymentId } from "../utils/deployment-id.js";
3
2
  import { useMergedRef } from "./use-merged-ref.js";
4
- import { hasRemoteMatch, isPrivateIp } from "./image-config.js";
3
+ import { RESPONSIVE_WIDTHS, __dangerouslyAllowSVG, __globallyUnoptimized, __isDev, generateImageAttributes, getFillStyle, getImageProps, imageOptimizationUrl, isRemoteUrl, isSvgUrl, resolveImageSource, sanitizeBlurDataURL, validateRemoteUrl } from "./image-props.js";
5
4
  import { forwardRef, useEffect, useLayoutEffect, useRef, useState } from "react";
6
5
  import { jsx } from "react/jsx-runtime";
7
6
  import * as ReactDOM from "react-dom";
@@ -18,108 +17,11 @@ import { Image as Image$1 } from "@unpic/react";
18
17
  * Remote images are validated against `images.remotePatterns` and
19
18
  * `images.domains` from next.config.js. Unmatched URLs are blocked
20
19
  * in production and warn in development, matching Next.js behavior.
21
- */
22
- /**
23
- * Image config injected at build time via Vite define.
24
- * Serialized as JSON — parsed once at module level.
25
- */
26
- const __imageRemotePatterns = (() => {
27
- try {
28
- return JSON.parse(process.env.__VINEXT_IMAGE_REMOTE_PATTERNS ?? "[]");
29
- } catch {
30
- return [];
31
- }
32
- })();
33
- const __imageDomains = (() => {
34
- try {
35
- return JSON.parse(process.env.__VINEXT_IMAGE_DOMAINS ?? "[]");
36
- } catch {
37
- return [];
38
- }
39
- })();
40
- const __hasImageConfig = __imageRemotePatterns.length > 0 || __imageDomains.length > 0;
41
- const __isDev = process.env.NODE_ENV !== "production";
42
- const __imageDeviceSizes = (() => {
43
- try {
44
- return JSON.parse(process.env.__VINEXT_IMAGE_DEVICE_SIZES ?? "[640,750,828,1080,1200,1920,2048,3840]");
45
- } catch {
46
- return [
47
- 640,
48
- 750,
49
- 828,
50
- 1080,
51
- 1200,
52
- 1920,
53
- 2048,
54
- 3840
55
- ];
56
- }
57
- })();
58
- const __imageSizes = (() => {
59
- try {
60
- return JSON.parse(process.env.__VINEXT_IMAGE_SIZES ?? "[32,48,64,96,128,256,384]");
61
- } catch {
62
- return [
63
- 32,
64
- 48,
65
- 64,
66
- 96,
67
- 128,
68
- 256,
69
- 384
70
- ];
71
- }
72
- })();
73
- /**
74
- * Whether dangerouslyAllowSVG is enabled in next.config.js.
75
- * When false (default), .svg sources auto-skip the optimization endpoint
76
- * and are served directly, matching Next.js behavior.
77
- * When true, .svg sources are routed through the optimizer (served as-is
78
- * with security headers).
79
- */
80
- const __dangerouslyAllowSVG = process.env.__VINEXT_IMAGE_DANGEROUSLY_ALLOW_SVG === "true";
81
- /**
82
- * Whether dangerouslyAllowLocalIP is enabled in next.config.js.
83
- * When false (default), remote image URLs with literal private-IP hostnames
84
- * are blocked to mitigate SSRF risk.
85
- */
86
- const __dangerouslyAllowLocalIP = process.env.__VINEXT_IMAGE_DANGEROUSLY_ALLOW_LOCAL_IP === "true";
87
- const __globallyUnoptimized = process.env.__VINEXT_IMAGE_UNOPTIMIZED === "true";
88
- /**
89
- * Validate that a remote URL is allowed by the configured remote patterns.
90
- * Returns true if the URL is allowed, false otherwise.
91
- *
92
- * When no remotePatterns/domains are configured, all remote URLs are allowed
93
- * (backwards-compatible — user hasn't opted into restriction).
94
- *
95
- * When patterns ARE configured, only matching URLs are allowed.
96
- * In development, non-matching URLs produce a console warning.
97
- * In production, non-matching URLs are blocked (src replaced with empty string).
98
20
  *
99
- * Private-IP hostnames are additionally rejected unless dangerouslyAllowLocalIP
100
- * is set, mirroring Next.js's fetchExternalImage guard.
21
+ * Prop building lives in the server-safe `image-props.ts`. In the RSC
22
+ * environment, next/image resolves to a server entry that exports
23
+ * `getImageProps` from there and this module's Image as a client reference.
101
24
  */
102
- function validateRemoteUrl(src) {
103
- let url;
104
- try {
105
- url = new URL(src, "http://n");
106
- } catch {
107
- return {
108
- allowed: false,
109
- reason: `Invalid URL: ${src}`
110
- };
111
- }
112
- if (!__dangerouslyAllowLocalIP && isPrivateIp(url.hostname)) return {
113
- allowed: false,
114
- reason: `Image URL "${src}" resolved to private IP. If this is expected and you understand SSRF risk, use images.dangerouslyAllowLocalIP = true to continue.`
115
- };
116
- if (!__hasImageConfig) return { allowed: true };
117
- if (hasRemoteMatch(__imageDomains, __imageRemotePatterns, url)) return { allowed: true };
118
- return {
119
- allowed: false,
120
- reason: `Image URL "${src}" is not configured in images.remotePatterns or images.domains in next.config.js. See: https://nextjs.org/docs/messages/next-image-unconfigured-host`
121
- };
122
- }
123
25
  /**
124
26
  * A version of useLayoutEffect that doesn't warn during SSR.
125
27
  * Do not rename this to "isomorphic layout effect". There is no such thing as
@@ -167,110 +69,6 @@ function createSyntheticLoadEvent(img) {
167
69
  }
168
70
  };
169
71
  }
170
- /**
171
- * Sanitize a blurDataURL to prevent CSS injection.
172
- *
173
- * A crafted data URL containing `)` can break out of the `url()` CSS function,
174
- * allowing injection of arbitrary CSS properties or rules. Characters like `{`,
175
- * `}`, and `\` can also assist in crafting injection payloads.
176
- *
177
- * This validates the URL starts with `data:image/` and rejects characters that
178
- * could escape the `url()` context. Semicolons are allowed since they're part
179
- * of valid data URLs (`data:image/png;base64,...`) and harmless inside `url()`.
180
- *
181
- * Returns undefined for invalid URLs, which causes the blur placeholder to be
182
- * skipped gracefully.
183
- */
184
- function sanitizeBlurDataURL(url) {
185
- if (!url.startsWith("data:image/")) return void 0;
186
- if (/[)(}{\\'"\n\r]/.test(url)) return void 0;
187
- return url;
188
- }
189
- /**
190
- * Determine if a src is a remote URL (CDN-optimizable) or local.
191
- */
192
- function isRemoteUrl(src) {
193
- return src.startsWith("http://") || src.startsWith("https://") || src.startsWith("//");
194
- }
195
- function isSvgUrl(src) {
196
- try {
197
- return new URL(src, "http://vinext.local").pathname.toLowerCase().endsWith(".svg");
198
- } catch {
199
- return false;
200
- }
201
- }
202
- function getFillStyle(style, backgroundStyle) {
203
- return {
204
- position: "absolute",
205
- inset: 0,
206
- width: "100%",
207
- height: "100%",
208
- objectFit: "cover",
209
- ...backgroundStyle,
210
- ...style
211
- };
212
- }
213
- /**
214
- * Resolve src, width, height, blurDataURL from Image props (string or StaticImageData).
215
- * Shared by the Image component and getImageProps to keep behavior in sync.
216
- */
217
- function resolveImageSource(v) {
218
- const staticData = typeof v.src === "object" && "default" in v.src ? v.src.default : v.src;
219
- const src = typeof staticData === "string" ? staticData : staticData.src;
220
- const sourceWidth = v.width ?? (typeof staticData === "object" ? staticData.width : void 0);
221
- const sourceHeight = v.height ?? (typeof staticData === "object" ? staticData.height : void 0);
222
- return {
223
- src,
224
- width: typeof sourceWidth === "string" ? Number(sourceWidth) : sourceWidth,
225
- height: typeof sourceHeight === "string" ? Number(sourceHeight) : sourceHeight,
226
- blurDataURL: v.blurDataURL ?? (typeof staticData === "object" ? staticData.blurDataURL : void 0)
227
- };
228
- }
229
- /**
230
- * Responsive image widths matching Next.js's device sizes config.
231
- * These are the breakpoints used for srcSet generation.
232
- * Configurable via `images.deviceSizes` in next.config.js.
233
- */
234
- const RESPONSIVE_WIDTHS = [...__imageDeviceSizes].sort((left, right) => left - right);
235
- const ALL_IMAGE_WIDTHS = [...RESPONSIVE_WIDTHS, ...__imageSizes].sort((left, right) => left - right);
236
- function extractLocalDeploymentId(src) {
237
- let deploymentId = getDeploymentId();
238
- if (!src.startsWith("/") || src.startsWith("//")) return {
239
- src,
240
- deploymentId
241
- };
242
- const queryIndex = src.indexOf("?");
243
- if (queryIndex === -1) return {
244
- src,
245
- deploymentId
246
- };
247
- const params = new URLSearchParams(src.slice(queryIndex + 1));
248
- const sourceDeploymentId = params.get("dpl");
249
- if (!sourceDeploymentId) return {
250
- src,
251
- deploymentId
252
- };
253
- deploymentId = sourceDeploymentId;
254
- params.delete("dpl");
255
- const remainingQuery = params.toString();
256
- return {
257
- src: src.slice(0, queryIndex) + (remainingQuery ? `?${remainingQuery}` : ""),
258
- deploymentId
259
- };
260
- }
261
- const __imageEndpoint = `${process.env.__NEXT_ROUTER_BASEPATH ?? ""}/_next/image${process.env.__VINEXT_TRAILING_SLASH === "true" ? "/" : ""}`;
262
- /**
263
- * Build a `/_next/image` optimization URL.
264
- *
265
- * In production (Cloudflare Workers), the worker intercepts this path and uses
266
- * the Images binding to resize/transcode on the fly. In dev, the Vite dev
267
- * server handles it as a passthrough (serves the original file).
268
- */
269
- function imageOptimizationUrl(src, width, quality = 75) {
270
- const source = extractLocalDeploymentId(src);
271
- const deploymentQuery = source.src.startsWith("/") && source.deploymentId ? `&dpl=${source.deploymentId}` : "";
272
- return `${__imageEndpoint}?url=${encodeURIComponent(source.src)}&w=${width}&q=${quality}${deploymentQuery}`;
273
- }
274
72
  function preloadImageResource(input) {
275
73
  if (!input.shouldPreload) return;
276
74
  if (typeof ReactDOM.preload !== "function") return;
@@ -281,31 +79,6 @@ function preloadImageResource(input) {
281
79
  fetchPriority: input.fetchPriority
282
80
  });
283
81
  }
284
- /**
285
- * Generate a srcSet string for responsive images.
286
- *
287
- * Each width points to the `/_next/image` optimization endpoint so the
288
- * server can resize and transcode the image.
289
- */
290
- function getImageWidths(width) {
291
- return [...new Set([width, width * 2].map((targetWidth) => ALL_IMAGE_WIDTHS.find((configuredWidth) => configuredWidth >= targetWidth) ?? ALL_IMAGE_WIDTHS[ALL_IMAGE_WIDTHS.length - 1]))];
292
- }
293
- function generateImageAttributes(src, width, quality = 75, sizes) {
294
- if (sizes) {
295
- const viewportPercentages = Array.from(sizes.matchAll(/(^|\s)(1?\d?\d)vw/g), (match) => Number.parseInt(match[2], 10));
296
- const minimumWidth = viewportPercentages.length > 0 ? RESPONSIVE_WIDTHS[0] * (Math.min(...viewportPercentages) * .01) : 0;
297
- const candidates = ALL_IMAGE_WIDTHS.filter((candidateWidth) => candidateWidth >= minimumWidth);
298
- return {
299
- src: imageOptimizationUrl(src, candidates[candidates.length - 1], quality),
300
- srcSet: candidates.map((candidateWidth) => `${imageOptimizationUrl(src, candidateWidth, quality)} ${candidateWidth}w`).join(", ")
301
- };
302
- }
303
- const widths = getImageWidths(width);
304
- return {
305
- src: imageOptimizationUrl(src, widths[widths.length - 1], quality),
306
- srcSet: widths.map((candidateWidth, index) => `${imageOptimizationUrl(src, candidateWidth, quality)} ${index + 1}x`).join(", ")
307
- };
308
- }
309
82
  const Image = forwardRef(function Image({ src: srcProp, alt, width, height, fill, preload, priority, quality, placeholder, blurDataURL, loader, sizes, className, style, onLoad, onLoadingComplete, onError, unoptimized: _unoptimized, overrideSrc, loading, ...rest }, ref) {
310
83
  const lastLoadedSrcRef = useRef(void 0);
311
84
  const lastErrorSrcRef = useRef(void 0);
@@ -562,93 +335,5 @@ const Image = forwardRef(function Image({ src: srcProp, alt, width, height, fill
562
335
  ...rest
563
336
  });
564
337
  });
565
- /**
566
- * getImageProps — for advanced use cases (picture elements, background images).
567
- * Returns the props that would be passed to the underlying <img> element.
568
- */
569
- function getImageProps(props) {
570
- const { src: srcProp, alt, width, height, fill, preload: _preload, priority, quality: _quality, placeholder, blurDataURL: blurDataURLProp, loader, sizes, className, style, onLoad: _onLoad, onLoadingComplete: _onLoadingComplete, unoptimized: _unoptimized, overrideSrc, loading, ...rest } = props;
571
- const { src, width: imgWidth, height: imgHeight, blurDataURL: imgBlurDataURL } = resolveImageSource({
572
- src: srcProp,
573
- width,
574
- height,
575
- blurDataURL: blurDataURLProp
576
- });
577
- const shouldPreload = _preload === true || priority === true;
578
- if (_unoptimized === true || __globallyUnoptimized) {
579
- const renderedSrc = overrideSrc || src;
580
- const sanitizedBlurURL = imgBlurDataURL ? sanitizeBlurDataURL(imgBlurDataURL) : void 0;
581
- const blurStyle = placeholder === "blur" && sanitizedBlurURL ? {
582
- backgroundImage: `url(${sanitizedBlurURL})`,
583
- backgroundSize: "cover",
584
- backgroundRepeat: "no-repeat",
585
- backgroundPosition: "center"
586
- } : void 0;
587
- const imageProps = {
588
- src: renderedSrc,
589
- alt,
590
- width: fill ? void 0 : imgWidth,
591
- height: fill ? void 0 : imgHeight,
592
- loading: priority ? "eager" : shouldPreload ? loading : loading ?? "lazy",
593
- fetchPriority: priority ? "high" : void 0,
594
- decoding: "async",
595
- className,
596
- style: fill ? getFillStyle(style, blurStyle) : {
597
- ...blurStyle,
598
- ...style
599
- },
600
- ...rest,
601
- sizes: sizes ?? (fill ? "100vw" : void 0),
602
- srcSet: void 0
603
- };
604
- return { props: Object.assign(imageProps, { "data-nimg": fill ? "fill" : "1" }) };
605
- }
606
- let blockedInProd = false;
607
- if (isRemoteUrl(src)) {
608
- const validation = validateRemoteUrl(src);
609
- if (!validation.allowed) {
610
- if (__isDev) console.warn(`[next/image] ${validation.reason}`);
611
- else {
612
- console.error(`[next/image] ${validation.reason}`);
613
- blockedInProd = true;
614
- }
615
- }
616
- }
617
- const imgQuality = typeof _quality === "string" ? Number(_quality) : _quality ?? 75;
618
- const resolvedSrc = blockedInProd ? "" : loader ? loader({
619
- src,
620
- width: imgWidth ?? 0,
621
- quality: imgQuality
622
- }) : src;
623
- const skipOpt = isSvgUrl(resolvedSrc) && !__dangerouslyAllowSVG || blockedInProd || !!loader || isRemoteUrl(resolvedSrc);
624
- const optimizedAttributes = imgWidth && !fill && !skipOpt ? generateImageAttributes(resolvedSrc, imgWidth, imgQuality, sizes) : null;
625
- const optimizedSrc = skipOpt ? resolvedSrc : optimizedAttributes ? optimizedAttributes.src : imageOptimizationUrl(resolvedSrc, RESPONSIVE_WIDTHS[0], imgQuality);
626
- const srcSet = optimizedAttributes?.srcSet;
627
- const sanitizedBlurURL = imgBlurDataURL ? sanitizeBlurDataURL(imgBlurDataURL) : void 0;
628
- const blurStyle = placeholder === "blur" && sanitizedBlurURL ? {
629
- backgroundImage: `url(${sanitizedBlurURL})`,
630
- backgroundSize: "cover",
631
- backgroundRepeat: "no-repeat",
632
- backgroundPosition: "center"
633
- } : void 0;
634
- const imageProps = {
635
- src: optimizedSrc,
636
- alt,
637
- width: fill ? void 0 : imgWidth,
638
- height: fill ? void 0 : imgHeight,
639
- loading: priority ? "eager" : shouldPreload ? loading : loading ?? "lazy",
640
- fetchPriority: priority ? "high" : void 0,
641
- decoding: "async",
642
- srcSet,
643
- sizes: sizes ?? (fill ? "100vw" : void 0),
644
- className,
645
- style: fill ? getFillStyle(style, blurStyle) : {
646
- ...blurStyle,
647
- ...style
648
- },
649
- ...rest
650
- };
651
- return { props: Object.assign(imageProps, { "data-nimg": fill ? "fill" : "1" }) };
652
- }
653
338
  //#endregion
654
339
  export { Image as default, getImageProps, imageOptimizationUrl };
@@ -57,10 +57,17 @@ export declare function isRenderDynamicLatched(): boolean;
57
57
  * already latched; check `isRenderDynamicLatched()` first.
58
58
  */
59
59
  export declare function onRenderDynamicLatched(listener: () => void): () => void;
60
+ /**
61
+ * Record an invalid dynamic API use on `state`, keeping the first one, like
62
+ * Next.js's `workStore.invalidDynamicUsageError ??= error`.
63
+ */
64
+ export declare function recordInvalidDynamicUsageError(state: VinextHeadersShimState, error: unknown): void;
60
65
  export declare function markRenderRequestApiUsage(kind: RenderRequestApiKind): void;
61
66
  export declare function throwIfStaticGenerationAccessError(): void;
62
67
  /** Whether work is executing inside a cache boundary that owns its reuse. */
63
68
  export declare function isInsideAnyCacheScope(): boolean;
69
+ /** The error a dynamic request API throws inside a public "use cache". */
70
+ export declare function createUseCacheAccessError(apiName: string): Error;
64
71
  /**
65
72
  * Throw if the current execution is inside a "use cache" or unstable_cache()
66
73
  * scope. Called by dynamic request APIs (headers, cookies, connection) to
@@ -87,6 +87,14 @@ function propagateInvalidDynamicUsageError(state, error) {
87
87
  if (target.invalidDynamicUsageError == null) target.invalidDynamicUsageError = error;
88
88
  });
89
89
  }
90
+ /**
91
+ * Record an invalid dynamic API use on `state`, keeping the first one, like
92
+ * Next.js's `workStore.invalidDynamicUsageError ??= error`.
93
+ */
94
+ function recordInvalidDynamicUsageError(state, error) {
95
+ state.invalidDynamicUsageError ??= error;
96
+ propagateInvalidDynamicUsageError(state, error);
97
+ }
90
98
  function markRenderRequestApiUsage(kind) {
91
99
  getHeadersShimState().renderRequestApiUsage.add(kind);
92
100
  }
@@ -121,6 +129,10 @@ function _isInsideUnstableCache() {
121
129
  function isInsideAnyCacheScope() {
122
130
  return _getUseCacheGuardContext() !== null || _isInsideUnstableCache();
123
131
  }
132
+ /** The error a dynamic request API throws inside a public "use cache". */
133
+ function createUseCacheAccessError(apiName) {
134
+ return /* @__PURE__ */ new Error(`\`${apiName}\` cannot be called inside "use cache". If you need this data inside a cached function, call \`${apiName}\` outside and pass the required data as an argument.`);
135
+ }
124
136
  /**
125
137
  * Throw if the current execution is inside a "use cache" or unstable_cache()
126
138
  * scope. Called by dynamic request APIs (headers, cookies, connection) to
@@ -130,7 +142,7 @@ function isInsideAnyCacheScope() {
130
142
  */
131
143
  function throwIfInsideCacheScope(apiName) {
132
144
  if (_isInsidePublicUseCache()) {
133
- const error = /* @__PURE__ */ new Error(`\`${apiName}\` cannot be called inside "use cache". If you need this data inside a cached function, call \`${apiName}\` outside and pass the required data as an argument.`);
145
+ const error = createUseCacheAccessError(apiName);
134
146
  try {
135
147
  const cacheCtx = _getUseCacheGuardContext();
136
148
  if (cacheCtx) cacheCtx.invalidDynamicUsageError = error;
@@ -154,4 +166,4 @@ function getHeadersAccessPhase() {
154
166
  return getHeadersShimState().phase;
155
167
  }
156
168
  //#endregion
157
- export { createRenderDynamicLatch, getHeadersAccessPhase, getHeadersShimState, headersShimAls, isInsideAnyCacheScope, isRenderDynamicLatched, markDynamicUsage, markRenderRequestApiUsage, onRenderDynamicLatched, throwIfInsideCacheScope, throwIfStaticGenerationAccessError };
169
+ export { createRenderDynamicLatch, createUseCacheAccessError, getHeadersAccessPhase, getHeadersShimState, headersShimAls, isInsideAnyCacheScope, isRenderDynamicLatched, markDynamicUsage, markRenderRequestApiUsage, onRenderDynamicLatched, recordInvalidDynamicUsageError, throwIfInsideCacheScope, throwIfStaticGenerationAccessError };
@@ -3,16 +3,19 @@ import { VinextLinkPrefetchRoute, VinextPagesLinkPrefetchRoute } from "../../cli
3
3
  export type HybridClientOwner = "app" | "document" | "pages";
4
4
  type HybridClientRouteMatches = {
5
5
  appMatch: VinextLinkPrefetchRoute | null;
6
+ /** A Pages Router URL under `/api`, which only API routes serve. */
7
+ pagesApiRequest: boolean;
6
8
  pagesMatch: VinextPagesLinkPrefetchRoute | null;
7
9
  };
8
10
  declare global {
9
11
  interface Window {
12
+ __VINEXT_APP_ROUTE_HANDLERS__?: VinextLinkPrefetchRoute[];
10
13
  __VINEXT_LINK_PREFETCH_ROUTES__?: VinextLinkPrefetchRoute[];
11
14
  __VINEXT_PAGES_LINK_PREFETCH_ROUTES__?: VinextPagesLinkPrefetchRoute[];
12
15
  }
13
16
  }
14
17
  export declare function resolveSameOriginAppPathname(href: string, basePath: string): string | null;
15
18
  export declare function matchDirectHybridClientRoutes(href: string, basePath: string): HybridClientRouteMatches;
16
- export declare function resolveMatchedHybridClientRouteOwner({ appMatch, pagesMatch }: HybridClientRouteMatches): HybridClientOwner | null;
19
+ export declare function resolveMatchedHybridClientRouteOwner({ appMatch, pagesApiRequest, pagesMatch }: HybridClientRouteMatches): HybridClientOwner | null;
17
20
  export declare function resolveDirectHybridClientRouteOwner(href: string, basePath: string): HybridClientOwner | null;
18
21
  //#endregion
@@ -1,12 +1,12 @@
1
- import { compareHybridRoutePatterns } from "../../routing/utils.js";
2
1
  import { stripBasePath } from "../../utils/base-path.js";
2
+ import { compareHybridRoutePatterns } from "../../routing/utils.js";
3
3
  import { createRouteTrieCache, matchRouteWithTrie } from "../../routing/route-matching.js";
4
4
  import { getLocalePathPrefix } from "../../utils/domain-locale.js";
5
+ import { hashRoutePathname } from "../../routing/hashed-route-patterns.js";
5
6
  //#region src/shims/internal/hybrid-client-route-owner-direct.ts
6
7
  const appRouteTrieCache = createRouteTrieCache();
8
+ const appRouteHandlerTrieCache = createRouteTrieCache();
7
9
  const pagesRouteTrieCache = createRouteTrieCache();
8
- const pagesPageRoutesCache = /* @__PURE__ */ new WeakMap();
9
- const pagesApiRoutesCache = /* @__PURE__ */ new WeakMap();
10
10
  function patternFromParts(parts) {
11
11
  return "/" + parts.join("/");
12
12
  }
@@ -32,34 +32,51 @@ function resolveSameOriginPathnames(href, basePath) {
32
32
  function resolveSameOriginAppPathname(href, basePath) {
33
33
  return resolveSameOriginPathnames(href, basePath)?.appPathname ?? null;
34
34
  }
35
- function selectPagesRoutes(routes, apiRequest) {
36
- const cache = apiRequest ? pagesApiRoutesCache : pagesPageRoutesCache;
37
- let selected = cache.get(routes);
38
- if (!selected) {
39
- selected = routes.filter((route) => Boolean(route.documentOnly) === apiRequest);
40
- cache.set(routes, selected);
35
+ function patternPartRank(part) {
36
+ if (part === void 0) return 0;
37
+ if (!part.startsWith(":")) return 1;
38
+ if (part.endsWith("+")) return 3;
39
+ if (part.endsWith("*")) return 4;
40
+ return 2;
41
+ }
42
+ /**
43
+ * The route the App router picks when both match the same URL: the first part
44
+ * whose kind differs decides, static > dynamic > catch-all > optional
45
+ * catch-all, as in `routing/route-trie.ts`.
46
+ */
47
+ function preferredAppRoute(page, handler) {
48
+ if (page === null || handler === null) return page ?? handler;
49
+ const length = Math.max(page.patternParts.length, handler.patternParts.length);
50
+ for (let index = 0; index < length; index++) {
51
+ const pageRank = patternPartRank(page.patternParts[index]);
52
+ const handlerRank = patternPartRank(handler.patternParts[index]);
53
+ if (pageRank !== handlerRank) return pageRank < handlerRank ? page : handler;
41
54
  }
42
- return selected;
55
+ return page;
43
56
  }
44
57
  function matchDirectHybridClientRoutes(href, basePath) {
45
58
  const pathnames = resolveSameOriginPathnames(href, basePath);
46
59
  if (pathnames === null) return {
47
60
  appMatch: null,
61
+ pagesApiRequest: false,
48
62
  pagesMatch: null
49
63
  };
50
64
  const appRoutes = window.__VINEXT_LINK_PREFETCH_ROUTES__;
65
+ const appRouteHandlers = window.__VINEXT_APP_ROUTE_HANDLERS__;
51
66
  const pagesRoutes = window.__VINEXT_PAGES_LINK_PREFETCH_ROUTES__;
52
67
  return {
53
- appMatch: appRoutes ? matchRouteWithTrie(pathnames.appPathname, appRoutes, appRouteTrieCache)?.route ?? null : null,
54
- pagesMatch: pagesRoutes ? matchRouteWithTrie(pathnames.pagesPathname, selectPagesRoutes(pagesRoutes, pathnames.pagesApiRequest), pagesRouteTrieCache)?.route ?? null : null
68
+ appMatch: preferredAppRoute(appRoutes ? matchRouteWithTrie(pathnames.appPathname, appRoutes, appRouteTrieCache)?.route ?? null : null, appRouteHandlers?.length ? matchRouteWithTrie(hashRoutePathname(pathnames.appPathname), appRouteHandlers, appRouteHandlerTrieCache)?.route ?? null : null),
69
+ pagesApiRequest: pathnames.pagesApiRequest,
70
+ pagesMatch: pagesRoutes && !pathnames.pagesApiRequest ? matchRouteWithTrie(pathnames.pagesPathname, pagesRoutes, pagesRouteTrieCache)?.route ?? null : null
55
71
  };
56
72
  }
57
- function resolveMatchedHybridClientRouteOwner({ appMatch, pagesMatch }) {
73
+ function resolveMatchedHybridClientRouteOwner({ appMatch, pagesApiRequest, pagesMatch }) {
74
+ if (pagesApiRequest) return appMatch?.isDynamic === false && !appMatch.documentOnly ? "app" : "document";
58
75
  if (appMatch === null && pagesMatch === null) return null;
59
76
  if (pagesMatch === null) return appMatch.documentOnly ? "document" : "app";
60
- if (appMatch === null) return pagesMatch.documentOnly ? "document" : "pages";
77
+ if (appMatch === null) return "pages";
61
78
  const owner = compareHybridRoutePatterns(patternFromParts(pagesMatch.patternParts), pagesMatch.isDynamic, patternFromParts(appMatch.patternParts), appMatch.isDynamic);
62
- return (owner === "app" ? appMatch : pagesMatch).documentOnly ? "document" : owner;
79
+ return owner === "app" && appMatch.documentOnly ? "document" : owner;
63
80
  }
64
81
  function resolveDirectHybridClientRouteOwner(href, basePath) {
65
82
  if (typeof window === "undefined") return null;
@@ -1,4 +1,5 @@
1
1
  import { parseCookies } from "../../config/request-context.js";
2
+ import "../../config/config-matchers.js";
2
3
  import { matchDirectHybridClientRoutes, resolveMatchedHybridClientRouteOwner, resolveSameOriginAppPathname } from "./hybrid-client-route-owner-direct.js";
3
4
  import { mergeRewriteQuery } from "../../utils/query.js";
4
5
  import { matchClientRewrite } from "../../client/client-rewrite-matcher.js";
@@ -63,23 +64,23 @@ function resolveHybridClientRewriteHref(href, basePath) {
63
64
  didRewrite = true;
64
65
  }
65
66
  let matches = matchDirectHybridClientRoutes(currentHref, basePath);
66
- if ((matches.appMatch === null || matches.appMatch.isDynamic) && (matches.pagesMatch === null || matches.pagesMatch.isDynamic)) for (const rewrite of rewrites.afterFiles) {
67
+ if (!matches.pagesApiRequest && (matches.appMatch === null || matches.appMatch.isDynamic) && (matches.pagesMatch === null || matches.pagesMatch.isDynamic)) for (const rewrite of rewrites.afterFiles) {
67
68
  const afterFilesRewrite = resolveClientRewrite(currentHref, basePath, [rewrite]);
68
69
  if (afterFilesRewrite?.kind === "document") return didRewrite ? currentHref : null;
69
70
  if (afterFilesRewrite?.kind !== "rewrite") continue;
70
71
  currentHref = afterFilesRewrite.href;
71
72
  didRewrite = true;
72
73
  matches = matchDirectHybridClientRoutes(currentHref, basePath);
73
- if (matches.appMatch || matches.pagesMatch) break;
74
+ if (matches.pagesApiRequest || matches.appMatch || matches.pagesMatch) break;
74
75
  }
75
- if (matches.appMatch === null && matches.pagesMatch === null) for (const rewrite of rewrites.fallback) {
76
+ if (!matches.pagesApiRequest && matches.appMatch === null && matches.pagesMatch === null) for (const rewrite of rewrites.fallback) {
76
77
  const fallbackRewrite = resolveClientRewrite(currentHref, basePath, [rewrite]);
77
78
  if (fallbackRewrite?.kind === "document") return didRewrite ? currentHref : null;
78
79
  if (fallbackRewrite?.kind !== "rewrite") continue;
79
80
  currentHref = fallbackRewrite.href;
80
81
  didRewrite = true;
81
82
  matches = matchDirectHybridClientRoutes(currentHref, basePath);
82
- if (matches.appMatch || matches.pagesMatch) break;
83
+ if (matches.pagesApiRequest || matches.appMatch || matches.pagesMatch) break;
83
84
  }
84
85
  return didRewrite ? currentHref : null;
85
86
  }
@@ -104,21 +105,21 @@ function resolveHybridClientRouteOwner(href, basePath) {
104
105
  if (beforeFilesRewrite?.kind === "rewrite") href = beforeFilesRewrite.href;
105
106
  }
106
107
  let matches = matchDirectHybridClientRoutes(href, basePath);
107
- if (rewrites && (matches.appMatch === null || matches.appMatch.isDynamic) && (matches.pagesMatch === null || matches.pagesMatch.isDynamic)) for (const rewrite of rewrites.afterFiles) {
108
+ if (rewrites && !matches.pagesApiRequest && (matches.appMatch === null || matches.appMatch.isDynamic) && (matches.pagesMatch === null || matches.pagesMatch.isDynamic)) for (const rewrite of rewrites.afterFiles) {
108
109
  const afterFilesRewrite = resolveClientRewrite(href, basePath, [rewrite]);
109
110
  if (afterFilesRewrite?.kind === "document") return "document";
110
111
  if (afterFilesRewrite?.kind !== "rewrite") continue;
111
112
  href = afterFilesRewrite.href;
112
113
  matches = matchDirectHybridClientRoutes(href, basePath);
113
- if (matches.appMatch || matches.pagesMatch) break;
114
+ if (matches.pagesApiRequest || matches.appMatch || matches.pagesMatch) break;
114
115
  }
115
- if (rewrites && matches.appMatch === null && matches.pagesMatch === null) for (const rewrite of rewrites.fallback) {
116
+ if (rewrites && !matches.pagesApiRequest && matches.appMatch === null && matches.pagesMatch === null) for (const rewrite of rewrites.fallback) {
116
117
  const fallbackRewrite = resolveClientRewrite(href, basePath, [rewrite]);
117
118
  if (fallbackRewrite?.kind === "document") return "document";
118
119
  if (fallbackRewrite?.kind !== "rewrite") continue;
119
120
  href = fallbackRewrite.href;
120
121
  matches = matchDirectHybridClientRoutes(href, basePath);
121
- if (matches.appMatch || matches.pagesMatch) break;
122
+ if (matches.pagesApiRequest || matches.appMatch || matches.pagesMatch) break;
122
123
  }
123
124
  return resolveMatchedHybridClientRouteOwner(matches);
124
125
  }
@@ -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
  }
@@ -11,7 +11,7 @@ import { formatUrlObject, formatUrlObjectWithValidation } from "./internal/forma
11
11
  import { normalizeRouterHref, resolvePagesRouterHref } from "./internal/normalize-router-href.js";
12
12
  import { getCurrentRoutePathnameForWarning } from "./internal/route-pattern-for-warning.js";
13
13
  import { getCurrentBrowserLocale } from "./client-locale.js";
14
- import { getNavigationRuntime, hasAppNavigationRuntime, registerNavigationRuntimeFunctions } from "../client/navigation-runtime.js";
14
+ import { getNavigationRuntime, hasAppNavigationRuntime, loadNavigationRuntimeRouteManifest, registerNavigationRuntimeFunctions } from "../client/navigation-runtime.js";
15
15
  import { navigatePagesRouterLinkWithFallback, resolvePagesRouterQueryOnlyHref } from "../client/pages-router-link-navigation.js";
16
16
  import { scheduleAppPrefetchFetch } from "./internal/app-prefetch-fetch-queue.js";
17
17
  import { getI18nContext } from "./i18n-context.js";
@@ -139,15 +139,17 @@ function prefetchUrl(href, mode, priority = "low", pagesRouteHref, locale) {
139
139
  (async () => {
140
140
  if (hasAppNavigationRuntime()) {
141
141
  if (isBotUserAgent(window.navigator?.userAgent ?? "")) return;
142
- const [navigation, { AppElementsWire }, rscCacheBusting, { resolveAppPrefetchRscRequest }, { APP_RSC_RENDER_MODE_PREFETCH_DYNAMIC_SHELL, APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL }, headersModule, hybridRouteOwner] = await Promise.all([
142
+ const [navigation, { AppElementsWire }, rscCacheBusting, { resolveAppPrefetchRscRequest }, { APP_RSC_RENDER_MODE_PREFETCH_DYNAMIC_SHELL, APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL }, headersModule, hybridRouteOwner, routeManifestLoaded] = await Promise.all([
143
143
  loadNavigationModule(),
144
144
  import("../server/app-elements.js"),
145
145
  import("../server/app-rsc-cache-busting.js"),
146
146
  import("./internal/app-prefetch-rsc-request.js"),
147
147
  import("../server/app-rsc-render-mode.js"),
148
148
  import("../server/headers.js"),
149
- HAS_PAGES_ROUTER || HAS_CLIENT_REWRITES ? loadHybridClientRouteOwnerModule() : null
149
+ HAS_PAGES_ROUTER || HAS_CLIENT_REWRITES ? loadHybridClientRouteOwnerModule() : null,
150
+ loadNavigationRuntimeRouteManifest()
150
151
  ]);
152
+ if (routeManifestLoaded === false) return;
151
153
  if (navigationEpoch !== linkPrefetchNavigationEpoch) return;
152
154
  const { getPrefetchInterceptionContext, getPrefetchCache, getPrefetchedUrls, getMountedSlotsHeader, createAppPrefetchRequestHeaders, discardLearningOnlyPrefetchCacheEntry, fetchRouteTreeGatedPrefetch, hasFreshLearningOnlyPrefetchCacheEntry, hasSearchAgnosticPrefetchShellForRoute, hasPrefetchCacheEntryForNavigation, prefetchRscResponse, prepareNavigationPrefetchSnapshot, DYNAMIC_NAVIGATION_CACHE_TTL, PREFETCH_CACHE_TTL } = navigation;
153
155
  const { createRscRequestUrl } = rscCacheBusting;
@@ -317,7 +319,7 @@ function prefetchUrl(href, mode, priority = "low", pagesRouteHref, locale) {
317
319
  async function promotePrefetchEntriesForNavigation(href) {
318
320
  if (typeof window === "undefined") return;
319
321
  if (!hasAppNavigationRuntime()) return;
320
- const [{ getPrefetchCache }, { stripRscCacheBustingSearchParam, stripRscSuffix }] = await Promise.all([loadNavigationModule(), import("../server/app-rsc-cache-busting.js")]);
322
+ const [{ getPrefetchCache }, { parseRscRequestPath, stripRscCacheBustingSearchParam, stripRscSuffix }] = await Promise.all([loadNavigationModule(), import("../server/app-rsc-cache-busting.js")]);
321
323
  let target;
322
324
  try {
323
325
  target = new URL(toCanonicalBrowserNavigationHref(href, window.location.href, __basePath, __trailingSlash), window.location.href);
@@ -330,7 +332,7 @@ async function promotePrefetchEntriesForNavigation(href) {
330
332
  const [rscUrl] = cacheKey.split("\0", 1);
331
333
  let cached;
332
334
  try {
333
- cached = new URL(rscUrl, window.location.href);
335
+ cached = parseRscRequestPath(rscUrl);
334
336
  } catch {
335
337
  continue;
336
338
  }
@@ -86,7 +86,7 @@ export type PrefetchCacheEntry = {
86
86
  timestamp: number;
87
87
  };
88
88
  export declare function getCurrentInterceptionContext(): string | null;
89
- export declare function getPrefetchInterceptionContext(targetHref: string): string | null;
89
+ export declare function getPrefetchInterceptionContext(targetHref: string, currentPathname?: string): string | null;
90
90
  export declare function getCurrentNextUrl(): string;
91
91
  export declare function createAppPrefetchRequestHeaders(options: {
92
92
  fetchPriority: "auto" | "high" | "low";