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.
- package/dist/build/css-url-assets.d.ts +3 -1
- package/dist/build/css-url-assets.js +18 -1
- package/dist/build/inject-pregenerated-paths.d.ts +3 -0
- package/dist/build/inject-pregenerated-paths.js +4 -1
- package/dist/build/prerender-server-entry.d.ts +1 -0
- package/dist/build/prerender-server-entry.js +49 -0
- package/dist/build/prerender-server-pool.d.ts +44 -0
- package/dist/build/prerender-server-pool.js +194 -0
- package/dist/build/prerender.js +86 -22
- package/dist/build/run-prerender.js +2 -1
- package/dist/check.d.ts +4 -6
- package/dist/check.js +8 -9
- package/dist/cli.js +1 -1
- package/dist/client/vinext-next-data.d.ts +4 -1
- package/dist/config/config-matchers.js +33 -11
- package/dist/config/next-config.d.ts +36 -6
- package/dist/config/next-config.js +16 -2
- package/dist/entries/app-browser-entry.js +5 -1
- package/dist/entries/app-rsc-entry.d.ts +3 -2
- package/dist/entries/app-rsc-entry.js +4 -0
- package/dist/entries/pages-client-entry.js +14 -3
- package/dist/entries/pages-server-entry.js +18 -4
- package/dist/index.js +320 -25
- package/dist/packages/cloudflare/src/cache/kv-data-adapter.runtime.js +8 -1
- package/dist/packages/cloudflare/src/deploy.js +1 -5
- package/dist/packages/cloudflare/src/tpr.js +26 -5
- package/dist/plugins/fonts.js +1 -1
- package/dist/plugins/ignore-dynamic-requests.js +1 -1
- package/dist/plugins/og-assets.js +2 -1
- package/dist/plugins/optimize-imports.js +1 -1
- package/dist/plugins/rsc-reference-validation-normalizer.d.ts +12 -0
- package/dist/plugins/rsc-reference-validation-normalizer.js +48 -0
- package/dist/plugins/sass.d.ts +17 -1
- package/dist/plugins/sass.js +74 -1
- package/dist/plugins/styled-jsx.d.ts +16 -0
- package/dist/plugins/styled-jsx.js +149 -0
- package/dist/routing/app-route-graph.js +22 -16
- package/dist/routing/file-matcher.d.ts +8 -1
- package/dist/routing/file-matcher.js +15 -3
- package/dist/server/app-browser-entry.js +46 -14
- package/dist/server/app-browser-navigation-controller.js +1 -1
- package/dist/server/app-browser-server-action-client.js +5 -3
- package/dist/server/app-browser-state.d.ts +1 -0
- package/dist/server/app-browser-state.js +2 -2
- package/dist/server/app-browser-visible-commit.js +1 -1
- package/dist/server/app-page-boundary.js +2 -1
- package/dist/server/app-page-cache-finalizer.d.ts +1 -0
- package/dist/server/app-page-cache-finalizer.js +3 -3
- package/dist/server/app-page-cache.js +23 -6
- package/dist/server/app-page-dispatch.d.ts +1 -0
- package/dist/server/app-page-dispatch.js +25 -8
- package/dist/server/app-page-element-builder.js +1 -0
- package/dist/server/app-page-execution.js +2 -1
- package/dist/server/app-page-render-identity.d.ts +1 -0
- package/dist/server/app-page-render-identity.js +2 -1
- package/dist/server/app-page-render.d.ts +2 -0
- package/dist/server/app-page-render.js +67 -16
- package/dist/server/app-page-response.d.ts +7 -0
- package/dist/server/app-page-response.js +16 -4
- package/dist/server/app-page-stream.d.ts +2 -1
- package/dist/server/app-page-stream.js +1 -1
- package/dist/server/app-route-handler-execution.js +1 -1
- package/dist/server/app-route-tree-prefetch.d.ts +43 -0
- package/dist/server/app-route-tree-prefetch.js +187 -0
- package/dist/server/app-router-entry.js +1 -1
- package/dist/server/app-rsc-cache-busting.d.ts +2 -1
- package/dist/server/app-rsc-cache-busting.js +9 -4
- package/dist/server/app-rsc-handler.d.ts +6 -0
- package/dist/server/app-rsc-handler.js +89 -11
- package/dist/server/app-rsc-render-mode.d.ts +3 -2
- package/dist/server/app-rsc-render-mode.js +4 -1
- package/dist/server/app-rsc-request-normalization.d.ts +3 -4
- package/dist/server/app-rsc-request-normalization.js +4 -5
- package/dist/server/app-server-action-execution.js +4 -3
- package/dist/server/app-ssr-entry.js +3 -3
- package/dist/server/cache-control.d.ts +3 -1
- package/dist/server/cache-control.js +13 -1
- package/dist/server/dev-server.d.ts +11 -1
- package/dist/server/dev-server.js +78 -20
- package/dist/server/headers.d.ts +6 -2
- package/dist/server/headers.js +11 -5
- package/dist/server/isr-cache.d.ts +2 -1
- package/dist/server/isr-cache.js +7 -3
- package/dist/server/pages-data-route.d.ts +4 -2
- package/dist/server/pages-data-route.js +18 -4
- package/dist/server/pages-dev-module-url.d.ts +2 -1
- package/dist/server/pages-dev-module-url.js +6 -3
- package/dist/server/pages-node-compat.d.ts +12 -1
- package/dist/server/pages-node-compat.js +50 -1
- package/dist/server/pages-page-data.d.ts +9 -0
- package/dist/server/pages-page-data.js +17 -9
- package/dist/server/pages-page-handler.js +19 -7
- package/dist/server/pages-page-response.d.ts +1 -0
- package/dist/server/pages-page-response.js +3 -2
- package/dist/server/pages-request-pipeline.d.ts +8 -6
- package/dist/server/pages-request-pipeline.js +30 -6
- package/dist/server/pages-router-entry.js +1 -1
- package/dist/server/prod-server.d.ts +4 -2
- package/dist/server/prod-server.js +38 -22
- package/dist/server/request-pipeline.js +1 -1
- package/dist/shims/cache-handler.js +8 -1
- package/dist/shims/cache.js +3 -0
- package/dist/shims/fetch-cache.d.ts +3 -1
- package/dist/shims/fetch-cache.js +77 -52
- package/dist/shims/form.d.ts +1 -1
- package/dist/shims/internal/app-prefetch-fetch-queue.d.ts +10 -0
- package/dist/shims/internal/app-prefetch-fetch-queue.js +61 -0
- package/dist/shims/internal/hybrid-client-route-owner.d.ts +2 -1
- package/dist/shims/internal/hybrid-client-route-owner.js +34 -1
- package/dist/shims/internal/pages-data-fetch-dedup.d.ts +2 -1
- package/dist/shims/internal/pages-data-fetch-dedup.js +13 -2
- package/dist/shims/internal/pages-data-target.d.ts +8 -3
- package/dist/shims/internal/pages-data-target.js +9 -4
- package/dist/shims/link.d.ts +1 -1
- package/dist/shims/link.js +179 -42
- package/dist/shims/navigation.d.ts +15 -3
- package/dist/shims/navigation.js +202 -39
- package/dist/shims/navigation.react-server.js +1 -1
- package/dist/shims/request-context.js +18 -0
- package/dist/shims/router.js +31 -15
- package/dist/shims/unified-request-context.js +1 -0
- package/dist/typegen.js +1 -1
- package/dist/utils/middleware-request-headers.js +1 -1
- package/dist/utils/protocol-headers.d.ts +7 -1
- package/dist/utils/protocol-headers.js +7 -1
- package/dist/utils/vite-version.d.ts +2 -0
- package/dist/utils/vite-version.js +11 -1
- 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 {
|
|
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
|
-
|
|
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
|
|
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
|
|
1012
|
-
if (!
|
|
1013
|
-
const ErrorComponent = (await importModule(runner,
|
|
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>
|
package/dist/server/headers.d.ts
CHANGED
|
@@ -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 };
|
package/dist/server/headers.js
CHANGED
|
@@ -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 = [
|
|
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 };
|
package/dist/server/isr-cache.js
CHANGED
|
@@ -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"
|
|
91
|
-
return
|
|
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(
|
|
161
|
+
normalizedUrl.pathname = hadBasePath ? addBasePathToPathname(pagePathname, basePath) : pagePathname;
|
|
148
162
|
return {
|
|
149
163
|
isDataReq: true,
|
|
150
164
|
request: new Request(normalizedUrl, request),
|
|
151
|
-
normalizedPathname:
|
|
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
|
|
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) +
|
|
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
|