vinext 1.0.0-beta.2 → 1.0.0-beta.4

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 (174) hide show
  1. package/README.md +6 -9
  2. package/dist/build/client-build-config.js +7 -1
  3. package/dist/build/precompress.js +1 -1
  4. package/dist/build/prerender.d.ts +2 -1
  5. package/dist/build/prerender.js +12 -2
  6. package/dist/check.js +4 -0
  7. package/dist/cli-args.d.ts +2 -1
  8. package/dist/cli-args.js +10 -0
  9. package/dist/cli.js +14 -8
  10. package/dist/client/navigation-runtime.d.ts +6 -0
  11. package/dist/client/navigation-runtime.js +1 -1
  12. package/dist/config/config-matchers.js +4 -1
  13. package/dist/config/dotenv.d.ts +3 -4
  14. package/dist/config/dotenv.js +30 -22
  15. package/dist/config/next-config.d.ts +16 -5
  16. package/dist/config/next-config.js +7 -0
  17. package/dist/entries/app-browser-entry.d.ts +4 -10
  18. package/dist/entries/app-browser-entry.js +20 -4
  19. package/dist/entries/app-rsc-entry.js +21 -3
  20. package/dist/entries/app-rsc-manifest.js +19 -0
  21. package/dist/entries/pages-client-entry.js +0 -1
  22. package/dist/entries/pages-server-entry.js +13 -3
  23. package/dist/index.js +122 -49
  24. package/dist/init-cloudflare.d.ts +3 -1
  25. package/dist/init-cloudflare.js +59 -5
  26. package/dist/plugins/client-reference-dedup.js +7 -5
  27. package/dist/plugins/extensionless-dynamic-import.js +23 -18
  28. package/dist/plugins/ignore-dynamic-requests.js +3 -1
  29. package/dist/plugins/import-meta-url.js +17 -6
  30. package/dist/plugins/require-context.js +20 -15
  31. package/dist/plugins/sass.js +3 -4
  32. package/dist/plugins/transform-cache.d.ts +19 -0
  33. package/dist/plugins/transform-cache.js +36 -0
  34. package/dist/routing/app-route-graph.d.ts +14 -6
  35. package/dist/routing/app-route-graph.js +106 -16
  36. package/dist/server/api-handler.d.ts +2 -0
  37. package/dist/server/api-handler.js +7 -6
  38. package/dist/server/app-bfcache-identity.js +28 -2
  39. package/dist/server/app-browser-entry.js +175 -75
  40. package/dist/server/app-browser-navigation-controller.d.ts +23 -21
  41. package/dist/server/app-browser-navigation-controller.js +30 -5
  42. package/dist/server/app-browser-prefetch-response.d.ts +9 -0
  43. package/dist/server/app-browser-prefetch-response.js +32 -0
  44. package/dist/server/app-browser-state.d.ts +8 -3
  45. package/dist/server/app-browser-state.js +9 -3
  46. package/dist/server/app-page-cache-finalizer.js +1 -1
  47. package/dist/server/app-page-cache.d.ts +1 -1
  48. package/dist/server/app-page-cache.js +16 -2
  49. package/dist/server/app-page-dispatch.d.ts +9 -1
  50. package/dist/server/app-page-dispatch.js +37 -8
  51. package/dist/server/app-page-element-builder.d.ts +4 -1
  52. package/dist/server/app-page-element-builder.js +34 -7
  53. package/dist/server/app-page-head.d.ts +3 -3
  54. package/dist/server/app-page-head.js +58 -13
  55. package/dist/server/app-page-http-access-fallback-metadata.d.ts +2 -2
  56. package/dist/server/app-page-http-access-fallback-metadata.js +9 -1
  57. package/dist/server/app-page-probe.d.ts +4 -0
  58. package/dist/server/app-page-probe.js +6 -2
  59. package/dist/server/app-page-render.d.ts +1 -1
  60. package/dist/server/app-page-render.js +13 -3
  61. package/dist/server/app-page-request.d.ts +3 -1
  62. package/dist/server/app-page-request.js +4 -13
  63. package/dist/server/app-page-response.d.ts +2 -0
  64. package/dist/server/app-page-response.js +7 -2
  65. package/dist/server/app-page-route-wiring.d.ts +10 -2
  66. package/dist/server/app-page-route-wiring.js +269 -59
  67. package/dist/server/app-pages-bridge.d.ts +2 -1
  68. package/dist/server/app-pages-bridge.js +3 -1
  69. package/dist/server/app-route-handler-cache.js +4 -0
  70. package/dist/server/app-route-handler-dispatch.js +9 -3
  71. package/dist/server/app-route-handler-response.js +1 -1
  72. package/dist/server/app-route-module-loader.d.ts +6 -0
  73. package/dist/server/app-route-module-loader.js +3 -0
  74. package/dist/server/app-router-entry.js +6 -3
  75. package/dist/server/app-rsc-cache-busting.d.ts +7 -0
  76. package/dist/server/app-rsc-cache-busting.js +6 -0
  77. package/dist/server/app-rsc-errors.js +10 -0
  78. package/dist/server/app-rsc-handler.d.ts +2 -0
  79. package/dist/server/app-rsc-handler.js +30 -13
  80. package/dist/server/app-rsc-render-mode.d.ts +3 -2
  81. package/dist/server/app-rsc-render-mode.js +4 -1
  82. package/dist/server/app-rsc-request-normalization.js +65 -4
  83. package/dist/server/app-rsc-route-matching.d.ts +9 -0
  84. package/dist/server/app-rsc-route-matching.js +6 -0
  85. package/dist/server/app-server-action-execution.d.ts +1 -1
  86. package/dist/server/app-ssr-entry.js +4 -4
  87. package/dist/server/app-visited-response-cache.d.ts +6 -1
  88. package/dist/server/app-visited-response-cache.js +49 -1
  89. package/dist/server/cache-control.d.ts +2 -2
  90. package/dist/server/cache-control.js +2 -1
  91. package/dist/server/dev-server.d.ts +3 -1
  92. package/dist/server/dev-server.js +204 -348
  93. package/dist/server/edge-api-runtime.d.ts +10 -1
  94. package/dist/server/edge-api-runtime.js +29 -1
  95. package/dist/server/headers.d.ts +4 -2
  96. package/dist/server/headers.js +6 -3
  97. package/dist/server/isr-cache.d.ts +10 -33
  98. package/dist/server/isr-cache.js +33 -43
  99. package/dist/server/isr-decision.d.ts +2 -2
  100. package/dist/server/pages-api-route.d.ts +4 -0
  101. package/dist/server/pages-api-route.js +6 -3
  102. package/dist/server/pages-asset-tags.d.ts +1 -0
  103. package/dist/server/pages-asset-tags.js +6 -4
  104. package/dist/server/pages-dev-hydration.js +0 -1
  105. package/dist/server/pages-document-asset-props.d.ts +26 -0
  106. package/dist/server/pages-document-asset-props.js +69 -0
  107. package/dist/server/pages-get-initial-props.d.ts +1 -1
  108. package/dist/server/pages-get-initial-props.js +1 -4
  109. package/dist/server/pages-i18n.js +64 -16
  110. package/dist/server/pages-node-compat.d.ts +2 -0
  111. package/dist/server/pages-node-compat.js +10 -6
  112. package/dist/server/pages-page-data.d.ts +61 -9
  113. package/dist/server/pages-page-data.js +334 -74
  114. package/dist/server/pages-page-handler.d.ts +6 -1
  115. package/dist/server/pages-page-handler.js +119 -20
  116. package/dist/server/pages-page-response.d.ts +7 -3
  117. package/dist/server/pages-page-response.js +41 -33
  118. package/dist/server/pages-request-pipeline.d.ts +7 -0
  119. package/dist/server/pages-request-pipeline.js +5 -1
  120. package/dist/server/pages-revalidate.d.ts +1 -1
  121. package/dist/server/pages-revalidate.js +54 -8
  122. package/dist/server/pages-router-entry.d.ts +1 -0
  123. package/dist/server/pages-router-entry.js +10 -4
  124. package/dist/server/prerender-manifest.d.ts +1 -0
  125. package/dist/server/prod-server.d.ts +2 -1
  126. package/dist/server/prod-server.js +38 -13
  127. package/dist/server/revalidation-host.d.ts +11 -0
  128. package/dist/server/revalidation-host.js +19 -0
  129. package/dist/server/seed-cache.js +1 -1
  130. package/dist/server/worker-revalidation-context.d.ts +13 -0
  131. package/dist/server/worker-revalidation-context.js +30 -0
  132. package/dist/shims/app-router-scroll-state.d.ts +2 -1
  133. package/dist/shims/app-router-scroll-state.js +7 -2
  134. package/dist/shims/cache-handler.d.ts +1 -1
  135. package/dist/shims/cache-handler.js +10 -7
  136. package/dist/shims/cache-runtime.d.ts +6 -0
  137. package/dist/shims/cache-runtime.js +4 -1
  138. package/dist/shims/cache.js +1 -1
  139. package/dist/shims/document.js +15 -2
  140. package/dist/shims/error.js +1 -1
  141. package/dist/shims/fetch-cache.js +1 -1
  142. package/dist/shims/font-google-base.js +17 -8
  143. package/dist/shims/font-local.js +41 -10
  144. package/dist/shims/hash-scroll.d.ts +1 -1
  145. package/dist/shims/hash-scroll.js +2 -1
  146. package/dist/shims/headers.d.ts +10 -13
  147. package/dist/shims/headers.js +85 -36
  148. package/dist/shims/internal/cookie-serialize.d.ts +10 -9
  149. package/dist/shims/internal/cookie-serialize.js +9 -8
  150. package/dist/shims/internal/interpolate-as.d.ts +15 -1
  151. package/dist/shims/internal/interpolate-as.js +27 -2
  152. package/dist/shims/link.d.ts +2 -0
  153. package/dist/shims/link.js +77 -24
  154. package/dist/shims/metadata.d.ts +18 -34
  155. package/dist/shims/metadata.js +98 -56
  156. package/dist/shims/navigation.d.ts +18 -2
  157. package/dist/shims/navigation.js +70 -21
  158. package/dist/shims/request-context.d.ts +6 -2
  159. package/dist/shims/router.js +18 -7
  160. package/dist/shims/script.js +1 -1
  161. package/dist/shims/server.d.ts +7 -6
  162. package/dist/shims/server.js +156 -57
  163. package/dist/shims/slot.d.ts +2 -1
  164. package/dist/shims/slot.js +6 -2
  165. package/dist/shims/streamed-icons.d.ts +9 -0
  166. package/dist/shims/streamed-icons.js +33 -0
  167. package/dist/shims/unified-request-context.d.ts +21 -2
  168. package/dist/shims/unified-request-context.js +82 -1
  169. package/dist/utils/cache-control-metadata.d.ts +2 -1
  170. package/dist/utils/cache-control-metadata.js +5 -1
  171. package/dist/utils/html-limited-bots.js +1 -1
  172. package/dist/utils/protocol-headers.d.ts +7 -1
  173. package/dist/utils/protocol-headers.js +7 -1
  174. package/package.json +1 -1
@@ -1,25 +1,21 @@
1
- import { createRequestContext, runWithRequestContext } from "../shims/unified-request-context.js";
2
- import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
3
- import { createValidFileMatcher, findFileWithExtensions, findFileWithExts } from "../routing/file-matcher.js";
1
+ import { closeAfterResponse, createRequestContext, runWithRequestContext } from "../shims/unified-request-context.js";
2
+ import { createValidFileMatcher, findFileWithExts } from "../routing/file-matcher.js";
4
3
  import { patternToNextFormat } from "../routing/route-validation.js";
5
4
  import { matchRoute } from "../routing/pages-router.js";
6
- import { NEXTJS_DEPLOYMENT_ID_HEADER } from "./headers.js";
5
+ import { PRERENDER_REVALIDATE_HEADER } from "../utils/protocol-headers.js";
6
+ import { NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER } from "./headers.js";
7
7
  import { isDangerousScheme } from "../shims/url-safety.js";
8
+ import { sanitizeDestination } from "../config/config-matchers.js";
8
9
  import { importModule, reportRequestError } from "./instrumentation.js";
9
- import { buildCacheStateHeaders } from "./cache-headers.js";
10
- import { isUnknownRecord } from "../utils/record.js";
11
- import { NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL } from "./cache-control.js";
12
- import { buildMissIsrCacheControl, decideIsr } from "./isr-decision.js";
13
- import { PRERENDER_REVALIDATE_HEADER, buildPagesCacheValue, getRevalidateDuration, isOnDemandRevalidateRequest, isrCacheKey, isrGet, isrSet, setRevalidateDuration, triggerBackgroundRegeneration } from "./isr-cache.js";
14
- import { _runWithCacheState } from "../shims/cache-request-state.js";
15
- import { ensureFetchPatch, runWithFetchCache } from "../shims/fetch-cache.js";
16
- import { runWithPrivateCache } from "../shims/cache-runtime.js";
10
+ import { NEVER_CACHE_CONTROL } from "./cache-control.js";
11
+ import { buildMissIsrCacheControl } from "./isr-decision.js";
12
+ import { isOnDemandRevalidateRequest } from "./isr-cache.js";
13
+ import { ensureFetchPatch } from "../shims/fetch-cache.js";
17
14
  import { mergeRouteParamsIntoQuery, parseQueryString } from "../utils/query.js";
18
15
  import "../shims/router-state.js";
19
- import { runWithHeadState } from "../shims/head-state.js";
20
- import { runWithServerInsertedHTMLState } from "../shims/navigation-state.js";
21
16
  import { withScriptNonce } from "../shims/script-nonce-context.js";
22
17
  import { createInlineScriptTag, createNonceAttribute, escapeHtmlAttr, safeJsonStringify } from "./html.js";
18
+ import { applyDocumentAssetProps, extractDocumentAssetProps, markDocumentAssetPropsProtectedTags, stripDocumentAssetPropsProtectionMarkers } from "./pages-document-asset-props.js";
23
19
  import { getClientTraceMetadataHTML } from "./client-trace-metadata.js";
24
20
  import { getScriptNonceFromNodeHeaderSources } from "./csp.js";
25
21
  import { logRequest, now } from "./request-log.js";
@@ -29,15 +25,16 @@ import { buildPagesReadinessNextData } from "./pages-readiness.js";
29
25
  import { resolvePagesPageMethodResponse } from "./pages-page-method.js";
30
26
  import { loadUserDocumentInitialProps, runDocumentRenderPage } from "./pages-document-initial-props.js";
31
27
  import { callDocumentGetInitialProps } from "./document-initial-head.js";
28
+ import { isBotUserAgent } from "../utils/html-limited-bots.js";
32
29
  import { hasPagesGetInitialProps, loadDevAppInitialProps, loadPagesGetInitialProps } from "./pages-get-initial-props.js";
33
30
  import { isSerializableProps } from "./pages-serializable-props.js";
34
- import { isBotUserAgent } from "../utils/html-limited-bots.js";
35
- import { getPagesRouteParams, matchesPagesStaticPath } from "./pages-page-data.js";
31
+ import { assertPages404DoesNotReturnNotFound, buildPagesRedirectProps, getPagesRouteParams, matchesPagesStaticPath, mergePagesDataProps, resolvePagesRedirect, resolvePagesRedirectLocation, resolvePagesRevalidateSeconds } from "./pages-page-data.js";
36
32
  import { createPagesDevAssetUrl, createPagesDevModuleUrl } from "./pages-dev-module-url.js";
37
33
  import { createPagesDevHydrationScript } from "./pages-dev-hydration.js";
38
34
  import { getManifestFilesForModule } from "./pages-asset-tags.js";
39
35
  import { PAGES_PREVIEW_CACHE_CONTROL, appendPagesPreviewClearCookies, getPagesPreviewState } from "./pages-preview.js";
40
36
  import { attachPagesRequestCookies } from "./pages-node-compat.js";
37
+ import { randomUUID } from "node:crypto";
41
38
  import React from "react";
42
39
  import { renderToReadableStream } from "react-dom/server.edge";
43
40
  //#region src/server/dev-server.ts
@@ -78,8 +75,15 @@ function applyDevPagesPreviewResponse(res, preview) {
78
75
  applyDevPagesPreviewHeaders(headers, preview);
79
76
  for (const [name, value] of Object.entries(headers)) res.setHeader(name, value);
80
77
  }
81
- async function renderIsrPassToStringAsync(element) {
82
- return await runWithServerInsertedHTMLState(() => runWithHeadState(() => _runWithCacheState(() => runWithPrivateCache(() => runWithFetchCache(async () => renderToStringAsync(element))))));
78
+ const DEV_PAGES_CACHE_CONTROL = "no-cache, must-revalidate";
79
+ const PAGES_CACHE_ONE_YEAR_SECONDS = 31536e3;
80
+ function applyDevPagesCacheHeaders(res, state, cacheControl = DEV_PAGES_CACHE_CONTROL) {
81
+ res.setHeader(NEXTJS_CACHE_HEADER, state);
82
+ res.setHeader("Cache-Control", cacheControl);
83
+ }
84
+ function buildDevPagesTerminalCacheControl(revalidateSeconds, expireSeconds) {
85
+ if (revalidateSeconds === false) return `s-maxage=${PAGES_CACHE_ONE_YEAR_SECONDS}`;
86
+ return buildMissIsrCacheControl(revalidateSeconds, expireSeconds);
83
87
  }
84
88
  const DEV_STYLESHEET_ASSET_RE = /\.(?:css|scss|sass)$/i;
85
89
  const transformedStylesheetAssetsCache = /* @__PURE__ */ new WeakMap();
@@ -172,11 +176,17 @@ async function collectDevInitialStylesheetHeadHTML(server, runner, moduleIds, no
172
176
  * (`buildPagesRedirectResponse`) and Next.js's `render.tsx` `__N_REDIRECT`
173
177
  * handling. See AGENTS.md "dev and prod server parity".
174
178
  */
175
- function writeGsspRedirect(res, redirect, isDataReq, props) {
176
- const status = redirect.statusCode ?? (redirect.permanent ? 308 : 307);
177
- let dest = redirect.destination;
178
- if (!dest.startsWith("http://") && !dest.startsWith("https://")) dest = dest.replace(/^[\\/]+/, "/");
179
- if (isDangerousScheme(dest)) {
179
+ function writeGsspRedirect(res, redirect, isDataReq, props, options) {
180
+ const resolved = resolvePagesRedirect(redirect, {
181
+ method: options.method,
182
+ routeUrl: options.routeUrl,
183
+ sanitizeDestination
184
+ });
185
+ const redirectProps = buildPagesRedirectProps(resolved, {
186
+ ...props,
187
+ pageProps: props.pageProps
188
+ });
189
+ if (isDangerousScheme(resolved.destination)) {
180
190
  const deploymentId = process.env.__VINEXT_DEPLOYMENT_ID || process.env.NEXT_DEPLOYMENT_ID;
181
191
  const headers = {
182
192
  "Cache-Control": "private, no-cache, no-store, max-age=0, must-revalidate",
@@ -192,18 +202,15 @@ function writeGsspRedirect(res, redirect, isDataReq, props) {
192
202
  const dataHeaders = { "Content-Type": "application/json" };
193
203
  if (deploymentId) dataHeaders[NEXTJS_DEPLOYMENT_ID_HEADER] = deploymentId;
194
204
  res.writeHead(200, dataHeaders);
195
- res.end(JSON.stringify({
196
- ...props,
197
- pageProps: {
198
- ...isUnknownRecord(props.pageProps) ? props.pageProps : {},
199
- __N_REDIRECT: dest,
200
- __N_REDIRECT_STATUS: status
201
- }
202
- }));
205
+ res.end(JSON.stringify({ ...redirectProps }));
203
206
  return;
204
207
  }
205
- res.writeHead(status, { Location: dest });
206
- res.end();
208
+ const location = resolvePagesRedirectLocation(resolved, options.basePath);
209
+ res.writeHead(resolved.statusCode, {
210
+ Location: location,
211
+ ...resolved.statusCode === 308 ? { Refresh: `0;url=${location}` } : {}
212
+ });
213
+ res.end(location);
207
214
  }
208
215
  /** Body placeholder used to split the document shell for streaming. */
209
216
  const STREAM_BODY_MARKER = "<!--VINEXT_STREAM_BODY-->";
@@ -221,7 +228,7 @@ const STREAM_BODY_MARKER = "<!--VINEXT_STREAM_BODY-->";
221
228
  * shell sooner).
222
229
  */
223
230
  async function streamPageToResponse(res, element, options) {
224
- const { url, server, fontHeadHTML, assetHeadHTML = "", scripts, DocumentComponent, statusCode, extraHeaders, getHeadHTML, enhancePageElement, scriptNonce, documentContext, setDocumentInitialHead, bufferBodyBeforeHeaders = false } = options;
231
+ const { url, server, fontHeadHTML, assetHeadHTML = "", scripts, DocumentComponent, statusCode, extraHeaders, getHeadHTML, enhancePageElement, scriptNonce, documentContext, setDocumentInitialHead, crossOrigin, bufferBodyBeforeHeaders = false } = options;
225
232
  const documentRenderPage = await runDocumentRenderPage({
226
233
  DocumentComponent,
227
234
  enhancePageElement,
@@ -244,26 +251,46 @@ async function streamPageToResponse(res, element, options) {
244
251
  let headHTML = getHeadHTML();
245
252
  if (documentRenderPage.status === "rendered" && documentRenderPage.stylesHTML) headHTML += `\n ${documentRenderPage.stylesHTML}`;
246
253
  let shellTemplate;
254
+ let documentAssetProps = {};
255
+ const protectedAssetMarker = `data-vinext-document-asset-props-protected-${randomUUID()}`;
256
+ const protectAssetTags = (html) => markDocumentAssetPropsProtectedTags(html, protectedAssetMarker);
257
+ const applyGeneratedAssetProps = (html, props, scriptOwner) => protectAssetTags(applyDocumentAssetProps(html, props, {
258
+ configuredCrossOrigin: crossOrigin,
259
+ scriptOwner
260
+ }));
247
261
  if (DocumentComponent) {
248
262
  const docProps = documentRenderPage.status === "skipped" ? await loadUserDocumentInitialProps(DocumentComponent) : documentRenderPage.docProps;
249
- let docHtml = await renderToStringAsync(docProps ? React.createElement(DocumentComponent, docProps) : React.createElement(DocumentComponent));
263
+ const renderedDocument = extractDocumentAssetProps(await renderToStringAsync(docProps ? React.createElement(DocumentComponent, docProps) : React.createElement(DocumentComponent)));
264
+ documentAssetProps = renderedDocument.props;
265
+ let docHtml = protectAssetTags(renderedDocument.html);
266
+ const generatedScripts = applyGeneratedAssetProps(scripts, renderedDocument.props, "next-script");
267
+ const generatedAssetHeadHTML = applyGeneratedAssetProps(assetHeadHTML, renderedDocument.props, "next-script");
250
268
  docHtml = docHtml.replace("__NEXT_MAIN__", STREAM_BODY_MARKER);
251
- if (headHTML || fontHeadHTML || assetHeadHTML) docHtml = docHtml.replace("</head>", ` ${fontHeadHTML}${headHTML}\n ${assetHeadHTML}\n</head>`);
252
- docHtml = docHtml.replace("<!-- __NEXT_SCRIPTS__ -->", scripts);
253
- if (!docHtml.includes("__NEXT_DATA__")) docHtml = docHtml.replace("</body>", ` ${scripts}\n</body>`);
269
+ if (headHTML || fontHeadHTML || generatedAssetHeadHTML) docHtml = docHtml.replace("</head>", ` ${protectAssetTags(fontHeadHTML)}${protectAssetTags(headHTML)}\n ${generatedAssetHeadHTML}\n</head>`);
270
+ docHtml = docHtml.replace("<!-- __NEXT_SCRIPTS__ -->", generatedScripts);
271
+ if (!docHtml.includes("__NEXT_DATA__")) docHtml = docHtml.replace("</body>", ` ${generatedScripts}\n</body>`);
254
272
  shellTemplate = docHtml;
255
- } else shellTemplate = `<!DOCTYPE html>
273
+ } else {
274
+ const generatedScripts = applyGeneratedAssetProps(scripts, {}, "next-script");
275
+ const generatedAssetHeadHTML = applyGeneratedAssetProps(assetHeadHTML, {}, "next-script");
276
+ shellTemplate = `<!DOCTYPE html>
256
277
  <html>
257
278
  <head>
258
- ${fontHeadHTML}${headHTML}
259
- ${assetHeadHTML}
279
+ ${protectAssetTags(fontHeadHTML)}${protectAssetTags(headHTML)}
280
+ ${generatedAssetHeadHTML}
260
281
  </head>
261
282
  <body>
262
283
  <div id="__next">${STREAM_BODY_MARKER}</div>
263
- ${scripts}
284
+ ${generatedScripts}
264
285
  </body>
265
286
  </html>`;
266
- const transformedShell = await server.transformIndexHtml(url, shellTemplate);
287
+ }
288
+ let transformedShell = await server.transformIndexHtml(url, shellTemplate);
289
+ transformedShell = stripDocumentAssetPropsProtectionMarkers(applyDocumentAssetProps(transformedShell, documentAssetProps, {
290
+ configuredCrossOrigin: crossOrigin,
291
+ protectedAssetMarker,
292
+ scriptOwner: "next-script"
293
+ }), protectedAssetMarker);
267
294
  const markerIdx = transformedShell.indexOf(STREAM_BODY_MARKER);
268
295
  const prefix = transformedShell.slice(0, markerIdx);
269
296
  const suffix = transformedShell.slice(markerIdx + 25);
@@ -324,7 +351,7 @@ function parseCookieLocale(req, i18nConfig) {
324
351
  * 4. Render the component to HTML
325
352
  * 5. Wrap in _document shell and send response
326
353
  */
327
- function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatcher, basePath = "", trailingSlash = false, hasMiddleware = false, hasRewrites = false, clientTraceMetadata, htmlLimitedBots, reactStrictMode = false) {
354
+ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatcher, basePath = "", trailingSlash = false, hasMiddleware = false, hasRewrites = false, clientTraceMetadata, htmlLimitedBots, reactStrictMode = false, expireTime = PAGES_CACHE_ONE_YEAR_SECONDS, crossOrigin) {
328
355
  const matcher = fileMatcher ?? createValidFileMatcher();
329
356
  const pagePatterns = routes.map((r) => patternToNextFormat(r.pattern));
330
357
  const _alsRegistration = Promise.all([runner.import("vinext/head-state"), runner.import("vinext/router-state")]);
@@ -350,22 +377,25 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
350
377
  let locale;
351
378
  let localeStrippedUrl = url;
352
379
  let currentDefaultLocale;
353
- let currentDomainLocaleDomain;
354
380
  const domainLocales = i18nConfig?.domains;
355
381
  if (i18nConfig) {
356
382
  const resolved = resolvePagesI18nRequest(url, i18nConfig, req.headers, req.headers.host, basePath, trailingSlash);
357
383
  locale = resolved.locale;
358
384
  localeStrippedUrl = resolved.url;
359
385
  currentDefaultLocale = resolved.domainLocale?.defaultLocale ?? i18nConfig.defaultLocale;
360
- currentDomainLocaleDomain = resolved.domainLocale?.domain;
361
386
  if (resolved.redirectUrl) {
362
387
  res.writeHead(307, { Location: resolved.redirectUrl });
363
388
  res.end();
364
389
  return;
365
390
  }
366
391
  }
367
- const i18nCacheVariant = i18nConfig ? currentDomainLocaleDomain ? "domain:" + currentDomainLocaleDomain.toLowerCase() : "locale:" + String(locale) : null;
368
- const pagesIsrCacheKey = i18nCacheVariant ? (pathname) => isrCacheKey("pages", pathname + "::i18n=" + encodeURIComponent(i18nCacheVariant), process.env.__VINEXT_BUILD_ID) : (pathname) => isrCacheKey("pages", pathname, process.env.__VINEXT_BUILD_ID);
392
+ const errorPageContext = {
393
+ basePath,
394
+ locale: locale ?? currentDefaultLocale,
395
+ locales: i18nConfig?.locales,
396
+ defaultLocale: currentDefaultLocale,
397
+ crossOrigin
398
+ };
369
399
  const match = matchRoute(localeStrippedUrl, routes);
370
400
  if (!match) {
371
401
  if (isDataReq) {
@@ -377,9 +407,13 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
377
407
  res.end("{}");
378
408
  return;
379
409
  }
380
- await runWithRequestContext(createRequestContext(), async () => {
410
+ const requestContext = createRequestContext();
411
+ const closeRequest = () => void closeAfterResponse(requestContext);
412
+ res.on("finish", closeRequest);
413
+ res.on("close", closeRequest);
414
+ await runWithRequestContext(requestContext, async () => {
381
415
  await _alsRegistration;
382
- await renderErrorPage(server, runner, req, res, url, pagesDir, 404, void 0, matcher, void 0, reactStrictMode);
416
+ await renderErrorPage(server, runner, req, res, url, pagesDir, 404, void 0, matcher, void 0, reactStrictMode, errorPageContext);
383
417
  });
384
418
  return;
385
419
  }
@@ -391,7 +425,11 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
391
425
  let requestAsPath = isDataReq ? gsspResolvedUrl : i18nConfig ? extractLocaleFromUrl$1(originalUrl, i18nConfig, locale).url : originalUrl;
392
426
  const userFacingParams = route.isDynamic ? params : null;
393
427
  let query = mergeRouteParamsIntoQuery(parseQueryString(url), params);
394
- return runWithRequestContext(createRequestContext(), async () => {
428
+ const requestContext = createRequestContext();
429
+ const closeRequest = () => void closeAfterResponse(requestContext);
430
+ res.on("finish", closeRequest);
431
+ res.on("close", closeRequest);
432
+ return runWithRequestContext(requestContext, async () => {
395
433
  ensureFetchPatch();
396
434
  try {
397
435
  await _alsRegistration;
@@ -419,10 +457,8 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
419
457
  };
420
458
  const requestPreviewData = requestPreview.data;
421
459
  let AppComponent = null;
422
- const appPath = path.join(pagesDir, "_app");
423
- if (findFileWithExtensions(appPath, matcher)) try {
424
- AppComponent = (await importModule(runner, appPath)).default ?? null;
425
- } catch {}
460
+ const appFilePath = findFileWithExts(pagesDir, "_app", matcher);
461
+ if (appFilePath) AppComponent = (await importModule(runner, appFilePath)).default ?? null;
426
462
  const pagesNextData = {
427
463
  ...buildPagesReadinessNextData({
428
464
  pageModule,
@@ -472,10 +508,9 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
472
508
  let pageProps = {};
473
509
  let renderProps = { pageProps };
474
510
  if (requestPreviewData !== false) renderProps.__N_PREVIEW = true;
475
- let isrRevalidateSeconds = null;
511
+ let isOnDemandRevalidate = false;
512
+ let staticPropsRevalidateSeconds = false;
476
513
  let isFallbackRender = false;
477
- let shouldPersistFallbackData = false;
478
- let staticPropsPreviewData = requestPreviewData;
479
514
  if (typeof pageModule.getStaticPaths === "function" && route.isDynamic) {
480
515
  const pathsResult = await pageModule.getStaticPaths({
481
516
  locales: i18nConfig?.locales ?? [],
@@ -491,17 +526,17 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
491
526
  const notFoundHeaders = { "Content-Type": "application/json" };
492
527
  if (deploymentId) notFoundHeaders[NEXTJS_DEPLOYMENT_ID_HEADER] = deploymentId;
493
528
  res.writeHead(404, notFoundHeaders);
494
- res.end("{}");
529
+ res.end("{\"notFound\":true}");
495
530
  return;
496
531
  }
497
- await renderErrorPage(server, runner, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext, matcher, void 0, reactStrictMode);
532
+ await renderErrorPage(server, runner, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext, matcher, void 0, reactStrictMode, errorPageContext);
498
533
  return;
499
534
  }
500
535
  const userAgentHeader = req.headers["user-agent"];
501
536
  const userAgent = Array.isArray(userAgentHeader) ? userAgentHeader[0] : userAgentHeader;
502
537
  const isBotRequest = !!userAgent && isBotUserAgent(userAgent, htmlLimitedBots);
503
538
  if (fallback === true && !isValidPath && !isDataReq && !isBotRequest && requestPreviewData === false) {
504
- isFallbackRender = (await isrGet(pagesIsrCacheKey(url.split("?")[0])))?.value.value?.kind !== "PAGES";
539
+ isFallbackRender = true;
505
540
  if (isFallbackRender && typeof routerShim.setSSRContext === "function") routerShim.setSSRContext({
506
541
  pathname: patternToNextFormat(route.pattern),
507
542
  query: {},
@@ -514,7 +549,6 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
514
549
  isFallback: true
515
550
  });
516
551
  }
517
- shouldPersistFallbackData = fallback === true && !isValidPath && isDataReq;
518
552
  }
519
553
  const gsspExtraHeaders = {};
520
554
  const hasAppGetInitialProps = hasPagesGetInitialProps(AppComponent);
@@ -526,7 +560,6 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
526
560
  const appTree = React.createElement(AppComponent, {
527
561
  ...appTreeProps,
528
562
  Component: PageComponent,
529
- pageProps: appTreeProps.pageProps,
530
563
  router: routerShim.default
531
564
  });
532
565
  return typeof routerShim.wrapWithRouterContext === "function" ? routerShim.wrapWithRouterContext(appTree) : appTree;
@@ -577,17 +610,18 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
577
610
  const result = await pageModule.getServerSideProps(context);
578
611
  if (res.writableEnded) return;
579
612
  if (result && "props" in result) {
580
- pageProps = {
581
- ...pageProps,
582
- ...await Promise.resolve(result.props)
583
- };
613
+ pageProps = mergePagesDataProps(renderProps.pageProps, await Promise.resolve(result.props));
584
614
  renderProps = {
585
615
  ...renderProps,
586
616
  pageProps
587
617
  };
588
618
  }
589
619
  if (result && "redirect" in result) {
590
- writeGsspRedirect(res, result.redirect, isDataReq, renderProps);
620
+ writeGsspRedirect(res, result.redirect, isDataReq, renderProps, {
621
+ basePath,
622
+ method: "getServerSideProps",
623
+ routeUrl: url
624
+ });
591
625
  return;
592
626
  }
593
627
  if (result && "notFound" in result && result.notFound) {
@@ -597,10 +631,10 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
597
631
  const notFoundHeaders = { "Content-Type": "application/json" };
598
632
  if (deploymentId) notFoundHeaders[NEXTJS_DEPLOYMENT_ID_HEADER] = deploymentId;
599
633
  res.writeHead(404, notFoundHeaders);
600
- res.end("{}");
634
+ res.end("{\"notFound\":true}");
601
635
  return;
602
636
  }
603
- await renderErrorPage(server, runner, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext, void 0, void 0, reactStrictMode);
637
+ await renderErrorPage(server, runner, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext, void 0, void 0, reactStrictMode, errorPageContext);
604
638
  return;
605
639
  }
606
640
  if (result && "props" in result) isSerializableProps(patternToNextFormat(route.pattern), "getServerSideProps", pageProps);
@@ -616,216 +650,19 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
616
650
  }
617
651
  const responseHeaders = typeof res.getHeaders === "function" ? res.getHeaders() : void 0;
618
652
  const scriptNonce = getScriptNonceFromNodeHeaderSources(req.headers, responseHeaders);
619
- let earlyFontLinkHeader = "";
620
- try {
621
- const earlyPreloads = [];
622
- const fontGoogleEarly = await importModule(runner, "next/font/google");
623
- if (typeof fontGoogleEarly.getSSRFontPreloads === "function") earlyPreloads.push(...fontGoogleEarly.getSSRFontPreloads());
624
- const fontLocalEarly = await importModule(runner, "next/font/local");
625
- if (typeof fontLocalEarly.getSSRFontPreloads === "function") earlyPreloads.push(...fontLocalEarly.getSSRFontPreloads());
626
- if (earlyPreloads.length > 0) earlyFontLinkHeader = earlyPreloads.map((p) => `<${p.href}>; rel=preload; as=font; type=${p.type}; crossorigin`).join(", ");
627
- } catch {}
628
653
  if (typeof pageModule.getStaticProps === "function" && !isFallbackRender) {
629
- const cacheKey = pagesIsrCacheKey(url.split("?")[0]);
630
- const cached = await isrGet(cacheKey);
631
- const isOnDemandRevalidate = isOnDemandRevalidateRequest(req.headers[PRERENDER_REVALIDATE_HEADER]);
654
+ isOnDemandRevalidate = isOnDemandRevalidateRequest(req.headers[PRERENDER_REVALIDATE_HEADER]);
655
+ if (isOnDemandRevalidate && req.headers["x-prerender-revalidate-if-generated"] !== void 0) {
656
+ res.writeHead(404, { [NEXTJS_CACHE_HEADER]: "REVALIDATED" });
657
+ res.end("This page could not be found");
658
+ return;
659
+ }
632
660
  const previewData = requestPreviewData;
633
- staticPropsPreviewData = previewData;
634
661
  const previewContext = previewData === false ? {} : {
635
662
  draftMode: true,
636
663
  preview: true,
637
664
  previewData
638
665
  };
639
- if (!isOnDemandRevalidate && cached && !cached.isStale && cached.value.value?.kind === "PAGES" && !cached.value.value.generatedFromDataRequest && !scriptNonce && !isDataReq && previewData === false) {
640
- const cachedHtml = cached.value.value.html;
641
- const transformedHtml = await server.transformIndexHtml(url, cachedHtml);
642
- const { cacheControl: hitCacheControl } = decideIsr({
643
- cacheState: "HIT",
644
- kind: "dev",
645
- revalidateSeconds: getRevalidateDuration(cacheKey) ?? 60
646
- });
647
- const hitHeaders = {
648
- "Content-Type": "text/html; charset=utf-8",
649
- ...buildCacheStateHeaders("HIT"),
650
- "Cache-Control": hitCacheControl
651
- };
652
- if (earlyFontLinkHeader) hitHeaders["Link"] = earlyFontLinkHeader;
653
- res.writeHead(200, hitHeaders);
654
- res.end(transformedHtml);
655
- return;
656
- }
657
- if (!isOnDemandRevalidate && cached && cached.isStale && cached.value.value?.kind === "PAGES" && !cached.value.value.generatedFromDataRequest && !scriptNonce && !isDataReq && previewData === false) {
658
- const cachedHtml = cached.value.value.html;
659
- const transformedHtml = await server.transformIndexHtml(url, cachedHtml);
660
- triggerBackgroundRegeneration(cacheKey, async () => {
661
- return runWithRequestContext(createRequestContext({
662
- executionContext: null,
663
- ssrContext: {
664
- pathname: patternToNextFormat(route.pattern),
665
- query,
666
- asPath: requestAsPath,
667
- navigationIsReady,
668
- locale: locale ?? currentDefaultLocale,
669
- locales: i18nConfig?.locales,
670
- defaultLocale: currentDefaultLocale
671
- },
672
- i18nContext: i18nConfig ? {
673
- locale: locale ?? currentDefaultLocale,
674
- locales: i18nConfig.locales,
675
- defaultLocale: currentDefaultLocale,
676
- domainLocales,
677
- hostname: req.headers.host?.split(":", 1)[0]
678
- } : null
679
- }), async () => {
680
- ensureFetchPatch();
681
- let freshPageProps = {};
682
- let freshRenderProps = { pageProps: freshPageProps };
683
- let RegenApp = null;
684
- const appPath = path.join(pagesDir, "_app");
685
- if (findFileWithExtensions(appPath, matcher)) try {
686
- RegenApp = (await runner.import(appPath)).default ?? null;
687
- } catch {}
688
- if (RegenApp && hasPagesGetInitialProps(RegenApp)) {
689
- const regenReq = {
690
- url: req.url,
691
- headers: req.headers,
692
- method: req.method
693
- };
694
- const regenRes = {
695
- headersSent: false,
696
- writableEnded: false,
697
- statusCode: 200,
698
- getHeaders() {
699
- return {};
700
- }
701
- };
702
- const initialProps = await loadPagesGetInitialProps(RegenApp, {
703
- AppTree: (appTreeProps) => {
704
- const appTree = React.createElement(RegenApp, {
705
- ...appTreeProps,
706
- Component: pageModule.default,
707
- pageProps: appTreeProps.pageProps,
708
- router: routerShim.default
709
- });
710
- return typeof routerShim.wrapWithRouterContext === "function" ? routerShim.wrapWithRouterContext(appTree) : appTree;
711
- },
712
- Component: pageModule.default,
713
- router: routerShim.default,
714
- ctx: {
715
- req: regenReq,
716
- res: regenRes,
717
- pathname: patternToNextFormat(route.pattern),
718
- query,
719
- asPath: requestAsPath,
720
- locale: locale ?? currentDefaultLocale,
721
- locales: i18nConfig?.locales,
722
- defaultLocale: currentDefaultLocale
723
- }
724
- });
725
- if (regenRes.headersSent || regenRes.writableEnded) return;
726
- if (initialProps) {
727
- freshRenderProps = initialProps;
728
- freshPageProps = isUnknownRecord(initialProps.pageProps) ? initialProps.pageProps : {};
729
- }
730
- }
731
- const freshResult = await pageModule.getStaticProps({
732
- params: userFacingParams,
733
- locale: locale ?? currentDefaultLocale,
734
- locales: i18nConfig?.locales,
735
- defaultLocale: currentDefaultLocale,
736
- revalidateReason: "stale"
737
- });
738
- if (freshResult && "props" in freshResult) {
739
- const revalidate = typeof freshResult.revalidate === "number" ? freshResult.revalidate : cached.value.cacheControl?.revalidate ?? 0;
740
- if (revalidate > 0) {
741
- freshPageProps = {
742
- ...freshPageProps,
743
- ...freshResult.props
744
- };
745
- freshRenderProps = {
746
- ...freshRenderProps,
747
- pageProps: freshPageProps
748
- };
749
- if (typeof routerShim.setSSRContext === "function") routerShim.setSSRContext({
750
- pathname: patternToNextFormat(route.pattern),
751
- query,
752
- asPath: requestAsPath,
753
- navigationIsReady,
754
- locale: locale ?? currentDefaultLocale,
755
- locales: i18nConfig?.locales,
756
- defaultLocale: currentDefaultLocale,
757
- domainLocales
758
- });
759
- if (i18nConfig) {
760
- await runner.import("vinext/i18n-state");
761
- const i18nCtx = await importModule(runner, "vinext/i18n-context");
762
- if (typeof i18nCtx.setI18nContext === "function") i18nCtx.setI18nContext({
763
- locale: locale ?? currentDefaultLocale,
764
- locales: i18nConfig.locales,
765
- defaultLocale: currentDefaultLocale,
766
- domainLocales,
767
- hostname: req.headers.host?.split(":", 1)[0]
768
- });
769
- }
770
- let el = RegenApp ? React.createElement(RegenApp, {
771
- ...freshRenderProps,
772
- Component: pageModule.default,
773
- pageProps: freshRenderProps.pageProps,
774
- router: routerShim.default
775
- }) : React.createElement(pageModule.default, freshPageProps);
776
- if (routerShim.wrapWithRouterContext) el = routerShim.wrapWithRouterContext(el);
777
- const freshBody = await renderIsrPassToStringAsync(withScriptNonce(el, scriptNonce));
778
- const viteRoot = server.config.root;
779
- const viteBase = server.config.base;
780
- const regenPageUrl = createPagesDevModuleUrl(viteRoot, route.filePath, viteBase);
781
- const regenAppUrl = RegenApp ? createPagesDevModuleUrl(viteRoot, path.join(pagesDir, "_app"), viteBase) : null;
782
- const freshPagesNextData = {
783
- ...pagesNextData,
784
- __vinext: {
785
- ...pagesNextData.__vinext,
786
- pageModuleUrl: regenPageUrl,
787
- appModuleUrl: regenAppUrl,
788
- hasMiddleware,
789
- routeUrl: requestAsPath
790
- }
791
- };
792
- const freshHtml = `<!DOCTYPE html><html><head></head><body><div id="__next">${freshBody}</div>${`<script id="__NEXT_DATA__" type="application/json">${safeJsonStringify({
793
- props: freshRenderProps,
794
- page: patternToNextFormat(route.pattern),
795
- query: params,
796
- buildId: process.env.__VINEXT_BUILD_ID,
797
- isFallback: false,
798
- locale: locale ?? currentDefaultLocale,
799
- locales: i18nConfig?.locales,
800
- defaultLocale: currentDefaultLocale,
801
- domainLocales,
802
- ...freshPagesNextData
803
- })}<\/script>`}\n ${cachedHtml.match(/<script type="module">[\s\S]*?<\/script>/)?.[0] ?? ""}</body></html>`;
804
- await isrSet(cacheKey, buildPagesCacheValue(freshHtml, freshRenderProps), revalidate);
805
- setRevalidateDuration(cacheKey, revalidate);
806
- }
807
- }
808
- });
809
- }, {
810
- routerKind: "Pages Router",
811
- routePath: route.pattern,
812
- routeType: "render"
813
- });
814
- const { cacheControl: staleCacheControl } = decideIsr({
815
- cacheState: "STALE",
816
- kind: "dev",
817
- revalidateSeconds: getRevalidateDuration(cacheKey) ?? 60
818
- });
819
- const staleHeaders = {
820
- "Content-Type": "text/html; charset=utf-8",
821
- ...buildCacheStateHeaders("STALE"),
822
- "Cache-Control": staleCacheControl
823
- };
824
- if (earlyFontLinkHeader) staleHeaders["Link"] = earlyFontLinkHeader;
825
- res.writeHead(200, staleHeaders);
826
- res.end(transformedHtml);
827
- return;
828
- }
829
666
  const context = {
830
667
  params: userFacingParams,
831
668
  locale: locale ?? currentDefaultLocale,
@@ -834,43 +671,47 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
834
671
  revalidateReason: isOnDemandRevalidate ? "on-demand" : "stale",
835
672
  ...previewContext
836
673
  };
837
- const generatedPageData = !isOnDemandRevalidate && previewData === false && cached?.isStale === false && cached?.value.value?.kind === "PAGES" && cached.value.value.generatedFromDataRequest && isUnknownRecord(cached.value.value.pageData) ? cached.value.value.pageData : null;
838
- if (!generatedPageData && await loadAppInitialProps()) return;
839
- const result = generatedPageData ? null : await pageModule.getStaticProps(context);
840
- if (generatedPageData) {
841
- renderProps = generatedPageData;
842
- pageProps = isUnknownRecord(renderProps.pageProps) ? renderProps.pageProps : {};
843
- }
674
+ if (await loadAppInitialProps()) return;
675
+ const result = await pageModule.getStaticProps(context);
676
+ const routePattern = patternToNextFormat(route.pattern);
677
+ assertPages404DoesNotReturnNotFound(routePattern, result);
678
+ if (result) staticPropsRevalidateSeconds = resolvePagesRevalidateSeconds(result, routePattern);
844
679
  if (result && "props" in result) {
845
- pageProps = {
846
- ...pageProps,
847
- ...await Promise.resolve(result.props)
848
- };
680
+ pageProps = mergePagesDataProps(renderProps.pageProps, await Promise.resolve(result.props));
849
681
  renderProps = {
850
682
  ...renderProps,
851
683
  pageProps
852
684
  };
853
685
  }
854
686
  if (result && "redirect" in result) {
855
- writeGsspRedirect(res, result.redirect, isDataReq, renderProps);
687
+ if (previewData === false) resolvePagesRedirect(result.redirect, {
688
+ method: "getStaticProps",
689
+ routeUrl: url,
690
+ sanitizeDestination
691
+ });
692
+ applyDevPagesCacheHeaders(res, isOnDemandRevalidate ? "REVALIDATED" : "HIT", buildDevPagesTerminalCacheControl(staticPropsRevalidateSeconds, expireTime));
693
+ writeGsspRedirect(res, result.redirect, isDataReq, renderProps, {
694
+ basePath,
695
+ method: "getStaticProps",
696
+ routeUrl: url
697
+ });
856
698
  return;
857
699
  }
858
700
  if (result && "notFound" in result && result.notFound) {
701
+ applyDevPagesCacheHeaders(res, isOnDemandRevalidate ? "REVALIDATED" : "HIT", isDataReq ? buildDevPagesTerminalCacheControl(staticPropsRevalidateSeconds, expireTime) : DEV_PAGES_CACHE_CONTROL);
859
702
  applyDevPagesPreviewResponse(res, requestPreview);
860
703
  if (isDataReq) {
861
704
  const deploymentId = process.env.__VINEXT_DEPLOYMENT_ID || process.env.NEXT_DEPLOYMENT_ID;
862
705
  const notFoundHeaders = { "Content-Type": "application/json" };
863
706
  if (deploymentId) notFoundHeaders[NEXTJS_DEPLOYMENT_ID_HEADER] = deploymentId;
864
707
  res.writeHead(404, notFoundHeaders);
865
- res.end("{}");
708
+ res.end("{\"notFound\":true}");
866
709
  return;
867
710
  }
868
- await renderErrorPage(server, runner, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext, void 0, void 0, reactStrictMode);
711
+ await renderErrorPage(server, runner, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext, void 0, void 0, reactStrictMode, errorPageContext);
869
712
  return;
870
713
  }
871
- if (result && "props" in result) isSerializableProps(patternToNextFormat(route.pattern), "getStaticProps", pageProps);
872
- if (previewData === false && typeof result?.revalidate === "number" && result.revalidate > 0) isrRevalidateSeconds = result.revalidate;
873
- else if (previewData === false && cached?.value.value?.kind === "PAGES" && cached.value.value.generatedFromDataRequest) isrRevalidateSeconds = cached.value.cacheControl?.revalidate ?? 31536e3;
714
+ if (result && "props" in result) isSerializableProps(routePattern, "getStaticProps", pageProps);
874
715
  }
875
716
  if (typeof pageModule.getServerSideProps !== "function" && typeof pageModule.getStaticProps !== "function" && hasAppGetInitialProps) {
876
717
  if (await loadAppInitialProps()) return;
@@ -899,20 +740,11 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
899
740
  }
900
741
  }
901
742
  if (isDataReq) {
902
- if (shouldPersistFallbackData && staticPropsPreviewData === false) {
903
- const cacheKey = pagesIsrCacheKey(url.split("?")[0]);
904
- const revalidateSeconds = isrRevalidateSeconds ?? 31536e3;
905
- await isrSet(cacheKey, {
906
- kind: "PAGES",
907
- html: "",
908
- pageData: renderProps,
909
- generatedFromDataRequest: true,
910
- headers: void 0,
911
- status: void 0
912
- }, revalidateSeconds);
913
- setRevalidateDuration(cacheKey, revalidateSeconds);
914
- }
915
743
  const dataHeaders = { "Content-Type": "application/json" };
744
+ if (typeof pageModule.getStaticProps === "function") {
745
+ dataHeaders["Cache-Control"] = DEV_PAGES_CACHE_CONTROL;
746
+ dataHeaders[NEXTJS_CACHE_HEADER] = isOnDemandRevalidate ? "REVALIDATED" : "HIT";
747
+ }
916
748
  if ((statusCode ?? 200) === 200) dataHeaders["x-nextjs-matched-path"] = `${locale ? `/${locale}` : ""}${patternToNextFormat(route.pattern)}`;
917
749
  if (gsspExtraHeaders) for (const [k, v] of Object.entries(gsspExtraHeaders)) dataHeaders[k] = v;
918
750
  applyDevPagesPreviewHeaders(dataHeaders, requestPreview);
@@ -932,7 +764,6 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
932
764
  if (AppComponent) element = createElement(AppComponent, {
933
765
  ...renderProps,
934
766
  Component: PageComponent,
935
- pageProps: renderProps.pageProps,
936
767
  router: routerShim.default
937
768
  });
938
769
  else element = createElement(PageComponent, pageProps);
@@ -943,7 +774,8 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
943
774
  if (typeof dynamicShim.flushPreloads === "function") await dynamicShim.flushPreloads();
944
775
  const nonceAttr = createNonceAttribute(scriptNonce);
945
776
  let fontHeadHTML = "";
946
- const assetHeadHTML = await collectDevInitialStylesheetHeadHTML(server, runner, [AppComponent ? findFileWithExts(pagesDir, "_app", matcher) : null, route.filePath], nonceAttr);
777
+ const appAssetPath = AppComponent ? appFilePath : null;
778
+ const assetHeadHTML = await collectDevInitialStylesheetHeadHTML(server, runner, [appAssetPath, route.filePath], nonceAttr);
947
779
  const allFontStyles = [];
948
780
  const allFontPreloads = [];
949
781
  try {
@@ -966,15 +798,15 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
966
798
  for (const { href, type } of allFontPreloads) {
967
799
  const safeHref = href.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
968
800
  const safeType = type.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
969
- fontHeadHTML += `<link rel="preload"${nonceAttr} href="${safeHref}" as="font" type="${safeType}" crossorigin />\n `;
801
+ fontHeadHTML += `<link rel="preload"${nonceAttr} href="${safeHref}" as="font" type="${safeType}" crossorigin="anonymous" />\n `;
970
802
  }
971
803
  if (allFontStyles.length > 0) fontHeadHTML += `<style data-vinext-fonts${nonceAttr}>${allFontStyles.join("\n")}</style>\n `;
972
804
  const viteRoot = server.config.root;
973
805
  const viteBase = server.config.base;
974
806
  const pageModuleUrl = createPagesDevModuleUrl(viteRoot, route.filePath, viteBase);
975
807
  const pageModuleSource = createPagesDevModuleUrl(viteRoot, route.filePath, "/");
976
- const appModuleUrl = AppComponent ? createPagesDevModuleUrl(viteRoot, path.join(pagesDir, "_app"), viteBase) : null;
977
- const appModuleSource = AppComponent ? createPagesDevModuleUrl(viteRoot, path.join(pagesDir, "_app"), "/") : null;
808
+ const appModuleUrl = appAssetPath ? createPagesDevModuleUrl(viteRoot, appAssetPath, viteBase) : null;
809
+ const appModuleSource = appAssetPath ? createPagesDevModuleUrl(viteRoot, appAssetPath, "/") : null;
978
810
  const serializedPagesNextData = {
979
811
  ...pagesNextData,
980
812
  __vinext: {
@@ -1004,17 +836,14 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
1004
836
  domainLocales,
1005
837
  ...serializedPagesNextData
1006
838
  })}<\/script>`;
1007
- const docPath = path.join(pagesDir, "_document");
839
+ const docFilePath = findFileWithExts(pagesDir, "_document", matcher);
1008
840
  let DocumentComponent = null;
1009
- if (findFileWithExtensions(docPath, matcher)) try {
1010
- DocumentComponent = (await runner.import(docPath)).default ?? null;
1011
- } catch {}
841
+ if (docFilePath) DocumentComponent = (await runner.import(docFilePath)).default ?? null;
1012
842
  const allScripts = `${nextDataScript}\n ${createInlineScriptTag(`window.__VINEXT_PAGE_PATTERNS__=${safeJsonStringify(pagePatterns)}`, scriptNonce)}\n ${hydrationScript}`;
1013
843
  const extraHeaders = { ...gsspExtraHeaders };
1014
- if (requestPreviewData === false && isrRevalidateSeconds) if (scriptNonce) extraHeaders["Cache-Control"] = NO_STORE_CACHE_CONTROL;
1015
- else {
1016
- extraHeaders["Cache-Control"] = buildMissIsrCacheControl(isrRevalidateSeconds);
1017
- Object.assign(extraHeaders, buildCacheStateHeaders("MISS"));
844
+ if (typeof pageModule.getStaticProps === "function") {
845
+ extraHeaders["Cache-Control"] = DEV_PAGES_CACHE_CONTROL;
846
+ extraHeaders[NEXTJS_CACHE_HEADER] = isFallbackRender ? "MISS" : isOnDemandRevalidate ? "REVALIDATED" : "HIT";
1018
847
  }
1019
848
  applyDevPagesPreviewHeaders(extraHeaders, requestPreview);
1020
849
  if (allFontPreloads.length > 0) extraHeaders["Link"] = allFontPreloads.map((p) => `<${p.href}>; rel=preload; as=font; type=${p.type}; crossorigin`).join(", ");
@@ -1046,7 +875,7 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
1046
875
  if (FinalApp) enhancedElement = createElement(FinalApp, {
1047
876
  ...renderProps,
1048
877
  Component: FinalComp,
1049
- pageProps
878
+ router: routerShim.default
1050
879
  });
1051
880
  else enhancedElement = createElement(FinalComp, pageProps);
1052
881
  if (wrapWithRouterContext) enhancedElement = wrapWithRouterContext(enhancedElement);
@@ -1058,22 +887,11 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
1058
887
  return traceHTML ? `${headHTML}\n ${traceHTML}` : headHTML;
1059
888
  },
1060
889
  setDocumentInitialHead: typeof headShim.setDocumentInitialHead === "function" ? headShim.setDocumentInitialHead : void 0,
890
+ crossOrigin,
1061
891
  bufferBodyBeforeHeaders: true
1062
892
  });
1063
893
  _renderEnd = now();
1064
894
  if (typeof routerShim.setSSRContext === "function") routerShim.setSSRContext(null);
1065
- if (!scriptNonce && isrRevalidateSeconds !== null && isrRevalidateSeconds > 0) {
1066
- let isrElement = AppComponent ? createElement(AppComponent, {
1067
- ...renderProps,
1068
- Component: pageModule.default,
1069
- pageProps
1070
- }) : createElement(pageModule.default, pageProps);
1071
- if (wrapWithRouterContext) isrElement = wrapWithRouterContext(isrElement);
1072
- const isrHtml = `<!DOCTYPE html><html><head>${assetHeadHTML}</head><body><div id="__next">${await renderIsrPassToStringAsync(withScriptNonce(isrElement, scriptNonce))}</div>${allScripts}</body></html>`;
1073
- const cacheKey = pagesIsrCacheKey(url.split("?")[0]);
1074
- await isrSet(cacheKey, buildPagesCacheValue(isrHtml, pageProps), isrRevalidateSeconds);
1075
- setRevalidateDuration(cacheKey, isrRevalidateSeconds);
1076
- }
1077
895
  } catch (e) {
1078
896
  console.error(e);
1079
897
  reportRequestError(e instanceof Error ? e : new Error(String(e)), {
@@ -1086,7 +904,7 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
1086
904
  routeType: "render"
1087
905
  }).catch(() => {});
1088
906
  try {
1089
- await renderErrorPage(server, runner, req, res, url, pagesDir, 500, void 0, matcher, e instanceof Error ? e : new Error(String(e)), reactStrictMode);
907
+ await renderErrorPage(server, runner, req, res, url, pagesDir, 500, void 0, matcher, e instanceof Error ? e : new Error(String(e)), reactStrictMode, errorPageContext);
1090
908
  } catch (fallbackErr) {
1091
909
  res.statusCode = 500;
1092
910
  res.end(`Internal Server Error: ${fallbackErr.message}`);
@@ -1103,23 +921,24 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
1103
921
  * - 500: pages/500.tsx -> pages/_error.tsx -> default
1104
922
  * - other: pages/_error.tsx -> default
1105
923
  */
1106
- async function renderErrorPage(server, runner, req, res, url, pagesDir, statusCode, wrapWithRouterContext, fileMatcher, err, reactStrictMode = false) {
924
+ async function renderErrorPage(server, runner, req, res, url, pagesDir, statusCode, wrapWithRouterContext, fileMatcher, err, reactStrictMode = false, context = { basePath: "" }) {
1107
925
  attachPagesRequestCookies(req);
1108
926
  const matcher = fileMatcher ?? createValidFileMatcher();
1109
927
  const candidates = statusCode === 404 ? ["404", "_error"] : statusCode === 500 ? ["500", "_error"] : ["_error"];
1110
928
  for (const candidate of candidates) {
1111
929
  let errorRouterShim = null;
930
+ let candidateLoaded = false;
931
+ let errorAssetPath = null;
1112
932
  try {
1113
- const errorAssetPath = findFileWithExts(pagesDir, candidate, matcher);
933
+ errorAssetPath = findFileWithExts(pagesDir, candidate, matcher);
1114
934
  if (!errorAssetPath && candidate !== "_error") continue;
1115
- const ErrorComponent = (await importModule(runner, errorAssetPath ?? "next/error")).default;
935
+ const errorModule = await importModule(runner, errorAssetPath ?? "next/error");
936
+ candidateLoaded = true;
937
+ const ErrorComponent = errorModule.default;
1116
938
  if (!ErrorComponent) continue;
1117
939
  let AppComponent = null;
1118
- const appPathErr = path.join(pagesDir, "_app");
1119
940
  const appAssetPath = findFileWithExts(pagesDir, "_app", matcher);
1120
- if (findFileWithExtensions(appPathErr, matcher)) try {
1121
- AppComponent = (await importModule(runner, appAssetPath ?? appPathErr)).default ?? null;
1122
- } catch {}
941
+ if (appAssetPath) AppComponent = (await importModule(runner, appAssetPath)).default ?? null;
1123
942
  const createElement = React.createElement;
1124
943
  res.statusCode = statusCode;
1125
944
  const errorPage = candidate === "_error" ? "/_error" : `/${candidate}`;
@@ -1146,10 +965,36 @@ async function renderErrorPage(server, runner, req, res, url, pagesDir, statusCo
1146
965
  asPath: url
1147
966
  });
1148
967
  if (res.headersSent || res.writableEnded) return;
1149
- const errorProps = {
968
+ let errorProps = {
1150
969
  ...initialErrorProps,
1151
970
  statusCode
1152
971
  };
972
+ if (typeof errorModule.getStaticProps === "function") {
973
+ const isOnDemandRevalidate = isOnDemandRevalidateRequest(req.headers[PRERENDER_REVALIDATE_HEADER]);
974
+ const staticResult = await errorModule.getStaticProps({
975
+ locale: context.locale,
976
+ locales: context.locales,
977
+ defaultLocale: context.defaultLocale,
978
+ revalidateReason: isOnDemandRevalidate ? "on-demand" : "stale"
979
+ });
980
+ assertPages404DoesNotReturnNotFound(errorPage, staticResult);
981
+ if (staticResult?.redirect) {
982
+ applyDevPagesCacheHeaders(res, isOnDemandRevalidate ? "REVALIDATED" : "HIT", NEVER_CACHE_CONTROL);
983
+ writeGsspRedirect(res, staticResult.redirect, false, { pageProps: errorProps }, {
984
+ basePath: context.basePath,
985
+ method: "getStaticProps",
986
+ routeUrl: url
987
+ });
988
+ return;
989
+ }
990
+ if (staticResult?.props !== void 0) {
991
+ const staticProps = await Promise.resolve(staticResult.props);
992
+ isSerializableProps(errorPage, "getStaticProps", staticProps);
993
+ errorProps = mergePagesDataProps(errorProps, staticProps);
994
+ }
995
+ if (staticResult) resolvePagesRevalidateSeconds(staticResult, errorPage);
996
+ applyDevPagesCacheHeaders(res, isOnDemandRevalidate ? "REVALIDATED" : "HIT");
997
+ }
1153
998
  let renderProps;
1154
999
  if (AppComponent && hasPagesGetInitialProps(AppComponent)) {
1155
1000
  const appInitialProps = await loadPagesGetInitialProps(AppComponent, {
@@ -1173,13 +1018,15 @@ async function renderErrorPage(server, runner, req, res, url, pagesDir, statusCo
1173
1018
  }
1174
1019
  });
1175
1020
  if (res.headersSent || res.writableEnded) return;
1176
- renderProps = appInitialProps ?? {};
1021
+ const appRenderProps = appInitialProps ?? {};
1022
+ renderProps = Object.hasOwn(appRenderProps, "pageProps") ? {
1023
+ ...appRenderProps,
1024
+ pageProps: mergePagesDataProps(appRenderProps.pageProps, errorProps)
1025
+ } : appRenderProps;
1177
1026
  } else renderProps = { pageProps: errorProps };
1178
1027
  let DocumentComponent = null;
1179
- const docPathErr = path.join(pagesDir, "_document");
1180
- if (findFileWithExtensions(docPathErr, matcher)) try {
1181
- DocumentComponent = (await importModule(runner, docPathErr)).default ?? null;
1182
- } catch {}
1028
+ const docFilePathErr = findFileWithExts(pagesDir, "_document", matcher);
1029
+ if (docFilePathErr) DocumentComponent = (await importModule(runner, docFilePathErr)).default ?? null;
1183
1030
  const createErrorElement = (FinalApp, FinalComponent) => {
1184
1031
  let errorElement = FinalApp ? createElement(FinalApp, {
1185
1032
  ...renderProps,
@@ -1237,9 +1084,14 @@ async function renderErrorPage(server, runner, req, res, url, pagesDir, statusCo
1237
1084
  return createErrorElement(FinalApp, FinalComponent);
1238
1085
  },
1239
1086
  getHeadHTML: () => typeof headShim.getSSRHeadHTML === "function" ? headShim.getSSRHeadHTML() : "",
1240
- setDocumentInitialHead: typeof headShim.setDocumentInitialHead === "function" ? headShim.setDocumentInitialHead : void 0
1087
+ setDocumentInitialHead: typeof headShim.setDocumentInitialHead === "function" ? headShim.setDocumentInitialHead : void 0,
1088
+ crossOrigin: context.crossOrigin
1241
1089
  });
1242
1090
  else {
1091
+ const bodyHtml = await renderToStringAsync(element);
1092
+ const protectedAssetMarker = `data-vinext-document-asset-props-protected-${randomUUID()}`;
1093
+ const protectAssetTags = (assetHtml) => markDocumentAssetPropsProtectedTags(assetHtml, protectedAssetMarker);
1094
+ const protectedErrorScripts = protectAssetTags(applyDocumentAssetProps(errorScripts, {}, { configuredCrossOrigin: context.crossOrigin }));
1243
1095
  const html = `<!DOCTYPE html>
1244
1096
  <html>
1245
1097
  <head>
@@ -1248,17 +1100,21 @@ async function renderErrorPage(server, runner, req, res, url, pagesDir, statusCo
1248
1100
  ${assetHeadHTML}
1249
1101
  </head>
1250
1102
  <body>
1251
- <div id="__next">${await renderToStringAsync(element)}</div>
1252
- ${errorScripts}
1103
+ <div id="__next">${protectAssetTags(bodyHtml)}</div>
1104
+ ${protectedErrorScripts}
1253
1105
  </body>
1254
1106
  </html>`;
1255
- const transformedHtml = await server.transformIndexHtml(url, html);
1107
+ const transformedHtml = stripDocumentAssetPropsProtectionMarkers(applyDocumentAssetProps(await server.transformIndexHtml(url, html), {}, {
1108
+ configuredCrossOrigin: context.crossOrigin,
1109
+ protectedAssetMarker
1110
+ }), protectedAssetMarker);
1256
1111
  res.writeHead(statusCode, { "Content-Type": "text/html; charset=utf-8" });
1257
1112
  res.end(transformedHtml);
1258
1113
  }
1259
1114
  return;
1260
- } catch {
1115
+ } catch (error) {
1261
1116
  if (res.headersSent || res.writableEnded) return;
1117
+ if (errorAssetPath || candidateLoaded) throw error;
1262
1118
  continue;
1263
1119
  } finally {
1264
1120
  if (typeof errorRouterShim?.setSSRContext === "function") errorRouterShim.setSSRContext(null);