vinext 1.0.0-beta.1 → 1.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/README.md +10 -10
  2. package/dist/build/client-build-config.js +8 -1
  3. package/dist/build/report.d.ts +8 -1
  4. package/dist/build/report.js +12 -2
  5. package/dist/check.js +19 -2
  6. package/dist/cli.js +3 -2
  7. package/dist/config/config-matchers.d.ts +13 -37
  8. package/dist/config/config-matchers.js +35 -122
  9. package/dist/config/next-config.d.ts +15 -5
  10. package/dist/config/next-config.js +7 -1
  11. package/dist/config/request-context.d.ts +14 -0
  12. package/dist/config/request-context.js +26 -0
  13. package/dist/entries/app-browser-entry.d.ts +4 -10
  14. package/dist/entries/app-browser-entry.js +20 -4
  15. package/dist/entries/app-rsc-entry.js +30 -9
  16. package/dist/entries/app-rsc-manifest.js +36 -0
  17. package/dist/entries/pages-client-entry.js +0 -1
  18. package/dist/entries/pages-server-entry.js +13 -5
  19. package/dist/index.js +194 -101
  20. package/dist/init-cloudflare.d.ts +3 -1
  21. package/dist/init-cloudflare.js +59 -5
  22. package/dist/init.d.ts +6 -4
  23. package/dist/init.js +20 -22
  24. package/dist/plugins/extensionless-dynamic-import.js +23 -18
  25. package/dist/plugins/ignore-dynamic-requests.js +3 -1
  26. package/dist/plugins/import-meta-url.js +17 -6
  27. package/dist/plugins/og-assets.js +1 -1
  28. package/dist/plugins/require-context.js +20 -15
  29. package/dist/plugins/transform-cache.d.ts +19 -0
  30. package/dist/plugins/transform-cache.js +36 -0
  31. package/dist/routing/app-route-graph.d.ts +27 -11
  32. package/dist/routing/app-route-graph.js +158 -22
  33. package/dist/routing/app-router.js +12 -6
  34. package/dist/routing/file-matcher.d.ts +11 -22
  35. package/dist/routing/file-matcher.js +21 -33
  36. package/dist/routing/pages-router.js +23 -29
  37. package/dist/routing/route-matching.d.ts +14 -1
  38. package/dist/routing/route-matching.js +13 -1
  39. package/dist/routing/route-pattern.d.ts +2 -1
  40. package/dist/routing/route-pattern.js +7 -4
  41. package/dist/routing/route-trie.d.ts +5 -1
  42. package/dist/routing/route-trie.js +5 -2
  43. package/dist/server/api-handler.d.ts +2 -0
  44. package/dist/server/api-handler.js +3 -3
  45. package/dist/server/app-browser-entry.js +110 -37
  46. package/dist/server/app-browser-history-controller.d.ts +1 -1
  47. package/dist/server/app-browser-history-controller.js +3 -3
  48. package/dist/server/app-browser-navigation-controller.d.ts +23 -21
  49. package/dist/server/app-browser-navigation-controller.js +2 -2
  50. package/dist/server/app-browser-state.js +1 -1
  51. package/dist/server/app-browser-visible-commit.js +1 -1
  52. package/dist/server/app-fallback-renderer.d.ts +3 -0
  53. package/dist/server/app-fallback-renderer.js +4 -2
  54. package/dist/server/app-middleware.js +2 -1
  55. package/dist/server/app-page-boundary-render.d.ts +12 -1
  56. package/dist/server/app-page-boundary-render.js +84 -2
  57. package/dist/server/app-page-cache-finalizer.d.ts +1 -1
  58. package/dist/server/app-page-cache-finalizer.js +1 -1
  59. package/dist/server/app-page-cache.d.ts +2 -2
  60. package/dist/server/app-page-cache.js +16 -2
  61. package/dist/server/app-page-dispatch.d.ts +19 -2
  62. package/dist/server/app-page-dispatch.js +45 -23
  63. package/dist/server/app-page-element-builder.d.ts +9 -2
  64. package/dist/server/app-page-element-builder.js +144 -33
  65. package/dist/server/app-page-head.d.ts +56 -4
  66. package/dist/server/app-page-head.js +216 -85
  67. package/dist/server/app-page-http-access-fallback-metadata.d.ts +29 -0
  68. package/dist/server/app-page-http-access-fallback-metadata.js +114 -0
  69. package/dist/server/app-page-probe.d.ts +4 -0
  70. package/dist/server/app-page-probe.js +6 -2
  71. package/dist/server/app-page-render.d.ts +2 -2
  72. package/dist/server/app-page-render.js +1 -1
  73. package/dist/server/app-page-request.d.ts +15 -0
  74. package/dist/server/app-page-request.js +134 -14
  75. package/dist/server/app-page-response.js +1 -1
  76. package/dist/server/app-page-route-wiring.d.ts +16 -0
  77. package/dist/server/app-page-route-wiring.js +254 -52
  78. package/dist/server/app-pages-bridge.d.ts +1 -1
  79. package/dist/server/app-pages-bridge.js +2 -1
  80. package/dist/server/app-post-middleware-context.d.ts +1 -1
  81. package/dist/server/app-post-middleware-context.js +1 -1
  82. package/dist/server/app-route-handler-cache.js +4 -0
  83. package/dist/server/app-route-handler-dispatch.js +40 -20
  84. package/dist/server/app-route-handler-execution.d.ts +6 -1
  85. package/dist/server/app-route-handler-execution.js +42 -16
  86. package/dist/server/app-route-handler-policy.d.ts +2 -0
  87. package/dist/server/app-route-handler-policy.js +3 -3
  88. package/dist/server/app-route-handler-response.js +1 -1
  89. package/dist/server/app-route-module-loader.d.ts +9 -1
  90. package/dist/server/app-route-module-loader.js +4 -0
  91. package/dist/server/app-router-entry.js +6 -3
  92. package/dist/server/app-rsc-cache-busting.d.ts +1 -1
  93. package/dist/server/app-rsc-errors.d.ts +1 -1
  94. package/dist/server/app-rsc-errors.js +22 -2
  95. package/dist/server/app-rsc-handler.d.ts +10 -5
  96. package/dist/server/app-rsc-handler.js +73 -33
  97. package/dist/server/app-rsc-request-normalization.d.ts +3 -2
  98. package/dist/server/app-rsc-request-normalization.js +8 -2
  99. package/dist/server/app-rsc-response-finalizer.d.ts +2 -2
  100. package/dist/server/app-rsc-response-finalizer.js +5 -11
  101. package/dist/server/app-rsc-route-matching.d.ts +29 -0
  102. package/dist/server/app-rsc-route-matching.js +93 -11
  103. package/dist/server/app-segment-config.js +13 -3
  104. package/dist/server/app-server-action-execution.d.ts +3 -1
  105. package/dist/server/app-server-action-execution.js +9 -7
  106. package/dist/server/app-ssr-entry.js +6 -6
  107. package/dist/server/app-ssr-stream.d.ts +1 -7
  108. package/dist/server/app-ssr-stream.js +2 -10
  109. package/dist/server/cache-control.d.ts +2 -2
  110. package/dist/server/cache-control.js +2 -1
  111. package/dist/server/config-headers.d.ts +24 -0
  112. package/dist/server/config-headers.js +52 -0
  113. package/dist/server/dev-initial-server-error.d.ts +1 -1
  114. package/dist/server/dev-server.d.ts +3 -1
  115. package/dist/server/dev-server.js +170 -321
  116. package/dist/server/headers.d.ts +2 -2
  117. package/dist/server/headers.js +4 -3
  118. package/dist/server/isr-cache.d.ts +11 -34
  119. package/dist/server/isr-cache.js +33 -43
  120. package/dist/server/isr-decision.d.ts +2 -2
  121. package/dist/server/middleware-matcher-pattern.d.ts +22 -0
  122. package/dist/server/middleware-matcher-pattern.js +219 -0
  123. package/dist/server/middleware-matcher.d.ts +2 -7
  124. package/dist/server/middleware-matcher.js +13 -60
  125. package/dist/server/middleware-path-to-regexp.d.ts +14 -0
  126. package/dist/server/middleware-path-to-regexp.js +228 -0
  127. package/dist/server/middleware-runtime.js +4 -3
  128. package/dist/server/middleware.d.ts +1 -1
  129. package/dist/server/middleware.js +2 -1
  130. package/dist/server/pages-api-route.d.ts +2 -0
  131. package/dist/server/pages-api-route.js +4 -1
  132. package/dist/server/pages-data-route.d.ts +11 -1
  133. package/dist/server/pages-data-route.js +16 -1
  134. package/dist/server/pages-dev-hydration.js +0 -1
  135. package/dist/server/pages-get-initial-props.d.ts +16 -3
  136. package/dist/server/pages-get-initial-props.js +17 -10
  137. package/dist/server/pages-i18n.js +64 -16
  138. package/dist/server/pages-node-compat.d.ts +2 -0
  139. package/dist/server/pages-node-compat.js +7 -3
  140. package/dist/server/pages-page-data.d.ts +64 -10
  141. package/dist/server/pages-page-data.js +349 -80
  142. package/dist/server/pages-page-handler.d.ts +9 -3
  143. package/dist/server/pages-page-handler.js +131 -36
  144. package/dist/server/pages-page-response.d.ts +5 -3
  145. package/dist/server/pages-page-response.js +24 -27
  146. package/dist/server/pages-readiness.d.ts +3 -3
  147. package/dist/server/pages-request-pipeline.d.ts +8 -0
  148. package/dist/server/pages-request-pipeline.js +30 -12
  149. package/dist/server/pages-revalidate.d.ts +1 -1
  150. package/dist/server/pages-revalidate.js +54 -8
  151. package/dist/server/pages-router-entry.d.ts +1 -0
  152. package/dist/server/pages-router-entry.js +16 -4
  153. package/dist/server/prerender-route-params.js +2 -2
  154. package/dist/server/prod-server.d.ts +3 -5
  155. package/dist/server/prod-server.js +72 -35
  156. package/dist/server/request-pipeline.d.ts +15 -29
  157. package/dist/server/request-pipeline.js +24 -59
  158. package/dist/server/revalidation-host.d.ts +11 -0
  159. package/dist/server/revalidation-host.js +19 -0
  160. package/dist/server/rsc-stream-hints.js +122 -18
  161. package/dist/server/worker-revalidation-context.d.ts +13 -0
  162. package/dist/server/worker-revalidation-context.js +29 -0
  163. package/dist/shims/app.d.ts +1 -1
  164. package/dist/shims/cache-handler.d.ts +1 -1
  165. package/dist/shims/cache-handler.js +10 -7
  166. package/dist/shims/cache-request-state.d.ts +27 -1
  167. package/dist/shims/cache-request-state.js +60 -1
  168. package/dist/shims/cache-runtime.d.ts +18 -1
  169. package/dist/shims/cache-runtime.js +26 -8
  170. package/dist/shims/cache.d.ts +5 -5
  171. package/dist/shims/cache.js +27 -11
  172. package/dist/shims/document.d.ts +42 -83
  173. package/dist/shims/document.js +62 -53
  174. package/dist/shims/dynamic.d.ts +5 -25
  175. package/dist/shims/dynamic.js +29 -8
  176. package/dist/shims/error.d.ts +1 -1
  177. package/dist/shims/error.js +2 -2
  178. package/dist/shims/fetch-cache.js +13 -4
  179. package/dist/shims/font-google-base.d.ts +11 -4
  180. package/dist/shims/font-google-base.js +17 -8
  181. package/dist/shims/font-local.d.ts +10 -3
  182. package/dist/shims/font-local.js +41 -10
  183. package/dist/shims/head.d.ts +13 -2
  184. package/dist/shims/head.js +3 -3
  185. package/dist/shims/headers.d.ts +37 -43
  186. package/dist/shims/headers.js +183 -74
  187. package/dist/shims/image.d.ts +27 -37
  188. package/dist/shims/image.js +21 -14
  189. package/dist/shims/internal/cookie-serialize.d.ts +12 -9
  190. package/dist/shims/internal/cookie-serialize.js +10 -7
  191. package/dist/shims/internal/hybrid-client-route-owner.js +3 -1
  192. package/dist/shims/internal/interpolate-as.d.ts +15 -1
  193. package/dist/shims/internal/interpolate-as.js +27 -2
  194. package/dist/shims/internal/utils.d.ts +3 -3
  195. package/dist/shims/legacy-image.d.ts +3 -37
  196. package/dist/shims/legacy-image.js +6 -16
  197. package/dist/shims/link.d.ts +15 -22
  198. package/dist/shims/link.js +34 -13
  199. package/dist/shims/metadata.d.ts +7 -17
  200. package/dist/shims/metadata.js +74 -24
  201. package/dist/shims/navigation-context-state.d.ts +1 -1
  202. package/dist/shims/navigation.d.ts +7 -3
  203. package/dist/shims/navigation.js +25 -14
  204. package/dist/shims/navigation.react-server.js +1 -1
  205. package/dist/shims/next-shims-augmentations.d.ts +1 -0
  206. package/dist/shims/next-shims-public.d.ts +1 -0
  207. package/dist/shims/public-shim-map.json.js +103 -0
  208. package/dist/shims/readonly-url-search-params.d.ts +3 -3
  209. package/dist/shims/readonly-url-search-params.js +3 -3
  210. package/dist/shims/request-context.d.ts +4 -1
  211. package/dist/shims/request-state-types.d.ts +1 -1
  212. package/dist/shims/router.d.ts +55 -21
  213. package/dist/shims/router.js +180 -44
  214. package/dist/shims/script.d.ts +6 -10
  215. package/dist/shims/script.js +7 -4
  216. package/dist/shims/server.d.ts +50 -26
  217. package/dist/shims/server.js +193 -55
  218. package/dist/shims/unified-request-context.d.ts +22 -3
  219. package/dist/shims/unified-request-context.js +84 -1
  220. package/dist/shims/url-safety.d.ts +1 -1
  221. package/dist/shims/url-safety.js +2 -2
  222. package/dist/typegen.d.ts +8 -2
  223. package/dist/typegen.js +76 -15
  224. package/dist/utils/cache-control-metadata.d.ts +2 -1
  225. package/dist/utils/cache-control-metadata.js +5 -1
  226. package/dist/utils/domain-locale.d.ts +2 -2
  227. package/dist/utils/external-url.d.ts +5 -0
  228. package/dist/utils/external-url.js +7 -0
  229. package/dist/utils/html-limited-bots.js +1 -1
  230. package/dist/utils/protocol-headers.d.ts +7 -1
  231. package/dist/utils/protocol-headers.js +7 -1
  232. package/dist/utils/regex-safety.d.ts +8 -0
  233. package/dist/utils/regex-safety.js +737 -0
  234. package/package.json +13 -2
@@ -1,10 +1,15 @@
1
1
  import { addBasePathToPathname, hasBasePath } from "../utils/base-path.js";
2
2
  import { patternToNextFormat } from "../routing/route-validation.js";
3
- import { applyMiddlewareRequestHeaders, isExternalUrl, matchRedirect, matchRewrite, preserveRedirectDestinationQuery, proxyExternalRequest, requestContextFromRequest, sanitizeDestination } from "../config/config-matchers.js";
4
- import { applyConfigHeadersToHeaderRecord, cloneRequestWithUrl, normalizeTrailingSlash } from "./request-pipeline.js";
3
+ import { PRERENDER_REVALIDATE_HEADER } from "../utils/protocol-headers.js";
4
+ import { requestContextFromRequest } from "../config/request-context.js";
5
+ import { isExternalUrl } from "../utils/external-url.js";
6
+ import { applyMiddlewareRequestHeaders, matchRedirect, matchRewrite, preserveRedirectDestinationQuery, proxyExternalRequest, sanitizeDestination } from "../config/config-matchers.js";
7
+ import { cloneRequestWithUrl, normalizeTrailingSlash } from "./request-pipeline.js";
8
+ import { isOnDemandRevalidateRequest } from "./isr-cache.js";
5
9
  import { mergeRewriteQuery } from "../utils/query.js";
6
10
  import { normalizeDefaultLocalePathname, stripI18nLocaleForApiRoute } from "./pages-i18n.js";
7
11
  import { buildMiddlewarePrefetchSkipResponse } from "./pages-data-route.js";
12
+ import { applyConfigHeadersToHeaderRecord } from "./config-headers.js";
8
13
  import { mergeHeaders } from "./worker-utils.js";
9
14
  //#region src/server/pages-request-pipeline.ts
10
15
  async function fetchWorkerFilesystemRoute(request, requestPathname, phase, fetchAsset) {
@@ -53,6 +58,9 @@ async function runPagesRequest(request, deps) {
53
58
  const url = new URL(request.url);
54
59
  let pathname = url.pathname;
55
60
  const search = url.search;
61
+ const revalidateHeader = request.headers.get(PRERENDER_REVALIDATE_HEADER);
62
+ const isOnDemandRevalidate = deps.authorizeOnDemandRevalidate ? deps.authorizeOnDemandRevalidate(revalidateHeader) : isOnDemandRevalidateRequest(revalidateHeader);
63
+ const requestConfigPathname = deps.configMatchPathname ?? pathname;
56
64
  const basePathState = {
57
65
  basePath,
58
66
  hadBasePath
@@ -66,9 +74,9 @@ async function runPagesRequest(request, deps) {
66
74
  }
67
75
  const reqCtx = requestContextFromRequest(request);
68
76
  const requestHostname = i18nConfig ? url.hostname : "";
69
- const matchPathname = i18nConfig ? normalizeDefaultLocalePathname(pathname, i18nConfig, { hostname: requestHostname }) : pathname;
77
+ const requestConfigMatchPathname = i18nConfig ? normalizeDefaultLocalePathname(requestConfigPathname, i18nConfig, { hostname: requestHostname }) : requestConfigPathname;
70
78
  if (configRedirects.length) {
71
- const redirect = matchRedirect(matchPathname, configRedirects, reqCtx, basePathState);
79
+ const redirect = matchRedirect(requestConfigMatchPathname, configRedirects, reqCtx, basePathState);
72
80
  if (redirect) {
73
81
  const location = preserveRedirectDestinationQuery(sanitizeDestination(basePath && hadBasePath && !isExternalUrl(redirect.destination) && !hasBasePath(redirect.destination, basePath) ? basePath + redirect.destination : redirect.destination), deps.rawSearch ?? search);
74
82
  return {
@@ -82,6 +90,7 @@ async function runPagesRequest(request, deps) {
82
90
  }
83
91
  const originalResolvedUrl = pathname + search;
84
92
  let resolvedUrl = originalResolvedUrl;
93
+ let resolvedPathnameIsRequestPathname = true;
85
94
  const middlewareHeaders = {};
86
95
  let middlewareStatus;
87
96
  const serveFilesystemRoute = async (requestPathname, phase) => {
@@ -93,7 +102,7 @@ async function runPagesRequest(request, deps) {
93
102
  };
94
103
  return served ? { type: "handled" } : null;
95
104
  };
96
- if (typeof deps.runMiddleware === "function") {
105
+ if (!isOnDemandRevalidate && typeof deps.runMiddleware === "function") {
97
106
  const result = await deps.runMiddleware(request, deps.ctx ?? null, { isDataRequest });
98
107
  if (result.waitUntilPromises && result.waitUntilPromises.length > 0) {
99
108
  const ctx = deps.ctx;
@@ -131,7 +140,10 @@ async function runPagesRequest(request, deps) {
131
140
  else if (existing) middlewareHeaders[key] = [existing, value];
132
141
  else middlewareHeaders[key] = [value];
133
142
  } else middlewareHeaders[key] = value;
134
- if (result.rewriteUrl) resolvedUrl = result.rewriteUrl;
143
+ if (result.rewriteUrl) {
144
+ resolvedUrl = result.rewriteUrl;
145
+ resolvedPathnameIsRequestPathname = false;
146
+ }
135
147
  middlewareStatus = result.status ?? result.rewriteStatus;
136
148
  }
137
149
  const { postMwReqCtx, request: postMwReq } = applyMiddlewareRequestHeaders(middlewareHeaders, request, { preserveCredentialHeaders: isExternalUrl(resolvedUrl) });
@@ -143,6 +155,7 @@ async function runPagesRequest(request, deps) {
143
155
  });
144
156
  let resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
145
157
  const matchResolvedPathname = (p) => i18nConfig ? normalizeDefaultLocalePathname(p, i18nConfig, { hostname: requestHostname }) : p;
158
+ const configSourcePathname = () => resolvedPathnameIsRequestPathname ? requestConfigMatchPathname : matchResolvedPathname(resolvedPathname);
146
159
  const matchedPathnameForRoute = (routePattern) => {
147
160
  const matchedPathname = routePattern ? patternToNextFormat(routePattern) : resolvedPathname;
148
161
  if (!i18nConfig) return matchedPathname;
@@ -161,7 +174,7 @@ async function runPagesRequest(request, deps) {
161
174
  };
162
175
  if (configHeaders.length) applyConfigHeadersToHeaderRecord(middlewareHeaders, {
163
176
  configHeaders,
164
- pathname: matchPathname,
177
+ pathname: requestConfigMatchPathname,
165
178
  requestContext: reqCtx,
166
179
  basePathState
167
180
  });
@@ -173,7 +186,7 @@ async function runPagesRequest(request, deps) {
173
186
  if (directFilesystemResult) return directFilesystemResult;
174
187
  let configRewriteFired = false;
175
188
  for (const rewrite of configRewrites.beforeFiles ?? []) {
176
- const rewritten = matchRewrite(matchResolvedPathname(resolvedPathname), [rewrite], rewriteRequestContext(), basePathState);
189
+ const rewritten = matchRewrite(configSourcePathname(), [rewrite], rewriteRequestContext(), basePathState);
177
190
  if (rewritten) {
178
191
  if (isExternalUrl(rewritten)) return {
179
192
  type: "response",
@@ -181,6 +194,7 @@ async function runPagesRequest(request, deps) {
181
194
  };
182
195
  resolvedUrl = mergeRewriteQuery(resolvedUrl, rewritten);
183
196
  resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
197
+ resolvedPathnameIsRequestPathname = false;
184
198
  configRewriteFired = true;
185
199
  }
186
200
  }
@@ -227,7 +241,7 @@ async function runPagesRequest(request, deps) {
227
241
  let pageMatch = !isOutsideBasePathUnclaimed() && deps.matchPageRoute ? deps.matchPageRoute(resolvedPathname, request) : null;
228
242
  let resolvedPathnameChanged = false;
229
243
  if (!pageMatch || pageMatch.route.isDynamic) for (const rewrite of configRewrites.afterFiles ?? []) {
230
- const rewritten = matchRewrite(matchResolvedPathname(resolvedPathname), [rewrite], rewriteRequestContext(), basePathState);
244
+ const rewritten = matchRewrite(configSourcePathname(), [rewrite], rewriteRequestContext(), basePathState);
231
245
  if (rewritten) {
232
246
  if (isExternalUrl(rewritten)) return {
233
247
  type: "response",
@@ -235,6 +249,7 @@ async function runPagesRequest(request, deps) {
235
249
  };
236
250
  resolvedUrl = mergeRewriteQuery(resolvedUrl, rewritten);
237
251
  resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
252
+ resolvedPathnameIsRequestPathname = false;
238
253
  configRewriteFired = true;
239
254
  resolvedPathnameChanged = true;
240
255
  const afterFilesFilesystemResult = await serveFilesystemRoute(resolvedPathname, "afterFiles");
@@ -253,7 +268,7 @@ async function runPagesRequest(request, deps) {
253
268
  if (typeof deps.renderPage === "function") {
254
269
  let renderPageMatch = pageMatch;
255
270
  if ((isOutsideBasePathUnclaimed() || isDataReq || isDataRequest) && !renderPageMatch && configRewrites.fallback?.length) for (const rewrite of configRewrites.fallback) {
256
- const fallbackRewrite = matchRewrite(matchResolvedPathname(resolvedPathname), [rewrite], rewriteRequestContext(), basePathState);
271
+ const fallbackRewrite = matchRewrite(configSourcePathname(), [rewrite], rewriteRequestContext(), basePathState);
257
272
  if (!fallbackRewrite) continue;
258
273
  if (isExternalUrl(fallbackRewrite)) return {
259
274
  type: "response",
@@ -261,6 +276,7 @@ async function runPagesRequest(request, deps) {
261
276
  };
262
277
  resolvedUrl = mergeRewriteQuery(resolvedUrl, fallbackRewrite);
263
278
  resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
279
+ resolvedPathnameIsRequestPathname = false;
264
280
  configRewriteFired = true;
265
281
  const fallbackFilesystemResult = await serveFilesystemRoute(resolvedPathname, "fallback");
266
282
  if (fallbackFilesystemResult) return fallbackFilesystemResult;
@@ -281,7 +297,7 @@ async function runPagesRequest(request, deps) {
281
297
  let response = await deps.renderPage(request, resolvedUrl, initialRenderOptions, stagedHeaders);
282
298
  let matchedFallbackRewrite = false;
283
299
  if (response.status === 404 && shouldDeferErrorPageOnMiss && configRewrites.fallback?.length) for (const rewrite of configRewrites.fallback) {
284
- const fallbackRewrite = matchRewrite(matchResolvedPathname(resolvedPathname), [rewrite], rewriteRequestContext(), basePathState);
300
+ const fallbackRewrite = matchRewrite(configSourcePathname(), [rewrite], rewriteRequestContext(), basePathState);
285
301
  if (!fallbackRewrite) continue;
286
302
  if (isExternalUrl(fallbackRewrite)) return {
287
303
  type: "response",
@@ -289,6 +305,7 @@ async function runPagesRequest(request, deps) {
289
305
  };
290
306
  resolvedUrl = mergeRewriteQuery(resolvedUrl, fallbackRewrite);
291
307
  resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
308
+ resolvedPathnameIsRequestPathname = false;
292
309
  configRewriteFired = true;
293
310
  const fallbackFilesystemResult = await serveFilesystemRoute(resolvedPathname, "fallback");
294
311
  if (fallbackFilesystemResult) return fallbackFilesystemResult;
@@ -325,7 +342,7 @@ async function runPagesRequest(request, deps) {
325
342
  }
326
343
  let devPageMatch = isOutsideBasePathUnclaimed() ? null : resolvedPathnameChanged ? deps.matchPageRoute ? deps.matchPageRoute(resolvedPathname, request) : null : pageMatch;
327
344
  if (!devPageMatch && configRewrites.fallback?.length) for (const rewrite of configRewrites.fallback) {
328
- const fallbackRewrite = matchRewrite(matchResolvedPathname(resolvedPathname), [rewrite], rewriteRequestContext(), basePathState);
345
+ const fallbackRewrite = matchRewrite(configSourcePathname(), [rewrite], rewriteRequestContext(), basePathState);
329
346
  if (!fallbackRewrite) continue;
330
347
  if (isExternalUrl(fallbackRewrite)) return {
331
348
  type: "response",
@@ -333,6 +350,7 @@ async function runPagesRequest(request, deps) {
333
350
  };
334
351
  resolvedUrl = mergeRewriteQuery(resolvedUrl, fallbackRewrite);
335
352
  resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
353
+ resolvedPathnameIsRequestPathname = false;
336
354
  configRewriteFired = true;
337
355
  const fallbackFilesystemResult = await serveFilesystemRoute(resolvedPathname, "fallback");
338
356
  if (fallbackFilesystemResult) return fallbackFilesystemResult;
@@ -10,6 +10,6 @@ type RevalidateOptions = {
10
10
  */
11
11
  unstable_onlyGenerated?: boolean;
12
12
  };
13
- declare function performOnDemandRevalidate(source: IncomingMessage | Headers, urlPath: string, opts?: RevalidateOptions): Promise<void>;
13
+ declare function performOnDemandRevalidate(source: IncomingMessage | Headers, urlPath: string, opts?: RevalidateOptions, trustedOrigin?: string, allowedRevalidateHeaderKeys?: readonly string[], dev?: boolean): Promise<void>;
14
14
  //#endregion
15
15
  export { RevalidateOptions, performOnDemandRevalidate };
@@ -1,19 +1,65 @@
1
+ import { getRequestExecutionContext } from "../shims/request-context.js";
2
+ import { PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER, VINEXT_REVALIDATE_HOST_HEADER } from "../utils/protocol-headers.js";
1
3
  import "./headers.js";
2
- import { PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER, getRevalidateSecret } from "./isr-cache.js";
4
+ import { getRevalidateSecret, isOnDemandRevalidateRequest } from "./isr-cache.js";
5
+ import { normalizeDomainHostname } from "../utils/domain-locale.js";
3
6
  import { resolveRequestHost, resolveRequestProtocol } from "./proxy-trust.js";
4
7
  //#region src/server/pages-revalidate.ts
5
- async function performOnDemandRevalidate(source, urlPath, opts = {}) {
8
+ async function performOnDemandRevalidate(source, urlPath, opts = {}, trustedOrigin, allowedRevalidateHeaderKeys = [], dev = false) {
6
9
  if (typeof urlPath !== "string" || !urlPath.startsWith("/")) throw new Error(`Invalid urlPath provided to revalidate(), must be a path e.g. /blog/post-1, received ${urlPath}`);
7
- const proto = resolveRequestProtocol(source);
8
- const host = resolveRequestHost(source, "localhost");
9
- const target = new URL(urlPath, `${proto}://${host}`);
10
+ if (isSourceRevalidationRequest(source)) throw new Error(`Cannot revalidate ${urlPath} from an internal revalidation request`);
11
+ const executionContext = getRequestExecutionContext();
12
+ const dispatchRevalidate = executionContext?.dispatchPagesRevalidate;
13
+ const target = createRevalidateTarget(source, urlPath, trustedOrigin);
10
14
  const headers = { [PRERENDER_REVALIDATE_HEADER]: getRevalidateSecret() };
15
+ if (trustedOrigin && !dispatchRevalidate) {
16
+ const logicalHostname = normalizeDomainHostname(resolveRequestHost(source, "localhost"));
17
+ if (logicalHostname) headers[VINEXT_REVALIDATE_HOST_HEADER] = logicalHostname;
18
+ }
11
19
  if (opts.unstable_onlyGenerated) headers[PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER] = "1";
12
- const res = await fetch(target, {
20
+ const allowedHeaders = new Set(allowedRevalidateHeaderKeys.map((key) => key.toLowerCase()));
21
+ if (dev) allowedHeaders.add("cookie");
22
+ for (const key of allowedHeaders) {
23
+ const value = readSourceHeader(source, key);
24
+ if (value !== void 0) headers[key] = value;
25
+ }
26
+ if (dispatchRevalidate && executionContext.isInternalPagesRevalidation) throw new Error(`Cannot revalidate ${urlPath} from an internal revalidation request`);
27
+ const res = await fetchRevalidateTarget(target, headers, dispatchRevalidate);
28
+ if (!((res.headers.get("x-vercel-cache") ?? res.headers.get("x-nextjs-cache"))?.toUpperCase() === "REVALIDATED" || res.status === 200 || res.status === 404 && opts.unstable_onlyGenerated === true)) throw new Error(`Failed to revalidate ${urlPath}: ${res.status}`);
29
+ }
30
+ function readSourceHeader(source, key) {
31
+ if (isWebHeaders(source)) return source.get(key) ?? void 0;
32
+ const value = source.headers[key];
33
+ return Array.isArray(value) ? value.join(", ") : value;
34
+ }
35
+ function isSourceRevalidationRequest(source) {
36
+ return isOnDemandRevalidateRequest(isWebHeaders(source) ? source.get(PRERENDER_REVALIDATE_HEADER) : source.headers[PRERENDER_REVALIDATE_HEADER]);
37
+ }
38
+ function isWebHeaders(source) {
39
+ return typeof source.get === "function";
40
+ }
41
+ async function fetchRevalidateTarget(initialTarget, headers, dispatchRevalidate) {
42
+ const request = new Request(initialTarget, {
13
43
  method: "HEAD",
14
- headers
44
+ headers,
45
+ redirect: "manual"
15
46
  });
16
- if (!(res.headers.get("x-nextjs-cache")?.toUpperCase() === "REVALIDATED" || res.status === 200 || res.status === 404 && opts.unstable_onlyGenerated === true)) throw new Error(`Failed to revalidate ${urlPath}: ${res.status}`);
47
+ return dispatchRevalidate ? await dispatchRevalidate(request) : await fetch(request);
48
+ }
49
+ function createRevalidateTarget(source, urlPath, trustedOrigin) {
50
+ const origin = resolveRevalidateOrigin(source, trustedOrigin);
51
+ const target = new URL(`${origin}${urlPath}`);
52
+ if (target.origin !== origin) throw new Error(`Invalid urlPath provided to revalidate(), resolved outside application origin`);
53
+ return target;
54
+ }
55
+ function resolveRevalidateOrigin(source, trustedOrigin) {
56
+ if (trustedOrigin) return normalizeRevalidateOrigin(trustedOrigin);
57
+ return normalizeRevalidateOrigin(`${resolveRequestProtocol(source)}://${resolveRequestHost(source, "localhost")}`);
58
+ }
59
+ function normalizeRevalidateOrigin(origin) {
60
+ const parsed = new URL(origin);
61
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new Error(`Invalid revalidate origin protocol: ${parsed.protocol}`);
62
+ return parsed.origin;
17
63
  }
18
64
  //#endregion
19
65
  export { performOnDemandRevalidate };
@@ -22,6 +22,7 @@ type PagesWorkerEnv = {
22
22
  type PagesWorkerExecutionContext = {
23
23
  waitUntil?(promise: Promise<unknown>): void;
24
24
  passThroughOnException?(): void;
25
+ cache?: unknown;
25
26
  };
26
27
  declare const _default: {
27
28
  fetch(request: Request, env?: PagesWorkerEnv, ctx?: PagesWorkerExecutionContext): Promise<Response>;
@@ -1,4 +1,6 @@
1
+ import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
1
2
  import { hasBasePath, stripBasePath } from "../utils/base-path.js";
3
+ import { VINEXT_REVALIDATE_HOST_HEADER } from "../utils/protocol-headers.js";
2
4
  import { notFoundStaticAssetResponse } from "./http-error-responses.js";
3
5
  import { isOpenRedirectShaped } from "./open-redirect.js";
4
6
  import { cloneRequestWithHeaders, cloneRequestWithUrl, filterInternalHeaders } from "./request-pipeline.js";
@@ -6,6 +8,7 @@ import { assetPrefixPathname, isNextStaticPath } from "../utils/asset-prefix.js"
6
8
  import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, handleConfiguredImageOptimization, isImageOptimizationPath } from "./image-optimization.js";
7
9
  import { finalizeMissingStaticAssetResponse } from "./worker-utils.js";
8
10
  import { fetchWorkerFilesystemRoute, runPagesRequest, wrapMiddlewareWithBasePath } from "./pages-request-pipeline.js";
11
+ import { createWorkerRevalidationContext } from "./worker-revalidation-context.js";
9
12
  import { registerConfiguredCacheAdapters } from "virtual:vinext-cache-adapters";
10
13
  import { registerConfiguredImageOptimizer } from "virtual:vinext-image-adapters";
11
14
  import * as pagesEntry from "virtual:vinext-server-entry";
@@ -24,7 +27,7 @@ import * as pagesEntry from "virtual:vinext-server-entry";
24
27
  * import handler from "vinext/server/pages-router-entry";
25
28
  * return handler.fetch(request, env, ctx);
26
29
  */
27
- const { handleApiRoute, hasMiddleware, matchPageRoute, normalizeDataRequest, renderPage, runMiddleware, vinextConfig } = pagesEntry;
30
+ const { authorizeOnDemandRevalidate, handleApiRoute, hasMiddleware, matchPageRoute, normalizeDataRequest, renderPage, runMiddleware, vinextConfig } = pagesEntry;
28
31
  const basePath = vinextConfig?.basePath ?? "";
29
32
  const assetPathPrefix = assetPrefixPathname(vinextConfig?.assetPrefix ?? "");
30
33
  const trailingSlash = vinextConfig?.trailingSlash ?? false;
@@ -46,14 +49,22 @@ const imageConfig = vinextConfig?.images ? {
46
49
  var pages_router_entry_default = { async fetch(request, env, ctx) {
47
50
  return handleRequest(request, env, ctx);
48
51
  } };
49
- async function handleRequest(request, env, ctx) {
52
+ async function handleRequest(request, env, platformCtx) {
53
+ const ctx = createWorkerRevalidationContext(platformCtx, (internalRequest, internalCtx) => handleRequest(internalRequest, env, internalCtx));
50
54
  registerConfiguredCacheAdapters(env);
51
55
  registerConfiguredImageOptimizer(env);
52
56
  try {
53
57
  let pathname = new URL(request.url).pathname;
54
58
  if (isOpenRedirectShaped(pathname)) return new Response("This page could not be found", { status: 404 });
59
+ try {
60
+ normalizePathnameForRouteMatchStrict(pathname);
61
+ } catch {
62
+ return new Response("Bad Request", { status: 400 });
63
+ }
55
64
  const missingBuildAsset = isNextStaticPath(pathname, basePath, assetPathPrefix);
56
- request = cloneRequestWithHeaders(request, filterInternalHeaders(request.headers));
65
+ const filteredHeaders = ctx.isInternalPagesRevalidation ? new Headers(request.headers) : filterInternalHeaders(request.headers);
66
+ filteredHeaders.delete(VINEXT_REVALIDATE_HOST_HEADER);
67
+ request = cloneRequestWithHeaders(request, filteredHeaders);
57
68
  const hadBasePath = !basePath || hasBasePath(pathname, basePath);
58
69
  {
59
70
  const stripped = stripBasePath(pathname, basePath);
@@ -87,10 +98,11 @@ async function handleRequest(request, env, ctx) {
87
98
  isDataRequest: isDataReq,
88
99
  hasMiddleware,
89
100
  ctx,
101
+ authorizeOnDemandRevalidate: typeof authorizeOnDemandRevalidate === "function" ? authorizeOnDemandRevalidate : void 0,
90
102
  matchPageRoute: typeof matchPageRoute === "function" ? matchPageRoute : null,
91
103
  runMiddleware: typeof runMiddleware === "function" ? wrapMiddlewareWithBasePath(runMiddleware, basePath, hadBasePath) : null,
92
104
  renderPage: typeof renderPage === "function" ? (req, resolvedUrl, options, stagedHeaders) => renderPage(req, resolvedUrl, null, ctx, stagedHeaders, options) : null,
93
- handleApi: typeof handleApiRoute === "function" ? (req, apiUrl) => handleApiRoute(req, apiUrl, ctx) : null,
105
+ handleApi: typeof handleApiRoute === "function" ? (req, apiUrl) => handleApiRoute(req, apiUrl, ctx, new URL(req.url).origin) : null,
94
106
  serveFilesystemRoute: async (requestPathname, _stagedHeaders, phase) => {
95
107
  if (!env?.ASSETS) return false;
96
108
  return fetchWorkerFilesystemRoute(request, requestPathname, phase, (assetRequest) => Promise.resolve(env.ASSETS.fetch(assetRequest)));
@@ -58,11 +58,11 @@ function decodedPrerenderRouteParamEquals(prerenderValue, matchedValue) {
58
58
  if (prerenderValue.length !== matchedValue.length) return false;
59
59
  return prerenderValue.every((item, index) => {
60
60
  const decoded = decodePrerenderRouteParam(item);
61
- return decoded !== null && decoded === matchedValue[index];
61
+ return item === matchedValue[index] || decoded !== null && decoded === matchedValue[index];
62
62
  });
63
63
  }
64
64
  const decoded = decodePrerenderRouteParam(prerenderValue);
65
- return decoded !== null && decoded === matchedValue;
65
+ return prerenderValue === matchedValue || decoded !== null && decoded === matchedValue;
66
66
  }
67
67
  function prerenderRouteParamsPayloadMatchesRoute(payload, routePattern, params) {
68
68
  return matchPrerenderRouteParamsPayload(payload, routePattern, params)?.kind === "exact";
@@ -1,3 +1,4 @@
1
+ import { NextI18nConfig } from "../config/next-config.js";
1
2
  import { negotiateEncoding } from "./accept-encoding.js";
2
3
  import { StaticFileCache } from "./static-file-cache.js";
3
4
  import { resolveRequestHost, trustProxy, trustedHosts } from "./proxy-trust.js";
@@ -101,12 +102,9 @@ declare function tryServeStatic(req: IncomingMessage, res: ServerResponse, clien
101
102
  * Convert a Node.js IncomingMessage to a Web Request object.
102
103
  *
103
104
  * When `urlOverride` is provided, it is used as the path + query string
104
- * instead of `req.url`. This avoids redundant path normalization when the
105
- * caller has already decoded and normalized the pathname (e.g. the App
106
- * Router prod server normalizes before static-asset lookup, and can pass
107
- * the result here so the downstream RSC handler doesn't re-normalize).
105
+ * instead of `req.url`.
108
106
  */
109
- declare function nodeToWebRequest(req: IncomingMessage, urlOverride?: string, prerenderSecret?: string): Request;
107
+ declare function nodeToWebRequest(req: IncomingMessage, urlOverride?: string, prerenderSecret?: string, i18nConfig?: NextI18nConfig | null, authorizeOnDemandRevalidate?: (headerValue: string | null) => boolean): Request;
110
108
  /**
111
109
  * Stream a Web Response back to a Node.js ServerResponse.
112
110
  * Supports streaming compression for SSR responses.
@@ -6,9 +6,9 @@ import { VINEXT_STATIC_FILE_HEADER } from "./headers.js";
6
6
  import { normalizePath } from "./normalize-path.js";
7
7
  import { notFoundResponse } from "./http-error-responses.js";
8
8
  import { isOpenRedirectShaped } from "./open-redirect.js";
9
- import { filterInternalHeaders } from "./request-pipeline.js";
9
+ import { canonicalizeRequestPathname, filterInternalHeaders } from "./request-pipeline.js";
10
10
  import { isUnknownRecord } from "../utils/record.js";
11
- import { buildNextDataNotFoundResponse, isNextDataPathname, normalizeNextDataPagePathname, parseNextDataPathname } from "./pages-data-route.js";
11
+ import { buildNextDataNotFoundResponse, encodeUrlParserIgnoredCharacters, isNextDataPathname, normalizeNextDataPagePathname, parseNextDataPathname, urlParserCreatesPagesDataPath } from "./pages-data-route.js";
12
12
  import { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix } from "../utils/asset-prefix.js";
13
13
  import { setPagesClientAssets } from "./pages-client-assets.js";
14
14
  import { resolveRequestHost, resolveRequestProtocol, trustProxy, trustedHosts } from "./proxy-trust.js";
@@ -16,6 +16,7 @@ import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, isImageOptimizationPath, isS
16
16
  import { installSocketErrorBackstop } from "./socket-error-backstop.js";
17
17
  import { CONTENT_TYPES, StaticFileCache, etagFromFilenameHash } from "./static-file-cache.js";
18
18
  import { collectInlineCssManifest } from "../build/inline-css.js";
19
+ import { readTrustedRevalidationHostname } from "./revalidation-host.js";
19
20
  import { mergeHeaders } from "./worker-utils.js";
20
21
  import { runPagesRequest, wrapMiddlewareWithBasePath } from "./pages-request-pipeline.js";
21
22
  import { computeClientRuntimeMetadata } from "../utils/client-runtime-metadata.js";
@@ -555,18 +556,18 @@ async function statIfFile(filePath) {
555
556
  * Convert a Node.js IncomingMessage to a Web Request object.
556
557
  *
557
558
  * When `urlOverride` is provided, it is used as the path + query string
558
- * instead of `req.url`. This avoids redundant path normalization when the
559
- * caller has already decoded and normalized the pathname (e.g. the App
560
- * Router prod server normalizes before static-asset lookup, and can pass
561
- * the result here so the downstream RSC handler doesn't re-normalize).
559
+ * instead of `req.url`.
562
560
  */
563
- function nodeToWebRequest(req, urlOverride, prerenderSecret) {
564
- const origin = `${resolveRequestProtocol(req)}://${resolveRequestHost(req, "localhost")}`;
565
- const url = new URL(urlOverride ?? req.url ?? "/", origin);
561
+ function nodeToWebRequest(req, urlOverride, prerenderSecret, i18nConfig, authorizeOnDemandRevalidate) {
562
+ const proto = resolveRequestProtocol(req);
566
563
  const rawHeaders = nodeHeadersToWebHeaders(req.headers);
564
+ const revalidationHostname = readTrustedRevalidationHostname(rawHeaders, i18nConfig, authorizeOnDemandRevalidate);
565
+ const origin = `${proto}://${revalidationHostname ?? resolveRequestHost(req, "localhost")}`;
566
+ const url = new URL(urlOverride ?? req.url ?? "/", origin);
567
567
  const prerenderRouteParamsPayload = readTrustedPrerenderRouteParamsFromHeaders(rawHeaders, prerenderSecret);
568
568
  const isTrustedSpeculativePrerender = process.env.VINEXT_PRERENDER === "1" && prerenderSecret !== void 0 && rawHeaders.get("x-vinext-prerender-secret") === prerenderSecret && rawHeaders.get("x-vinext-prerender-speculative") === "1";
569
569
  const headers = filterInternalHeaders(rawHeaders);
570
+ if (revalidationHostname) headers.set("host", revalidationHostname);
570
571
  const prerenderRouteParamsHeader = serializePrerenderRouteParamsHeader(prerenderRouteParamsPayload);
571
572
  if (prerenderRouteParamsHeader !== null) headers.set(VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, prerenderRouteParamsHeader);
572
573
  if (isTrustedSpeculativePrerender) headers.set(VINEXT_PRERENDER_SPECULATIVE_HEADER, "1");
@@ -663,19 +664,32 @@ async function startProdServer(options = {}) {
663
664
  silent
664
665
  });
665
666
  }
666
- function createNodeExecutionContext() {
667
+ function createNodeExecutionContext(trustedRevalidateOrigin) {
667
668
  return {
668
669
  waitUntil(promise) {
669
670
  Promise.resolve(promise).catch(() => {});
670
671
  },
671
- passThroughOnException() {}
672
+ passThroughOnException() {},
673
+ trustedRevalidateOrigin
672
674
  };
673
675
  }
676
+ function resolveTrustedNodeRevalidateOrigin(req, configuredHost, configuredPort) {
677
+ const port = req.socket.localPort ?? configuredPort;
678
+ return `http://${normalizeInternalFetchHost(configuredHost)}:${port}`;
679
+ }
680
+ function normalizeInternalFetchHost(host) {
681
+ if (!host || host === "0.0.0.0" || host === "::") return "localhost";
682
+ if (host.includes(":") && !host.startsWith("[")) return `[${host}]`;
683
+ return host;
684
+ }
674
685
  function resolveAppRouterHandler(entry) {
675
- if (typeof entry === "function") return (request) => Promise.resolve(entry(request));
686
+ if (typeof entry === "function") {
687
+ const handler = entry;
688
+ return (request, ctx) => Promise.resolve(handler(request, ctx));
689
+ }
676
690
  if (entry && typeof entry === "object" && "fetch" in entry) {
677
691
  const workerEntry = entry;
678
- if (typeof workerEntry.fetch === "function") return (request) => Promise.resolve(workerEntry.fetch(request, void 0, createNodeExecutionContext()));
692
+ if (typeof workerEntry.fetch === "function") return (request, ctx) => Promise.resolve(workerEntry.fetch(request, void 0, ctx));
679
693
  }
680
694
  console.error("[vinext] App Router entry must export either a default handler function or a Worker-style default export with fetch()");
681
695
  process.exit(1);
@@ -796,6 +810,8 @@ async function startAppRouterServer(options) {
796
810
  const appRouterBasePath = typeof rscModule.__basePath === "string" ? rscModule.__basePath : "";
797
811
  const appRouterInlineCss = rscModule.__inlineCss === true;
798
812
  const appRouterHasPagesDir = rscModule.__hasPagesDir === true;
813
+ const appRouterI18nConfig = rscModule.__i18nConfig ?? null;
814
+ const appRouterAuthorizeOnDemandRevalidate = typeof rscModule.authorizeOnDemandRevalidate === "function" ? rscModule.authorizeOnDemandRevalidate : void 0;
799
815
  const appImageAllowedWidths = Array.isArray(rscModule.__imageAllowedWidths) ? rscModule.__imageAllowedWidths : [...DEFAULT_DEVICE_SIZES, ...DEFAULT_IMAGE_SIZES];
800
816
  let imageConfig = typeof rscModule.__imageConfig === "object" && rscModule.__imageConfig !== null ? rscModule.__imageConfig : void 0;
801
817
  if (imageConfig === void 0) {
@@ -873,9 +889,14 @@ async function startAppRouterServer(options) {
873
889
  return;
874
890
  }
875
891
  try {
876
- const qs = rawUrl.includes("?") ? rawUrl.slice(rawUrl.indexOf("?")) : "";
877
- const request = nodeToWebRequest(req, pathname + qs, prerenderSecret);
878
- const response = await rscHandler(request);
892
+ const request = nodeToWebRequest(req, rawUrl, prerenderSecret, appRouterI18nConfig, appRouterAuthorizeOnDemandRevalidate);
893
+ const response = await rscHandler(request, createNodeExecutionContext(resolveTrustedNodeRevalidateOrigin(req, host, port)));
894
+ if (missingBuildAsset && response.status === 404) {
895
+ cancelResponseBody(response);
896
+ res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
897
+ res.end("Not Found");
898
+ return;
899
+ }
879
900
  const staticFileSignal = response.headers.get(VINEXT_STATIC_FILE_HEADER);
880
901
  if (staticFileSignal) {
881
902
  let staticFilePath = "/";
@@ -891,12 +912,6 @@ async function startAppRouterServer(options) {
891
912
  await sendWebResponse(notFoundResponse({ headers: toWebHeaders(staticResponseHeaders) }), req, res, compress);
892
913
  return;
893
914
  }
894
- if (missingBuildAsset && response.status === 404) {
895
- cancelResponseBody(response);
896
- res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
897
- res.end("Not Found");
898
- return;
899
- }
900
915
  await sendWebResponse(response, req, res, compress);
901
916
  } catch (e) {
902
917
  console.error("[vinext] Server error:", e);
@@ -939,14 +954,16 @@ function readPagesServerEntryPageRoutes(value) {
939
954
  *
940
955
  * Uses the server entry (dist/server/entry.js) which exports:
941
956
  * - renderPage(request, url, manifest, ctx?, middlewareHeaders?) — SSR rendering (Web Request → Response)
942
- * - handleApiRoute(request, url, ctx?) — API route handling (ctx optional; pass for ctx.waitUntil() on Workers)
957
+ * - handleApiRoute(request, url, ctx?, trustedRevalidateOrigin?) — API route handling
958
+ * (ctx optional; pass for ctx.waitUntil() on Workers)
959
+ * - authorizeOnDemandRevalidate(header) — validates against the bundled build secret
943
960
  * - runMiddleware(request, ctx?) — middleware execution (ctx optional; pass for ctx.waitUntil() on Workers)
944
961
  * - vinextConfig — embedded next.config.js settings
945
962
  */
946
963
  async function startPagesRouterServer(options) {
947
964
  const { port, host, clientDir, serverEntryPath, compress, purpose, silent } = options;
948
965
  const serverEntry = await importServerEntryModule(serverEntryPath);
949
- const { renderPage, handleApiRoute: handleApi, runMiddleware, vinextConfig, buildId: pagesBuildId } = serverEntry;
966
+ const { renderPage, handleApiRoute: handleApi, authorizeOnDemandRevalidate, runMiddleware, vinextConfig, buildId: pagesBuildId } = serverEntry;
950
967
  const matchPageRoute = typeof serverEntry.matchPageRoute === "function" ? serverEntry.matchPageRoute : void 0;
951
968
  const hasMiddleware = serverEntry.hasMiddleware === true;
952
969
  const pageRoutes = readPagesServerEntryPageRoutes(serverEntry.pageRoutes);
@@ -987,8 +1004,9 @@ async function startPagesRouterServer(options) {
987
1004
  res.end("This page could not be found");
988
1005
  return;
989
1006
  }
990
- const rawPagesPathname = rawPagesPathnameBeforeNormalize.replaceAll("\\", "/");
1007
+ const rawPagesPathname = canonicalizeRequestPathname(rawPagesPathnameBeforeNormalize.replaceAll("\\", "/"));
991
1008
  const rawQs = rawUrl.includes("?") ? rawUrl.slice(rawUrl.indexOf("?")) : "";
1009
+ let requestPathname = normalizePath(rawPagesPathname);
992
1010
  let pathname;
993
1011
  try {
994
1012
  pathname = normalizePath(normalizePathnameForRouteMatchStrict(rawPagesPathname));
@@ -997,7 +1015,7 @@ async function startPagesRouterServer(options) {
997
1015
  res.end("Bad Request");
998
1016
  return;
999
1017
  }
1000
- let url = pathname + rawQs;
1018
+ let url = requestPathname + rawQs;
1001
1019
  if (pathname === "/__vinext/prerender/pages-static-paths") {
1002
1020
  const secret = req.headers[VINEXT_PRERENDER_SECRET_HEADER];
1003
1021
  if (!prerenderSecret || secret !== prerenderSecret) {
@@ -1058,18 +1076,31 @@ async function startPagesRouterServer(options) {
1058
1076
  return;
1059
1077
  }
1060
1078
  try {
1061
- const hadBasePath = !basePath || hasBasePath(pathname, basePath);
1079
+ const hadBasePath = !basePath || hasBasePath(requestPathname, basePath);
1080
+ let configMatchPathname = stripBasePath(requestPathname, basePath);
1062
1081
  {
1063
- const stripped = stripBasePath(pathname, basePath);
1064
- if (stripped !== pathname) {
1065
- url = stripped + (url.includes("?") ? url.slice(url.indexOf("?")) : "");
1066
- pathname = stripped;
1082
+ const strippedPathname = stripBasePath(pathname, basePath);
1083
+ const strippedRequestPathname = stripBasePath(requestPathname, basePath);
1084
+ pathname = strippedPathname;
1085
+ if (strippedRequestPathname !== requestPathname) {
1086
+ requestPathname = strippedRequestPathname;
1087
+ url = requestPathname + rawQs;
1067
1088
  }
1068
1089
  }
1090
+ if (urlParserCreatesPagesDataPath(pathname)) {
1091
+ res.writeHead(404);
1092
+ res.end("This page could not be found");
1093
+ return;
1094
+ }
1095
+ requestPathname = encodeUrlParserIgnoredCharacters(requestPathname);
1096
+ {
1097
+ const qs = url.includes("?") ? url.slice(url.indexOf("?")) : "";
1098
+ url = requestPathname + qs;
1099
+ }
1069
1100
  let isDataReq = false;
1070
1101
  const originalRenderUrl = url;
1071
- if (isNextDataPathname(pathname)) {
1072
- const dataMatch = pagesBuildId ? parseNextDataPathname(pathname, pagesBuildId) : null;
1102
+ if (isNextDataPathname(requestPathname)) {
1103
+ const dataMatch = pagesBuildId ? parseNextDataPathname(requestPathname, pagesBuildId) : null;
1073
1104
  if (!dataMatch) {
1074
1105
  await sendWebResponse(buildNextDataNotFoundResponse(), req, res, compress);
1075
1106
  return;
@@ -1078,16 +1109,20 @@ async function startPagesRouterServer(options) {
1078
1109
  const qs = url.includes("?") ? url.slice(url.indexOf("?")) : "";
1079
1110
  const pagePathname = normalizeNextDataPagePathname(dataMatch.pagePathname, hasMiddleware && trailingSlash);
1080
1111
  url = pagePathname + qs;
1112
+ requestPathname = pagePathname;
1081
1113
  pathname = pagePathname;
1114
+ configMatchPathname = pagePathname;
1082
1115
  }
1083
1116
  const protocol = resolveRequestProtocol(req);
1084
1117
  const hostHeader = resolveRequestHost(req, `${host}:${port}`);
1085
1118
  const rawReqHeaders = nodeHeadersToWebHeaders(req.headers);
1119
+ const revalidationHostname = readTrustedRevalidationHostname(rawReqHeaders, i18nConfig, typeof authorizeOnDemandRevalidate === "function" ? authorizeOnDemandRevalidate : void 0);
1086
1120
  const isDataRequest = isDataReq;
1087
1121
  const reqHeaders = filterInternalHeaders(rawReqHeaders);
1122
+ if (revalidationHostname) reqHeaders.set("host", revalidationHostname);
1088
1123
  const method = req.method ?? "GET";
1089
1124
  const hasBody = method !== "GET" && method !== "HEAD";
1090
- const result = await runPagesRequest(new Request(`${protocol}://${hostHeader}${url}`, {
1125
+ const result = await runPagesRequest(new Request(`${protocol}://${revalidationHostname ?? hostHeader}${url}`, {
1091
1126
  method,
1092
1127
  headers: reqHeaders,
1093
1128
  body: hasBody ? readNodeStream(req) : void 0,
@@ -1105,13 +1140,15 @@ async function startPagesRouterServer(options) {
1105
1140
  hasMiddleware,
1106
1141
  ctx: void 0,
1107
1142
  rawSearch: rawQs,
1143
+ authorizeOnDemandRevalidate: typeof authorizeOnDemandRevalidate === "function" ? authorizeOnDemandRevalidate : void 0,
1144
+ configMatchPathname,
1108
1145
  matchPageRoute: matchPageRoute ?? null,
1109
1146
  runMiddleware: typeof runMiddleware === "function" ? wrapMiddlewareWithBasePath(runMiddleware, basePath, hadBasePath) : null,
1110
1147
  renderPage: typeof renderPage === "function" ? (request, resolvedUrl, options, stagedHeaders) => renderPage(request, resolvedUrl, ssrManifest, void 0, stagedHeaders, {
1111
1148
  ...options,
1112
1149
  originalUrl: originalRenderUrl
1113
1150
  }) : null,
1114
- handleApi: typeof handleApi === "function" ? (request, apiUrl) => handleApi(request, apiUrl, createNodeExecutionContext()) : null,
1151
+ handleApi: typeof handleApi === "function" ? (request, apiUrl) => handleApi(request, apiUrl, createNodeExecutionContext(), resolveTrustedNodeRevalidateOrigin(req, host, port)) : null,
1115
1152
  serveFilesystemRoute: async (requestPathname, stagedHeaders, phase) => {
1116
1153
  if (req.method !== "GET" && req.method !== "HEAD" || requestPathname === "/" || requestPathname === "/api" || requestPathname.startsWith("/api/") || phase === "direct" && requestPathname.startsWith(`/_next/static/`)) return false;
1117
1154
  return tryServeStatic(req, res, clientDir, requestPathname, compress, staticCache, stagedHeaders);