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
@@ -77,7 +77,7 @@ function isMiddlewareModule(value) {
77
77
  * @param request - The incoming Request object
78
78
  * @returns Middleware result describing what action to take
79
79
  */
80
- async function runMiddleware(runner, middlewarePath, request, i18nConfig, basePath, trailingSlash, isDataRequest, normalizedPathname) {
80
+ async function runMiddleware(runner, middlewarePath, request, i18nConfig, basePath, trailingSlash, isDataRequest, normalizedPathname, isStaleDataRequest, skipTrailingSlashRedirect) {
81
81
  const mod = await runner.import(middlewarePath);
82
82
  if (!isMiddlewareModule(mod)) throw new Error(`Middleware module "${middlewarePath}" did not evaluate to an object.`);
83
83
  return runGeneratedMiddleware({
@@ -87,10 +87,12 @@ async function runMiddleware(runner, middlewarePath, request, i18nConfig, basePa
87
87
  i18nConfig,
88
88
  includeErrorDetails: process.env.NODE_ENV !== "production",
89
89
  isDataRequest,
90
+ isStaleDataRequest,
90
91
  isProxy: isProxyFile(middlewarePath),
91
92
  module: mod,
92
93
  normalizedPathname,
93
94
  request,
95
+ skipTrailingSlashRedirect,
94
96
  trailingSlash
95
97
  });
96
98
  }
@@ -29,6 +29,17 @@ export type VinextResponseStageDispatchOptions = {
29
29
  * partition its lookup or stored entry by it.
30
30
  */
31
31
  renderWholeDocument?: boolean;
32
+ /**
33
+ * Regenerate an entry the adapter already stores for this build. Set only
34
+ * from a platform-invoked regeneration callback or a platform cache's
35
+ * conditional revalidation of a stored entry, never from anything a client
36
+ * can send: core then treats the path as admitted and skips
37
+ * request-time admission work such as a Pages `getStaticPaths()` call, as
38
+ * Next.js regenerates a generated path without consulting it. It is not a
39
+ * cache selector: a transport must not partition its lookup or stored entry
40
+ * by it.
41
+ */
42
+ regeneratesStoredEntry?: boolean;
32
43
  /**
33
44
  * Query-free identity of a shared App page GET/HEAD dispatch, supplied only
34
45
  * to adapters that declare `responseStageCacheIdentity: "query-free"` and
@@ -1,5 +1,5 @@
1
- import { splitPathnameForRouteMatch } from "../routing/utils.js";
2
1
  import { stripBasePath } from "../utils/base-path.js";
2
+ import { splitPathnameForRouteMatch } from "../routing/utils.js";
3
3
  import { matchRoutePattern, matchRoutePatternPrefix, matchRoutePatternWithOptionalDynamicSegments } from "../routing/route-pattern.js";
4
4
  import { AppElementsWire, compareAppElementsSlotIds } from "./app-elements-wire.js";
5
5
  import "./app-elements.js";
@@ -1,17 +1,4 @@
1
1
  //#region src/server/normalize-path.d.ts
2
- export declare function escapePathDelimiters(segment: string, escapeEncoded?: boolean): string;
3
- /**
4
- * Decode a URL pathname segment-by-segment, preserving encoded path delimiters.
5
- * Non-ASCII characters (e.g. %C3%A9 -> e) are decoded, but structural characters
6
- * like %2F (/) %23 (#) %3F (?) %5C (\) are re-encoded after decoding.
7
- *
8
- * This prevents encoded slashes from changing the path structure (e.g.
9
- * /admin%2Fpanel stays as a single segment, not /admin/panel).
10
- *
11
- * Ported from Next.js: packages/next/src/server/lib/router-utils/decode-path-params.ts
12
- * https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/router-utils/decode-path-params.ts
13
- */
14
- export declare function decodePathParams(pathname: string): string;
15
2
  export declare function isInterceptionMatchedUrlPath(value: string): boolean;
16
3
  /**
17
4
  * Path normalization utility for request handling.
@@ -1,37 +1,4 @@
1
1
  //#region src/server/normalize-path.ts
2
- /**
3
- * Re-encode path delimiter characters that were decoded by decodeURIComponent.
4
- * After decoding a URL segment, characters like / # ? \ need to be re-encoded
5
- * so they don't change the path structure.
6
- *
7
- * Ported from Next.js: packages/next/src/shared/lib/router/utils/escape-path-delimiters.ts
8
- * https://github.com/vercel/next.js/blob/canary/packages/next/src/shared/lib/router/utils/escape-path-delimiters.ts
9
- */
10
- const PATH_DELIMITERS_RE = /([/#?])/gi;
11
- const PATH_DELIMITERS_ENCODED_RE = /([/#?]|%(2f|23|3f|5c))/gi;
12
- function escapePathDelimiters(segment, escapeEncoded) {
13
- return segment.replace(escapeEncoded ? PATH_DELIMITERS_ENCODED_RE : PATH_DELIMITERS_RE, (char) => encodeURIComponent(char));
14
- }
15
- /**
16
- * Decode a URL pathname segment-by-segment, preserving encoded path delimiters.
17
- * Non-ASCII characters (e.g. %C3%A9 -> e) are decoded, but structural characters
18
- * like %2F (/) %23 (#) %3F (?) %5C (\) are re-encoded after decoding.
19
- *
20
- * This prevents encoded slashes from changing the path structure (e.g.
21
- * /admin%2Fpanel stays as a single segment, not /admin/panel).
22
- *
23
- * Ported from Next.js: packages/next/src/server/lib/router-utils/decode-path-params.ts
24
- * https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/router-utils/decode-path-params.ts
25
- */
26
- function decodePathParams(pathname) {
27
- return pathname.split("/").map((seg) => {
28
- try {
29
- return escapePathDelimiters(decodeURIComponent(seg), true);
30
- } catch {
31
- return seg;
32
- }
33
- }).join("/");
34
- }
35
2
  function isInterceptionMatchedUrlPath(value) {
36
3
  return value.startsWith("/") && !value.startsWith("//") && !value.includes("?") && !value.includes("#") && !value.includes("\\") && !value.includes("\0");
37
4
  }
@@ -67,4 +34,4 @@ function normalizePath(pathname) {
67
34
  return "/" + resolved.join("/");
68
35
  }
69
36
  //#endregion
70
- export { decodePathParams, escapePathDelimiters, isInterceptionMatchedUrlPath, normalizePath };
37
+ export { isInterceptionMatchedUrlPath, normalizePath };
@@ -11,7 +11,7 @@ export declare function isOpenRedirectShaped(rawPathname: string): boolean;
11
11
  /**
12
12
  * What a request entry answers for a raw path containing a backslash or a
13
13
  * repeated slash: Next.js's 308, or a 404 when the collapsed path would still
14
- * be open-redirect shaped.
14
+ * be open-redirect shaped or the raw target is not origin-form.
15
15
  */
16
16
  export type RepeatedSlashRedirect = {
17
17
  status: 308;
@@ -33,17 +33,24 @@ export type RepeatedSlashRedirect = {
33
33
  * `//evil.com` → `/evil.com`. Percent-encoded `%2F`/`%5C` are not touched and
34
34
  * keep falling through to `isOpenRedirectShaped` (404), as in Next.js.
35
35
  *
36
- * The Location is always same-origin. Absolute-form request targets
37
- * (`GET http://host//x`) are left alone. A collapsed path that still starts
36
+ * The Location is always same-origin. A collapsed path that still starts
38
37
  * with an encoded slash or backslash (`//%2Fevil.com`, `/.//%2Fevil.com`) is
39
38
  * never echoed into a Location or routed: it gets a 404 instead.
40
39
  *
40
+ * A raw Node request target that is not origin-form (`GET http://evil.example/x`,
41
+ * which Node's parser accepts, or `OPTIONS *`) also gets a 404. Next.js never
42
+ * routes these either: it 308s absolute-form targets to their own URL (the
43
+ * `//` after the scheme trips the same check) and 500s `OPTIONS *`. Routing
44
+ * them would let the request line choose the origin of `request.url` that
45
+ * middleware and handlers see, bypassing the Host trust policy. Request-based
46
+ * entries always pass a pathname.
47
+ *
41
48
  * @param rawUrl - The raw request target: pathname plus optional `?query`
42
49
  */
43
50
  export declare function getRepeatedSlashRedirect(rawUrl: string): RepeatedSlashRedirect | null;
44
51
  /**
45
52
  * Build the Response for a raw request path containing a backslash or a
46
- * repeated slash. See `getRepeatedSlashRedirect`.
53
+ * repeated slash, or a non-origin-form target. See `getRepeatedSlashRedirect`.
47
54
  */
48
55
  export declare function repeatedSlashRedirectResponse(rawUrl: string): Response | null;
49
56
  /**
@@ -34,15 +34,22 @@ const REDIRECT_LOCATION_BASE = "http://vinext.invalid";
34
34
  * `//evil.com` → `/evil.com`. Percent-encoded `%2F`/`%5C` are not touched and
35
35
  * keep falling through to `isOpenRedirectShaped` (404), as in Next.js.
36
36
  *
37
- * The Location is always same-origin. Absolute-form request targets
38
- * (`GET http://host//x`) are left alone. A collapsed path that still starts
37
+ * The Location is always same-origin. A collapsed path that still starts
39
38
  * with an encoded slash or backslash (`//%2Fevil.com`, `/.//%2Fevil.com`) is
40
39
  * never echoed into a Location or routed: it gets a 404 instead.
41
40
  *
41
+ * A raw Node request target that is not origin-form (`GET http://evil.example/x`,
42
+ * which Node's parser accepts, or `OPTIONS *`) also gets a 404. Next.js never
43
+ * routes these either: it 308s absolute-form targets to their own URL (the
44
+ * `//` after the scheme trips the same check) and 500s `OPTIONS *`. Routing
45
+ * them would let the request line choose the origin of `request.url` that
46
+ * middleware and handlers see, bypassing the Host trust policy. Request-based
47
+ * entries always pass a pathname.
48
+ *
42
49
  * @param rawUrl - The raw request target: pathname plus optional `?query`
43
50
  */
44
51
  function getRepeatedSlashRedirect(rawUrl) {
45
- if (!rawUrl.startsWith("/")) return null;
52
+ if (!rawUrl.startsWith("/")) return { status: 404 };
46
53
  const urlParts = rawUrl.split("?");
47
54
  const pathname = urlParts[0];
48
55
  if (!REPEATED_SLASH_OR_BACKSLASH_RE.test(pathname)) return null;
@@ -62,7 +69,7 @@ function repeatedSlashRedirectHeaders(location) {
62
69
  }
63
70
  /**
64
71
  * Build the Response for a raw request path containing a backslash or a
65
- * repeated slash. See `getRepeatedSlashRedirect`.
72
+ * repeated slash, or a non-origin-form target. See `getRepeatedSlashRedirect`.
66
73
  */
67
74
  function repeatedSlashRedirectResponse(rawUrl) {
68
75
  const redirect = getRepeatedSlashRedirect(rawUrl);
@@ -2,7 +2,7 @@ import { NextI18nConfig } from "../config/next-config.js";
2
2
  import { Route } from "../routing/pages-router.js";
3
3
  import { ExecutionContextLike } from "../shims/request-context.js";
4
4
  import { EdgeApiExecutionRuntime } from "./edge-api-runtime.js";
5
- import { PagesReqResRequest, PagesReqResResponse, PagesRequestQuery } from "./pages-node-compat.js";
5
+ import { PagesApiReqResResponse, PagesReqResRequest, PagesRequestQuery } from "./pages-node-compat.js";
6
6
  //#region src/server/pages-api-route.d.ts
7
7
  type PagesApiRouteConfig = {
8
8
  runtime?: string;
@@ -36,7 +36,7 @@ type PagesApiRouteConfig = {
36
36
  externalResolver?: boolean;
37
37
  };
38
38
  };
39
- type PagesNodeApiRouteHandler = (req: PagesReqResRequest, res: PagesReqResResponse) => unknown;
39
+ type PagesNodeApiRouteHandler = (req: PagesReqResRequest, res: PagesApiReqResResponse) => unknown;
40
40
  type PagesEdgeApiRouteHandler = (request: Request) => Response | Promise<Response>;
41
41
  type PagesApiRouteModule = {
42
42
  /**
@@ -80,6 +80,7 @@ type HandlePagesApiRouteOptions = {
80
80
  allowedRevalidateHeaderKeys?: readonly string[];
81
81
  i18n?: NextI18nConfig | null;
82
82
  trailingSlash?: boolean;
83
+ skipTrailingSlashRedirect?: boolean;
83
84
  };
84
85
  trustedRevalidateOrigin?: string;
85
86
  };
@@ -1,15 +1,15 @@
1
+ import { internalServerErrorResponse } from "./http-error-responses.js";
2
+ import { cloneRequestWithUrl } from "./request-pipeline.js";
1
3
  import { getRequestExecutionContext, runWithExecutionContext } from "../shims/request-context.js";
2
4
  import "./server-globals.js";
3
5
  import { NextRequest } from "../shims/server.js";
4
- import { internalServerErrorResponse } from "./http-error-responses.js";
5
- import { cloneRequestWithUrl } from "./request-pipeline.js";
6
6
  import { markRouteCacheabilityExplicitResponsePolicy } from "../shims/cacheability-classification.js";
7
7
  import { hasCdnResponsePolicy } from "./cache-control.js";
8
8
  import { mergeRouteParamsIntoQuery, parseQueryString, urlQueryToSearchParams } from "../utils/query.js";
9
9
  import { tracePagesApiHandler } from "./pages-execution-tracing.js";
10
10
  import { resolveBodyParserConfig } from "./pages-body-parser-config.js";
11
11
  import { PagesBodyParseError } from "./pages-media-type.js";
12
- import { createPagesReqRes, markVinextStreamedApiResponse, parsePagesApiBody } from "./pages-node-compat.js";
12
+ import { createPagesApiReqRes, markVinextStreamedApiResponse, parsePagesApiBody } from "./pages-node-compat.js";
13
13
  import { finalizeEdgeApiResponse, isEdgeApiRuntime } from "./edge-api-runtime.js";
14
14
  //#region src/server/pages-api-route.ts
15
15
  function resolveModuleRuntime(module) {
@@ -47,7 +47,8 @@ async function _handlePagesApiRoute(options) {
47
47
  const nextRequest = new NextRequest(createEdgeApiRequest(options.request, options.url, params), options.nextConfig ? { nextConfig: {
48
48
  basePath: options.nextConfig.basePath,
49
49
  i18n: options.nextConfig.i18n ?? void 0,
50
- trailingSlash: options.nextConfig.trailingSlash
50
+ trailingSlash: options.nextConfig.trailingSlash,
51
+ skipTrailingSlashRedirect: options.nextConfig.skipTrailingSlashRedirect
51
52
  } } : void 0);
52
53
  const response = await tracePagesApiHandler(route.pattern, () => handler(nextRequest));
53
54
  if (response instanceof Response) {
@@ -70,7 +71,7 @@ async function _handlePagesApiRoute(options) {
70
71
  const query = buildPagesApiQuery(options.url, params);
71
72
  const bodyParserConfig = resolveBodyParserConfig(route.module.config);
72
73
  const body = bodyParserConfig.enabled ? await parsePagesApiBody(options.request, bodyParserConfig.sizeLimit) : void 0;
73
- const { req, res, responsePromise } = createPagesReqRes({
74
+ const { req, res, responsePromise } = createPagesApiReqRes({
74
75
  allowedRevalidateHeaderKeys: options.nextConfig?.allowedRevalidateHeaderKeys,
75
76
  body,
76
77
  initialResponseHeaders: options.initialResponseHeaders,
@@ -0,0 +1,12 @@
1
+ //#region src/server/pages-cache-tag.d.ts
2
+ /**
3
+ * Build the edge path tag for a Pages ISR pathname.
4
+ *
5
+ * Every spelling of one pathname shares a single ISR entry, because
6
+ * `pagesIsrCacheKey()` decodes each segment first. The tag must use that same
7
+ * decoded identity. Otherwise `/users/%40alice` is stored at the edge under its
8
+ * own tag, and `res.revalidate("/users/@alice")` or
9
+ * `revalidatePath("/users/@alice")` never purges it.
10
+ */
11
+ export declare function pagesPathCacheTag(isrPathname: string): string;
12
+ //#endregion
@@ -0,0 +1,19 @@
1
+ import { normalizePathnameForRouteMatch } from "../routing/utils.js";
2
+ import { encodeCacheTag } from "../utils/encode-cache-tag.js";
3
+ //#region src/server/pages-cache-tag.ts
4
+ /**
5
+ * Build the edge path tag for a Pages ISR pathname.
6
+ *
7
+ * Every spelling of one pathname shares a single ISR entry, because
8
+ * `pagesIsrCacheKey()` decodes each segment first. The tag must use that same
9
+ * decoded identity. Otherwise `/users/%40alice` is stored at the edge under its
10
+ * own tag, and `res.revalidate("/users/@alice")` or
11
+ * `revalidatePath("/users/@alice")` never purges it.
12
+ */
13
+ function pagesPathCacheTag(isrPathname) {
14
+ const pathname = normalizePathnameForRouteMatch(isrPathname);
15
+ const stem = pathname.endsWith("/") ? pathname.slice(0, -1) : pathname;
16
+ return encodeCacheTag(`_N_T_${stem || "/"}`);
17
+ }
18
+ //#endregion
19
+ export { pagesPathCacheTag };
@@ -14,6 +14,8 @@ export type PagesClientAssets = {
14
14
  dynamicPreloads?: Record<string, string[]>;
15
15
  /** next.config crossOrigin, defaulting to anonymous for Vite's CORS-fetched assets. */
16
16
  crossOrigin?: AssetCrossOrigin;
17
+ /** Client bundle files outside `_next/static`, as URL pathnames. */
18
+ clientBuildFiles?: string[];
17
19
  };
18
20
  export declare function setPagesClientAssets(assets: PagesClientAssets | undefined): void;
19
21
  export declare function getPagesClientAssets(): PagesClientAssets;
@@ -61,6 +61,16 @@ export declare function encodeUrlParserIgnoredCharacters(pathname: string): stri
61
61
  * `/_next/data/<buildId>/about` → null (missing .json suffix)
62
62
  */
63
63
  export declare function parseNextDataPathname(pathname: string, buildId: string): NextDataMatch | null;
64
+ /**
65
+ * The page pathname a `_next/data` URL addresses, whatever its buildId. Next.js
66
+ * compiles every middleware matcher with an optional `/_next/data/<buildId>`
67
+ * prefix and `.json` suffix, so a matcher written for `/admin` also guards that
68
+ * page's data URL, even one with a stale buildId. Middleware matching uses this
69
+ * when the request reaches middleware with its raw data URL
70
+ * (`skipProxyUrlNormalize`).
71
+ * https://github.com/vercel/next.js/blob/v16.2.7/packages/next/src/build/analysis/get-page-static-info.ts
72
+ */
73
+ export declare function nextDataMiddlewareMatchPathname(pathname: string): string | null;
64
74
  export declare function normalizeNextDataPagePathname(pagePathname: string, trailingSlash?: boolean): string;
65
75
  /**
66
76
  * Next.js only applies `trailingSlash` while normalizing a data URL for
@@ -1,6 +1,7 @@
1
1
  import { MIDDLEWARE_SKIP_HEADER } from "../utils/protocol-headers.js";
2
2
  import { addBasePathToPathname, hasBasePath, stripBasePath } from "../utils/base-path.js";
3
3
  import { NEXTJS_DEPLOYMENT_ID_HEADER } from "./headers.js";
4
+ import { cloneRequestWithUrl } from "./request-pipeline.js";
4
5
  //#region src/server/pages-data-route.ts
5
6
  /**
6
7
  * Helpers for the Pages Router `/_next/data/{buildId}/{...page}.json` endpoint.
@@ -67,7 +68,24 @@ function parseNextDataPathname(pathname, buildId) {
67
68
  if (!isNextDataPathname(pathname)) return null;
68
69
  const expectedPrefix = `${NEXT_DATA_PREFIX}${buildId}/`;
69
70
  if (!pathname.startsWith(expectedPrefix)) return null;
70
- const rest = pathname.slice(expectedPrefix.length, -5);
71
+ return pageFromNextDataRest(pathname.slice(expectedPrefix.length, -5));
72
+ }
73
+ /**
74
+ * The page pathname a `_next/data` URL addresses, whatever its buildId. Next.js
75
+ * compiles every middleware matcher with an optional `/_next/data/<buildId>`
76
+ * prefix and `.json` suffix, so a matcher written for `/admin` also guards that
77
+ * page's data URL, even one with a stale buildId. Middleware matching uses this
78
+ * when the request reaches middleware with its raw data URL
79
+ * (`skipProxyUrlNormalize`).
80
+ * https://github.com/vercel/next.js/blob/v16.2.7/packages/next/src/build/analysis/get-page-static-info.ts
81
+ */
82
+ function nextDataMiddlewareMatchPathname(pathname) {
83
+ if (!isNextDataPathname(pathname)) return null;
84
+ const buildIdEnd = pathname.indexOf("/", 12);
85
+ if (buildIdEnd <= 12) return null;
86
+ return pageFromNextDataRest(pathname.slice(buildIdEnd + 1, -5))?.pagePathname ?? null;
87
+ }
88
+ function pageFromNextDataRest(rest) {
71
89
  if (rest.length === 0) return null;
72
90
  if (rest === "index") return { pagePathname: "/" };
73
91
  if (rest.endsWith("/index")) return { pagePathname: `/${rest.slice(0, -6)}` };
@@ -174,11 +192,11 @@ function normalizePagesDataRequest(request, buildId, basePath = "", trailingSlas
174
192
  normalizedUrl.pathname = hadBasePath ? addBasePathToPathname(pagePathname, basePath) : pagePathname;
175
193
  return {
176
194
  isDataReq: true,
177
- request: new Request(normalizedUrl, request),
195
+ request: cloneRequestWithUrl(request, normalizedUrl.href),
178
196
  normalizedPathname: pagePathname,
179
197
  search: reqUrl.search,
180
198
  notFoundResponse: null
181
199
  };
182
200
  }
183
201
  //#endregion
184
- export { buildMiddlewarePrefetchSkipResponse, buildNextDataNotFoundResponse, buildNextDataPropsJsonResponse, encodeUrlParserIgnoredCharacters, isNextDataPathname, normalizeNextDataPagePathname, normalizePagesDataRequest, parseNextDataPathname, shouldAddTrailingSlashToPagesDataPath, urlParserCreatesPagesDataPath };
202
+ export { buildMiddlewarePrefetchSkipResponse, buildNextDataNotFoundResponse, buildNextDataPropsJsonResponse, encodeUrlParserIgnoredCharacters, isNextDataPathname, nextDataMiddlewareMatchPathname, normalizeNextDataPagePathname, normalizePagesDataRequest, parseNextDataPathname, shouldAddTrailingSlashToPagesDataPath, urlParserCreatesPagesDataPath };
@@ -10,7 +10,11 @@ type ApplyDocumentAssetPropsOptions = {
10
10
  protectedAssetMarker?: string;
11
11
  scriptOwner?: "head" | "next-script";
12
12
  };
13
- export declare function extractDocumentAssetProps(html: string): {
13
+ /**
14
+ * Read and strip the nonce and crossOrigin props that `<Head>` and
15
+ * `<NextScript />` recorded on their tags, found by this render's token.
16
+ */
17
+ export declare function extractDocumentAssetProps(html: string, token: string): {
14
18
  html: string;
15
19
  props: DocumentAssetProps;
16
20
  };
@@ -1,4 +1,6 @@
1
+ import { escapeRegExp } from "../utils/regex.js";
1
2
  import { escapeHtmlAttr } from "./html.js";
3
+ import { getDocumentHeadOpenMarker, getDocumentScriptsPlaceholder } from "../shims/document-placeholders.js";
2
4
  //#region src/server/pages-document-asset-props.ts
3
5
  const HEAD_NONCE_ATTR = "data-vinext-head-nonce";
4
6
  const HEAD_CROSS_ORIGIN_ATTR = "data-vinext-head-cross-origin";
@@ -11,9 +13,15 @@ function readAttribute(tag, name) {
11
13
  function removeAttributes(tag, names) {
12
14
  return tag.replace(new RegExp(`\\s(?:${names.join("|")})="[^"]*"`, "g"), "");
13
15
  }
14
- function extractDocumentAssetProps(html) {
15
- const headTag = html.match(/<head\b[^>]*>/i)?.[0];
16
- const nextScriptTag = html.match(/<span\b[^>]*>(?=<!-- __NEXT_SCRIPTS__ -->)/i)?.[0];
16
+ /**
17
+ * Read and strip the nonce and crossOrigin props that `<Head>` and
18
+ * `<NextScript />` recorded on their tags, found by this render's token.
19
+ */
20
+ function extractDocumentAssetProps(html, token) {
21
+ const headTagPattern = new RegExp(`<head\\b[^>]*${escapeRegExp(getDocumentHeadOpenMarker(token))}[^>]*>`, "i");
22
+ const nextScriptTagPattern = new RegExp(`<span\\b[^>]*>(?=${escapeRegExp(getDocumentScriptsPlaceholder(token))})`, "i");
23
+ const headTag = html.match(headTagPattern)?.[0];
24
+ const nextScriptTag = html.match(nextScriptTagPattern)?.[0];
17
25
  const props = {
18
26
  headNonce: readAttribute(headTag, HEAD_NONCE_ATTR),
19
27
  headCrossOrigin: readAttribute(headTag, HEAD_CROSS_ORIGIN_ATTR),
@@ -21,14 +29,14 @@ function extractDocumentAssetProps(html) {
21
29
  scriptCrossOrigin: readAttribute(nextScriptTag, SCRIPT_CROSS_ORIGIN_ATTR)
22
30
  };
23
31
  return {
24
- html: html.replace(/<head\b[^>]*>/i, (tag) => removeAttributes(tag, [HEAD_NONCE_ATTR, HEAD_CROSS_ORIGIN_ATTR])).replace(/<span\b[^>]*>(?=<!-- __NEXT_SCRIPTS__ -->)/i, (tag) => removeAttributes(tag, [SCRIPT_NONCE_ATTR, SCRIPT_CROSS_ORIGIN_ATTR])),
32
+ html: html.replace(headTagPattern, (tag) => removeAttributes(tag, [HEAD_NONCE_ATTR, HEAD_CROSS_ORIGIN_ATTR])).replace(nextScriptTagPattern, (tag) => removeAttributes(tag, [SCRIPT_NONCE_ATTR, SCRIPT_CROSS_ORIGIN_ATTR])),
25
33
  props
26
34
  };
27
35
  }
28
36
  function addAttribute(tag, name, value, replaceExisting = false) {
29
37
  if (value === void 0) return tag;
30
38
  const attributePattern = new RegExp(`\\s${name}(?:="[^"]*")?`, "i");
31
- if (attributePattern.test(tag)) return replaceExisting ? tag.replace(attributePattern, ` ${name}="${escapeHtmlAttr(value)}"`) : tag;
39
+ if (attributePattern.test(tag)) return replaceExisting ? tag.replace(attributePattern, () => ` ${name}="${escapeHtmlAttr(value)}"`) : tag;
32
40
  if (!tag.endsWith(">")) return tag;
33
41
  const selfClosing = tag.endsWith("/>");
34
42
  const closingStart = selfClosing ? tag.length - 2 : tag.length - 1;
@@ -0,0 +1,19 @@
1
+ //#region src/server/pages-document-splice.d.ts
2
+ /**
3
+ * Drop `<Head>`'s marker attribute and insert `insertion` right after its
4
+ * opening tag. Dev HTML transforms may add attributes after the marker.
5
+ */
6
+ export declare function spliceDocumentHeadOpen(html: string, token: string, insertion: string): string;
7
+ /** Insert `insertion` right before `<Head>`'s closing tag. */
8
+ export declare function spliceDocumentHeadClose(html: string, token: string, insertion: string): string;
9
+ /** Put the generated hydration scripts where `<NextScript />` rendered. */
10
+ export declare function spliceDocumentScripts(html: string, token: string, scripts: string): string;
11
+ /**
12
+ * Split the Document where `<Main />` rendered. Like Next.js, a Document
13
+ * without `<Main />` gets the page body after its own markup.
14
+ */
15
+ export declare function splitDocumentAtMain(html: string, token: string): {
16
+ prefix: string;
17
+ suffix: string;
18
+ };
19
+ //#endregion
@@ -0,0 +1,55 @@
1
+ import { getDocumentHeadCloseMarker, getDocumentHeadOpenMarker, getDocumentMainPlaceholder, getDocumentScriptsPlaceholder } from "../shims/document-placeholders.js";
2
+ //#region src/server/pages-document-splice.ts
3
+ /**
4
+ * Splices framework output into a serialized custom Pages Router Document.
5
+ *
6
+ * `<Head>`, `<Main />` and `<NextScript />` mark their splice points with the
7
+ * per-render token from `vinext/shims/document-placeholders`, so nothing the
8
+ * Document renders can be taken for one. Each edit slices at a located index
9
+ * instead of calling `String#replace`, so inserted HTML is never parsed for
10
+ * `$&`-style replacement patterns. Like Next.js, a Document that leaves out
11
+ * one of those components gets nothing inserted for it.
12
+ */
13
+ function spliceMarker(html, marker, insertion) {
14
+ const index = html.indexOf(marker);
15
+ if (index === -1) return html;
16
+ return html.slice(0, index) + insertion + html.slice(index + marker.length);
17
+ }
18
+ /**
19
+ * Drop `<Head>`'s marker attribute and insert `insertion` right after its
20
+ * opening tag. Dev HTML transforms may add attributes after the marker.
21
+ */
22
+ function spliceDocumentHeadOpen(html, token, insertion) {
23
+ const marker = getDocumentHeadOpenMarker(token);
24
+ const index = html.indexOf(marker);
25
+ if (index === -1) return html;
26
+ const tagEnd = html.indexOf(">", index + marker.length) + 1;
27
+ if (tagEnd === 0) return html;
28
+ return html.slice(0, index) + html.slice(index + marker.length, tagEnd) + insertion + html.slice(tagEnd);
29
+ }
30
+ /** Insert `insertion` right before `<Head>`'s closing tag. */
31
+ function spliceDocumentHeadClose(html, token, insertion) {
32
+ return spliceMarker(html, getDocumentHeadCloseMarker(token), insertion);
33
+ }
34
+ /** Put the generated hydration scripts where `<NextScript />` rendered. */
35
+ function spliceDocumentScripts(html, token, scripts) {
36
+ return spliceMarker(html, getDocumentScriptsPlaceholder(token), scripts);
37
+ }
38
+ /**
39
+ * Split the Document where `<Main />` rendered. Like Next.js, a Document
40
+ * without `<Main />` gets the page body after its own markup.
41
+ */
42
+ function splitDocumentAtMain(html, token) {
43
+ const placeholder = getDocumentMainPlaceholder(token);
44
+ const index = html.indexOf(placeholder);
45
+ if (index === -1) return {
46
+ prefix: html,
47
+ suffix: ""
48
+ };
49
+ return {
50
+ prefix: html.slice(0, index),
51
+ suffix: html.slice(index + placeholder.length)
52
+ };
53
+ }
54
+ //#endregion
55
+ export { spliceDocumentHeadClose, spliceDocumentHeadOpen, spliceDocumentScripts, splitDocumentAtMain };
@@ -24,6 +24,15 @@ type PagesI18nRequestInfo = {
24
24
  domainLocale?: DomainLocale;
25
25
  redirectUrl?: string;
26
26
  };
27
+ /**
28
+ * Return the configured locale that the first pathname segment names, compared
29
+ * case-insensitively (`/EN/about` -> `"en"`), or `undefined` when there is none.
30
+ * The configured casing is returned, not the request's.
31
+ *
32
+ * Ported from Next.js: packages/next/src/shared/lib/i18n/normalize-locale-path.ts
33
+ * https://github.com/vercel/next.js/blob/canary/packages/next/src/shared/lib/i18n/normalize-locale-path.ts
34
+ */
35
+ export declare function detectPathnameLocale(pathname: string, locales: readonly string[]): string | undefined;
27
36
  /**
28
37
  * Prepend the default locale prefix to a pathname when i18n is configured and
29
38
  * the path does not already carry a locale prefix. Mirrors Next.js's
@@ -42,7 +51,9 @@ type PagesI18nRequestInfo = {
42
51
  * - `/__vinext/*` (vinext-internal endpoints)
43
52
  *
44
53
  * Returns the input unchanged when i18n is not configured or when the path
45
- * already starts with one of the configured locales. The host-based default
54
+ * already starts with one of the configured locales (compared
55
+ * case-insensitively, like Next.js's `normalizeLocalePath`, so `/EN/about`
56
+ * is left alone rather than becoming `/en/EN/about`). The host-based default
46
57
  * locale (i18n.domains[].defaultLocale) is preferred over the global default
47
58
  * when supplied, matching Next.js's `domainLocale.defaultLocale` branch.
48
59
  *
@@ -9,6 +9,19 @@ function readHeader(headers, name) {
9
9
  }
10
10
  const normalizeHostname = normalizeDomainHostname;
11
11
  /**
12
+ * Return the configured locale that the first pathname segment names, compared
13
+ * case-insensitively (`/EN/about` -> `"en"`), or `undefined` when there is none.
14
+ * The configured casing is returned, not the request's.
15
+ *
16
+ * Ported from Next.js: packages/next/src/shared/lib/i18n/normalize-locale-path.ts
17
+ * https://github.com/vercel/next.js/blob/canary/packages/next/src/shared/lib/i18n/normalize-locale-path.ts
18
+ */
19
+ function detectPathnameLocale(pathname, locales) {
20
+ const segment = pathname.split("/", 2)[1]?.toLowerCase();
21
+ if (!segment) return void 0;
22
+ return locales.find((locale) => locale.toLowerCase() === segment);
23
+ }
24
+ /**
12
25
  * Prepend the default locale prefix to a pathname when i18n is configured and
13
26
  * the path does not already carry a locale prefix. Mirrors Next.js's
14
27
  * server-side path normalisation in `resolve-routes.ts` (lines ~250-263):
@@ -26,7 +39,9 @@ const normalizeHostname = normalizeDomainHostname;
26
39
  * - `/__vinext/*` (vinext-internal endpoints)
27
40
  *
28
41
  * Returns the input unchanged when i18n is not configured or when the path
29
- * already starts with one of the configured locales. The host-based default
42
+ * already starts with one of the configured locales (compared
43
+ * case-insensitively, like Next.js's `normalizeLocalePath`, so `/EN/about`
44
+ * is left alone rather than becoming `/en/EN/about`). The host-based default
30
45
  * locale (i18n.domains[].defaultLocale) is preferred over the global default
31
46
  * when supplied, matching Next.js's `domainLocale.defaultLocale` branch.
32
47
  *
@@ -42,8 +57,7 @@ const normalizeHostname = normalizeDomainHostname;
42
57
  function normalizeDefaultLocalePathname(pathname, i18n, options = {}) {
43
58
  if (!i18n) return pathname;
44
59
  if (pathname.startsWith("/_next/") || pathname.startsWith("/__vinext/")) return pathname;
45
- const parts = pathname.split("/", 3);
46
- if (parts[1] && i18n.locales.includes(parts[1])) return pathname;
60
+ if (detectPathnameLocale(pathname, i18n.locales)) return pathname;
47
61
  const defaultLocale = detectDomainLocale(i18n.domains, options.hostname ?? void 0)?.defaultLocale ?? i18n.defaultLocale;
48
62
  if (pathname === "/") return `/${defaultLocale}`;
49
63
  return `/${defaultLocale}${pathname}`;
@@ -229,4 +243,4 @@ function resolvePagesI18nRequest(url, i18nConfig, headers, hostname, basePath =
229
243
  };
230
244
  }
231
245
  //#endregion
232
- export { detectDomainLocale, detectLocaleFromAcceptLanguage, extractLocaleFromUrl, getLocaleRedirect, normalizeDefaultLocalePathname, parseCookieLocaleFromHeader, resolvePagesI18nRequest, stripI18nLocaleForApiRoute };
246
+ export { detectDomainLocale, detectLocaleFromAcceptLanguage, detectPathnameLocale, extractLocaleFromUrl, getLocaleRedirect, normalizeDefaultLocalePathname, parseCookieLocaleFromHeader, resolvePagesI18nRequest, stripI18nLocaleForApiRoute };
@@ -29,7 +29,6 @@ export type PagesReqResResponse = Writable & {
29
29
  send: (data: unknown) => void;
30
30
  redirect: (statusOrUrl: number | string, url?: string) => PagesReqResResponse;
31
31
  getHeaders: () => PagesReqResHeaders;
32
- revalidate: (urlPath: string, opts?: RevalidateOptions) => Promise<void>;
33
32
  setPreviewData: (data: object | string, options?: {
34
33
  maxAge?: number;
35
34
  path?: string;
@@ -41,6 +40,9 @@ export type PagesReqResResponse = Writable & {
41
40
  enable?: boolean;
42
41
  }) => PagesReqResResponse;
43
42
  };
43
+ export type PagesApiReqResResponse = PagesReqResResponse & {
44
+ revalidate: (urlPath: string, opts?: RevalidateOptions) => Promise<void>;
45
+ };
44
46
  type PagesRequestCookiesCarrier = {
45
47
  headers: {
46
48
  cookie?: string | string[] | null | undefined;
@@ -48,17 +50,19 @@ type PagesRequestCookiesCarrier = {
48
50
  cookies?: unknown;
49
51
  };
50
52
  type CreatePagesReqResOptions = {
51
- allowedRevalidateHeaderKeys?: readonly string[];
52
53
  body: unknown;
53
54
  initialResponseHeaders?: Headers;
54
55
  query: PagesRequestQuery;
55
56
  request: Request;
56
- trustedRevalidateOrigin?: string;
57
57
  url: string;
58
58
  };
59
- type CreatePagesReqResResult = {
59
+ type CreatePagesApiReqResOptions = CreatePagesReqResOptions & {
60
+ allowedRevalidateHeaderKeys?: readonly string[];
61
+ trustedRevalidateOrigin?: string;
62
+ };
63
+ type CreatePagesReqResResult<Res extends PagesReqResResponse = PagesReqResResponse> = {
60
64
  req: PagesReqResRequest;
61
- res: PagesReqResResponse;
65
+ res: Res;
62
66
  responsePromise: Promise<Response>;
63
67
  };
64
68
  /**
@@ -85,5 +89,12 @@ export declare function attachPagesPreviewApi(req: PagesReqResRequest, res: Page
85
89
  export declare function markVinextStreamedApiResponse(response: Response): void;
86
90
  export declare function isVinextStreamedApiResponse(response: Response): boolean;
87
91
  export declare function createPagesReqRes(options: CreatePagesReqResOptions): CreatePagesReqResResult;
92
+ /**
93
+ * Create the req/res pair for a Pages API route. Only API responses get
94
+ * `res.revalidate()`: Next.js attaches it in apiResolver
95
+ * (`packages/next/src/server/api-utils/node/api-resolver.ts`), so a page's
96
+ * getServerSideProps `res` never carries the revalidation secret.
97
+ */
98
+ export declare function createPagesApiReqRes(options: CreatePagesApiReqResOptions): CreatePagesReqResResult<PagesApiReqResResponse>;
88
99
  //#endregion
89
100
  export { PagesBodyParseError as PagesApiBodyParseError };