vinext 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/dist/build/css-url-assets.d.ts +3 -1
  2. package/dist/build/css-url-assets.js +18 -1
  3. package/dist/build/inject-pregenerated-paths.d.ts +3 -0
  4. package/dist/build/inject-pregenerated-paths.js +4 -1
  5. package/dist/build/prerender-server-entry.d.ts +1 -0
  6. package/dist/build/prerender-server-entry.js +49 -0
  7. package/dist/build/prerender-server-pool.d.ts +44 -0
  8. package/dist/build/prerender-server-pool.js +194 -0
  9. package/dist/build/prerender.js +86 -22
  10. package/dist/build/run-prerender.js +2 -1
  11. package/dist/check.d.ts +4 -6
  12. package/dist/check.js +8 -9
  13. package/dist/cli.js +1 -1
  14. package/dist/client/vinext-next-data.d.ts +4 -1
  15. package/dist/config/config-matchers.js +33 -11
  16. package/dist/config/next-config.d.ts +36 -6
  17. package/dist/config/next-config.js +16 -2
  18. package/dist/entries/app-browser-entry.js +5 -1
  19. package/dist/entries/app-rsc-entry.d.ts +3 -2
  20. package/dist/entries/app-rsc-entry.js +4 -0
  21. package/dist/entries/pages-client-entry.js +14 -3
  22. package/dist/entries/pages-server-entry.js +18 -4
  23. package/dist/index.js +320 -25
  24. package/dist/packages/cloudflare/src/cache/kv-data-adapter.runtime.js +8 -1
  25. package/dist/packages/cloudflare/src/deploy.js +1 -5
  26. package/dist/packages/cloudflare/src/tpr.js +26 -5
  27. package/dist/plugins/fonts.js +1 -1
  28. package/dist/plugins/ignore-dynamic-requests.js +1 -1
  29. package/dist/plugins/og-assets.js +2 -1
  30. package/dist/plugins/optimize-imports.js +1 -1
  31. package/dist/plugins/rsc-reference-validation-normalizer.d.ts +12 -0
  32. package/dist/plugins/rsc-reference-validation-normalizer.js +48 -0
  33. package/dist/plugins/sass.d.ts +17 -1
  34. package/dist/plugins/sass.js +74 -1
  35. package/dist/plugins/styled-jsx.d.ts +16 -0
  36. package/dist/plugins/styled-jsx.js +149 -0
  37. package/dist/routing/app-route-graph.js +22 -16
  38. package/dist/routing/file-matcher.d.ts +8 -1
  39. package/dist/routing/file-matcher.js +15 -3
  40. package/dist/server/app-browser-entry.js +46 -14
  41. package/dist/server/app-browser-navigation-controller.js +1 -1
  42. package/dist/server/app-browser-server-action-client.js +5 -3
  43. package/dist/server/app-browser-state.d.ts +1 -0
  44. package/dist/server/app-browser-state.js +2 -2
  45. package/dist/server/app-browser-visible-commit.js +1 -1
  46. package/dist/server/app-page-boundary.js +2 -1
  47. package/dist/server/app-page-cache-finalizer.d.ts +1 -0
  48. package/dist/server/app-page-cache-finalizer.js +3 -3
  49. package/dist/server/app-page-cache.js +23 -6
  50. package/dist/server/app-page-dispatch.d.ts +1 -0
  51. package/dist/server/app-page-dispatch.js +25 -8
  52. package/dist/server/app-page-element-builder.js +1 -0
  53. package/dist/server/app-page-execution.js +2 -1
  54. package/dist/server/app-page-render-identity.d.ts +1 -0
  55. package/dist/server/app-page-render-identity.js +2 -1
  56. package/dist/server/app-page-render.d.ts +2 -0
  57. package/dist/server/app-page-render.js +67 -16
  58. package/dist/server/app-page-response.d.ts +7 -0
  59. package/dist/server/app-page-response.js +16 -4
  60. package/dist/server/app-page-stream.d.ts +2 -1
  61. package/dist/server/app-page-stream.js +1 -1
  62. package/dist/server/app-route-handler-execution.js +1 -1
  63. package/dist/server/app-route-tree-prefetch.d.ts +43 -0
  64. package/dist/server/app-route-tree-prefetch.js +187 -0
  65. package/dist/server/app-router-entry.js +1 -1
  66. package/dist/server/app-rsc-cache-busting.d.ts +2 -1
  67. package/dist/server/app-rsc-cache-busting.js +9 -4
  68. package/dist/server/app-rsc-handler.d.ts +6 -0
  69. package/dist/server/app-rsc-handler.js +89 -11
  70. package/dist/server/app-rsc-render-mode.d.ts +3 -2
  71. package/dist/server/app-rsc-render-mode.js +4 -1
  72. package/dist/server/app-rsc-request-normalization.d.ts +3 -4
  73. package/dist/server/app-rsc-request-normalization.js +4 -5
  74. package/dist/server/app-server-action-execution.js +4 -3
  75. package/dist/server/app-ssr-entry.js +3 -3
  76. package/dist/server/cache-control.d.ts +3 -1
  77. package/dist/server/cache-control.js +13 -1
  78. package/dist/server/dev-server.d.ts +11 -1
  79. package/dist/server/dev-server.js +78 -20
  80. package/dist/server/headers.d.ts +6 -2
  81. package/dist/server/headers.js +11 -5
  82. package/dist/server/isr-cache.d.ts +2 -1
  83. package/dist/server/isr-cache.js +7 -3
  84. package/dist/server/pages-data-route.d.ts +4 -2
  85. package/dist/server/pages-data-route.js +18 -4
  86. package/dist/server/pages-dev-module-url.d.ts +2 -1
  87. package/dist/server/pages-dev-module-url.js +6 -3
  88. package/dist/server/pages-node-compat.d.ts +12 -1
  89. package/dist/server/pages-node-compat.js +50 -1
  90. package/dist/server/pages-page-data.d.ts +9 -0
  91. package/dist/server/pages-page-data.js +17 -9
  92. package/dist/server/pages-page-handler.js +19 -7
  93. package/dist/server/pages-page-response.d.ts +1 -0
  94. package/dist/server/pages-page-response.js +3 -2
  95. package/dist/server/pages-request-pipeline.d.ts +8 -6
  96. package/dist/server/pages-request-pipeline.js +30 -6
  97. package/dist/server/pages-router-entry.js +1 -1
  98. package/dist/server/prod-server.d.ts +4 -2
  99. package/dist/server/prod-server.js +38 -22
  100. package/dist/server/request-pipeline.js +1 -1
  101. package/dist/shims/cache-handler.js +8 -1
  102. package/dist/shims/cache.js +3 -0
  103. package/dist/shims/fetch-cache.d.ts +3 -1
  104. package/dist/shims/fetch-cache.js +77 -52
  105. package/dist/shims/form.d.ts +1 -1
  106. package/dist/shims/internal/app-prefetch-fetch-queue.d.ts +10 -0
  107. package/dist/shims/internal/app-prefetch-fetch-queue.js +61 -0
  108. package/dist/shims/internal/hybrid-client-route-owner.d.ts +2 -1
  109. package/dist/shims/internal/hybrid-client-route-owner.js +34 -1
  110. package/dist/shims/internal/pages-data-fetch-dedup.d.ts +2 -1
  111. package/dist/shims/internal/pages-data-fetch-dedup.js +13 -2
  112. package/dist/shims/internal/pages-data-target.d.ts +8 -3
  113. package/dist/shims/internal/pages-data-target.js +9 -4
  114. package/dist/shims/link.d.ts +1 -1
  115. package/dist/shims/link.js +179 -42
  116. package/dist/shims/navigation.d.ts +15 -3
  117. package/dist/shims/navigation.js +202 -39
  118. package/dist/shims/navigation.react-server.js +1 -1
  119. package/dist/shims/request-context.js +18 -0
  120. package/dist/shims/router.js +31 -15
  121. package/dist/shims/unified-request-context.js +1 -0
  122. package/dist/typegen.js +1 -1
  123. package/dist/utils/middleware-request-headers.js +1 -1
  124. package/dist/utils/protocol-headers.d.ts +7 -1
  125. package/dist/utils/protocol-headers.js +7 -1
  126. package/dist/utils/vite-version.d.ts +2 -0
  127. package/dist/utils/vite-version.js +11 -1
  128. package/package.json +2 -2
@@ -1,5 +1,5 @@
1
1
  import { createRequestContext, runWithRequestContext } from "../shims/unified-request-context.js";
2
- import { createValidFileMatcher, findFileWithExtensions } from "../routing/file-matcher.js";
2
+ import { createValidFileMatcher, findFileWithExtensions, findFileWithExts } from "../routing/file-matcher.js";
3
3
  import { patternToNextFormat } from "../routing/route-validation.js";
4
4
  import { matchRoute } from "../routing/pages-router.js";
5
5
  import { NEXTJS_DEPLOYMENT_ID_HEADER } from "./headers.js";
@@ -17,7 +17,7 @@ import "../shims/router-state.js";
17
17
  import { runWithHeadState } from "../shims/head-state.js";
18
18
  import { runWithServerInsertedHTMLState } from "../shims/navigation-state.js";
19
19
  import { withScriptNonce } from "../shims/script-nonce-context.js";
20
- import { createInlineScriptTag, createNonceAttribute, safeJsonStringify } from "./html.js";
20
+ import { createInlineScriptTag, createNonceAttribute, escapeHtmlAttr, safeJsonStringify } from "./html.js";
21
21
  import { getClientTraceMetadataHTML } from "./client-trace-metadata.js";
22
22
  import { getScriptNonceFromNodeHeaderSources } from "./csp.js";
23
23
  import { logRequest, now } from "./request-log.js";
@@ -31,8 +31,9 @@ import { hasPagesGetInitialProps, loadDevAppInitialProps, loadPagesGetInitialPro
31
31
  import { isSerializableProps } from "./pages-serializable-props.js";
32
32
  import { isBotUserAgent } from "../utils/html-limited-bots.js";
33
33
  import { getPagesRouteParams, matchesPagesStaticPath } from "./pages-page-data.js";
34
- import { createPagesDevModuleUrl } from "./pages-dev-module-url.js";
35
- import { attachPagesRequestCookies } from "./pages-node-compat.js";
34
+ import { createPagesDevAssetUrl, createPagesDevModuleUrl } from "./pages-dev-module-url.js";
35
+ import { getManifestFilesForModule } from "./pages-asset-tags.js";
36
+ import { attachPagesRequestCookies, getPagesPreviewDataFromCookieHeader } from "./pages-node-compat.js";
36
37
  import path from "node:path";
37
38
  import React from "react";
38
39
  import { renderToReadableStream } from "react-dom/server.edge";
@@ -52,6 +53,35 @@ async function renderToStringAsync(element) {
52
53
  async function renderIsrPassToStringAsync(element) {
53
54
  return await runWithServerInsertedHTMLState(() => runWithHeadState(() => _runWithCacheState(() => runWithPrivateCache(() => runWithFetchCache(async () => renderToStringAsync(element))))));
54
55
  }
56
+ const DEV_STYLESHEET_ASSET_RE = /\.(?:css|scss|sass)$/i;
57
+ function createDevInitialStylesheetHeadHTML(options) {
58
+ const { ssrManifest, moduleIds, nonceAttr } = options;
59
+ if (!ssrManifest || moduleIds.length === 0) return "";
60
+ const seen = /* @__PURE__ */ new Set();
61
+ let html = "";
62
+ for (const moduleId of moduleIds) {
63
+ const files = getManifestFilesForModule(ssrManifest, moduleId);
64
+ if (!files) continue;
65
+ for (const file of files) {
66
+ if (!DEV_STYLESHEET_ASSET_RE.test(file) || seen.has(file)) continue;
67
+ seen.add(file);
68
+ const href = createPagesDevAssetUrl(file);
69
+ html += `<link rel="stylesheet"${nonceAttr} href="${escapeHtmlAttr(href)}" />\n `;
70
+ }
71
+ }
72
+ return html;
73
+ }
74
+ async function collectDevInitialStylesheetHeadHTML(runner, moduleIds, nonceAttr) {
75
+ try {
76
+ return createDevInitialStylesheetHeadHTML({
77
+ ssrManifest: (await runner.import("virtual:vinext-pages-client-assets")).default?.ssrManifest,
78
+ moduleIds,
79
+ nonceAttr
80
+ });
81
+ } catch {
82
+ return "";
83
+ }
84
+ }
55
85
  /**
56
86
  * Emit a `getServerSideProps` / `getStaticProps` `{ redirect }` result.
57
87
  *
@@ -102,7 +132,7 @@ const STREAM_BODY_MARKER = "<!--VINEXT_STREAM_BODY-->";
102
132
  * shell sooner).
103
133
  */
104
134
  async function streamPageToResponse(res, element, options) {
105
- const { url, server, fontHeadHTML, scripts, DocumentComponent, statusCode, extraHeaders, getHeadHTML, enhancePageElement, scriptNonce, documentContext, setDocumentInitialHead, bufferBodyBeforeHeaders = false } = options;
135
+ const { url, server, fontHeadHTML, assetHeadHTML = "", scripts, DocumentComponent, statusCode, extraHeaders, getHeadHTML, enhancePageElement, scriptNonce, documentContext, setDocumentInitialHead, bufferBodyBeforeHeaders = false } = options;
106
136
  const documentRenderPage = await runDocumentRenderPage({
107
137
  DocumentComponent,
108
138
  enhancePageElement,
@@ -129,7 +159,7 @@ async function streamPageToResponse(res, element, options) {
129
159
  const docProps = documentRenderPage.status === "skipped" ? await loadUserDocumentInitialProps(DocumentComponent) : documentRenderPage.docProps;
130
160
  let docHtml = await renderToStringAsync(docProps ? React.createElement(DocumentComponent, docProps) : React.createElement(DocumentComponent));
131
161
  docHtml = docHtml.replace("__NEXT_MAIN__", STREAM_BODY_MARKER);
132
- if (headHTML || fontHeadHTML) docHtml = docHtml.replace("</head>", ` ${fontHeadHTML}${headHTML}\n</head>`);
162
+ if (headHTML || fontHeadHTML || assetHeadHTML) docHtml = docHtml.replace("</head>", ` ${fontHeadHTML}${headHTML}\n ${assetHeadHTML}\n</head>`);
133
163
  docHtml = docHtml.replace("<!-- __NEXT_SCRIPTS__ -->", scripts);
134
164
  if (!docHtml.includes("__NEXT_DATA__")) docHtml = docHtml.replace("</body>", ` ${scripts}\n</body>`);
135
165
  shellTemplate = docHtml;
@@ -137,6 +167,7 @@ async function streamPageToResponse(res, element, options) {
137
167
  <html>
138
168
  <head>
139
169
  ${fontHeadHTML}${headHTML}
170
+ ${assetHeadHTML}
140
171
  </head>
141
172
  <body>
142
173
  <div id="__next">${STREAM_BODY_MARKER}</div>
@@ -204,7 +235,7 @@ function parseCookieLocale(req, i18nConfig) {
204
235
  * 4. Render the component to HTML
205
236
  * 5. Wrap in _document shell and send response
206
237
  */
207
- function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatcher, basePath = "", trailingSlash = false, hasMiddleware = false, hasRewrites = false, clientTraceMetadata, htmlLimitedBots) {
238
+ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatcher, basePath = "", trailingSlash = false, hasMiddleware = false, hasRewrites = false, clientTraceMetadata, htmlLimitedBots, reactStrictMode = false) {
208
239
  const matcher = fileMatcher ?? createValidFileMatcher();
209
240
  const pagePatterns = routes.map((r) => patternToNextFormat(r.pattern));
210
241
  const _alsRegistration = Promise.all([runner.import("vinext/head-state"), runner.import("vinext/router-state")]);
@@ -337,6 +368,7 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
337
368
  let isrRevalidateSeconds = null;
338
369
  let isFallbackRender = false;
339
370
  let shouldPersistFallbackData = false;
371
+ let staticPropsPreviewData = false;
340
372
  if (typeof pageModule.getStaticPaths === "function" && route.isDynamic) {
341
373
  const pathsResult = await pageModule.getStaticPaths({
342
374
  locales: i18nConfig?.locales ?? [],
@@ -416,6 +448,12 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
416
448
  __N_SSP: true
417
449
  };
418
450
  const headersBeforeGSSP = new Set(Object.keys(res.getHeaders()));
451
+ const previewData = getPagesPreviewDataFromCookieHeader(req.headers.cookie);
452
+ const previewContext = previewData === false ? {} : {
453
+ draftMode: true,
454
+ preview: true,
455
+ previewData
456
+ };
419
457
  const context = {
420
458
  params: userFacingParams,
421
459
  req,
@@ -424,7 +462,8 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
424
462
  resolvedUrl: gsspResolvedUrl,
425
463
  locale: locale ?? currentDefaultLocale,
426
464
  locales: i18nConfig?.locales,
427
- defaultLocale: currentDefaultLocale
465
+ defaultLocale: currentDefaultLocale,
466
+ ...previewContext
428
467
  };
429
468
  const result = await pageModule.getServerSideProps(context);
430
469
  if (res.writableEnded) return;
@@ -480,7 +519,14 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
480
519
  const cacheKey = pagesIsrCacheKey(url.split("?")[0]);
481
520
  const cached = await isrGet(cacheKey);
482
521
  const isOnDemandRevalidate = isOnDemandRevalidateRequest(req.headers[PRERENDER_REVALIDATE_HEADER]);
483
- if (!isOnDemandRevalidate && cached && !cached.isStale && cached.value.value?.kind === "PAGES" && !cached.value.value.generatedFromDataRequest && !scriptNonce && !isDataReq) {
522
+ const previewData = getPagesPreviewDataFromCookieHeader(req.headers.cookie, { isOnDemandRevalidate });
523
+ staticPropsPreviewData = previewData;
524
+ const previewContext = previewData === false ? {} : {
525
+ draftMode: true,
526
+ preview: true,
527
+ previewData
528
+ };
529
+ if (!isOnDemandRevalidate && cached && !cached.isStale && cached.value.value?.kind === "PAGES" && !cached.value.value.generatedFromDataRequest && !scriptNonce && !isDataReq && previewData === false) {
484
530
  const cachedHtml = cached.value.value.html;
485
531
  const transformedHtml = await server.transformIndexHtml(url, cachedHtml);
486
532
  const { cacheControl: hitCacheControl } = decideIsr({
@@ -498,7 +544,7 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
498
544
  res.end(transformedHtml);
499
545
  return;
500
546
  }
501
- if (!isOnDemandRevalidate && cached && cached.isStale && cached.value.value?.kind === "PAGES" && !cached.value.value.generatedFromDataRequest && !scriptNonce && !isDataReq) {
547
+ if (!isOnDemandRevalidate && cached && cached.isStale && cached.value.value?.kind === "PAGES" && !cached.value.value.generatedFromDataRequest && !scriptNonce && !isDataReq && previewData === false) {
502
548
  const cachedHtml = cached.value.value.html;
503
549
  const transformedHtml = await server.transformIndexHtml(url, cachedHtml);
504
550
  triggerBackgroundRegeneration(cacheKey, async () => {
@@ -677,9 +723,10 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
677
723
  locale: locale ?? currentDefaultLocale,
678
724
  locales: i18nConfig?.locales,
679
725
  defaultLocale: currentDefaultLocale,
680
- revalidateReason: isOnDemandRevalidate ? "on-demand" : "stale"
726
+ revalidateReason: isOnDemandRevalidate ? "on-demand" : "stale",
727
+ ...previewContext
681
728
  };
682
- const generatedPageData = !isOnDemandRevalidate && cached?.isStale === false && cached?.value.value?.kind === "PAGES" && cached.value.value.generatedFromDataRequest && isUnknownRecord(cached.value.value.pageData) ? cached.value.value.pageData : null;
729
+ 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;
683
730
  if (!generatedPageData && await loadAppInitialProps()) return;
684
731
  const result = generatedPageData ? null : await pageModule.getStaticProps(context);
685
732
  if (generatedPageData) {
@@ -713,8 +760,8 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
713
760
  return;
714
761
  }
715
762
  if (result && "props" in result) isSerializableProps(patternToNextFormat(route.pattern), "getStaticProps", pageProps);
716
- if (typeof result?.revalidate === "number" && result.revalidate > 0) isrRevalidateSeconds = result.revalidate;
717
- else if (cached?.value.value?.kind === "PAGES" && cached.value.value.generatedFromDataRequest) isrRevalidateSeconds = cached.value.cacheControl?.revalidate ?? 31536e3;
763
+ if (previewData === false && typeof result?.revalidate === "number" && result.revalidate > 0) isrRevalidateSeconds = result.revalidate;
764
+ else if (previewData === false && cached?.value.value?.kind === "PAGES" && cached.value.value.generatedFromDataRequest) isrRevalidateSeconds = cached.value.cacheControl?.revalidate ?? 31536e3;
718
765
  }
719
766
  if (typeof pageModule.getServerSideProps !== "function" && typeof pageModule.getStaticProps !== "function" && hasAppGetInitialProps) {
720
767
  if (await loadAppInitialProps()) return;
@@ -743,7 +790,7 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
743
790
  }
744
791
  }
745
792
  if (isDataReq) {
746
- if (shouldPersistFallbackData) {
793
+ if (shouldPersistFallbackData && staticPropsPreviewData === false) {
747
794
  const cacheKey = pagesIsrCacheKey(url.split("?")[0]);
748
795
  const revalidateSeconds = isrRevalidateSeconds ?? 31536e3;
749
796
  await isrSet(cacheKey, {
@@ -786,6 +833,7 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
786
833
  if (typeof dynamicShim.flushPreloads === "function") await dynamicShim.flushPreloads();
787
834
  const nonceAttr = createNonceAttribute(scriptNonce);
788
835
  let fontHeadHTML = "";
836
+ const assetHeadHTML = await collectDevInitialStylesheetHeadHTML(runner, [AppComponent ? findFileWithExts(pagesDir, "_app", matcher) : null, route.filePath], nonceAttr);
789
837
  const allFontStyles = [];
790
838
  const allFontPreloads = [];
791
839
  try {
@@ -847,6 +895,9 @@ const rawPageProps = props.pageProps;
847
895
  const pageProps = rawPageProps && typeof rawPageProps === "object" ? rawPageProps : {};
848
896
  window.__VINEXT_PAGE_LOADERS__ = { [nextData.page]: () => import("${pageModuleSource}") };
849
897
  window.__VINEXT_APP_LOADER__ = ${appModuleSource ? `() => import("${appModuleSource}")` : "undefined"};
898
+ // reactStrictMode flag — read by wrapWithRouterContext so the <React.StrictMode>
899
+ // wrap is applied on initial hydration and every navigation (matches Next.js).
900
+ window.__VINEXT_REACT_STRICT_MODE__ = ${JSON.stringify(reactStrictMode === true)};
850
901
 
851
902
  async function hydrate() {
852
903
  let hydrateRootOptions;
@@ -923,6 +974,7 @@ hydrate();
923
974
  url,
924
975
  server,
925
976
  fontHeadHTML,
977
+ assetHeadHTML,
926
978
  scripts: allScripts,
927
979
  DocumentComponent,
928
980
  statusCode,
@@ -969,7 +1021,7 @@ hydrate();
969
1021
  pageProps
970
1022
  }) : createElement(pageModule.default, pageProps);
971
1023
  if (wrapWithRouterContext) isrElement = wrapWithRouterContext(isrElement);
972
- const isrHtml = `<!DOCTYPE html><html><head></head><body><div id="__next">${await renderIsrPassToStringAsync(withScriptNonce(isrElement, scriptNonce))}</div>${allScripts}</body></html>`;
1024
+ const isrHtml = `<!DOCTYPE html><html><head>${assetHeadHTML}</head><body><div id="__next">${await renderIsrPassToStringAsync(withScriptNonce(isrElement, scriptNonce))}</div>${allScripts}</body></html>`;
973
1025
  const cacheKey = pagesIsrCacheKey(url.split("?")[0]);
974
1026
  await isrSet(cacheKey, buildPagesCacheValue(isrHtml, pageProps), isrRevalidateSeconds);
975
1027
  setRevalidateDuration(cacheKey, isrRevalidateSeconds);
@@ -1008,14 +1060,15 @@ async function renderErrorPage(server, runner, req, res, url, pagesDir, statusCo
1008
1060
  const matcher = fileMatcher ?? createValidFileMatcher();
1009
1061
  const candidates = statusCode === 404 ? ["404", "_error"] : statusCode === 500 ? ["500", "_error"] : ["_error"];
1010
1062
  for (const candidate of candidates) try {
1011
- const candidatePath = path.join(pagesDir, candidate);
1012
- if (!findFileWithExtensions(candidatePath, matcher)) continue;
1013
- const ErrorComponent = (await importModule(runner, candidatePath)).default;
1063
+ const errorAssetPath = findFileWithExts(pagesDir, candidate, matcher);
1064
+ if (!errorAssetPath) continue;
1065
+ const ErrorComponent = (await importModule(runner, errorAssetPath)).default;
1014
1066
  if (!ErrorComponent) continue;
1015
1067
  let AppComponent = null;
1016
1068
  const appPathErr = path.join(pagesDir, "_app");
1069
+ const appAssetPath = findFileWithExts(pagesDir, "_app", matcher);
1017
1070
  if (findFileWithExtensions(appPathErr, matcher)) try {
1018
- AppComponent = (await importModule(runner, appPathErr)).default ?? null;
1071
+ AppComponent = (await importModule(runner, appAssetPath ?? appPathErr)).default ?? null;
1019
1072
  } catch {}
1020
1073
  const createElement = React.createElement;
1021
1074
  const initialErrorProps = await loadPagesGetInitialProps(ErrorComponent, {
@@ -1051,12 +1104,16 @@ async function renderErrorPage(server, runner, req, res, url, pagesDir, statusCo
1051
1104
  const element = createErrorElement(AppComponent, ErrorComponent);
1052
1105
  const headShim = await importModule(runner, "next/head");
1053
1106
  if (typeof headShim.resetSSRHead === "function") headShim.resetSSRHead();
1107
+ const responseHeaders = typeof res.getHeaders === "function" ? res.getHeaders() : void 0;
1108
+ const nonceAttr = createNonceAttribute(getScriptNonceFromNodeHeaderSources(req.headers, responseHeaders));
1109
+ const assetHeadHTML = await collectDevInitialStylesheetHeadHTML(runner, [appAssetPath, errorAssetPath], nonceAttr);
1054
1110
  if (DocumentComponent) {
1055
1111
  const errorPathname = candidate === "_error" ? "/_error" : `/${candidate}`;
1056
1112
  await streamPageToResponse(res, element, {
1057
1113
  url,
1058
1114
  server,
1059
1115
  fontHeadHTML: "",
1116
+ assetHeadHTML,
1060
1117
  scripts: "",
1061
1118
  DocumentComponent,
1062
1119
  statusCode,
@@ -1084,6 +1141,7 @@ async function renderErrorPage(server, runner, req, res, url, pagesDir, statusCo
1084
1141
  <head>
1085
1142
  <meta charset="utf-8" />
1086
1143
  <meta name="viewport" content="width=device-width, initial-scale=1" />
1144
+ ${assetHeadHTML}
1087
1145
  </head>
1088
1146
  <body>
1089
1147
  <div id="__next">${await renderToStringAsync(element)}</div>
@@ -1,4 +1,4 @@
1
- import { MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_SET_COOKIE_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER } from "../utils/protocol-headers.js";
1
+ import { MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER } from "../utils/protocol-headers.js";
2
2
 
3
3
  //#region src/server/headers.d.ts
4
4
  /**
@@ -33,6 +33,10 @@ declare const VINEXT_PARAMS_HEADER = "X-Vinext-Params";
33
33
  declare const VINEXT_MOUNTED_SLOTS_HEADER = "X-Vinext-Mounted-Slots";
34
34
  /** Per-page dynamic stale time in seconds for App Router RSC responses. */
35
35
  declare const VINEXT_DYNAMIC_STALE_TIME_HEADER = "X-Vinext-Dynamic-Stale-Time";
36
+ /** URL-encoded rendered path and search after middleware/config rewrites. */
37
+ declare const VINEXT_RENDERED_PATH_AND_SEARCH_HEADER = "X-Vinext-Rendered-Path-And-Search";
38
+ /** Prerender-only JSON side channel carrying request cacheLife metadata. */
39
+ declare const VINEXT_PRERENDER_CACHE_LIFE_HEADER = "x-vinext-prerender-cache-life";
36
40
  /** Route interception context for parallel/intercepting routes. */
37
41
  declare const VINEXT_INTERCEPTION_CONTEXT_HEADER = "X-Vinext-Interception-Context";
38
42
  /** RSC render mode (e.g. "navigation", "prefetch"). */
@@ -101,4 +105,4 @@ declare const INTERNAL_HEADERS: string[];
101
105
  /** Vinext-only internal headers stripped alongside Next.js protocol internals. */
102
106
  declare const VINEXT_INTERNAL_HEADERS: string[];
103
107
  //#endregion
104
- export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_HTML_REQUEST_ID_HEADER, NEXT_REQUEST_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_PAGES_STATIC_PATHS_PATH, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_STATIC_PARAMS_PATH, VINEXT_REVALIDATE_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_STATIC_FILE_HEADER, VINEXT_TIMING_HEADER };
108
+ export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_HTML_REQUEST_ID_HEADER, NEXT_REQUEST_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_CACHE_LIFE_HEADER, VINEXT_PRERENDER_PAGES_STATIC_PATHS_PATH, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER, VINEXT_PRERENDER_STATIC_PARAMS_PATH, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, VINEXT_REVALIDATE_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_STATIC_FILE_HEADER, VINEXT_TIMING_HEADER };
@@ -1,4 +1,4 @@
1
- import { MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_OVERRIDE_HEADERS, MIDDLEWARE_SET_COOKIE_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER } from "../utils/protocol-headers.js";
1
+ import { MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_OVERRIDE_HEADERS, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER } from "../utils/protocol-headers.js";
2
2
  //#region src/server/headers.ts
3
3
  /**
4
4
  * Internal HTTP header name constants used throughout vinext.
@@ -32,6 +32,10 @@ const VINEXT_PARAMS_HEADER = "X-Vinext-Params";
32
32
  const VINEXT_MOUNTED_SLOTS_HEADER = "X-Vinext-Mounted-Slots";
33
33
  /** Per-page dynamic stale time in seconds for App Router RSC responses. */
34
34
  const VINEXT_DYNAMIC_STALE_TIME_HEADER = "X-Vinext-Dynamic-Stale-Time";
35
+ /** URL-encoded rendered path and search after middleware/config rewrites. */
36
+ const VINEXT_RENDERED_PATH_AND_SEARCH_HEADER = "X-Vinext-Rendered-Path-And-Search";
37
+ /** Prerender-only JSON side channel carrying request cacheLife metadata. */
38
+ const VINEXT_PRERENDER_CACHE_LIFE_HEADER = "x-vinext-prerender-cache-life";
35
39
  /** Route interception context for parallel/intercepting routes. */
36
40
  const VINEXT_INTERCEPTION_CONTEXT_HEADER = "X-Vinext-Interception-Context";
37
41
  /** RSC render mode (e.g. "navigation", "prefetch"). */
@@ -82,8 +86,6 @@ const MIDDLEWARE_NEXT_HEADER = "x-middleware-next";
82
86
  const MIDDLEWARE_REWRITE_HEADER = "x-middleware-rewrite";
83
87
  /** Redirect URL set by middleware. */
84
88
  const MIDDLEWARE_REDIRECT_HEADER = "x-middleware-redirect";
85
- /** Skip-middleware signal. */
86
- const MIDDLEWARE_SKIP_HEADER = "x-middleware-skip";
87
89
  const NEXT_ROUTER_STATE_TREE_HEADER = "Next-Router-State-Tree";
88
90
  const NEXT_ROUTER_PREFETCH_HEADER = "Next-Router-Prefetch";
89
91
  const NEXT_ROUTER_SEGMENT_PREFETCH_HEADER = "Next-Router-Segment-Prefetch";
@@ -120,6 +122,10 @@ const INTERNAL_HEADERS = [
120
122
  ACTION_FORWARDED_HEADER
121
123
  ];
122
124
  /** Vinext-only internal headers stripped alongside Next.js protocol internals. */
123
- const VINEXT_INTERNAL_HEADERS = [VINEXT_PRERENDER_ROUTE_PARAMS_HEADER];
125
+ const VINEXT_INTERNAL_HEADERS = [
126
+ VINEXT_PRERENDER_ROUTE_PARAMS_HEADER,
127
+ VINEXT_PRERENDER_SPECULATIVE_HEADER,
128
+ VINEXT_PRERENDER_CACHE_LIFE_HEADER
129
+ ];
124
130
  //#endregion
125
- export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_HTML_REQUEST_ID_HEADER, NEXT_REQUEST_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_PAGES_STATIC_PATHS_PATH, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_STATIC_PARAMS_PATH, VINEXT_REVALIDATE_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_STATIC_FILE_HEADER, VINEXT_TIMING_HEADER };
131
+ export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_HTML_REQUEST_ID_HEADER, NEXT_REQUEST_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_CACHE_LIFE_HEADER, VINEXT_PRERENDER_PAGES_STATIC_PATHS_PATH, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER, VINEXT_PRERENDER_STATIC_PARAMS_PATH, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, VINEXT_REVALIDATE_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_STATIC_FILE_HEADER, VINEXT_TIMING_HEADER };
@@ -34,6 +34,7 @@ declare const PRERENDER_REVALIDATE_HEADER = "x-prerender-revalidate";
34
34
  */
35
35
  declare const PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER = "x-prerender-revalidate-if-generated";
36
36
  declare function getRevalidateSecret(): string;
37
+ declare function isRevalidateSecret(value: string | null | undefined): boolean;
37
38
  /**
38
39
  * Authorize an incoming request as an on-demand revalidation trigger. Mirrors
39
40
  * Next.js's `checkIsOnDemandRevalidate`: the {@link PRERENDER_REVALIDATE_HEADER}
@@ -124,4 +125,4 @@ declare function setRevalidateDuration(key: string, seconds: number): void;
124
125
  */
125
126
  declare function getRevalidateDuration(key: string): number | undefined;
126
127
  //#endregion
127
- export { ISRCacheEntry, PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER, appIsrHtmlKey, appIsrRouteKey, appIsrRscKey, buildAppPageCacheValue, buildPagesCacheValue, getRevalidateDuration, getRevalidateSecret, isOnDemandRevalidateRequest, isrCacheKey, isrGet, isrSet, isrSetPrerenderedAppPage, normalizeMountedSlotsHeader, setRevalidateDuration, triggerBackgroundRegeneration };
128
+ export { ISRCacheEntry, PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER, appIsrHtmlKey, appIsrRouteKey, appIsrRscKey, buildAppPageCacheValue, buildPagesCacheValue, getRevalidateDuration, getRevalidateSecret, isOnDemandRevalidateRequest, isRevalidateSecret, isrCacheKey, isrGet, isrSet, isrSetPrerenderedAppPage, normalizeMountedSlotsHeader, setRevalidateDuration, triggerBackgroundRegeneration };
@@ -80,6 +80,10 @@ function safeEqual(a, b) {
80
80
  for (let i = 0; i < a.length; i++) mismatch |= a.charCodeAt(i) ^ b.charCodeAt(i);
81
81
  return mismatch === 0;
82
82
  }
83
+ function isRevalidateSecret(value) {
84
+ if (typeof value !== "string" || value.length === 0) return false;
85
+ return safeEqual(value, getRevalidateSecret());
86
+ }
83
87
  /**
84
88
  * Authorize an incoming request as an on-demand revalidation trigger. Mirrors
85
89
  * Next.js's `checkIsOnDemandRevalidate`: the {@link PRERENDER_REVALIDATE_HEADER}
@@ -87,8 +91,8 @@ function safeEqual(a, b) {
87
91
  * NOT sufficient — see the security note on {@link PRERENDER_REVALIDATE_HEADER}.
88
92
  */
89
93
  function isOnDemandRevalidateRequest(headerValue) {
90
- if (typeof headerValue !== "string" || headerValue.length === 0) return false;
91
- return safeEqual(headerValue, getRevalidateSecret());
94
+ if (typeof headerValue !== "string") return false;
95
+ return isRevalidateSecret(headerValue);
92
96
  }
93
97
  /**
94
98
  * Get a cache entry with staleness information.
@@ -279,4 +283,4 @@ function getRevalidateDuration(key) {
279
283
  return revalidateDurations.get(key);
280
284
  }
281
285
  //#endregion
282
- export { PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER, appIsrHtmlKey, appIsrRouteKey, appIsrRscKey, buildAppPageCacheValue, buildPagesCacheValue, getRevalidateDuration, getRevalidateSecret, isOnDemandRevalidateRequest, isrCacheKey, isrGet, isrSet, isrSetPrerenderedAppPage, normalizeMountedSlotsHeader, setRevalidateDuration, triggerBackgroundRegeneration };
286
+ export { PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER, appIsrHtmlKey, appIsrRouteKey, appIsrRscKey, buildAppPageCacheValue, buildPagesCacheValue, getRevalidateDuration, getRevalidateSecret, isOnDemandRevalidateRequest, isRevalidateSecret, isrCacheKey, isrGet, isrSet, isrSetPrerenderedAppPage, normalizeMountedSlotsHeader, setRevalidateDuration, triggerBackgroundRegeneration };
@@ -51,6 +51,7 @@ declare function isNextDataPathname(pathname: string): boolean;
51
51
  * `/_next/data/<buildId>/about` → null (missing .json suffix)
52
52
  */
53
53
  declare function parseNextDataPathname(pathname: string, buildId: string): NextDataMatch | null;
54
+ declare function normalizeNextDataPagePathname(pagePathname: string, trailingSlash?: boolean): string;
54
55
  /**
55
56
  * Build the JSON envelope returned by `/_next/data/<buildId>/<page>.json`.
56
57
  * Mirrors Next.js' `RenderResult(JSON.stringify(props))` path in
@@ -79,6 +80,7 @@ declare function buildNextDataPropsJsonResponse(props: Record<string, unknown>,
79
80
  * before checking the status code.
80
81
  */
81
82
  declare function buildNextDataNotFoundResponse(): Response;
83
+ declare function buildMiddlewarePrefetchSkipResponse(matchedPathname: string): Response;
82
84
  type NormalizePagesDataRequestResult = {
83
85
  isDataReq: false;
84
86
  request: Request;
@@ -116,6 +118,6 @@ type NormalizePagesDataRequestResult = {
116
118
  * Extracted from `entries/pages-server-entry.ts` so both `renderPage` and
117
119
  * `runMiddleware` share a single implementation.
118
120
  */
119
- declare function normalizePagesDataRequest(request: Request, buildId: string | null, basePath?: string): NormalizePagesDataRequestResult;
121
+ declare function normalizePagesDataRequest(request: Request, buildId: string | null, basePath?: string, trailingSlash?: boolean): NormalizePagesDataRequestResult;
120
122
  //#endregion
121
- export { buildNextDataJsonResponse, buildNextDataNotFoundResponse, buildNextDataPropsJsonResponse, isNextDataPathname, normalizePagesDataRequest, parseNextDataPathname };
123
+ export { buildMiddlewarePrefetchSkipResponse, buildNextDataJsonResponse, buildNextDataNotFoundResponse, buildNextDataPropsJsonResponse, isNextDataPathname, normalizeNextDataPagePathname, normalizePagesDataRequest, parseNextDataPathname };
@@ -1,4 +1,5 @@
1
1
  import { addBasePathToPathname, hasBasePath, stripBasePath } from "../utils/base-path.js";
2
+ import { MIDDLEWARE_SKIP_HEADER } from "../utils/protocol-headers.js";
2
3
  import { NEXTJS_DEPLOYMENT_ID_HEADER } from "./headers.js";
3
4
  //#region src/server/pages-data-route.ts
4
5
  /**
@@ -58,6 +59,10 @@ function parseNextDataPathname(pathname, buildId) {
58
59
  if (rest.startsWith("index/")) return { pagePathname: `/${rest.slice(6)}` };
59
60
  return { pagePathname: `/${rest}` };
60
61
  }
62
+ function normalizeNextDataPagePathname(pagePathname, trailingSlash = false) {
63
+ if (!trailingSlash || pagePathname === "/" || pagePathname.endsWith("/")) return pagePathname;
64
+ return `${pagePathname}/`;
65
+ }
61
66
  /**
62
67
  * Build the JSON envelope returned by `/_next/data/<buildId>/<page>.json`.
63
68
  * Mirrors Next.js' `RenderResult(JSON.stringify(props))` path in
@@ -106,6 +111,14 @@ function buildNextDataNotFoundResponse() {
106
111
  headers
107
112
  });
108
113
  }
114
+ function buildMiddlewarePrefetchSkipResponse(matchedPathname) {
115
+ return new Response("{}", { headers: {
116
+ "Content-Type": "application/json",
117
+ "x-matched-path": matchedPathname,
118
+ [MIDDLEWARE_SKIP_HEADER]: "1",
119
+ "Cache-Control": "private, no-cache, no-store, max-age=0, must-revalidate"
120
+ } });
121
+ }
109
122
  /**
110
123
  * Detect and normalize `/_next/data/<buildId>/<page>.json` requests in one
111
124
  * place so the Pages Router pipeline and middleware shim do not need to know
@@ -124,7 +137,7 @@ function buildNextDataNotFoundResponse() {
124
137
  * Extracted from `entries/pages-server-entry.ts` so both `renderPage` and
125
138
  * `runMiddleware` share a single implementation.
126
139
  */
127
- function normalizePagesDataRequest(request, buildId, basePath = "") {
140
+ function normalizePagesDataRequest(request, buildId, basePath = "", trailingSlash = false) {
128
141
  const reqUrl = new URL(request.url);
129
142
  const hadBasePath = !!basePath && hasBasePath(reqUrl.pathname, basePath);
130
143
  const dataPathname = basePath ? stripBasePath(reqUrl.pathname, basePath) : reqUrl.pathname;
@@ -143,15 +156,16 @@ function normalizePagesDataRequest(request, buildId, basePath = "") {
143
156
  search: "",
144
157
  notFoundResponse: buildNextDataNotFoundResponse()
145
158
  };
159
+ const pagePathname = normalizeNextDataPagePathname(dataMatch.pagePathname, trailingSlash);
146
160
  const normalizedUrl = new URL(reqUrl);
147
- normalizedUrl.pathname = hadBasePath ? addBasePathToPathname(dataMatch.pagePathname, basePath) : dataMatch.pagePathname;
161
+ normalizedUrl.pathname = hadBasePath ? addBasePathToPathname(pagePathname, basePath) : pagePathname;
148
162
  return {
149
163
  isDataReq: true,
150
164
  request: new Request(normalizedUrl, request),
151
- normalizedPathname: dataMatch.pagePathname,
165
+ normalizedPathname: pagePathname,
152
166
  search: reqUrl.search,
153
167
  notFoundResponse: null
154
168
  };
155
169
  }
156
170
  //#endregion
157
- export { buildNextDataJsonResponse, buildNextDataNotFoundResponse, buildNextDataPropsJsonResponse, isNextDataPathname, normalizePagesDataRequest, parseNextDataPathname };
171
+ export { buildMiddlewarePrefetchSkipResponse, buildNextDataJsonResponse, buildNextDataNotFoundResponse, buildNextDataPropsJsonResponse, isNextDataPathname, normalizeNextDataPagePathname, normalizePagesDataRequest, parseNextDataPathname };
@@ -1,4 +1,5 @@
1
1
  //#region src/server/pages-dev-module-url.d.ts
2
+ declare function createPagesDevAssetUrl(assetPath: string): string;
2
3
  declare function createPagesDevModuleUrl(viteRoot: string, moduleFilePath: string, viteBase: string): string;
3
4
  //#endregion
4
- export { createPagesDevModuleUrl };
5
+ export { createPagesDevAssetUrl, createPagesDevModuleUrl };
@@ -4,12 +4,15 @@ function normalizeBase(base) {
4
4
  if (!base || base === "/") return "/";
5
5
  return `/${base.replace(/^\/+|\/+$/g, "")}/`;
6
6
  }
7
- function encodeModulePath(modulePath) {
7
+ function encodePagesDevModulePath(modulePath) {
8
8
  return encodeURI(modulePath).replace(/%5B/gi, "[").replace(/%5D/gi, "]").replace(/\?/g, "%3F").replace(/#/g, "%23");
9
9
  }
10
+ function createPagesDevAssetUrl(assetPath) {
11
+ return "/" + encodePagesDevModulePath(assetPath.replace(/^\/+/, ""));
12
+ }
10
13
  function createPagesDevModuleUrl(viteRoot, moduleFilePath, viteBase) {
11
14
  const relativePath = (/^[A-Za-z]:[\\/]/.test(viteRoot) ? path.win32 : path).relative(viteRoot, moduleFilePath).replace(/\\/g, "/");
12
- return normalizeBase(viteBase) + encodeModulePath(relativePath);
15
+ return normalizeBase(viteBase) + encodePagesDevModulePath(relativePath);
13
16
  }
14
17
  //#endregion
15
- export { createPagesDevModuleUrl };
18
+ export { createPagesDevAssetUrl, createPagesDevModuleUrl };
@@ -27,6 +27,10 @@ type PagesReqResResponse = Writable & {
27
27
  redirect: (statusOrUrl: number | string, url?: string) => void;
28
28
  getHeaders: () => PagesReqResHeaders;
29
29
  revalidate: (urlPath: string, opts?: RevalidateOptions) => Promise<void>;
30
+ setPreviewData: (data: object | string, options?: {
31
+ maxAge?: number;
32
+ path?: string;
33
+ }) => PagesReqResResponse;
30
34
  };
31
35
  type PagesRequestCookiesCarrier = {
32
36
  headers: {
@@ -45,6 +49,7 @@ type CreatePagesReqResResult = {
45
49
  res: PagesReqResResponse;
46
50
  responsePromise: Promise<Response>;
47
51
  };
52
+ type PagesPreviewData = object | string;
48
53
  /**
49
54
  * Read and parse a Pages Router API request body for the Workers/prod path.
50
55
  *
@@ -56,7 +61,13 @@ type CreatePagesReqResResult = {
56
61
  * @see https://nextjs.org/docs/pages/building-your-application/routing/api-routes#custom-config
57
62
  */
58
63
  declare function parsePagesApiBody(request: Request, maxBytes?: number): Promise<unknown>;
64
+ declare function getPagesPreviewDataFromCookieHeader(cookieHeader: string | string[] | null | undefined, options?: {
65
+ isOnDemandRevalidate?: boolean;
66
+ }): PagesPreviewData | false;
67
+ declare function getPagesPreviewData(request: Request, options?: {
68
+ isOnDemandRevalidate?: boolean;
69
+ }): PagesPreviewData | false;
59
70
  declare function attachPagesRequestCookies(req: PagesRequestCookiesCarrier): void;
60
71
  declare function createPagesReqRes(options: CreatePagesReqResOptions): CreatePagesReqResResult;
61
72
  //#endregion
62
- export { PagesBodyParseError as PagesApiBodyParseError, PagesReqResRequest, PagesReqResResponse, PagesRequestQuery, attachPagesRequestCookies, createPagesReqRes, parsePagesApiBody };
73
+ export { PagesBodyParseError as PagesApiBodyParseError, PagesPreviewData, PagesReqResRequest, PagesReqResResponse, PagesRequestQuery, attachPagesRequestCookies, createPagesReqRes, getPagesPreviewData, getPagesPreviewDataFromCookieHeader, parsePagesApiBody };
@@ -1,4 +1,5 @@
1
1
  import { parseCookieHeader } from "../utils/parse-cookie.js";
2
+ import { getRevalidateSecret, isRevalidateSecret } from "./isr-cache.js";
2
3
  import { readStreamAsTextWithLimit } from "../utils/text-stream.js";
3
4
  import { DEFAULT_PAGES_API_BODY_SIZE_LIMIT } from "./pages-body-parser-config.js";
4
5
  import { PagesBodyParseError, getMediaType, isJsonMediaType } from "./pages-media-type.js";
@@ -64,6 +65,44 @@ function createRequestReadable(request) {
64
65
  function parsePagesRequestCookies(cookieHeader) {
65
66
  return parseCookieHeader(Array.isArray(cookieHeader) ? cookieHeader.join("; ") : cookieHeader);
66
67
  }
68
+ function serializePreviewCookie(name, value, options = {}) {
69
+ const parts = [
70
+ `${name}=${encodeURIComponent(value)}`,
71
+ "HttpOnly",
72
+ `Path=${options.path ?? "/"}`,
73
+ process.env.NODE_ENV !== "development" ? "SameSite=None" : "SameSite=Lax"
74
+ ];
75
+ if (process.env.NODE_ENV !== "development") parts.push("Secure");
76
+ if (options.maxAge !== void 0) parts.push(`Max-Age=${Math.trunc(options.maxAge)}`);
77
+ return parts.join("; ");
78
+ }
79
+ function decodePagesPreviewPayload(payload) {
80
+ try {
81
+ const decoded = Buffer.from(payload, "base64url").toString("utf8");
82
+ const value = JSON.parse(decoded);
83
+ return typeof value === "object" && value !== null ? value : String(value);
84
+ } catch {
85
+ return false;
86
+ }
87
+ }
88
+ function getPagesPreviewDataFromCookieHeader(cookieHeader, options = {}) {
89
+ if (options.isOnDemandRevalidate) return false;
90
+ const cookies = parsePagesRequestCookies(cookieHeader);
91
+ const bypass = cookies.__prerender_bypass;
92
+ const payload = cookies.__next_preview_data;
93
+ if (!bypass && !payload) return false;
94
+ if (!isRevalidateSecret(bypass)) return false;
95
+ if (!payload) return {};
96
+ return decodePagesPreviewPayload(payload);
97
+ }
98
+ function getPagesPreviewData(request, options = {}) {
99
+ return getPagesPreviewDataFromCookieHeader(request.headers.get("cookie"), options);
100
+ }
101
+ function normalizeSetCookieHeader(value) {
102
+ if (value === void 0) return [];
103
+ if (Array.isArray(value)) return value.map(String);
104
+ return [String(value)];
105
+ }
67
106
  function attachPagesRequestCookies(req) {
68
107
  if (Object.hasOwn(req, "cookies")) return;
69
108
  Object.defineProperty(req, "cookies", {
@@ -170,6 +209,16 @@ var PagesResponseStream = class extends Writable {
170
209
  async revalidate(urlPath, opts) {
171
210
  await performOnDemandRevalidate(this.requestHeaders, urlPath, opts);
172
211
  }
212
+ setPreviewData(data, options = {}) {
213
+ const payload = Buffer.from(JSON.stringify(data)).toString("base64url");
214
+ if (payload.length > 2048) throw new Error("Preview data is limited to 2KB currently, reduce how much data you are storing as preview data to continue");
215
+ this.setHeader("Set-Cookie", [
216
+ ...normalizeSetCookieHeader(this.getHeader("Set-Cookie")),
217
+ serializePreviewCookie("__prerender_bypass", getRevalidateSecret(), options),
218
+ serializePreviewCookie("__next_preview_data", payload, options)
219
+ ]);
220
+ return this;
221
+ }
173
222
  _write(chunk, encoding, callback) {
174
223
  const buffer = typeof chunk === "string" ? Buffer.from(chunk, encoding) : Buffer.from(chunk);
175
224
  if (this.controller && !this.streamEnded) try {
@@ -260,4 +309,4 @@ function createPagesReqRes(options) {
260
309
  };
261
310
  }
262
311
  //#endregion
263
- export { PagesBodyParseError as PagesApiBodyParseError, attachPagesRequestCookies, createPagesReqRes, parsePagesApiBody };
312
+ export { PagesBodyParseError as PagesApiBodyParseError, attachPagesRequestCookies, createPagesReqRes, getPagesPreviewData, getPagesPreviewDataFromCookieHeader, parsePagesApiBody };
@@ -2,6 +2,7 @@ import { Route } from "../routing/pages-router.js";
2
2
  import { VinextNextData } from "../client/vinext-next-data.js";
3
3
  import { CachedPagesValue } from "../shims/cache-handler.js";
4
4
  import { ISRCacheEntry } from "./isr-cache.js";
5
+ import { PagesPreviewData } from "./pages-node-compat.js";
5
6
  import { PagesGsspResponse, PagesI18nRenderContext, PagesNextDataExtras } from "./pages-page-response.js";
6
7
  import { ReactNode } from "react";
7
8
 
@@ -62,12 +63,18 @@ type PagesPageModule = {
62
63
  locale?: string;
63
64
  locales?: string[];
64
65
  defaultLocale?: string;
66
+ draftMode?: true;
67
+ preview?: true;
68
+ previewData?: PagesPreviewData;
65
69
  }) => Promise<PagesPagePropsResult> | PagesPagePropsResult;
66
70
  getStaticProps?: (context: {
67
71
  params: Record<string, unknown> | null;
68
72
  locale?: string;
69
73
  locales?: string[];
70
74
  defaultLocale?: string;
75
+ draftMode?: true;
76
+ preview?: true;
77
+ previewData?: PagesPreviewData;
71
78
  /**
72
79
  * Indicates why `getStaticProps` was invoked.
73
80
  *
@@ -127,6 +134,7 @@ type ResolvePagesPageDataOptions = {
127
134
  * `.nextjs-ref/packages/next/src/server/render.tsx`.
128
135
  */
129
136
  isBuildTimePrerendering?: boolean;
137
+ validatePropsSerialization?: boolean;
130
138
  /**
131
139
  * When true, this dispatch was triggered by an on-demand revalidation
132
140
  * request (e.g. `res.revalidate()` in a Pages Router API route, or an
@@ -143,6 +151,7 @@ type ResolvePagesPageDataOptions = {
143
151
  * presence — see the security note in `isr-cache.ts`.
144
152
  */
145
153
  isOnDemandRevalidate?: boolean;
154
+ previewData?: PagesPreviewData | false;
146
155
  /**
147
156
  * The deployment ID used for deployment-skew protection. When set, it is
148
157
  * included as `x-nextjs-deployment-id` on all `_next/data` responses