vinext 0.2.0 → 1.0.0-beta.0
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/README.md +50 -29
- package/dist/build/client-build-config.d.ts +13 -92
- package/dist/build/client-build-config.js +17 -95
- 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-paths.d.ts +23 -0
- package/dist/build/prerender-paths.js +296 -0
- 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.d.ts +12 -1
- package/dist/build/prerender.js +87 -23
- package/dist/build/run-prerender.js +2 -1
- package/dist/cache/cache-adapters-virtual.d.ts +7 -1
- package/dist/cache/cache-adapters-virtual.js +26 -2
- package/dist/check.d.ts +4 -6
- package/dist/check.js +13 -9
- package/dist/cli.js +34 -54
- 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 +17 -4
- package/dist/config/prerender.d.ts +11 -1
- package/dist/config/prerender.js +19 -1
- package/dist/config/tsconfig-paths.js +5 -1
- 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/image/image-adapters-virtual.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +549 -152
- package/dist/init-cloudflare.js +12 -3
- package/dist/init-platform.d.ts +10 -1
- package/dist/init-platform.js +78 -12
- package/dist/init.d.ts +11 -3
- package/dist/init.js +129 -31
- 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 +44 -14
- package/dist/server/app-browser-navigation-controller.js +5 -2
- 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 +1 -1
- package/dist/server/app-layout-param-observation.d.ts +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-render.d.ts +1 -1
- package/dist/server/app-page-cache.js +23 -6
- package/dist/server/app-page-dispatch.d.ts +2 -1
- package/dist/server/app-page-dispatch.js +27 -10
- 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 +3 -1
- 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-route-wiring.js +3 -3
- 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.d.ts +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 -5
- package/dist/server/app-rsc-handler.d.ts +6 -0
- package/dist/server/app-rsc-handler.js +88 -10
- package/dist/server/app-rsc-render-mode.d.ts +3 -5
- package/dist/server/app-rsc-render-mode.js +5 -12
- 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 +6 -5
- package/dist/server/app-ssr-entry.js +1 -1
- package/dist/server/cache-control.d.ts +3 -1
- package/dist/server/cache-control.js +13 -1
- package/dist/server/dev-module-runner.js +1 -1
- package/dist/server/dev-origin-check.d.ts +2 -2
- package/dist/server/dev-origin-check.js +2 -2
- package/dist/server/dev-server.d.ts +11 -1
- package/dist/server/dev-server.js +82 -24
- package/dist/server/headers.d.ts +6 -2
- package/dist/server/headers.js +11 -5
- package/dist/server/image-optimization.d.ts +12 -1
- package/dist/server/image-optimization.js +13 -1
- package/dist/server/isr-cache.d.ts +13 -4
- package/dist/server/isr-cache.js +8 -4
- 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 +18 -10
- 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 +5 -4
- package/dist/server/pages-request-pipeline.d.ts +8 -6
- package/dist/server/pages-request-pipeline.js +31 -7
- package/dist/server/pages-router-entry.js +1 -1
- package/dist/server/prerender-manifest.d.ts +15 -1
- package/dist/server/prerender-manifest.js +29 -1
- package/dist/server/prod-server.d.ts +10 -7
- package/dist/server/prod-server.js +43 -28
- package/dist/server/request-pipeline.js +1 -1
- package/dist/server/static-file-cache.js +1 -1
- package/dist/shims/cache-handler.js +8 -1
- package/dist/shims/cache.d.ts +1 -1
- package/dist/shims/cache.js +3 -0
- package/dist/shims/cdn-cache.d.ts +2 -7
- package/dist/shims/cdn-cache.js +2 -14
- package/dist/shims/fetch-cache.d.ts +3 -1
- package/dist/shims/fetch-cache.js +77 -52
- 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/request-context.js +18 -0
- package/dist/shims/request-state-types.d.ts +2 -2
- package/dist/shims/router.js +31 -15
- package/dist/shims/unified-request-context.d.ts +1 -1
- 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/project.d.ts +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 +4 -12
- package/dist/utils/vite-version.js +39 -21
- package/package.json +31 -13
- package/dist/cloudflare/index.d.ts +0 -3
- package/dist/cloudflare/index.js +0 -3
- package/dist/packages/cloudflare/src/cache/cdn-adapter.runtime.js +0 -102
- package/dist/packages/cloudflare/src/cache/kv-data-adapter.runtime.d.ts +0 -126
- package/dist/packages/cloudflare/src/cache/kv-data-adapter.runtime.js +0 -435
- package/dist/packages/cloudflare/src/deploy-config.js +0 -150
- package/dist/packages/cloudflare/src/deploy-help.js +0 -55
- package/dist/packages/cloudflare/src/deploy.js +0 -276
- package/dist/packages/cloudflare/src/tpr.d.ts +0 -45
- package/dist/packages/cloudflare/src/tpr.js +0 -561
- package/dist/packages/cloudflare/src/utils/cache-control-metadata.js +0 -20
|
@@ -27,8 +27,20 @@ const IMAGE_OPTIMIZATION_PATH = "/_next/image";
|
|
|
27
27
|
* for any newly generated URLs.
|
|
28
28
|
*/
|
|
29
29
|
const VINEXT_IMAGE_OPTIMIZATION_PATH = "/_vinext/image";
|
|
30
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* Returns true when `pathname` is either supported image optimization
|
|
32
|
+
* endpoint.
|
|
33
|
+
*
|
|
34
|
+
* A single trailing slash is accepted (`/_next/image/`): with
|
|
35
|
+
* `trailingSlash: true`, Next.js 308-redirects `/_next/image?url=...` to
|
|
36
|
+
* `/_next/image/?url=...` and then serves the slashed form — its route
|
|
37
|
+
* matching strips a trailing slash before matching internal paths (see
|
|
38
|
+
* getItem in packages/next/src/server/lib/router-utils/filesystem.ts).
|
|
39
|
+
* Rejecting the slashed form 404'd every dev-mode next/image request under
|
|
40
|
+
* `trailingSlash: true`.
|
|
41
|
+
*/
|
|
31
42
|
function isImageOptimizationPath(pathname) {
|
|
43
|
+
if (pathname.length > 1 && pathname.endsWith("/")) pathname = pathname.slice(0, -1);
|
|
32
44
|
return pathname === "/_next/image" || pathname === "/_vinext/image";
|
|
33
45
|
}
|
|
34
46
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { RenderObservation } from "./cache-proof.js";
|
|
2
|
-
import { CacheHandlerValue, CachedAppPageValue, CachedPagesValue, IncrementalCacheValue } from "../shims/cache-handler.js";
|
|
3
1
|
import { OnRequestErrorContext } from "./instrumentation.js";
|
|
2
|
+
import { RenderObservation } from "./cache-proof.js";
|
|
4
3
|
import { normalizeMountedSlotsHeader } from "./app-mounted-slots-header.js";
|
|
4
|
+
import { CacheHandlerValue, CachedAppPageValue, CachedPagesValue, IncrementalCacheValue } from "../shims/cache-handler.js";
|
|
5
5
|
import { AppRscRenderMode } from "./app-rsc-render-mode.js";
|
|
6
6
|
|
|
7
7
|
//#region src/server/isr-cache.d.ts
|
|
@@ -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}
|
|
@@ -101,13 +102,21 @@ declare function buildAppPageCacheValue(html: string, rscData?: ArrayBuffer, sta
|
|
|
101
102
|
* Long pathnames are hashed to stay within KV key-length limits (512 bytes).
|
|
102
103
|
*/
|
|
103
104
|
declare function isrCacheKey(router: string, pathname: string, buildId?: string): string;
|
|
105
|
+
/**
|
|
106
|
+
* Compute an App Router ISR key for one cache artifact.
|
|
107
|
+
*
|
|
108
|
+
* App pages store HTML, RSC payloads, and route-handler responses separately.
|
|
109
|
+
* The suffix mirrors Next.js's separate on-disk app artifacts while keeping the
|
|
110
|
+
* Cloudflare KV key under its 512-byte limit for long pathnames.
|
|
111
|
+
*/
|
|
112
|
+
declare function appIsrCacheKey(pathname: string, suffix: string, buildId?: string | undefined): string;
|
|
104
113
|
declare function appIsrHtmlKey(pathname: string): string;
|
|
105
114
|
/**
|
|
106
115
|
* Build the ISR cache key for an RSC payload.
|
|
107
116
|
*
|
|
108
117
|
* Variants are sequenced in order: `source:<hash>` (intercepted source context,
|
|
109
118
|
* only when an interception context is present), `slots:<hash>` (mounted parallel
|
|
110
|
-
* route slots), and optionally `<render-mode-variant>` (
|
|
119
|
+
* route slots), and optionally `<render-mode-variant>` (for example,
|
|
111
120
|
* `prefetch-loading-shell`). Existing cached entries under the old format will
|
|
112
121
|
* become unreachable after deployment. This is acceptable because ISR entries
|
|
113
122
|
* have TTLs and will be regenerated on the next request.
|
|
@@ -124,4 +133,4 @@ declare function setRevalidateDuration(key: string, seconds: number): void;
|
|
|
124
133
|
*/
|
|
125
134
|
declare function getRevalidateDuration(key: string): number | undefined;
|
|
126
135
|
//#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 };
|
|
136
|
+
export { ISRCacheEntry, PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER, appIsrCacheKey, 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.
|
|
@@ -238,7 +242,7 @@ function normalizeInterceptionContextForCacheKey(interceptionContext) {
|
|
|
238
242
|
*
|
|
239
243
|
* Variants are sequenced in order: `source:<hash>` (intercepted source context,
|
|
240
244
|
* only when an interception context is present), `slots:<hash>` (mounted parallel
|
|
241
|
-
* route slots), and optionally `<render-mode-variant>` (
|
|
245
|
+
* route slots), and optionally `<render-mode-variant>` (for example,
|
|
242
246
|
* `prefetch-loading-shell`). Existing cached entries under the old format will
|
|
243
247
|
* become unreachable after deployment. This is acceptable because ISR entries
|
|
244
248
|
* have TTLs and will be regenerated on the next request.
|
|
@@ -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, appIsrCacheKey, 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
|
|
@@ -179,7 +179,7 @@ function buildPagesCacheResponse(html, cacheState, fontLinkHeader, revalidateSec
|
|
|
179
179
|
cacheControlMeta: cacheControl
|
|
180
180
|
});
|
|
181
181
|
const headers = new Headers({
|
|
182
|
-
"Content-Type": "text/html",
|
|
182
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
183
183
|
...buildCacheStateHeaders(cacheState)
|
|
184
184
|
});
|
|
185
185
|
applyCdnResponseHeaders(headers, { cacheControl: cacheControlHeader });
|
|
@@ -264,6 +264,12 @@ async function resolvePagesPageData(options) {
|
|
|
264
264
|
}
|
|
265
265
|
let pageProps = {};
|
|
266
266
|
let gsspRes = null;
|
|
267
|
+
const previewData = options.isOnDemandRevalidate ? false : options.previewData ?? false;
|
|
268
|
+
const previewContext = previewData === false ? {} : {
|
|
269
|
+
draftMode: true,
|
|
270
|
+
preview: true,
|
|
271
|
+
previewData
|
|
272
|
+
};
|
|
267
273
|
let sharedReqRes = null;
|
|
268
274
|
function getSharedReqRes() {
|
|
269
275
|
sharedReqRes ??= options.createGsspReqRes();
|
|
@@ -317,7 +323,8 @@ async function resolvePagesPageData(options) {
|
|
|
317
323
|
resolvedUrl: options.resolvedUrl ?? options.routeUrl,
|
|
318
324
|
locale: options.i18n.locale,
|
|
319
325
|
locales: options.i18n.locales,
|
|
320
|
-
defaultLocale: options.i18n.defaultLocale
|
|
326
|
+
defaultLocale: options.i18n.defaultLocale,
|
|
327
|
+
...previewContext
|
|
321
328
|
});
|
|
322
329
|
if (isResponseSent(res)) return {
|
|
323
330
|
kind: "response",
|
|
@@ -338,7 +345,7 @@ async function resolvePagesPageData(options) {
|
|
|
338
345
|
response: buildPagesRedirectResponse(result.redirect, options, renderProps)
|
|
339
346
|
};
|
|
340
347
|
if (result?.notFound) return buildPagesNotFoundResult(options);
|
|
341
|
-
if (result?.props !== void 0) isSerializableProps(options.routePattern, "getServerSideProps", pageProps);
|
|
348
|
+
if (result?.props !== void 0 && options.validatePropsSerialization !== false) isSerializableProps(options.routePattern, "getServerSideProps", pageProps);
|
|
342
349
|
gsspRes = res;
|
|
343
350
|
}
|
|
344
351
|
let isrRevalidateSeconds = null;
|
|
@@ -347,7 +354,7 @@ async function resolvePagesPageData(options) {
|
|
|
347
354
|
const cacheKey = options.isrCacheKey("pages", pathname);
|
|
348
355
|
const cached = await options.isrGet(cacheKey);
|
|
349
356
|
const cachedValue = cached?.value.value;
|
|
350
|
-
if (!options.isOnDemandRevalidate && cached?.isStale === false && cachedValue?.kind === "PAGES" && !cachedValue.generatedFromDataRequest && cached && !cached.isStale && !options.scriptNonce && !options.isDataReq) {
|
|
357
|
+
if (!options.isOnDemandRevalidate && cached?.isStale === false && cachedValue?.kind === "PAGES" && !cachedValue.generatedFromDataRequest && cached && !cached.isStale && !options.scriptNonce && !options.isDataReq && previewData === false) {
|
|
351
358
|
const hitResponse = buildPagesCacheResponse(cachedValue.html, "HIT", options.fontLinkHeader, void 0, options.expireSeconds, cached.value.cacheControl, cachedValue.status);
|
|
352
359
|
const hitBotResult = applyBotETagAndCheck(hitResponse, cachedValue.html, options);
|
|
353
360
|
if (hitBotResult) return hitBotResult;
|
|
@@ -356,7 +363,7 @@ async function resolvePagesPageData(options) {
|
|
|
356
363
|
response: hitResponse
|
|
357
364
|
};
|
|
358
365
|
}
|
|
359
|
-
if (!options.isOnDemandRevalidate && cachedValue?.kind === "PAGES" && !cachedValue.generatedFromDataRequest && cached && cached.isStale && !options.scriptNonce && !options.isDataReq) {
|
|
366
|
+
if (!options.isOnDemandRevalidate && cachedValue?.kind === "PAGES" && !cachedValue.generatedFromDataRequest && cached && cached.isStale && !options.scriptNonce && !options.isDataReq && previewData === false) {
|
|
360
367
|
options.triggerBackgroundRegeneration(cacheKey, async function() {
|
|
361
368
|
return options.runInFreshUnifiedContext(async () => {
|
|
362
369
|
options.applyRequestContexts();
|
|
@@ -413,7 +420,7 @@ async function resolvePagesPageData(options) {
|
|
|
413
420
|
response: staleResponse
|
|
414
421
|
};
|
|
415
422
|
}
|
|
416
|
-
const generatedPageData = !options.isOnDemandRevalidate && cached?.isStale === false && cachedValue?.kind === "PAGES" && cachedValue.generatedFromDataRequest && isUnknownRecord(cachedValue.pageData) ? cachedValue.pageData : null;
|
|
423
|
+
const generatedPageData = !options.isOnDemandRevalidate && previewData === false && cached?.isStale === false && cachedValue?.kind === "PAGES" && cachedValue.generatedFromDataRequest && isUnknownRecord(cachedValue.pageData) ? cachedValue.pageData : null;
|
|
417
424
|
if (!generatedPageData) {
|
|
418
425
|
const shortCircuit = await loadForegroundAppInitialRenderProps();
|
|
419
426
|
if (shortCircuit) return shortCircuit;
|
|
@@ -423,6 +430,7 @@ async function resolvePagesPageData(options) {
|
|
|
423
430
|
locale: options.i18n.locale,
|
|
424
431
|
locales: options.i18n.locales,
|
|
425
432
|
defaultLocale: options.i18n.defaultLocale,
|
|
433
|
+
...previewContext,
|
|
426
434
|
revalidateReason: options.isOnDemandRevalidate ? "on-demand" : options.isBuildTimePrerendering ? "build" : "stale"
|
|
427
435
|
});
|
|
428
436
|
if (generatedPageData) {
|
|
@@ -444,10 +452,10 @@ async function resolvePagesPageData(options) {
|
|
|
444
452
|
response: buildPagesRedirectResponse(result.redirect, options, renderProps)
|
|
445
453
|
};
|
|
446
454
|
if (result?.notFound) return buildPagesNotFoundResult(options);
|
|
447
|
-
if (result?.props !== void 0) isSerializableProps(options.routePattern, "getStaticProps", pageProps);
|
|
448
|
-
if (typeof result?.revalidate === "number" && result.revalidate > 0) isrRevalidateSeconds = result.revalidate;
|
|
449
|
-
else if (cachedValue?.kind === "PAGES" && cachedValue.generatedFromDataRequest) isrRevalidateSeconds = cached?.value.cacheControl?.revalidate ?? 31536e3;
|
|
450
|
-
if (shouldPersistFallbackData) {
|
|
455
|
+
if (result?.props !== void 0 && options.validatePropsSerialization !== false) isSerializableProps(options.routePattern, "getStaticProps", pageProps);
|
|
456
|
+
if (previewData === false && typeof result?.revalidate === "number" && result.revalidate > 0) isrRevalidateSeconds = result.revalidate;
|
|
457
|
+
else if (previewData === false && cachedValue?.kind === "PAGES" && cachedValue.generatedFromDataRequest) isrRevalidateSeconds = cached?.value.cacheControl?.revalidate ?? 31536e3;
|
|
458
|
+
if (shouldPersistFallbackData && previewData === false) {
|
|
451
459
|
const revalidateSeconds = isrRevalidateSeconds ?? 31536e3;
|
|
452
460
|
await options.isrSet(cacheKey, {
|
|
453
461
|
kind: "PAGES",
|
|
@@ -3,8 +3,8 @@ import { patternToNextFormat } from "../routing/route-validation.js";
|
|
|
3
3
|
import { getRequestExecutionContext } from "../shims/request-context.js";
|
|
4
4
|
import { NEXTJS_DEPLOYMENT_ID_HEADER } from "./headers.js";
|
|
5
5
|
import { reportRequestError } from "./instrumentation.js";
|
|
6
|
-
import { NEVER_CACHE_CONTROL } from "./cache-control.js";
|
|
7
|
-
import "./isr-decision.js";
|
|
6
|
+
import { BROWSER_REVALIDATE_CACHE_CONTROL, NEVER_CACHE_CONTROL, applyCdnResponseHeaders, shouldUseNextDeployCacheControl } from "./cache-control.js";
|
|
7
|
+
import { buildMissIsrCacheControl } from "./isr-decision.js";
|
|
8
8
|
import { PRERENDER_REVALIDATE_HEADER, isOnDemandRevalidateRequest, isrCacheKey, isrGet, isrSet, triggerBackgroundRegeneration } from "./isr-cache.js";
|
|
9
9
|
import { ensureFetchPatch } from "../shims/fetch-cache.js";
|
|
10
10
|
import { appendAssetDeploymentIdQuery } from "../utils/deployment-id.js";
|
|
@@ -18,8 +18,8 @@ import { renderPagesPageResponse } from "./pages-page-response.js";
|
|
|
18
18
|
import { hasPagesGetInitialProps } from "./pages-get-initial-props.js";
|
|
19
19
|
import { buildNextDataNotFoundResponse, buildNextDataPropsJsonResponse, normalizePagesDataRequest, parseNextDataPathname } from "./pages-data-route.js";
|
|
20
20
|
import { resolvePagesPageData } from "./pages-page-data.js";
|
|
21
|
-
import { createPagesReqRes } from "./pages-node-compat.js";
|
|
22
21
|
import { collectAssetTags, resolveClientModuleUrl } from "./pages-asset-tags.js";
|
|
22
|
+
import { createPagesReqRes, getPagesPreviewData } from "./pages-node-compat.js";
|
|
23
23
|
//#region src/server/pages-page-handler.ts
|
|
24
24
|
function shouldEmitPagesClientTraceMetadata(pageModule, appComponent) {
|
|
25
25
|
if (typeof pageModule.getServerSideProps === "function") return true;
|
|
@@ -61,7 +61,7 @@ function createPagesPageHandler(opts) {
|
|
|
61
61
|
const originalRequestPathAndSearch = originalRequestUrl.pathname + originalRequestUrl.search;
|
|
62
62
|
let dataRequestPathname = null;
|
|
63
63
|
let dataRequestSearch = "";
|
|
64
|
-
const initialDataNorm = normalizePagesDataRequest(request, buildId);
|
|
64
|
+
const initialDataNorm = normalizePagesDataRequest(request, buildId, vinextConfig.basePath, hasMiddleware && vinextConfig.trailingSlash);
|
|
65
65
|
if (!isDataReq) {
|
|
66
66
|
if (initialDataNorm.notFoundResponse) return initialDataNorm.notFoundResponse;
|
|
67
67
|
if (initialDataNorm.isDataReq) {
|
|
@@ -133,6 +133,7 @@ function createPagesPageHandler(opts) {
|
|
|
133
133
|
const renderStatusCode = renderStatusCodeOverride ?? (routePattern === "/404" ? 404 : void 0);
|
|
134
134
|
const query = mergeRouteParamsIntoQuery(parseQueryString(routeUrl), params);
|
|
135
135
|
const pageModule = route.module;
|
|
136
|
+
const isStaticPropsRoute = typeof pageModule.getStaticProps === "function";
|
|
136
137
|
const pagesNextData = buildPagesReadinessNextData({
|
|
137
138
|
pageModule,
|
|
138
139
|
appComponent: AppComponent,
|
|
@@ -177,7 +178,8 @@ function createPagesPageHandler(opts) {
|
|
|
177
178
|
...pagesNextData.__vinext,
|
|
178
179
|
pageModuleUrl,
|
|
179
180
|
appModuleUrl,
|
|
180
|
-
hasMiddleware
|
|
181
|
+
hasMiddleware,
|
|
182
|
+
routeUrl
|
|
181
183
|
}
|
|
182
184
|
};
|
|
183
185
|
const scriptNonce = getScriptNonceFromHeaderSources(request.headers, middlewareHeaders);
|
|
@@ -194,6 +196,7 @@ function createPagesPageHandler(opts) {
|
|
|
194
196
|
request,
|
|
195
197
|
url: originalRequestPathAndSearch
|
|
196
198
|
});
|
|
199
|
+
const isOnDemandRevalidate = isOnDemandRevalidateRequest(request.headers.get(PRERENDER_REVALIDATE_HEADER));
|
|
197
200
|
const pageDataResult = await resolvePagesPageData({
|
|
198
201
|
isDataReq,
|
|
199
202
|
err: err instanceof Error ? err : void 0,
|
|
@@ -217,7 +220,9 @@ function createPagesPageHandler(opts) {
|
|
|
217
220
|
isrSet,
|
|
218
221
|
expireSeconds: vinextConfig.expireTime,
|
|
219
222
|
isBuildTimePrerendering: typeof process !== "undefined" && process.env && process.env.VINEXT_PRERENDER === "1",
|
|
220
|
-
|
|
223
|
+
validatePropsSerialization: process.env.NODE_ENV !== "production" || process.env.VINEXT_PRERENDER === "1",
|
|
224
|
+
isOnDemandRevalidate,
|
|
225
|
+
previewData: getPagesPreviewData(request, { isOnDemandRevalidate }),
|
|
221
226
|
pageModule,
|
|
222
227
|
AppComponent,
|
|
223
228
|
params,
|
|
@@ -300,6 +305,12 @@ function createPagesPageHandler(opts) {
|
|
|
300
305
|
break;
|
|
301
306
|
}
|
|
302
307
|
if (!hasUserCacheControl) init.headers["Cache-Control"] = NEVER_CACHE_CONTROL;
|
|
308
|
+
} else if (isStaticPropsRoute) {
|
|
309
|
+
if (isrRevalidateSeconds) {
|
|
310
|
+
const headers = new Headers(init.headers);
|
|
311
|
+
applyCdnResponseHeaders(headers, { cacheControl: buildMissIsrCacheControl(isrRevalidateSeconds, vinextConfig.expireTime) });
|
|
312
|
+
for (const [key, value] of headers) init.headers[key] = value;
|
|
313
|
+
} else if (shouldUseNextDeployCacheControl()) init.headers["Cache-Control"] = BROWSER_REVALIDATE_CACHE_CONTROL;
|
|
303
314
|
}
|
|
304
315
|
if (routePattern !== "/_error" && routePattern !== "/500") {
|
|
305
316
|
const deploymentId = process.env.__VINEXT_DEPLOYMENT_ID || process.env.NEXT_DEPLOYMENT_ID;
|
|
@@ -308,8 +319,8 @@ function createPagesPageHandler(opts) {
|
|
|
308
319
|
return buildNextDataPropsJsonResponse(renderProps, safeJsonStringify, init);
|
|
309
320
|
}
|
|
310
321
|
const pageModuleIds = [];
|
|
311
|
-
if (route.filePath) pageModuleIds.push(route.filePath);
|
|
312
322
|
if (appAssetPath) pageModuleIds.push(appAssetPath);
|
|
323
|
+
if (route.filePath) pageModuleIds.push(route.filePath);
|
|
313
324
|
return await renderPagesPageResponse({
|
|
314
325
|
assetTags: collectAssetTags({
|
|
315
326
|
manifest,
|
|
@@ -346,6 +357,7 @@ function createPagesPageHandler(opts) {
|
|
|
346
357
|
isrCacheKey: pageIsrCacheKey,
|
|
347
358
|
expireSeconds: vinextConfig.expireTime,
|
|
348
359
|
isrRevalidateSeconds,
|
|
360
|
+
isStaticPropsRoute,
|
|
349
361
|
isrSet,
|
|
350
362
|
i18n: buildI18nRenderContext(i18nConfig, locale, currentDefaultLocale, domainLocales),
|
|
351
363
|
isFallback: isFallbackRender,
|
|
@@ -91,6 +91,7 @@ type RenderPagesPageResponseOptions = {
|
|
|
91
91
|
isrCacheKey: (router: string, pathname: string) => string;
|
|
92
92
|
expireSeconds?: number;
|
|
93
93
|
isrRevalidateSeconds: number | null;
|
|
94
|
+
isStaticPropsRoute?: boolean;
|
|
94
95
|
isrSet: (key: string, data: CachedPagesValue, revalidateSeconds: number, tags?: string[], expireSeconds?: number) => Promise<void>;
|
|
95
96
|
i18n: PagesI18nRenderContext;
|
|
96
97
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getRequestExecutionContext } from "../shims/request-context.js";
|
|
2
2
|
import { reportRequestError } from "./instrumentation.js";
|
|
3
3
|
import { setCacheStateHeaders } from "./cache-headers.js";
|
|
4
|
-
import { NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL, applyCdnResponseHeaders } from "./cache-control.js";
|
|
4
|
+
import { BROWSER_REVALIDATE_CACHE_CONTROL, NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL, applyCdnResponseHeaders, shouldUseNextDeployCacheControl } from "./cache-control.js";
|
|
5
5
|
import { buildMissIsrCacheControl } from "./isr-decision.js";
|
|
6
6
|
import { fnv1a52 } from "../utils/hash.js";
|
|
7
7
|
import { encodeCacheTag } from "../utils/encode-cache-tag.js";
|
|
@@ -167,7 +167,7 @@ function applyGsspHeaders(headers, gsspRes, statusCode) {
|
|
|
167
167
|
}
|
|
168
168
|
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") headers.set(key, String(value));
|
|
169
169
|
}
|
|
170
|
-
headers.set("Content-Type", "text/html");
|
|
170
|
+
headers.set("Content-Type", "text/html; charset=utf-8");
|
|
171
171
|
return statusCode ?? gsspRes.statusCode;
|
|
172
172
|
}
|
|
173
173
|
async function renderPagesPageResponse(options) {
|
|
@@ -232,7 +232,7 @@ async function renderPagesPageResponse(options) {
|
|
|
232
232
|
const markerIndex = shellHtml.indexOf(bodyMarker);
|
|
233
233
|
const shellPrefix = shellHtml.slice(0, markerIndex);
|
|
234
234
|
const shellSuffix = shellHtml.slice(markerIndex + 25);
|
|
235
|
-
const responseHeaders = new Headers({ "Content-Type": "text/html" });
|
|
235
|
+
const responseHeaders = new Headers({ "Content-Type": "text/html; charset=utf-8" });
|
|
236
236
|
const finalStatus = applyGsspHeaders(responseHeaders, options.gsspRes ?? options.documentReqRes?.res ?? null, options.statusCode);
|
|
237
237
|
let responseBodyStream = bodyStream;
|
|
238
238
|
if (!options.scriptNonce && options.isrRevalidateSeconds !== null && options.isrRevalidateSeconds > 0) {
|
|
@@ -264,7 +264,8 @@ async function renderPagesPageResponse(options) {
|
|
|
264
264
|
tags: [encodeCacheTag(`_N_T_${stem || "/"}`)]
|
|
265
265
|
});
|
|
266
266
|
setCacheStateHeaders(responseHeaders, "MISS");
|
|
267
|
-
} else if (options.
|
|
267
|
+
} else if (options.isStaticPropsRoute && shouldUseNextDeployCacheControl()) responseHeaders.set("Cache-Control", BROWSER_REVALIDATE_CACHE_CONTROL);
|
|
268
|
+
else if (options.gsspRes && !userSetCacheControl) responseHeaders.set("Cache-Control", NEVER_CACHE_CONTROL);
|
|
268
269
|
if (options.fontLinkHeader) responseHeaders.set("Link", options.fontLinkHeader);
|
|
269
270
|
if (options.userAgent && isPagesStreamingBot(options.userAgent)) {
|
|
270
271
|
const fullHtml = await readStreamAsText(compositeStream);
|
|
@@ -8,6 +8,13 @@ type PagesRenderOptions = {
|
|
|
8
8
|
originalUrl?: string;
|
|
9
9
|
};
|
|
10
10
|
type FilesystemRoutePhase = "direct" | "beforeFiles" | "afterFiles" | "fallback";
|
|
11
|
+
type PageRouteMatch = {
|
|
12
|
+
route: {
|
|
13
|
+
isDynamic: boolean;
|
|
14
|
+
pattern?: string;
|
|
15
|
+
dataKind?: "static" | "server" | "none";
|
|
16
|
+
};
|
|
17
|
+
};
|
|
11
18
|
declare function fetchWorkerFilesystemRoute(request: Request, requestPathname: string, phase: FilesystemRoutePhase, fetchAsset: (request: Request) => Promise<Response>): Promise<Response | false>;
|
|
12
19
|
type MiddlewareResult = {
|
|
13
20
|
continue: boolean;
|
|
@@ -37,12 +44,7 @@ type PagesPipelineDeps = {
|
|
|
37
44
|
hasMiddleware: boolean;
|
|
38
45
|
ctx?: unknown;
|
|
39
46
|
rawSearch?: string;
|
|
40
|
-
matchPageRoute?: ((pathname: string, request: Request) =>
|
|
41
|
-
route: {
|
|
42
|
-
isDynamic: boolean;
|
|
43
|
-
pattern?: string;
|
|
44
|
-
};
|
|
45
|
-
} | null) | null;
|
|
47
|
+
matchPageRoute?: ((pathname: string, request: Request) => PageRouteMatch | null) | null;
|
|
46
48
|
runMiddleware?: ((request: Request, ctx: unknown, opts: {
|
|
47
49
|
isDataRequest: boolean;
|
|
48
50
|
}) => Promise<MiddlewareResult>) | null;
|