vinext 1.0.0-beta.2 → 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 (126) hide show
  1. package/README.md +6 -9
  2. package/dist/build/client-build-config.js +7 -1
  3. package/dist/check.js +4 -0
  4. package/dist/config/config-matchers.js +4 -1
  5. package/dist/config/next-config.d.ts +12 -3
  6. package/dist/config/next-config.js +4 -0
  7. package/dist/entries/app-browser-entry.d.ts +4 -10
  8. package/dist/entries/app-browser-entry.js +20 -4
  9. package/dist/entries/app-rsc-entry.js +3 -0
  10. package/dist/entries/app-rsc-manifest.js +19 -0
  11. package/dist/entries/pages-client-entry.js +0 -1
  12. package/dist/entries/pages-server-entry.js +10 -3
  13. package/dist/index.js +112 -43
  14. package/dist/init-cloudflare.d.ts +3 -1
  15. package/dist/init-cloudflare.js +59 -5
  16. package/dist/plugins/extensionless-dynamic-import.js +23 -18
  17. package/dist/plugins/ignore-dynamic-requests.js +3 -1
  18. package/dist/plugins/import-meta-url.js +17 -6
  19. package/dist/plugins/require-context.js +20 -15
  20. package/dist/plugins/transform-cache.d.ts +19 -0
  21. package/dist/plugins/transform-cache.js +36 -0
  22. package/dist/routing/app-route-graph.d.ts +14 -6
  23. package/dist/routing/app-route-graph.js +106 -16
  24. package/dist/server/api-handler.d.ts +2 -0
  25. package/dist/server/api-handler.js +3 -3
  26. package/dist/server/app-browser-entry.js +103 -31
  27. package/dist/server/app-browser-navigation-controller.d.ts +23 -21
  28. package/dist/server/app-browser-navigation-controller.js +1 -1
  29. package/dist/server/app-page-cache-finalizer.js +1 -1
  30. package/dist/server/app-page-cache.d.ts +1 -1
  31. package/dist/server/app-page-cache.js +16 -2
  32. package/dist/server/app-page-dispatch.d.ts +8 -0
  33. package/dist/server/app-page-dispatch.js +31 -6
  34. package/dist/server/app-page-element-builder.d.ts +2 -0
  35. package/dist/server/app-page-element-builder.js +33 -7
  36. package/dist/server/app-page-head.d.ts +3 -3
  37. package/dist/server/app-page-head.js +58 -13
  38. package/dist/server/app-page-http-access-fallback-metadata.d.ts +2 -2
  39. package/dist/server/app-page-http-access-fallback-metadata.js +9 -1
  40. package/dist/server/app-page-probe.d.ts +4 -0
  41. package/dist/server/app-page-probe.js +6 -2
  42. package/dist/server/app-page-request.d.ts +3 -0
  43. package/dist/server/app-page-request.js +1 -1
  44. package/dist/server/app-page-response.js +1 -1
  45. package/dist/server/app-page-route-wiring.d.ts +7 -0
  46. package/dist/server/app-page-route-wiring.js +211 -49
  47. package/dist/server/app-pages-bridge.d.ts +1 -1
  48. package/dist/server/app-pages-bridge.js +2 -1
  49. package/dist/server/app-route-handler-cache.js +4 -0
  50. package/dist/server/app-route-handler-dispatch.js +9 -3
  51. package/dist/server/app-route-handler-response.js +1 -1
  52. package/dist/server/app-route-module-loader.d.ts +6 -0
  53. package/dist/server/app-route-module-loader.js +3 -0
  54. package/dist/server/app-router-entry.js +6 -3
  55. package/dist/server/app-rsc-errors.js +10 -0
  56. package/dist/server/app-rsc-handler.js +21 -9
  57. package/dist/server/app-rsc-route-matching.d.ts +9 -0
  58. package/dist/server/app-rsc-route-matching.js +6 -0
  59. package/dist/server/app-ssr-entry.js +4 -4
  60. package/dist/server/cache-control.d.ts +2 -2
  61. package/dist/server/cache-control.js +2 -1
  62. package/dist/server/dev-server.d.ts +3 -1
  63. package/dist/server/dev-server.js +145 -312
  64. package/dist/server/headers.d.ts +2 -2
  65. package/dist/server/headers.js +4 -3
  66. package/dist/server/isr-cache.d.ts +10 -33
  67. package/dist/server/isr-cache.js +33 -43
  68. package/dist/server/isr-decision.d.ts +2 -2
  69. package/dist/server/pages-api-route.d.ts +2 -0
  70. package/dist/server/pages-api-route.js +4 -1
  71. package/dist/server/pages-dev-hydration.js +0 -1
  72. package/dist/server/pages-get-initial-props.d.ts +1 -1
  73. package/dist/server/pages-get-initial-props.js +1 -4
  74. package/dist/server/pages-i18n.js +64 -16
  75. package/dist/server/pages-node-compat.d.ts +2 -0
  76. package/dist/server/pages-node-compat.js +7 -3
  77. package/dist/server/pages-page-data.d.ts +61 -9
  78. package/dist/server/pages-page-data.js +334 -74
  79. package/dist/server/pages-page-handler.d.ts +5 -1
  80. package/dist/server/pages-page-handler.js +115 -19
  81. package/dist/server/pages-page-response.d.ts +5 -3
  82. package/dist/server/pages-page-response.js +23 -26
  83. package/dist/server/pages-request-pipeline.d.ts +7 -0
  84. package/dist/server/pages-request-pipeline.js +5 -1
  85. package/dist/server/pages-revalidate.d.ts +1 -1
  86. package/dist/server/pages-revalidate.js +54 -8
  87. package/dist/server/pages-router-entry.d.ts +1 -0
  88. package/dist/server/pages-router-entry.js +10 -4
  89. package/dist/server/prod-server.d.ts +2 -1
  90. package/dist/server/prod-server.js +37 -13
  91. package/dist/server/revalidation-host.d.ts +11 -0
  92. package/dist/server/revalidation-host.js +19 -0
  93. package/dist/server/worker-revalidation-context.d.ts +13 -0
  94. package/dist/server/worker-revalidation-context.js +29 -0
  95. package/dist/shims/cache-handler.d.ts +1 -1
  96. package/dist/shims/cache-handler.js +10 -7
  97. package/dist/shims/cache-runtime.d.ts +6 -0
  98. package/dist/shims/cache-runtime.js +4 -1
  99. package/dist/shims/cache.js +1 -1
  100. package/dist/shims/error.js +1 -1
  101. package/dist/shims/fetch-cache.js +1 -1
  102. package/dist/shims/font-google-base.js +17 -8
  103. package/dist/shims/font-local.js +41 -10
  104. package/dist/shims/headers.d.ts +10 -13
  105. package/dist/shims/headers.js +85 -36
  106. package/dist/shims/internal/cookie-serialize.d.ts +10 -9
  107. package/dist/shims/internal/cookie-serialize.js +9 -8
  108. package/dist/shims/internal/interpolate-as.d.ts +15 -1
  109. package/dist/shims/internal/interpolate-as.js +27 -2
  110. package/dist/shims/link.js +18 -6
  111. package/dist/shims/metadata.d.ts +7 -17
  112. package/dist/shims/metadata.js +74 -24
  113. package/dist/shims/navigation.js +1 -1
  114. package/dist/shims/request-context.d.ts +4 -1
  115. package/dist/shims/router.js +18 -7
  116. package/dist/shims/script.js +1 -1
  117. package/dist/shims/server.d.ts +7 -6
  118. package/dist/shims/server.js +156 -57
  119. package/dist/shims/unified-request-context.d.ts +21 -2
  120. package/dist/shims/unified-request-context.js +82 -1
  121. package/dist/utils/cache-control-metadata.d.ts +2 -1
  122. package/dist/utils/cache-control-metadata.js +5 -1
  123. package/dist/utils/html-limited-bots.js +1 -1
  124. package/dist/utils/protocol-headers.d.ts +7 -1
  125. package/dist/utils/protocol-headers.js +7 -1
  126. package/package.json +1 -1
@@ -1,19 +1,21 @@
1
- import { createRequestContext, runWithRequestContext } from "../shims/unified-request-context.js";
1
+ import { closeAfterResponse, closeAfterResponseWithBody, createRequestContext, runWithRequestContext } from "../shims/unified-request-context.js";
2
2
  import { patternToNextFormat } from "../routing/route-validation.js";
3
3
  import { getRequestExecutionContext } from "../shims/request-context.js";
4
- import { NEXTJS_DEPLOYMENT_ID_HEADER } from "./headers.js";
4
+ import { PRERENDER_REVALIDATE_HEADER } from "../utils/protocol-headers.js";
5
+ import { NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, VINEXT_CACHE_HEADER } from "./headers.js";
5
6
  import { reportRequestError } from "./instrumentation.js";
6
7
  import { BROWSER_REVALIDATE_CACHE_CONTROL, NEVER_CACHE_CONTROL, applyCdnResponseHeaders, shouldUseNextDeployCacheControl } from "./cache-control.js";
7
8
  import { buildMissIsrCacheControl } from "./isr-decision.js";
8
- import { PRERENDER_REVALIDATE_HEADER, isOnDemandRevalidateRequest, isrCacheKey, isrGet, isrSet, triggerBackgroundRegeneration } from "./isr-cache.js";
9
+ import { coalesceOnDemandRevalidation, isOnDemandRevalidateRequest, isrCacheKey, isrGet, isrSet, triggerBackgroundRegeneration } from "./isr-cache.js";
10
+ import { encodeCacheTag } from "../utils/encode-cache-tag.js";
9
11
  import { ensureFetchPatch } from "../shims/fetch-cache.js";
10
- import { appendAssetDeploymentIdQuery } from "../utils/deployment-id.js";
11
12
  import { mergeRouteParamsIntoQuery, parseQueryString } from "../utils/query.js";
12
13
  import { getScriptNonceFromHeaderSources } from "./csp.js";
13
14
  import { extractLocaleFromUrl, resolvePagesI18nRequest } from "./pages-i18n.js";
14
15
  import { buildDefaultPagesNotFoundResponse } from "./pages-default-404.js";
15
16
  import { buildPagesReadinessNextData } from "./pages-readiness.js";
16
17
  import { resolvePagesPageMethodResponse } from "./pages-page-method.js";
18
+ import { setCacheStateHeaders } from "./cache-headers.js";
17
19
  import { renderPagesPageResponse } from "./pages-page-response.js";
18
20
  import { hasPagesGetInitialProps } from "./pages-get-initial-props.js";
19
21
  import { buildNextDataNotFoundResponse, buildNextDataPropsJsonResponse, normalizePagesDataRequest, parseNextDataPathname } from "./pages-data-route.js";
@@ -25,7 +27,12 @@ import { createPagesReqRes } from "./pages-node-compat.js";
25
27
  function finalizePagesPreviewResponse(response, preview) {
26
28
  if (preview.data === false && !preview.shouldClear) return response;
27
29
  const headers = new Headers(response.headers);
28
- if (preview.data !== false) headers.set("Cache-Control", PAGES_PREVIEW_CACHE_CONTROL);
30
+ if (preview.data !== false) {
31
+ headers.set("Cache-Control", PAGES_PREVIEW_CACHE_CONTROL);
32
+ headers.delete("CDN-Cache-Control");
33
+ headers.delete("Cloudflare-CDN-Cache-Control");
34
+ headers.delete("Cache-Tag");
35
+ }
29
36
  if (preview.shouldClear) appendPagesPreviewClearCookies(headers);
30
37
  return new Response(response.body, {
31
38
  headers,
@@ -33,6 +40,41 @@ function finalizePagesPreviewResponse(response, preview) {
33
40
  statusText: response.statusText
34
41
  });
35
42
  }
43
+ function withPagesCacheState(response, state) {
44
+ const headers = new Headers(response.headers);
45
+ if (state === "REVALIDATED") {
46
+ headers.set(NEXTJS_CACHE_HEADER, state);
47
+ headers.delete(VINEXT_CACHE_HEADER);
48
+ } else setCacheStateHeaders(headers, state);
49
+ return new Response(response.body, {
50
+ headers,
51
+ status: response.status,
52
+ statusText: response.statusText
53
+ });
54
+ }
55
+ function applyPagesErrorCachePolicy(response, revalidateSeconds, expireSeconds, cacheTagPathname) {
56
+ const headers = new Headers(response.headers);
57
+ const browserPolicy = headers.get("Cache-Control");
58
+ const sharedPolicies = [headers.get("CDN-Cache-Control"), headers.get("Cloudflare-CDN-Cache-Control")];
59
+ const hasCacheableSharedPolicy = sharedPolicies.some((value) => value && /(?:^|,)\s*s-maxage\s*=/i.test(value));
60
+ if (sharedPolicies.some((value) => value && /(?:private|no-store|no-cache)/i.test(value)) || !hasCacheableSharedPolicy && browserPolicy && /(?:private|no-store|no-cache)/i.test(browserPolicy)) return response;
61
+ headers.delete("CDN-Cache-Control");
62
+ headers.delete("Cloudflare-CDN-Cache-Control");
63
+ headers.delete("Cache-Tag");
64
+ if (revalidateSeconds === void 0) applyCdnResponseHeaders(headers, { cacheControl: NEVER_CACHE_CONTROL });
65
+ else {
66
+ const stem = cacheTagPathname.endsWith("/") ? cacheTagPathname.slice(0, -1) : cacheTagPathname;
67
+ applyCdnResponseHeaders(headers, {
68
+ cacheControl: buildMissIsrCacheControl(revalidateSeconds, expireSeconds),
69
+ tags: [encodeCacheTag(`_N_T_${stem || "/"}`)]
70
+ });
71
+ }
72
+ return new Response(response.body, {
73
+ headers,
74
+ status: response.status,
75
+ statusText: response.statusText
76
+ });
77
+ }
36
78
  function shouldEmitPagesClientTraceMetadata(pageModule, appComponent) {
37
79
  if (typeof pageModule.getServerSideProps === "function") return true;
38
80
  if (typeof pageModule.getStaticProps === "function") return false;
@@ -122,6 +164,7 @@ function createPagesPageHandler(opts) {
122
164
  route: options.__forcedRoute,
123
165
  params: {}
124
166
  } : matchRoute(routeUrl, pageRoutes);
167
+ let isRouteMissErrorRender = false;
125
168
  let renderStatusCodeOverride = statusCode;
126
169
  let renderAsPath = asPath;
127
170
  if (!match) {
@@ -133,6 +176,7 @@ function createPagesPageHandler(opts) {
133
176
  route: notFoundRoute,
134
177
  params: {}
135
178
  };
179
+ isRouteMissErrorRender = true;
136
180
  renderStatusCodeOverride = 404;
137
181
  renderAsPath = routeUrl;
138
182
  } else return buildDefaultPagesNotFoundResponse();
@@ -141,16 +185,43 @@ function createPagesPageHandler(opts) {
141
185
  const pageModule = route.module;
142
186
  const isStaticPropsRoute = typeof pageModule.getStaticProps === "function";
143
187
  const isStaticPropsRender = isStaticPropsRoute && typeof pageModule.getServerSideProps !== "function";
188
+ if (!options?.__skipOnDemandCoalesce && !options?.__forcedRoute && isStaticPropsRoute && isOnDemandRevalidateRequest(request.headers.get("x-prerender-revalidate"))) {
189
+ const snapshot = await coalesceOnDemandRevalidation(pageIsrCacheKey("pages", routeUrl.split("?")[0]), async () => {
190
+ const response = await renderPage(request, url, manifest, middlewareHeaders, {
191
+ ...options,
192
+ __skipOnDemandCoalesce: true
193
+ });
194
+ return {
195
+ body: request.method === "HEAD" || response.status === 204 || response.status === 304 ? null : new Uint8Array(await response.arrayBuffer()),
196
+ headers: [...response.headers.entries()],
197
+ status: response.status,
198
+ statusText: response.statusText
199
+ };
200
+ });
201
+ return new Response(snapshot.body?.slice() ?? null, {
202
+ headers: snapshot.headers,
203
+ status: snapshot.status,
204
+ statusText: snapshot.statusText
205
+ });
206
+ }
144
207
  const renderRouteUrl = isStaticPropsRender ? routeUrl.split("?")[0] : routeUrl;
145
208
  const routerAsPathSource = isStaticPropsRender ? renderRouteUrl : renderAsPath ?? renderRouteUrl;
146
209
  const routerAsPath = i18nConfig ? extractLocaleFromUrl(routerAsPathSource, i18nConfig, locale).url : routerAsPathSource;
147
- return runWithRequestContext(createRequestContext({ executionContext: getRequestExecutionContext() }), async () => {
210
+ const uCtx = createRequestContext({ executionContext: getRequestExecutionContext() });
211
+ return closeAfterResponseWithBody(await runWithRequestContext(uCtx, async () => {
148
212
  ensureFetchPatch();
149
213
  try {
150
214
  const routePattern = patternToNextFormat(route.pattern);
151
215
  const renderStatusCode = renderStatusCodeOverride ?? (routePattern === "/404" ? 404 : void 0);
216
+ const isrCachePathname = isStaticPropsRender && (routePattern === "/404" || routePattern === "/500" || routePattern === "/_error") ? routePattern : renderRouteUrl.split("?")[0];
217
+ const isNotFoundErrorRender = routePattern === "/404" || routePattern === "/_error" && renderStatusCode === 404;
218
+ const isStatusErrorRender = isNotFoundErrorRender || routePattern === "/500" || routePattern === "/_error" && renderStatusCode === 500;
219
+ const errorPageRevalidateSeconds = isNotFoundErrorRender ? options?.__notFoundRevalidateSeconds : void 0;
220
+ const errorPageExpireSeconds = isNotFoundErrorRender ? options?.__notFoundExpireSeconds : void 0;
221
+ const errorResponseCachePathname = options?.__notFoundCachePathname ?? isrCachePathname;
152
222
  const query = mergeRouteParamsIntoQuery(parseQueryString(renderRouteUrl), params);
153
223
  const isOnDemandRevalidate = isOnDemandRevalidateRequest(request.headers.get(PRERENDER_REVALIDATE_HEADER));
224
+ const revalidateOnlyGenerated = isOnDemandRevalidate && request.headers.has("x-prerender-revalidate-if-generated");
154
225
  const preview = isStaticPropsRoute || typeof pageModule.getServerSideProps === "function" ? getPagesPreviewState(request.headers.get("cookie"), { isOnDemandRevalidate }) : {
155
226
  data: false,
156
227
  shouldClear: false
@@ -211,11 +282,12 @@ function createPagesPageHandler(opts) {
211
282
  }
212
283
  };
213
284
  const scriptNonce = getScriptNonceFromHeaderSources(request.headers, middlewareHeaders);
285
+ const shouldApplyErrorResponsePolicy = previewData === false && !scriptNonce && isStatusErrorRender && (isStaticPropsRoute || isRouteMissErrorRender || options?.__forcedRoute !== void 0);
214
286
  let fontLinkHeader = "";
215
287
  let allFontPreloads = [];
216
288
  try {
217
289
  allFontPreloads = getFontPreloads();
218
- if (allFontPreloads.length > 0) fontLinkHeader = allFontPreloads.map((p) => "<" + appendAssetDeploymentIdQuery(p.href) + ">; rel=preload; as=font; type=" + p.type + "; crossorigin").join(", ");
290
+ if (allFontPreloads.length > 0) fontLinkHeader = allFontPreloads.map((p) => "<" + p.href + ">; rel=preload; as=font; type=" + p.type + "; crossorigin").join(", ");
219
291
  } catch {}
220
292
  const pagesResolvedUrl = (new URL(routeUrl, originalRequestUrl).pathname || "/") + originalRequestUrl.search;
221
293
  const createPageReqRes = () => {
@@ -232,6 +304,7 @@ function createPagesPageHandler(opts) {
232
304
  isDataReq,
233
305
  err: err instanceof Error ? err : void 0,
234
306
  applyRequestContexts: applySSRContext,
307
+ basePath: vinextConfig.basePath,
235
308
  buildId,
236
309
  deploymentId: process.env.__VINEXT_DEPLOYMENT_ID || process.env.NEXT_DEPLOYMENT_ID,
237
310
  htmlLimitedBots: vinextConfig.htmlLimitedBots,
@@ -253,6 +326,7 @@ function createPagesPageHandler(opts) {
253
326
  isBuildTimePrerendering: typeof process !== "undefined" && process.env && process.env.VINEXT_PRERENDER === "1",
254
327
  validatePropsSerialization: process.env.NODE_ENV !== "production" || process.env.VINEXT_PRERENDER === "1",
255
328
  isOnDemandRevalidate,
329
+ revalidateOnlyGenerated,
256
330
  previewData,
257
331
  pageModule,
258
332
  AppComponent,
@@ -265,10 +339,16 @@ function createPagesPageHandler(opts) {
265
339
  route: { isDynamic: route.isDynamic },
266
340
  routePattern,
267
341
  routeUrl: renderRouteUrl,
342
+ isrCachePathname,
268
343
  runInFreshUnifiedContext(callback) {
269
- return runWithRequestContext(createRequestContext({ executionContext: null }), async () => {
344
+ const revalCtx = createRequestContext({ executionContext: null });
345
+ return runWithRequestContext(revalCtx, async () => {
270
346
  ensureFetchPatch();
271
- return callback();
347
+ try {
348
+ return await callback();
349
+ } finally {
350
+ await closeAfterResponse(revalCtx);
351
+ }
272
352
  });
273
353
  },
274
354
  safeJsonStringify,
@@ -284,15 +364,26 @@ function createPagesPageHandler(opts) {
284
364
  });
285
365
  if (pageDataResult.kind === "notFound") {
286
366
  const notFoundRoute = findNotFoundRoute();
287
- if (notFoundRoute && routePattern !== "/404" && routePattern !== "/_error") return finalizePagesPreviewResponse(await renderPage(request, url, manifest, middlewareHeaders, {
367
+ let notFoundResponse;
368
+ if (notFoundRoute && routePattern !== "/404" && routePattern !== "/_error") notFoundResponse = await renderPage(request, url, manifest, middlewareHeaders, {
288
369
  statusCode: 404,
289
370
  asPath: routerAsPath,
290
371
  renderErrorPageOnMiss: false,
291
- __forcedRoute: notFoundRoute
292
- }), preview);
293
- return finalizePagesPreviewResponse(buildDefaultPagesNotFoundResponse(), preview);
372
+ __forcedRoute: notFoundRoute,
373
+ __notFoundRevalidateSeconds: pageDataResult.revalidateSeconds,
374
+ __notFoundExpireSeconds: pageDataResult.expireSeconds,
375
+ __notFoundCachePathname: isrCachePathname
376
+ });
377
+ else notFoundResponse = buildDefaultPagesNotFoundResponse();
378
+ if (isOnDemandRevalidate) notFoundResponse = withPagesCacheState(notFoundResponse, "REVALIDATED");
379
+ else if (pageDataResult.cacheState) notFoundResponse = withPagesCacheState(notFoundResponse, pageDataResult.cacheState);
380
+ return finalizePagesPreviewResponse(notFoundResponse, preview);
381
+ }
382
+ if (pageDataResult.kind === "response") {
383
+ let response = isOnDemandRevalidate && pageDataResult.onDemandRevalidateSuccess !== false ? withPagesCacheState(pageDataResult.response, "REVALIDATED") : pageDataResult.response;
384
+ if (shouldApplyErrorResponsePolicy) response = applyPagesErrorCachePolicy(response, errorPageRevalidateSeconds, errorPageExpireSeconds, errorResponseCachePathname);
385
+ return finalizePagesPreviewResponse(response, preview);
294
386
  }
295
- if (pageDataResult.kind === "response") return finalizePagesPreviewResponse(pageDataResult.response, preview);
296
387
  let pageProps = pageDataResult.pageProps;
297
388
  let renderProps = pageDataResult.props;
298
389
  if (previewData !== false) renderProps = {
@@ -312,6 +403,7 @@ function createPagesPageHandler(opts) {
312
403
  const gsspRes = pageDataResult.gsspRes;
313
404
  const documentReqRes = serializedPagesNextData.autoExport === true ? null : pageDataResult.documentReqRes ?? createPageReqRes();
314
405
  const isrRevalidateSeconds = pageDataResult.isrRevalidateSeconds;
406
+ const isrExpireSeconds = pageDataResult.isrExpireSeconds;
315
407
  const isFallbackRender = pageDataResult.isFallback === true;
316
408
  if (isFallbackRender) applySSRContext({
317
409
  query: {},
@@ -337,7 +429,7 @@ function createPagesPageHandler(opts) {
337
429
  }
338
430
  if (!hasUserCacheControl) init.headers["Cache-Control"] = NEVER_CACHE_CONTROL;
339
431
  } else if (isStaticPropsRoute) {
340
- if (isrRevalidateSeconds) {
432
+ if (isrRevalidateSeconds !== null) {
341
433
  const headers = new Headers(init.headers);
342
434
  applyCdnResponseHeaders(headers, { cacheControl: buildMissIsrCacheControl(isrRevalidateSeconds, vinextConfig.expireTime) });
343
435
  for (const [key, value] of headers) init.headers[key] = value;
@@ -352,7 +444,7 @@ function createPagesPageHandler(opts) {
352
444
  const pageModuleIds = [];
353
445
  if (appAssetPath) pageModuleIds.push(appAssetPath);
354
446
  if (route.filePath) pageModuleIds.push(route.filePath);
355
- return finalizePagesPreviewResponse(await renderPagesPageResponse({
447
+ let pageResponse = await renderPagesPageResponse({
356
448
  assetTags: collectAssetTags({
357
449
  manifest,
358
450
  moduleIds: pageModuleIds,
@@ -386,8 +478,10 @@ function createPagesPageHandler(opts) {
386
478
  documentReqRes,
387
479
  gsspRes,
388
480
  isrCacheKey: pageIsrCacheKey,
389
- expireSeconds: vinextConfig.expireTime,
481
+ isrCachePathname,
482
+ expireSeconds: isrExpireSeconds,
390
483
  isrRevalidateSeconds,
484
+ isOnDemandRevalidate,
391
485
  isStaticPropsRoute,
392
486
  isrSet,
393
487
  i18n: buildI18nRenderContext(i18nConfig, locale, currentDefaultLocale, domainLocales),
@@ -411,7 +505,9 @@ function createPagesPageHandler(opts) {
411
505
  userAgent: request.headers.get("user-agent") ?? void 0,
412
506
  ifNoneMatch: request.headers.get("if-none-match") ?? void 0,
413
507
  requestCacheControl: request.headers.get("cache-control") ?? void 0
414
- }), preview);
508
+ });
509
+ if (shouldApplyErrorResponsePolicy) pageResponse = applyPagesErrorCachePolicy(pageResponse, errorPageRevalidateSeconds, errorPageExpireSeconds, errorResponseCachePathname);
510
+ return finalizePagesPreviewResponse(pageResponse, preview);
415
511
  } catch (e) {
416
512
  console.error("[vinext] SSR error:", e);
417
513
  reportRequestError(e instanceof Error ? e : new Error(String(e)), {
@@ -445,7 +541,7 @@ function createPagesPageHandler(opts) {
445
541
  }
446
542
  return new Response("Internal Server Error", { status: 500 });
447
543
  }
448
- });
544
+ }), uCtx);
449
545
  }
450
546
  return renderPage;
451
547
  }
@@ -88,11 +88,13 @@ type RenderPagesPageResponseOptions = {
88
88
  setDocumentInitialHead?: ((head: ReactNode[]) => void) | undefined;
89
89
  documentReqRes?: PagesDocumentReqRes | null;
90
90
  gsspRes: PagesGsspResponse | null;
91
- isrCacheKey: (router: string, pathname: string) => string;
91
+ isrCacheKey: (router: string, pathname: string) => string; /** Filesystem-route identity used for ISR persistence and cache tags. */
92
+ isrCachePathname?: string;
92
93
  expireSeconds?: number;
93
- isrRevalidateSeconds: number | null;
94
+ isrRevalidateSeconds: number | false | null; /** Synchronous `res.revalidate()` render; cache persistence must finish before returning. */
95
+ isOnDemandRevalidate?: boolean;
94
96
  isStaticPropsRoute?: boolean;
95
- isrSet: (key: string, data: CachedPagesValue, revalidateSeconds: number, tags?: string[], expireSeconds?: number) => Promise<void>;
97
+ isrSet: (key: string, data: CachedPagesValue, revalidateSeconds: number | false, tags?: string[], expireSeconds?: number) => Promise<void>;
96
98
  i18n: PagesI18nRenderContext;
97
99
  /**
98
100
  * True when rendering a `getStaticPaths` fallback shell for a path that
@@ -1,6 +1,6 @@
1
1
  import { getRequestExecutionContext } from "../shims/request-context.js";
2
+ import { NEXTJS_CACHE_HEADER } from "./headers.js";
2
3
  import { reportRequestError } from "./instrumentation.js";
3
- import { setCacheStateHeaders } from "./cache-headers.js";
4
4
  import { BROWSER_REVALIDATE_CACHE_CONTROL, NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL, applyCdnResponseHeaders, shouldUseNextDeployCacheControl } from "./cache-control.js";
5
5
  import { buildMissIsrCacheControl } from "./isr-decision.js";
6
6
  import { fnv1a52 } from "../utils/hash.js";
@@ -9,29 +9,19 @@ import { appendAssetDeploymentIdQuery } from "../utils/deployment-id.js";
9
9
  import { withScriptNonce } from "../shims/script-nonce-context.js";
10
10
  import { createNonceAttribute, escapeHtmlAttr } from "./html.js";
11
11
  import { getClientTraceMetadataHTML } from "./client-trace-metadata.js";
12
+ import { setCacheStateHeaders } from "./cache-headers.js";
12
13
  import { readStreamAsText } from "../utils/text-stream.js";
13
14
  import { loadUserDocumentInitialProps, runDocumentRenderPage } from "./pages-document-initial-props.js";
14
15
  import { callDocumentGetInitialProps } from "./document-initial-head.js";
16
+ import { isBotUserAgent } from "../utils/html-limited-bots.js";
15
17
  import React from "react";
16
18
  //#region src/server/pages-page-response.ts
17
19
  /**
18
- * Crawlers that cannot handle streamed HTML: they read metadata only from
19
- * the first network chunk, so streaming would give them an incomplete <head>.
20
- * Pattern sourced from Next.js html-bots.ts (updated to match the canary).
21
- */
22
- const HTML_LIMITED_BOT_UA_RE = /[\w-]+-Google|Google-[\w-]+|Chrome-Lighthouse|Slurp|DuckDuckBot|baiduspider|yandex|sogou|bitlybot|tumblr|vkShare|quora link preview|redditbot|ia_archiver|Bingbot|BingPreview|applebot|facebookexternalhit|facebookcatalog|Twitterbot|LinkedInBot|Slackbot|Discordbot|WhatsApp|SkypeUriPreview|Yeti|googleweblight/i;
23
- /**
24
- * Googlebot (the main search crawler) executes JavaScript via a headless
25
- * browser, so it too cannot safely handle mid-stream HTML mutations.
26
- * Matches "Googlebot" but NOT suffixed variants like "Googlebot-Image".
27
- */
28
- const HEADLESS_BROWSER_BOT_UA_RE = /Googlebot(?!-)|Googlebot$/i;
29
- /**
30
20
  * Returns true when the User-Agent belongs to a bot or crawler that cannot
31
21
  * reliably consume a streamed HTML response.
32
22
  */
33
23
  function isPagesStreamingBot(userAgent) {
34
- return HEADLESS_BROWSER_BOT_UA_RE.test(userAgent) || HTML_LIMITED_BOT_UA_RE.test(userAgent);
24
+ return isBotUserAgent(userAgent);
35
25
  }
36
26
  function generatePagesETag(payload) {
37
27
  return "\"" + fnv1a52(payload).toString(36) + payload.length.toString(36) + "\"";
@@ -64,7 +54,7 @@ function buildPagesFontHeadHtml(fontLinks, fontPreloads, fontStyles, scriptNonce
64
54
  let html = "";
65
55
  const nonceAttr = createNonceAttribute(scriptNonce);
66
56
  for (const link of fontLinks) html += `<link rel="stylesheet"${nonceAttr} href="${escapeHtmlAttr(appendAssetDeploymentIdQuery(link))}" />\n `;
67
- for (const preload of fontPreloads) html += `<link rel="preload"${nonceAttr} href="${escapeHtmlAttr(appendAssetDeploymentIdQuery(preload.href))}" as="font" type="${escapeHtmlAttr(preload.type)}" crossorigin />\n `;
57
+ for (const preload of fontPreloads) html += `<link rel="preload"${nonceAttr} href="${escapeHtmlAttr(preload.href)}" as="font" type="${escapeHtmlAttr(preload.type)}" crossorigin />\n `;
68
58
  if (fontStyles.length > 0) html += `<style data-vinext-fonts${nonceAttr}>${fontStyles.join("\n")}</style>\n `;
69
59
  return html;
70
60
  }
@@ -142,14 +132,18 @@ async function reportPagesIsrCacheWriteError(error, cacheKey, routePattern) {
142
132
  });
143
133
  } catch {}
144
134
  }
145
- function schedulePagesIsrCacheWrite(options) {
146
- const cacheWritePromise = readStreamAsText(options.stream).then((bodyHtml) => options.setCache(options.cacheKey, {
135
+ async function writePagesIsrCache(options) {
136
+ const bodyHtml = await readStreamAsText(options.stream);
137
+ await options.setCache(options.cacheKey, {
147
138
  kind: "PAGES",
148
139
  html: options.shellPrefix + bodyHtml + options.shellSuffix,
149
140
  pageData: options.pageData,
150
141
  headers: void 0,
151
142
  status: options.status
152
- }, options.revalidateSeconds, void 0, options.expireSeconds)).catch((error) => reportPagesIsrCacheWriteError(error, options.cacheKey, options.routePattern));
143
+ }, options.revalidateSeconds, void 0, options.expireSeconds);
144
+ }
145
+ function schedulePagesIsrCacheWrite(options) {
146
+ const cacheWritePromise = writePagesIsrCache(options).catch((error) => reportPagesIsrCacheWriteError(error, options.cacheKey, options.routePattern));
153
147
  getRequestExecutionContext()?.waitUntil(cacheWritePromise);
154
148
  }
155
149
  function applyGsspHeaders(headers, gsspRes, statusCode) {
@@ -235,15 +229,15 @@ async function renderPagesPageResponse(options) {
235
229
  const responseHeaders = new Headers({ "Content-Type": "text/html; charset=utf-8" });
236
230
  const finalStatus = applyGsspHeaders(responseHeaders, options.gsspRes ?? options.documentReqRes?.res ?? null, options.statusCode);
237
231
  let responseBodyStream = bodyStream;
238
- if (!options.scriptNonce && options.isrRevalidateSeconds !== null && options.isrRevalidateSeconds > 0) {
232
+ if (!options.scriptNonce && options.isrRevalidateSeconds !== null && (options.isrRevalidateSeconds === false || options.isrRevalidateSeconds > 0)) {
239
233
  const cacheBodyStreamPair = bodyStream.tee();
240
234
  responseBodyStream = cacheBodyStreamPair[0];
241
235
  const cacheBodyStream = cacheBodyStreamPair[1];
242
- const isrPathname = options.routeUrl.split("?")[0];
243
- schedulePagesIsrCacheWrite({
236
+ const isrPathname = options.isrCachePathname ?? options.routeUrl.split("?")[0];
237
+ const cacheWriteOptions = {
244
238
  cacheKey: options.isrCacheKey("pages", isrPathname),
245
239
  expireSeconds: options.expireSeconds,
246
- pageData: options.pageProps,
240
+ pageData: options.props ?? { pageProps: options.pageProps },
247
241
  revalidateSeconds: options.isrRevalidateSeconds,
248
242
  routePattern: options.routePattern,
249
243
  setCache: options.isrSet,
@@ -251,19 +245,22 @@ async function renderPagesPageResponse(options) {
251
245
  shellSuffix,
252
246
  status: finalStatus,
253
247
  stream: cacheBodyStream
254
- });
248
+ };
249
+ if (options.isOnDemandRevalidate) await writePagesIsrCache(cacheWriteOptions);
250
+ else schedulePagesIsrCacheWrite(cacheWriteOptions);
255
251
  }
256
252
  const compositeStream = await buildPagesCompositeStream(responseBodyStream, shellPrefix, shellSuffix);
257
253
  const userSetCacheControl = responseHeaders.has("Cache-Control");
258
254
  if (options.scriptNonce) responseHeaders.set("Cache-Control", NO_STORE_CACHE_CONTROL);
259
- else if (options.isrRevalidateSeconds) {
260
- const isrPathname = options.routeUrl.split("?")[0];
255
+ else if (options.isrRevalidateSeconds !== null) {
256
+ const isrPathname = options.isrCachePathname ?? options.routeUrl.split("?")[0];
261
257
  const stem = isrPathname.endsWith("/") ? isrPathname.slice(0, -1) : isrPathname;
262
258
  applyCdnResponseHeaders(responseHeaders, {
263
259
  cacheControl: buildMissIsrCacheControl(options.isrRevalidateSeconds, options.expireSeconds),
264
260
  tags: [encodeCacheTag(`_N_T_${stem || "/"}`)]
265
261
  });
266
- setCacheStateHeaders(responseHeaders, "MISS");
262
+ if (options.isOnDemandRevalidate) responseHeaders.set(NEXTJS_CACHE_HEADER, "REVALIDATED");
263
+ else setCacheStateHeaders(responseHeaders, "MISS");
267
264
  } else if (options.isStaticPropsRoute && shouldUseNextDeployCacheControl()) responseHeaders.set("Cache-Control", BROWSER_REVALIDATE_CACHE_CONTROL);
268
265
  else if (options.gsspRes && !userSetCacheControl) responseHeaders.set("Cache-Control", NEVER_CACHE_CONTROL);
269
266
  if (options.fontLinkHeader) responseHeaders.set("Link", options.fontLinkHeader);
@@ -45,6 +45,13 @@ type PagesPipelineDeps = {
45
45
  ctx?: unknown;
46
46
  rawSearch?: string;
47
47
  configMatchPathname?: string;
48
+ /**
49
+ * Validate an on-demand revalidation credential using the runtime's
50
+ * authoritative build secret. Production adapters must inject the verifier
51
+ * exported by the generated server entry so this early middleware decision
52
+ * uses the same baked secret as the eventual page renderer.
53
+ */
54
+ authorizeOnDemandRevalidate?: (headerValue: string | null) => boolean;
48
55
  matchPageRoute?: ((pathname: string, request: Request) => PageRouteMatch | null) | null;
49
56
  runMiddleware?: ((request: Request, ctx: unknown, opts: {
50
57
  isDataRequest: boolean;
@@ -1,9 +1,11 @@
1
1
  import { addBasePathToPathname, hasBasePath } from "../utils/base-path.js";
2
2
  import { patternToNextFormat } from "../routing/route-validation.js";
3
+ import { PRERENDER_REVALIDATE_HEADER } from "../utils/protocol-headers.js";
3
4
  import { requestContextFromRequest } from "../config/request-context.js";
4
5
  import { isExternalUrl } from "../utils/external-url.js";
5
6
  import { applyMiddlewareRequestHeaders, matchRedirect, matchRewrite, preserveRedirectDestinationQuery, proxyExternalRequest, sanitizeDestination } from "../config/config-matchers.js";
6
7
  import { cloneRequestWithUrl, normalizeTrailingSlash } from "./request-pipeline.js";
8
+ import { isOnDemandRevalidateRequest } from "./isr-cache.js";
7
9
  import { mergeRewriteQuery } from "../utils/query.js";
8
10
  import { normalizeDefaultLocalePathname, stripI18nLocaleForApiRoute } from "./pages-i18n.js";
9
11
  import { buildMiddlewarePrefetchSkipResponse } from "./pages-data-route.js";
@@ -56,6 +58,8 @@ async function runPagesRequest(request, deps) {
56
58
  const url = new URL(request.url);
57
59
  let pathname = url.pathname;
58
60
  const search = url.search;
61
+ const revalidateHeader = request.headers.get(PRERENDER_REVALIDATE_HEADER);
62
+ const isOnDemandRevalidate = deps.authorizeOnDemandRevalidate ? deps.authorizeOnDemandRevalidate(revalidateHeader) : isOnDemandRevalidateRequest(revalidateHeader);
59
63
  const requestConfigPathname = deps.configMatchPathname ?? pathname;
60
64
  const basePathState = {
61
65
  basePath,
@@ -98,7 +102,7 @@ async function runPagesRequest(request, deps) {
98
102
  };
99
103
  return served ? { type: "handled" } : null;
100
104
  };
101
- if (typeof deps.runMiddleware === "function") {
105
+ if (!isOnDemandRevalidate && typeof deps.runMiddleware === "function") {
102
106
  const result = await deps.runMiddleware(request, deps.ctx ?? null, { isDataRequest });
103
107
  if (result.waitUntilPromises && result.waitUntilPromises.length > 0) {
104
108
  const ctx = deps.ctx;
@@ -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,5 +1,6 @@
1
1
  import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
2
2
  import { hasBasePath, stripBasePath } from "../utils/base-path.js";
3
+ import { VINEXT_REVALIDATE_HOST_HEADER } from "../utils/protocol-headers.js";
3
4
  import { notFoundStaticAssetResponse } from "./http-error-responses.js";
4
5
  import { isOpenRedirectShaped } from "./open-redirect.js";
5
6
  import { cloneRequestWithHeaders, cloneRequestWithUrl, filterInternalHeaders } from "./request-pipeline.js";
@@ -7,6 +8,7 @@ import { assetPrefixPathname, isNextStaticPath } from "../utils/asset-prefix.js"
7
8
  import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, handleConfiguredImageOptimization, isImageOptimizationPath } from "./image-optimization.js";
8
9
  import { finalizeMissingStaticAssetResponse } from "./worker-utils.js";
9
10
  import { fetchWorkerFilesystemRoute, runPagesRequest, wrapMiddlewareWithBasePath } from "./pages-request-pipeline.js";
11
+ import { createWorkerRevalidationContext } from "./worker-revalidation-context.js";
10
12
  import { registerConfiguredCacheAdapters } from "virtual:vinext-cache-adapters";
11
13
  import { registerConfiguredImageOptimizer } from "virtual:vinext-image-adapters";
12
14
  import * as pagesEntry from "virtual:vinext-server-entry";
@@ -25,7 +27,7 @@ import * as pagesEntry from "virtual:vinext-server-entry";
25
27
  * import handler from "vinext/server/pages-router-entry";
26
28
  * return handler.fetch(request, env, ctx);
27
29
  */
28
- const { handleApiRoute, hasMiddleware, matchPageRoute, normalizeDataRequest, renderPage, runMiddleware, vinextConfig } = pagesEntry;
30
+ const { authorizeOnDemandRevalidate, handleApiRoute, hasMiddleware, matchPageRoute, normalizeDataRequest, renderPage, runMiddleware, vinextConfig } = pagesEntry;
29
31
  const basePath = vinextConfig?.basePath ?? "";
30
32
  const assetPathPrefix = assetPrefixPathname(vinextConfig?.assetPrefix ?? "");
31
33
  const trailingSlash = vinextConfig?.trailingSlash ?? false;
@@ -47,7 +49,8 @@ const imageConfig = vinextConfig?.images ? {
47
49
  var pages_router_entry_default = { async fetch(request, env, ctx) {
48
50
  return handleRequest(request, env, ctx);
49
51
  } };
50
- async function handleRequest(request, env, ctx) {
52
+ async function handleRequest(request, env, platformCtx) {
53
+ const ctx = createWorkerRevalidationContext(platformCtx, (internalRequest, internalCtx) => handleRequest(internalRequest, env, internalCtx));
51
54
  registerConfiguredCacheAdapters(env);
52
55
  registerConfiguredImageOptimizer(env);
53
56
  try {
@@ -59,7 +62,9 @@ async function handleRequest(request, env, ctx) {
59
62
  return new Response("Bad Request", { status: 400 });
60
63
  }
61
64
  const missingBuildAsset = isNextStaticPath(pathname, basePath, assetPathPrefix);
62
- 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);
63
68
  const hadBasePath = !basePath || hasBasePath(pathname, basePath);
64
69
  {
65
70
  const stripped = stripBasePath(pathname, basePath);
@@ -93,10 +98,11 @@ async function handleRequest(request, env, ctx) {
93
98
  isDataRequest: isDataReq,
94
99
  hasMiddleware,
95
100
  ctx,
101
+ authorizeOnDemandRevalidate: typeof authorizeOnDemandRevalidate === "function" ? authorizeOnDemandRevalidate : void 0,
96
102
  matchPageRoute: typeof matchPageRoute === "function" ? matchPageRoute : null,
97
103
  runMiddleware: typeof runMiddleware === "function" ? wrapMiddlewareWithBasePath(runMiddleware, basePath, hadBasePath) : null,
98
104
  renderPage: typeof renderPage === "function" ? (req, resolvedUrl, options, stagedHeaders) => renderPage(req, resolvedUrl, null, ctx, stagedHeaders, options) : null,
99
- 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,
100
106
  serveFilesystemRoute: async (requestPathname, _stagedHeaders, phase) => {
101
107
  if (!env?.ASSETS) return false;
102
108
  return fetchWorkerFilesystemRoute(request, requestPathname, phase, (assetRequest) => Promise.resolve(env.ASSETS.fetch(assetRequest)));
@@ -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";
@@ -103,7 +104,7 @@ declare function tryServeStatic(req: IncomingMessage, res: ServerResponse, clien
103
104
  * When `urlOverride` is provided, it is used as the path + query string
104
105
  * instead of `req.url`.
105
106
  */
106
- 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;
107
108
  /**
108
109
  * Stream a Web Response back to a Node.js ServerResponse.
109
110
  * Supports streaming compression for SSR responses.