vinext 0.2.1 → 1.0.0-beta.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/README.md +67 -30
- package/dist/build/assets-ignore.js +1 -1
- package/dist/build/clean-output.js +1 -1
- package/dist/build/client-build-config.d.ts +13 -92
- package/dist/build/client-build-config.js +23 -99
- package/dist/build/css-url-assets.js +3 -3
- package/dist/build/google-fonts/fallback-metrics.js +1 -1
- package/dist/build/google-fonts/font-metadata.js +1 -1
- package/dist/build/inject-pregenerated-paths.js +1 -1
- package/dist/build/inline-css.js +2 -5
- package/dist/build/module-dependency-cache.d.ts +4 -0
- package/dist/build/module-dependency-cache.js +13 -0
- package/dist/build/next-client-runtime-manifests.js +1 -1
- package/dist/build/pages-client-assets-module.js +1 -1
- package/dist/build/precompress.js +7 -7
- package/dist/build/prerender-paths.d.ts +23 -0
- package/dist/build/prerender-paths.js +291 -0
- package/dist/build/prerender-server-pool.js +2 -2
- package/dist/build/prerender.d.ts +12 -1
- package/dist/build/prerender.js +2 -2
- package/dist/build/preview-credentials.d.ts +11 -0
- package/dist/build/preview-credentials.js +19 -0
- package/dist/build/report.d.ts +0 -3
- package/dist/build/report.js +2 -4
- package/dist/build/run-prerender.d.ts +2 -11
- package/dist/build/run-prerender.js +2 -10
- package/dist/build/server-manifest.js +1 -1
- package/dist/build/ssr-manifest.d.ts +4 -0
- package/dist/build/ssr-manifest.js +10 -6
- package/dist/build/standalone.js +37 -37
- package/dist/cache/cache-adapters-virtual.d.ts +7 -17
- package/dist/cache/cache-adapters-virtual.js +35 -2
- package/dist/check.d.ts +0 -16
- package/dist/check.js +17 -34
- package/dist/cli.js +86 -101
- package/dist/client/instrumentation-client-inject.js +1 -1
- package/dist/client/pages-router-link-navigation.js +1 -1
- package/dist/config/config-matchers.js +2 -2
- package/dist/config/dotenv.js +1 -1
- package/dist/config/next-config.d.ts +4 -1
- package/dist/config/next-config.js +82 -16
- package/dist/config/prerender.d.ts +12 -2
- package/dist/config/prerender.js +22 -12
- package/dist/config/server-external-packages.js +1 -1
- package/dist/config/tsconfig-paths.js +7 -3
- package/dist/{node_modules/.pnpm/am-i-vibing@0.5.0/node_modules → deps/.pnpm/am-i-vibing@0.5.0/deps}/am-i-vibing/dist/detector-1yx2Hoe0.js +2 -2
- package/dist/deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js +50 -0
- package/dist/entries/app-rsc-entry.js +12 -5
- package/dist/entries/app-rsc-manifest.js +4 -4
- package/dist/entries/pages-client-entry.js +6 -5
- package/dist/entries/pages-server-entry.js +15 -26
- package/dist/entries/runtime-entry-module.js +2 -2
- package/dist/image/image-adapters-virtual.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +500 -338
- package/dist/init-cloudflare.js +13 -4
- package/dist/init-platform.d.ts +10 -1
- package/dist/init-platform.js +79 -13
- package/dist/init.d.ts +11 -3
- package/dist/init.js +133 -35
- package/dist/plugins/ast-utils.js +1 -1
- package/dist/plugins/dynamic-preload-metadata.js +7 -7
- package/dist/plugins/extensionless-dynamic-import.js +1 -1
- package/dist/plugins/fonts.js +3 -3
- package/dist/plugins/ignore-dynamic-requests.js +13 -8
- package/dist/plugins/import-meta-url.js +16 -17
- package/dist/plugins/middleware-export-validation.js +1 -1
- package/dist/plugins/middleware-server-only.js +2 -2
- package/dist/plugins/og-asset-ownership.js +7 -6
- package/dist/plugins/og-assets.js +4 -5
- package/dist/plugins/optimize-imports.js +12 -13
- package/dist/plugins/postcss.js +1 -1
- package/dist/plugins/remove-console.js +1 -1
- package/dist/plugins/require-context.js +1 -1
- package/dist/plugins/sass.js +2 -2
- package/dist/plugins/server-externals-manifest.js +1 -1
- package/dist/plugins/strip-server-exports.js +1 -1
- package/dist/plugins/styled-jsx.js +3 -2
- package/dist/plugins/typeof-window.js +1 -1
- package/dist/routing/app-route-graph.d.ts +3 -10
- package/dist/routing/app-route-graph.js +28 -76
- package/dist/routing/app-router.d.ts +0 -5
- package/dist/routing/app-router.js +0 -5
- package/dist/routing/file-matcher.d.ts +2 -6
- package/dist/routing/file-matcher.js +10 -11
- package/dist/routing/pages-router.js +3 -3
- package/dist/server/api-handler.js +41 -33
- package/dist/server/app-browser-entry.js +15 -11
- package/dist/server/app-browser-navigation-controller.js +9 -4
- package/dist/server/app-browser-server-action-client.js +1 -1
- package/dist/server/app-elements-wire.d.ts +2 -1
- package/dist/server/app-fallback-renderer.d.ts +8 -0
- package/dist/server/app-fallback-renderer.js +13 -1
- package/dist/server/app-layout-param-observation.d.ts +1 -1
- package/dist/server/app-page-boundary-render.d.ts +14 -1
- package/dist/server/app-page-boundary-render.js +107 -54
- package/dist/server/app-page-cache-render.d.ts +1 -1
- package/dist/server/app-page-dispatch.d.ts +2 -1
- package/dist/server/app-page-dispatch.js +29 -40
- package/dist/server/app-page-element-builder.d.ts +2 -1
- package/dist/server/app-page-element-builder.js +3 -3
- package/dist/server/app-page-execution.d.ts +3 -1
- package/dist/server/app-page-execution.js +31 -39
- package/dist/server/app-page-render.d.ts +1 -1
- package/dist/server/app-page-request.js +1 -1
- package/dist/server/app-page-route-wiring.js +4 -4
- package/dist/server/app-route-handler-execution.d.ts +1 -1
- package/dist/server/app-route-tree-prefetch.js +1 -1
- package/dist/server/app-rsc-cache-busting.js +1 -2
- package/dist/server/app-rsc-handler.js +2 -2
- package/dist/server/app-rsc-redirect-flight.d.ts +66 -0
- package/dist/server/app-rsc-redirect-flight.js +71 -0
- package/dist/server/app-rsc-render-mode.d.ts +2 -5
- package/dist/server/app-rsc-render-mode.js +2 -12
- package/dist/server/app-segment-config.js +2 -2
- package/dist/server/app-server-action-execution.js +3 -3
- package/dist/server/app-ssr-entry.js +4 -1
- package/dist/server/app-ssr-stream.d.ts +2 -1
- package/dist/server/app-ssr-stream.js +4 -1
- package/dist/server/cache-proof.js +1 -1
- package/dist/server/default-global-error-module.d.ts +3 -1
- package/dist/server/default-not-found-module.d.ts +3 -1
- package/dist/server/dev-lockfile.js +2 -3
- 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-route-files.js +1 -1
- package/dist/server/dev-server.js +256 -164
- package/dist/server/dev-stack-sourcemap.js +4 -3
- package/dist/server/headers.d.ts +3 -1
- package/dist/server/headers.js +3 -1
- package/dist/server/image-optimization.d.ts +12 -1
- package/dist/server/image-optimization.js +15 -3
- package/dist/server/implicit-tags.js +2 -1
- package/dist/server/instrumentation.js +2 -2
- package/dist/server/isr-cache.d.ts +12 -4
- package/dist/server/isr-cache.js +3 -3
- package/dist/server/metadata-routes.js +4 -4
- package/dist/server/middleware-response-headers.js +1 -1
- package/dist/server/middleware-runtime.js +1 -1
- package/dist/server/middleware.d.ts +1 -0
- package/dist/server/middleware.js +1 -1
- package/dist/server/navigation-planner.d.ts +2 -0
- package/dist/server/navigation-planner.js +8 -2
- package/dist/server/next-error-digest.d.ts +9 -24
- package/dist/server/next-error-digest.js +23 -15
- package/dist/server/operation-token.js +1 -1
- package/dist/server/pages-body-parser-config.js +2 -1
- package/dist/server/pages-dev-hydration.d.ts +14 -0
- package/dist/server/pages-dev-hydration.js +83 -0
- package/dist/server/pages-dev-module-url.js +2 -2
- package/dist/server/pages-node-compat.d.ts +13 -6
- package/dist/server/pages-node-compat.js +44 -45
- package/dist/server/pages-page-data.d.ts +1 -1
- package/dist/server/pages-page-data.js +5 -4
- package/dist/server/pages-page-handler.js +61 -29
- package/dist/server/pages-page-response.d.ts +2 -2
- package/dist/server/pages-page-response.js +2 -2
- package/dist/server/pages-preview.d.ts +26 -0
- package/dist/server/pages-preview.js +165 -0
- package/dist/server/pages-readiness.d.ts +2 -1
- package/dist/server/pages-readiness.js +4 -2
- package/dist/server/pages-request-pipeline.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 +6 -5
- package/dist/server/prod-server.js +15 -15
- package/dist/server/request-pipeline.js +1 -1
- package/dist/server/seed-cache.js +1 -1
- package/dist/server/socket-error-backstop.js +1 -1
- package/dist/server/static-file-cache.js +8 -9
- package/dist/server/worker-utils.js +1 -1
- package/dist/shims/cache.d.ts +1 -1
- package/dist/shims/cache.js +2 -1
- package/dist/shims/cdn-cache.d.ts +2 -7
- package/dist/shims/cdn-cache.js +2 -14
- package/dist/shims/constants.js +1 -1
- package/dist/shims/error-boundary.d.ts +4 -4
- package/dist/shims/error.d.ts +21 -5
- package/dist/shims/error.js +52 -14
- package/dist/shims/fetch-cache.js +1 -1
- package/dist/shims/font-utils.js +1 -1
- package/dist/shims/form.d.ts +1 -1
- package/dist/shims/head.js +3 -3
- package/dist/shims/headers.js +6 -4
- package/dist/shims/internal/pages-data-fetch-dedup.js +5 -3
- package/dist/shims/layout-segment-context.d.ts +1 -1
- package/dist/shims/link.d.ts +3 -1
- package/dist/shims/link.js +6 -3
- package/dist/shims/navigation-errors.d.ts +2 -1
- package/dist/shims/navigation-errors.js +9 -13
- package/dist/shims/navigation.d.ts +1 -1
- package/dist/shims/navigation.js +5 -5
- package/dist/shims/request-state-types.d.ts +2 -2
- package/dist/shims/router-state.d.ts +1 -0
- package/dist/shims/router.d.ts +2 -1
- package/dist/shims/router.js +18 -11
- package/dist/shims/script.js +1 -1
- package/dist/shims/server.js +2 -2
- package/dist/shims/slot.d.ts +1 -1
- package/dist/shims/thenable-params.js +1 -1
- package/dist/shims/unified-request-context.d.ts +1 -1
- package/dist/typegen.js +9 -11
- package/dist/utils/client-build-manifest.js +2 -2
- package/dist/utils/client-entry-manifest.js +1 -1
- package/dist/utils/client-runtime-metadata.js +1 -1
- package/dist/utils/commonjs-loader.js +3 -3
- package/dist/utils/mdx-scan.js +1 -1
- package/dist/utils/path.d.ts +1 -13
- package/dist/utils/path.js +1 -15
- package/dist/utils/plugin-options.d.ts +4 -0
- package/dist/utils/plugin-options.js +8 -0
- package/dist/utils/project.d.ts +1 -7
- package/dist/utils/project.js +2 -8
- package/dist/utils/public-routes.js +2 -2
- package/dist/utils/react-version.js +1 -1
- package/dist/utils/redirect-digest.d.ts +9 -0
- package/dist/utils/redirect-digest.js +25 -0
- package/dist/utils/vinext-root.js +1 -1
- package/dist/utils/vite-version.d.ts +6 -13
- package/dist/utils/vite-version.js +68 -34
- package/package.json +33 -14
- 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 -442
- 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 -272
- package/dist/packages/cloudflare/src/tpr.d.ts +0 -45
- package/dist/packages/cloudflare/src/tpr.js +0 -582
- package/dist/packages/cloudflare/src/utils/cache-control-metadata.js +0 -20
- /package/dist/build/google-fonts/{fallback-metrics-data.js → fallback-metrics-data.json.js} +0 -0
- /package/dist/build/google-fonts/{font-data.js → font-data.json.js} +0 -0
- /package/dist/{node_modules/.pnpm/process-ancestry@0.1.0/node_modules → deps/.pnpm/process-ancestry@0.1.0/deps}/process-ancestry/dist/index.js +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { PagesBodyParseError } from "./pages-media-type.js";
|
|
2
2
|
import { RevalidateOptions } from "./pages-revalidate.js";
|
|
3
|
+
import { PagesPreviewData } from "./pages-preview.js";
|
|
3
4
|
import { Readable, Writable } from "node:stream";
|
|
4
5
|
|
|
5
6
|
//#region src/server/pages-node-compat.d.ts
|
|
@@ -11,6 +12,9 @@ type PagesReqResRequest = Readable & {
|
|
|
11
12
|
query: PagesRequestQuery;
|
|
12
13
|
body: unknown;
|
|
13
14
|
cookies: Record<string, string>;
|
|
15
|
+
preview?: true;
|
|
16
|
+
draftMode?: true;
|
|
17
|
+
previewData: PagesPreviewData | false;
|
|
14
18
|
};
|
|
15
19
|
type PagesReqResHeaders = {
|
|
16
20
|
[key: string]: string | number | boolean | string[];
|
|
@@ -24,13 +28,19 @@ type PagesReqResResponse = Writable & {
|
|
|
24
28
|
status: (code: number) => PagesReqResResponse;
|
|
25
29
|
json: (data: unknown) => void;
|
|
26
30
|
send: (data: unknown) => void;
|
|
27
|
-
redirect: (statusOrUrl: number | string, url?: string) =>
|
|
31
|
+
redirect: (statusOrUrl: number | string, url?: string) => PagesReqResResponse;
|
|
28
32
|
getHeaders: () => PagesReqResHeaders;
|
|
29
33
|
revalidate: (urlPath: string, opts?: RevalidateOptions) => Promise<void>;
|
|
30
34
|
setPreviewData: (data: object | string, options?: {
|
|
31
35
|
maxAge?: number;
|
|
32
36
|
path?: string;
|
|
33
37
|
}) => PagesReqResResponse;
|
|
38
|
+
clearPreviewData: (options?: {
|
|
39
|
+
path?: string;
|
|
40
|
+
}) => PagesReqResResponse;
|
|
41
|
+
setDraftMode: (options?: {
|
|
42
|
+
enable?: boolean;
|
|
43
|
+
}) => PagesReqResResponse;
|
|
34
44
|
};
|
|
35
45
|
type PagesRequestCookiesCarrier = {
|
|
36
46
|
headers: {
|
|
@@ -49,7 +59,6 @@ type CreatePagesReqResResult = {
|
|
|
49
59
|
res: PagesReqResResponse;
|
|
50
60
|
responsePromise: Promise<Response>;
|
|
51
61
|
};
|
|
52
|
-
type PagesPreviewData = object | string;
|
|
53
62
|
/**
|
|
54
63
|
* Read and parse a Pages Router API request body for the Workers/prod path.
|
|
55
64
|
*
|
|
@@ -61,13 +70,11 @@ type PagesPreviewData = object | string;
|
|
|
61
70
|
* @see https://nextjs.org/docs/pages/building-your-application/routing/api-routes#custom-config
|
|
62
71
|
*/
|
|
63
72
|
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
73
|
declare function getPagesPreviewData(request: Request, options?: {
|
|
68
74
|
isOnDemandRevalidate?: boolean;
|
|
69
75
|
}): PagesPreviewData | false;
|
|
70
76
|
declare function attachPagesRequestCookies(req: PagesRequestCookiesCarrier): void;
|
|
77
|
+
declare function attachPagesPreviewApi(req: PagesReqResRequest, res: PagesReqResResponse): void;
|
|
71
78
|
declare function createPagesReqRes(options: CreatePagesReqResOptions): CreatePagesReqResResult;
|
|
72
79
|
//#endregion
|
|
73
|
-
export { PagesBodyParseError as PagesApiBodyParseError,
|
|
80
|
+
export { PagesBodyParseError as PagesApiBodyParseError, PagesReqResRequest, PagesReqResResponse, PagesRequestQuery, attachPagesPreviewApi, attachPagesRequestCookies, createPagesReqRes, getPagesPreviewData, parsePagesApiBody };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { parseCookieHeader } from "../utils/parse-cookie.js";
|
|
2
|
-
import { getRevalidateSecret, isRevalidateSecret } from "./isr-cache.js";
|
|
3
2
|
import { readStreamAsTextWithLimit } from "../utils/text-stream.js";
|
|
4
3
|
import { DEFAULT_PAGES_API_BODY_SIZE_LIMIT } from "./pages-body-parser-config.js";
|
|
5
4
|
import { PagesBodyParseError, getMediaType, isJsonMediaType } from "./pages-media-type.js";
|
|
6
5
|
import { performOnDemandRevalidate } from "./pages-revalidate.js";
|
|
6
|
+
import { clearPagesPreviewData, getPagesPreviewState, setPagesDraftMode, setPagesPreviewData } from "./pages-preview.js";
|
|
7
7
|
import { decode } from "node:querystring";
|
|
8
8
|
import { Readable, Writable } from "node:stream";
|
|
9
9
|
//#region src/server/pages-node-compat.ts
|
|
@@ -65,44 +65,12 @@ function createRequestReadable(request) {
|
|
|
65
65
|
function parsePagesRequestCookies(cookieHeader) {
|
|
66
66
|
return parseCookieHeader(Array.isArray(cookieHeader) ? cookieHeader.join("; ") : cookieHeader);
|
|
67
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
68
|
function getPagesPreviewDataFromCookieHeader(cookieHeader, options = {}) {
|
|
89
|
-
|
|
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);
|
|
69
|
+
return getPagesPreviewState(cookieHeader, options).data;
|
|
97
70
|
}
|
|
98
71
|
function getPagesPreviewData(request, options = {}) {
|
|
99
72
|
return getPagesPreviewDataFromCookieHeader(request.headers.get("cookie"), options);
|
|
100
73
|
}
|
|
101
|
-
function normalizeSetCookieHeader(value) {
|
|
102
|
-
if (value === void 0) return [];
|
|
103
|
-
if (Array.isArray(value)) return value.map(String);
|
|
104
|
-
return [String(value)];
|
|
105
|
-
}
|
|
106
74
|
function attachPagesRequestCookies(req) {
|
|
107
75
|
if (Object.hasOwn(req, "cookies")) return;
|
|
108
76
|
Object.defineProperty(req, "cookies", {
|
|
@@ -128,6 +96,27 @@ function attachPagesRequestCookies(req) {
|
|
|
128
96
|
}
|
|
129
97
|
});
|
|
130
98
|
}
|
|
99
|
+
function attachPagesPreviewApi(req, res) {
|
|
100
|
+
const preview = getPagesPreviewState(req.headers.cookie);
|
|
101
|
+
req.previewData = preview.data;
|
|
102
|
+
if (preview.data !== false) {
|
|
103
|
+
req.preview = true;
|
|
104
|
+
req.draftMode = true;
|
|
105
|
+
}
|
|
106
|
+
res.setPreviewData = (data, options = {}) => {
|
|
107
|
+
setPagesPreviewData(res, data, options);
|
|
108
|
+
return res;
|
|
109
|
+
};
|
|
110
|
+
res.clearPreviewData = (options = {}) => {
|
|
111
|
+
clearPagesPreviewData(res, options);
|
|
112
|
+
return res;
|
|
113
|
+
};
|
|
114
|
+
res.setDraftMode = (options = { enable: true }) => {
|
|
115
|
+
setPagesDraftMode(res, options.enable !== false);
|
|
116
|
+
return res;
|
|
117
|
+
};
|
|
118
|
+
if (preview.shouldClear) clearPagesPreviewData(res);
|
|
119
|
+
}
|
|
131
120
|
var PagesResponseStream = class extends Writable {
|
|
132
121
|
resolveResponse;
|
|
133
122
|
rejectResponse;
|
|
@@ -197,9 +186,15 @@ var PagesResponseStream = class extends Writable {
|
|
|
197
186
|
this.end(String(data));
|
|
198
187
|
}
|
|
199
188
|
redirect(statusOrUrl, url) {
|
|
200
|
-
if (typeof statusOrUrl === "string")
|
|
201
|
-
|
|
189
|
+
if (typeof statusOrUrl === "string") {
|
|
190
|
+
url = statusOrUrl;
|
|
191
|
+
statusOrUrl = 307;
|
|
192
|
+
}
|
|
193
|
+
if (typeof statusOrUrl !== "number" || typeof url !== "string") throw new Error("Invalid redirect arguments. Please use a single argument URL, e.g. res.redirect('/destination') or use a status code and URL, e.g. res.redirect(307, '/destination').");
|
|
194
|
+
this.writeHead(statusOrUrl, { Location: url });
|
|
195
|
+
this.write(url);
|
|
202
196
|
this.end();
|
|
197
|
+
return this;
|
|
203
198
|
}
|
|
204
199
|
getHeaders() {
|
|
205
200
|
const headers = { ...this.resHeaders };
|
|
@@ -210,13 +205,15 @@ var PagesResponseStream = class extends Writable {
|
|
|
210
205
|
await performOnDemandRevalidate(this.requestHeaders, urlPath, opts);
|
|
211
206
|
}
|
|
212
207
|
setPreviewData(data, options = {}) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
208
|
+
setPagesPreviewData(this, data, options);
|
|
209
|
+
return this;
|
|
210
|
+
}
|
|
211
|
+
clearPreviewData(options = {}) {
|
|
212
|
+
clearPagesPreviewData(this, options);
|
|
213
|
+
return this;
|
|
214
|
+
}
|
|
215
|
+
setDraftMode(options = { enable: true }) {
|
|
216
|
+
setPagesDraftMode(this, options.enable !== false);
|
|
220
217
|
return this;
|
|
221
218
|
}
|
|
222
219
|
_write(chunk, encoding, callback) {
|
|
@@ -302,11 +299,13 @@ function createPagesReqRes(options) {
|
|
|
302
299
|
resolveResponse = resolve;
|
|
303
300
|
rejectResponse = reject;
|
|
304
301
|
});
|
|
302
|
+
const res = new PagesResponseStream(resolveResponse, rejectResponse, options.request.headers);
|
|
303
|
+
attachPagesPreviewApi(req, res);
|
|
305
304
|
return {
|
|
306
305
|
req,
|
|
307
|
-
res
|
|
306
|
+
res,
|
|
308
307
|
responsePromise
|
|
309
308
|
};
|
|
310
309
|
}
|
|
311
310
|
//#endregion
|
|
312
|
-
export { PagesBodyParseError as PagesApiBodyParseError, attachPagesRequestCookies, createPagesReqRes, getPagesPreviewData,
|
|
311
|
+
export { PagesBodyParseError as PagesApiBodyParseError, attachPagesPreviewApi, attachPagesRequestCookies, createPagesReqRes, getPagesPreviewData, parsePagesApiBody };
|
|
@@ -2,7 +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-
|
|
5
|
+
import { PagesPreviewData } from "./pages-preview.js";
|
|
6
6
|
import { PagesGsspResponse, PagesI18nRenderContext, PagesNextDataExtras } from "./pages-page-response.js";
|
|
7
7
|
import { ReactNode } from "react";
|
|
8
8
|
|
|
@@ -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 });
|
|
@@ -248,6 +248,7 @@ async function resolvePagesPageData(options) {
|
|
|
248
248
|
const userFacingParams = options.route.isDynamic ? options.params : null;
|
|
249
249
|
let isFallback = false;
|
|
250
250
|
let shouldPersistFallbackData = false;
|
|
251
|
+
const previewData = options.isOnDemandRevalidate ? false : options.previewData ?? false;
|
|
251
252
|
if (typeof options.pageModule.getStaticPaths === "function" && options.route.isDynamic) {
|
|
252
253
|
const pathsResult = await options.pageModule.getStaticPaths({
|
|
253
254
|
locales: options.i18n.locales ?? [],
|
|
@@ -257,14 +258,13 @@ async function resolvePagesPageData(options) {
|
|
|
257
258
|
const paths = pathsResult?.paths ?? [];
|
|
258
259
|
const routeParams = getPagesRouteParams(options.routePattern);
|
|
259
260
|
const isValidPath = paths.some((pathEntry) => matchesPagesStaticPath(pathEntry, options.params, routeParams, options.routeUrl));
|
|
260
|
-
if (fallback === false && !isValidPath) return buildPagesNotFoundResult(options);
|
|
261
|
+
if (fallback === false && !isValidPath && previewData === false) return buildPagesNotFoundResult(options);
|
|
261
262
|
const isBotRequest = !!options.userAgent && isBotUserAgent(options.userAgent, options.htmlLimitedBots);
|
|
262
|
-
if (fallback === true && !isValidPath && !options.isDataReq && !isBotRequest) isFallback = true;
|
|
263
|
+
if (fallback === true && !isValidPath && !options.isDataReq && !isBotRequest && previewData === false) isFallback = true;
|
|
263
264
|
shouldPersistFallbackData = fallback === true && !isValidPath && options.isDataReq === true;
|
|
264
265
|
}
|
|
265
266
|
let pageProps = {};
|
|
266
267
|
let gsspRes = null;
|
|
267
|
-
const previewData = options.isOnDemandRevalidate ? false : options.previewData ?? false;
|
|
268
268
|
const previewContext = previewData === false ? {} : {
|
|
269
269
|
draftMode: true,
|
|
270
270
|
preview: true,
|
|
@@ -276,6 +276,7 @@ async function resolvePagesPageData(options) {
|
|
|
276
276
|
return sharedReqRes;
|
|
277
277
|
}
|
|
278
278
|
let renderProps = { pageProps };
|
|
279
|
+
if (previewData !== false) renderProps.__N_PREVIEW = true;
|
|
279
280
|
async function loadForegroundAppInitialRenderProps() {
|
|
280
281
|
const result = await loadPagesAppInitialRenderProps(options, getSharedReqRes);
|
|
281
282
|
if (result.kind === "response") return {
|
|
@@ -10,7 +10,7 @@ import { ensureFetchPatch } from "../shims/fetch-cache.js";
|
|
|
10
10
|
import { appendAssetDeploymentIdQuery } from "../utils/deployment-id.js";
|
|
11
11
|
import { mergeRouteParamsIntoQuery, parseQueryString } from "../utils/query.js";
|
|
12
12
|
import { getScriptNonceFromHeaderSources } from "./csp.js";
|
|
13
|
-
import { resolvePagesI18nRequest } from "./pages-i18n.js";
|
|
13
|
+
import { extractLocaleFromUrl, resolvePagesI18nRequest } from "./pages-i18n.js";
|
|
14
14
|
import { buildDefaultPagesNotFoundResponse } from "./pages-default-404.js";
|
|
15
15
|
import { buildPagesReadinessNextData } from "./pages-readiness.js";
|
|
16
16
|
import { resolvePagesPageMethodResponse } from "./pages-page-method.js";
|
|
@@ -19,8 +19,20 @@ 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
21
|
import { collectAssetTags, resolveClientModuleUrl } from "./pages-asset-tags.js";
|
|
22
|
-
import {
|
|
22
|
+
import { PAGES_PREVIEW_CACHE_CONTROL, appendPagesPreviewClearCookies, getPagesPreviewState } from "./pages-preview.js";
|
|
23
|
+
import { createPagesReqRes } from "./pages-node-compat.js";
|
|
23
24
|
//#region src/server/pages-page-handler.ts
|
|
25
|
+
function finalizePagesPreviewResponse(response, preview) {
|
|
26
|
+
if (preview.data === false && !preview.shouldClear) return response;
|
|
27
|
+
const headers = new Headers(response.headers);
|
|
28
|
+
if (preview.data !== false) headers.set("Cache-Control", PAGES_PREVIEW_CACHE_CONTROL);
|
|
29
|
+
if (preview.shouldClear) appendPagesPreviewClearCookies(headers);
|
|
30
|
+
return new Response(response.body, {
|
|
31
|
+
headers,
|
|
32
|
+
status: response.status,
|
|
33
|
+
statusText: response.statusText
|
|
34
|
+
});
|
|
35
|
+
}
|
|
24
36
|
function shouldEmitPagesClientTraceMetadata(pageModule, appComponent) {
|
|
25
37
|
if (typeof pageModule.getServerSideProps === "function") return true;
|
|
26
38
|
if (typeof pageModule.getStaticProps === "function") return false;
|
|
@@ -126,6 +138,7 @@ function createPagesPageHandler(opts) {
|
|
|
126
138
|
} else return buildDefaultPagesNotFoundResponse();
|
|
127
139
|
}
|
|
128
140
|
const { route, params } = match;
|
|
141
|
+
const routerAsPath = i18nConfig ? extractLocaleFromUrl(renderAsPath ?? routeUrl, i18nConfig, locale).url : renderAsPath ?? routeUrl;
|
|
129
142
|
return runWithRequestContext(createRequestContext({ executionContext: getRequestExecutionContext() }), async () => {
|
|
130
143
|
ensureFetchPatch();
|
|
131
144
|
try {
|
|
@@ -134,17 +147,26 @@ function createPagesPageHandler(opts) {
|
|
|
134
147
|
const query = mergeRouteParamsIntoQuery(parseQueryString(routeUrl), params);
|
|
135
148
|
const pageModule = route.module;
|
|
136
149
|
const isStaticPropsRoute = typeof pageModule.getStaticProps === "function";
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
const
|
|
150
|
+
const isOnDemandRevalidate = isOnDemandRevalidateRequest(request.headers.get(PRERENDER_REVALIDATE_HEADER));
|
|
151
|
+
const preview = isStaticPropsRoute || typeof pageModule.getServerSideProps === "function" ? getPagesPreviewState(request.headers.get("cookie"), { isOnDemandRevalidate }) : {
|
|
152
|
+
data: false,
|
|
153
|
+
shouldClear: false
|
|
154
|
+
};
|
|
155
|
+
const previewData = preview.data;
|
|
156
|
+
const pagesNextData = {
|
|
157
|
+
...buildPagesReadinessNextData({
|
|
158
|
+
pageModule,
|
|
159
|
+
appComponent: AppComponent,
|
|
160
|
+
hasRewrites
|
|
161
|
+
}),
|
|
162
|
+
...previewData === false ? {} : { isPreview: true }
|
|
163
|
+
};
|
|
164
|
+
const navigationIsReady = typeof getPagesNavigationIsReadyFromSerializedState === "function" ? getPagesNavigationIsReadyFromSerializedState(routePattern, new URL(routerAsPath, "http://_").search, pagesNextData) : true;
|
|
143
165
|
function applySSRContext(extra) {
|
|
144
166
|
if (typeof setSSRContext === "function") setSSRContext({
|
|
145
167
|
pathname: routePattern,
|
|
146
168
|
query,
|
|
147
|
-
asPath:
|
|
169
|
+
asPath: routerAsPath,
|
|
148
170
|
navigationIsReady,
|
|
149
171
|
locale,
|
|
150
172
|
locales: i18nConfig ? i18nConfig.locales : void 0,
|
|
@@ -160,7 +182,10 @@ function createPagesPageHandler(opts) {
|
|
|
160
182
|
hostname: new URL(request.url).hostname
|
|
161
183
|
});
|
|
162
184
|
}
|
|
163
|
-
applySSRContext({
|
|
185
|
+
applySSRContext({
|
|
186
|
+
isPreview: previewData !== false,
|
|
187
|
+
nextData: pagesNextData
|
|
188
|
+
});
|
|
164
189
|
const PageComponent = pageModule.default;
|
|
165
190
|
if (!PageComponent) return new Response("Page has no default export", { status: 500 });
|
|
166
191
|
if (!isDataReq && routePattern !== "/_error" && routePattern !== "/404" && routePattern !== "/500" && renderStatusCodeOverride === void 0) {
|
|
@@ -190,13 +215,16 @@ function createPagesPageHandler(opts) {
|
|
|
190
215
|
if (allFontPreloads.length > 0) fontLinkHeader = allFontPreloads.map((p) => "<" + appendAssetDeploymentIdQuery(p.href) + ">; rel=preload; as=font; type=" + p.type + "; crossorigin").join(", ");
|
|
191
216
|
} catch {}
|
|
192
217
|
const pagesResolvedUrl = (new URL(routeUrl, originalRequestUrl).pathname || "/") + originalRequestUrl.search;
|
|
193
|
-
const createPageReqRes = () =>
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
218
|
+
const createPageReqRes = () => {
|
|
219
|
+
const reqRes = createPagesReqRes({
|
|
220
|
+
body: void 0,
|
|
221
|
+
query,
|
|
222
|
+
request,
|
|
223
|
+
url: originalRequestPathAndSearch
|
|
224
|
+
});
|
|
225
|
+
if (typeof renderStatusCode === "number") reqRes.res.statusCode = renderStatusCode;
|
|
226
|
+
return reqRes;
|
|
227
|
+
};
|
|
200
228
|
const pageDataResult = await resolvePagesPageData({
|
|
201
229
|
isDataReq,
|
|
202
230
|
err: err instanceof Error ? err : void 0,
|
|
@@ -222,12 +250,12 @@ function createPagesPageHandler(opts) {
|
|
|
222
250
|
isBuildTimePrerendering: typeof process !== "undefined" && process.env && process.env.VINEXT_PRERENDER === "1",
|
|
223
251
|
validatePropsSerialization: process.env.NODE_ENV !== "production" || process.env.VINEXT_PRERENDER === "1",
|
|
224
252
|
isOnDemandRevalidate,
|
|
225
|
-
previewData
|
|
253
|
+
previewData,
|
|
226
254
|
pageModule,
|
|
227
255
|
AppComponent,
|
|
228
256
|
params,
|
|
229
257
|
query,
|
|
230
|
-
asPath:
|
|
258
|
+
asPath: routerAsPath,
|
|
231
259
|
resolvedUrl: pagesResolvedUrl,
|
|
232
260
|
renderIsrPassToStringAsync,
|
|
233
261
|
route: { isDynamic: route.isDynamic },
|
|
@@ -252,18 +280,22 @@ function createPagesPageHandler(opts) {
|
|
|
252
280
|
});
|
|
253
281
|
if (pageDataResult.kind === "notFound") {
|
|
254
282
|
const notFoundRoute = findNotFoundRoute();
|
|
255
|
-
if (notFoundRoute && routePattern !== "/404" && routePattern !== "/_error") return renderPage(request, url, manifest, middlewareHeaders, {
|
|
283
|
+
if (notFoundRoute && routePattern !== "/404" && routePattern !== "/_error") return finalizePagesPreviewResponse(await renderPage(request, url, manifest, middlewareHeaders, {
|
|
256
284
|
statusCode: 404,
|
|
257
|
-
asPath:
|
|
285
|
+
asPath: routerAsPath,
|
|
258
286
|
renderErrorPageOnMiss: false,
|
|
259
287
|
__forcedRoute: notFoundRoute
|
|
260
|
-
});
|
|
261
|
-
return buildDefaultPagesNotFoundResponse();
|
|
288
|
+
}), preview);
|
|
289
|
+
return finalizePagesPreviewResponse(buildDefaultPagesNotFoundResponse(), preview);
|
|
262
290
|
}
|
|
263
|
-
if (pageDataResult.kind === "response") return pageDataResult.response;
|
|
291
|
+
if (pageDataResult.kind === "response") return finalizePagesPreviewResponse(pageDataResult.response, preview);
|
|
264
292
|
let pageProps = pageDataResult.pageProps;
|
|
265
293
|
let renderProps = pageDataResult.props;
|
|
266
|
-
if (
|
|
294
|
+
if (previewData !== false) renderProps = {
|
|
295
|
+
...renderProps,
|
|
296
|
+
__N_PREVIEW: true
|
|
297
|
+
};
|
|
298
|
+
if (routePattern === "/_error" && typeof renderStatusCode === "number" && renderProps.pageProps !== void 0) {
|
|
267
299
|
pageProps = {
|
|
268
300
|
...pageProps,
|
|
269
301
|
statusCode: renderStatusCode
|
|
@@ -280,7 +312,7 @@ function createPagesPageHandler(opts) {
|
|
|
280
312
|
if (isFallbackRender && typeof setSSRContext === "function") setSSRContext({
|
|
281
313
|
pathname: routePattern,
|
|
282
314
|
query,
|
|
283
|
-
asPath:
|
|
315
|
+
asPath: routerAsPath,
|
|
284
316
|
navigationIsReady: false,
|
|
285
317
|
locale,
|
|
286
318
|
locales: i18nConfig ? i18nConfig.locales : void 0,
|
|
@@ -316,12 +348,12 @@ function createPagesPageHandler(opts) {
|
|
|
316
348
|
const deploymentId = process.env.__VINEXT_DEPLOYMENT_ID || process.env.NEXT_DEPLOYMENT_ID;
|
|
317
349
|
if (deploymentId) init.headers[NEXTJS_DEPLOYMENT_ID_HEADER] = deploymentId;
|
|
318
350
|
}
|
|
319
|
-
return buildNextDataPropsJsonResponse(renderProps, safeJsonStringify, init);
|
|
351
|
+
return finalizePagesPreviewResponse(buildNextDataPropsJsonResponse(renderProps, safeJsonStringify, init), preview);
|
|
320
352
|
}
|
|
321
353
|
const pageModuleIds = [];
|
|
322
354
|
if (appAssetPath) pageModuleIds.push(appAssetPath);
|
|
323
355
|
if (route.filePath) pageModuleIds.push(route.filePath);
|
|
324
|
-
return await renderPagesPageResponse({
|
|
356
|
+
return finalizePagesPreviewResponse(await renderPagesPageResponse({
|
|
325
357
|
assetTags: collectAssetTags({
|
|
326
358
|
manifest,
|
|
327
359
|
moduleIds: pageModuleIds,
|
|
@@ -380,7 +412,7 @@ function createPagesPageHandler(opts) {
|
|
|
380
412
|
userAgent: request.headers.get("user-agent") ?? void 0,
|
|
381
413
|
ifNoneMatch: request.headers.get("if-none-match") ?? void 0,
|
|
382
414
|
requestCacheControl: request.headers.get("cache-control") ?? void 0
|
|
383
|
-
});
|
|
415
|
+
}), preview);
|
|
384
416
|
} catch (e) {
|
|
385
417
|
console.error("[vinext] SSR error:", e);
|
|
386
418
|
reportRequestError(e instanceof Error ? e : new Error(String(e)), {
|
|
@@ -33,11 +33,11 @@ type PagesFontPreload = {
|
|
|
33
33
|
/**
|
|
34
34
|
* The `__NEXT_DATA__` fields beyond the always-present core that the Pages
|
|
35
35
|
* renderer serializes: the `__vinext` block plus the readiness flags
|
|
36
|
-
* (gssp/gsp/gip/appGip/autoExport/isExperimentalCompile) the client uses to
|
|
36
|
+
* (gssp/gsp/gip/appGip/autoExport/nextExport/isExperimentalCompile) the client uses to
|
|
37
37
|
* recompute the initial `router.isReady`. Shared by every render path
|
|
38
38
|
* (initial, ISR regeneration) so they emit identical readiness state.
|
|
39
39
|
*/
|
|
40
|
-
type PagesNextDataExtras = Pick<VinextNextData, "__vinext" | "appGip" | "autoExport" | "gip" | "gsp" | "gssp" | "isExperimentalCompile">;
|
|
40
|
+
type PagesNextDataExtras = Pick<VinextNextData, "__vinext" | "appGip" | "autoExport" | "gip" | "gsp" | "gssp" | "isExperimentalCompile" | "nextExport">;
|
|
41
41
|
type PagesI18nRenderContext = {
|
|
42
42
|
locale?: string;
|
|
43
43
|
locales?: string[];
|
|
@@ -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) {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region src/server/pages-preview.d.ts
|
|
2
|
+
declare const PAGES_PREVIEW_CACHE_CONTROL = "private, no-cache, no-store, max-age=0, must-revalidate";
|
|
3
|
+
type PagesPreviewData = object | string;
|
|
4
|
+
type PagesPreviewState = {
|
|
5
|
+
data: PagesPreviewData | false;
|
|
6
|
+
shouldClear: boolean;
|
|
7
|
+
};
|
|
8
|
+
type PreviewResponse = {
|
|
9
|
+
getHeader(name: string): string | number | boolean | string[] | undefined;
|
|
10
|
+
setHeader(name: string, value: string | number | boolean | string[]): unknown;
|
|
11
|
+
};
|
|
12
|
+
declare function getPagesPreviewModeId(): string;
|
|
13
|
+
declare function getPagesPreviewState(cookieHeader: string | string[] | null | undefined, options?: {
|
|
14
|
+
isOnDemandRevalidate?: boolean;
|
|
15
|
+
}): PagesPreviewState;
|
|
16
|
+
declare function setPagesDraftMode(response: PreviewResponse, enabled: boolean): void;
|
|
17
|
+
declare function setPagesPreviewData(response: PreviewResponse, data: PagesPreviewData, options?: {
|
|
18
|
+
maxAge?: number;
|
|
19
|
+
path?: string;
|
|
20
|
+
}): void;
|
|
21
|
+
declare function clearPagesPreviewData(response: PreviewResponse, options?: {
|
|
22
|
+
path?: string;
|
|
23
|
+
}): void;
|
|
24
|
+
declare function appendPagesPreviewClearCookies(headers: Headers): void;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { PAGES_PREVIEW_CACHE_CONTROL, PagesPreviewData, PagesPreviewState, appendPagesPreviewClearCookies, clearPagesPreviewData, getPagesPreviewModeId, getPagesPreviewState, setPagesDraftMode, setPagesPreviewData };
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { parseCookieHeader } from "../utils/parse-cookie.js";
|
|
2
|
+
import { createCipheriv, createDecipheriv, createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
3
|
+
//#region src/server/pages-preview.ts
|
|
4
|
+
const PAGES_PREVIEW_CACHE_CONTROL = "private, no-cache, no-store, max-age=0, must-revalidate";
|
|
5
|
+
const pagesPreviewCookiesCleared = Symbol("__prerender_bypass");
|
|
6
|
+
let devCredentials;
|
|
7
|
+
function decodeKey(value) {
|
|
8
|
+
if (!value || !/^[0-9a-f]{64}$/i.test(value)) return null;
|
|
9
|
+
return Buffer.from(value, "hex");
|
|
10
|
+
}
|
|
11
|
+
function getPagesPreviewCredentials() {
|
|
12
|
+
const bypassId = process.env.__VINEXT_PREVIEW_MODE_ID;
|
|
13
|
+
const encryptionKey = decodeKey(process.env.__VINEXT_PREVIEW_MODE_ENCRYPTION_KEY);
|
|
14
|
+
const signingKey = decodeKey(process.env.__VINEXT_PREVIEW_MODE_SIGNING_KEY);
|
|
15
|
+
if (bypassId && encryptionKey && signingKey) return {
|
|
16
|
+
bypassId,
|
|
17
|
+
encryptionKey,
|
|
18
|
+
signingKey
|
|
19
|
+
};
|
|
20
|
+
if (!devCredentials) devCredentials = {
|
|
21
|
+
bypassId: randomBytes(16).toString("hex"),
|
|
22
|
+
encryptionKey: randomBytes(32),
|
|
23
|
+
signingKey: randomBytes(32)
|
|
24
|
+
};
|
|
25
|
+
return devCredentials;
|
|
26
|
+
}
|
|
27
|
+
function getPagesPreviewModeId() {
|
|
28
|
+
return getPagesPreviewCredentials().bypassId;
|
|
29
|
+
}
|
|
30
|
+
function serializeCookie(name, value, options = {}) {
|
|
31
|
+
const parts = [
|
|
32
|
+
`${name}=${encodeURIComponent(value)}`,
|
|
33
|
+
"HttpOnly",
|
|
34
|
+
`Path=${options.path ?? "/"}`,
|
|
35
|
+
process.env.NODE_ENV !== "development" ? "SameSite=None" : "SameSite=Lax"
|
|
36
|
+
];
|
|
37
|
+
if (process.env.NODE_ENV !== "development") parts.push("Secure");
|
|
38
|
+
if (options.maxAge !== void 0) parts.push(`Max-Age=${Math.trunc(options.maxAge)}`);
|
|
39
|
+
return parts.join("; ");
|
|
40
|
+
}
|
|
41
|
+
function serializeClearedCookie(name, options = {}) {
|
|
42
|
+
return [
|
|
43
|
+
`${name}=`,
|
|
44
|
+
"Expires=Thu, 01 Jan 1970 00:00:00 GMT",
|
|
45
|
+
"HttpOnly",
|
|
46
|
+
`Path=${options.path ?? "/"}`,
|
|
47
|
+
process.env.NODE_ENV !== "development" ? "SameSite=None" : "SameSite=Lax",
|
|
48
|
+
...process.env.NODE_ENV !== "development" ? ["Secure"] : []
|
|
49
|
+
].join("; ");
|
|
50
|
+
}
|
|
51
|
+
function normalizeSetCookie(value) {
|
|
52
|
+
if (value === void 0) return [];
|
|
53
|
+
return Array.isArray(value) ? value.map(String) : [String(value)];
|
|
54
|
+
}
|
|
55
|
+
function encodePayload(data, maxAge) {
|
|
56
|
+
const iv = randomBytes(12);
|
|
57
|
+
const credentials = getPagesPreviewCredentials();
|
|
58
|
+
const cipher = createCipheriv("aes-256-gcm", credentials.encryptionKey, iv);
|
|
59
|
+
const plaintext = JSON.stringify({
|
|
60
|
+
data,
|
|
61
|
+
...maxAge === void 0 ? {} : { expiresAt: Date.now() + maxAge * 1e3 }
|
|
62
|
+
});
|
|
63
|
+
const encryptedToken = [
|
|
64
|
+
iv,
|
|
65
|
+
Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]),
|
|
66
|
+
cipher.getAuthTag()
|
|
67
|
+
].map((part) => part.toString("base64url")).join(".");
|
|
68
|
+
return `${encryptedToken}.${createHmac("sha256", credentials.signingKey).update(encryptedToken).digest("base64url")}`;
|
|
69
|
+
}
|
|
70
|
+
function decodePayload(payload) {
|
|
71
|
+
try {
|
|
72
|
+
const [ivValue, encryptedValue, tagValue, signatureValue, ...extra] = payload.split(".");
|
|
73
|
+
if (!ivValue || !encryptedValue || !tagValue || !signatureValue || extra.length > 0) return false;
|
|
74
|
+
const encryptedToken = `${ivValue}.${encryptedValue}.${tagValue}`;
|
|
75
|
+
const credentials = getPagesPreviewCredentials();
|
|
76
|
+
const expected = createHmac("sha256", credentials.signingKey).update(encryptedToken).digest();
|
|
77
|
+
const signature = Buffer.from(signatureValue, "base64url");
|
|
78
|
+
if (signature.length !== expected.length || !timingSafeEqual(signature, expected)) return false;
|
|
79
|
+
const decipher = createDecipheriv("aes-256-gcm", credentials.encryptionKey, Buffer.from(ivValue, "base64url"));
|
|
80
|
+
decipher.setAuthTag(Buffer.from(tagValue, "base64url"));
|
|
81
|
+
const decoded = Buffer.concat([decipher.update(Buffer.from(encryptedValue, "base64url")), decipher.final()]).toString("utf8");
|
|
82
|
+
const value = JSON.parse(decoded);
|
|
83
|
+
if (typeof value !== "object" || value === null || !("data" in value)) return false;
|
|
84
|
+
const expiresAt = "expiresAt" in value ? value.expiresAt : void 0;
|
|
85
|
+
if (typeof expiresAt === "number" && Date.now() >= expiresAt) return false;
|
|
86
|
+
const data = value.data;
|
|
87
|
+
return typeof data === "object" && data !== null ? data : String(data);
|
|
88
|
+
} catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function getPagesPreviewState(cookieHeader, options = {}) {
|
|
93
|
+
if (options.isOnDemandRevalidate) return {
|
|
94
|
+
data: false,
|
|
95
|
+
shouldClear: false
|
|
96
|
+
};
|
|
97
|
+
const cookies = parseCookieHeader(Array.isArray(cookieHeader) ? cookieHeader.join("; ") : cookieHeader);
|
|
98
|
+
const bypass = cookies.__prerender_bypass;
|
|
99
|
+
const payload = cookies.__next_preview_data;
|
|
100
|
+
if (!bypass && !payload) return {
|
|
101
|
+
data: false,
|
|
102
|
+
shouldClear: false
|
|
103
|
+
};
|
|
104
|
+
if (!bypass || bypass !== getPagesPreviewCredentials().bypassId) return {
|
|
105
|
+
data: false,
|
|
106
|
+
shouldClear: true
|
|
107
|
+
};
|
|
108
|
+
if (!payload) return {
|
|
109
|
+
data: {},
|
|
110
|
+
shouldClear: false
|
|
111
|
+
};
|
|
112
|
+
const data = decodePayload(payload);
|
|
113
|
+
return {
|
|
114
|
+
data,
|
|
115
|
+
shouldClear: data === false
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function setPagesDraftMode(response, enabled) {
|
|
119
|
+
const cookie = enabled ? serializeCookie("__prerender_bypass", getPagesPreviewModeId()) : serializeClearedCookie("__prerender_bypass");
|
|
120
|
+
response.setHeader("Set-Cookie", [...normalizeSetCookie(response.getHeader("Set-Cookie")), cookie]);
|
|
121
|
+
}
|
|
122
|
+
function setPagesPreviewData(response, data, options = {}) {
|
|
123
|
+
const payload = encodePayload(data, options.maxAge);
|
|
124
|
+
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");
|
|
125
|
+
response.setHeader("Set-Cookie", [
|
|
126
|
+
...normalizeSetCookie(response.getHeader("Set-Cookie")),
|
|
127
|
+
serializeCookie("__prerender_bypass", getPagesPreviewModeId(), options),
|
|
128
|
+
serializeCookie("__next_preview_data", payload, options)
|
|
129
|
+
]);
|
|
130
|
+
}
|
|
131
|
+
function clearPagesPreviewData(response, options = {}) {
|
|
132
|
+
if (pagesPreviewCookiesCleared in response) return;
|
|
133
|
+
response.setHeader("Set-Cookie", [
|
|
134
|
+
...normalizeSetCookie(response.getHeader("Set-Cookie")),
|
|
135
|
+
serializeClearedCookie("__prerender_bypass", options),
|
|
136
|
+
serializeClearedCookie("__next_preview_data", options)
|
|
137
|
+
]);
|
|
138
|
+
Object.defineProperty(response, pagesPreviewCookiesCleared, {
|
|
139
|
+
value: true,
|
|
140
|
+
enumerable: false
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
function appendPagesPreviewClearCookies(headers) {
|
|
144
|
+
const cookies = headers.getSetCookie();
|
|
145
|
+
const hasExpiredCookie = (name) => cookies.some((cookie) => {
|
|
146
|
+
const [cookieValue, ...attributes] = cookie.split(";").map((part) => part.trim());
|
|
147
|
+
if (!cookieValue?.startsWith(`${name}=`)) return false;
|
|
148
|
+
let expiresAtEpoch = false;
|
|
149
|
+
let path;
|
|
150
|
+
let hasDomain = false;
|
|
151
|
+
for (const attribute of attributes) {
|
|
152
|
+
const separator = attribute.indexOf("=");
|
|
153
|
+
const attributeName = (separator === -1 ? attribute : attribute.slice(0, separator)).trim().toLowerCase();
|
|
154
|
+
const attributeValue = separator === -1 ? "" : attribute.slice(separator + 1).trim();
|
|
155
|
+
if (attributeName === "expires") expiresAtEpoch = Date.parse(attributeValue) === 0;
|
|
156
|
+
if (attributeName === "path") path = attributeValue;
|
|
157
|
+
if (attributeName === "domain") hasDomain = true;
|
|
158
|
+
}
|
|
159
|
+
return expiresAtEpoch && path === "/" && !hasDomain;
|
|
160
|
+
});
|
|
161
|
+
if (!hasExpiredCookie("__prerender_bypass")) headers.append("Set-Cookie", serializeClearedCookie("__prerender_bypass"));
|
|
162
|
+
if (!hasExpiredCookie("__next_preview_data")) headers.append("Set-Cookie", serializeClearedCookie("__next_preview_data"));
|
|
163
|
+
}
|
|
164
|
+
//#endregion
|
|
165
|
+
export { PAGES_PREVIEW_CACHE_CONTROL, appendPagesPreviewClearCookies, clearPagesPreviewData, getPagesPreviewModeId, getPagesPreviewState, setPagesDraftMode, setPagesPreviewData };
|