vinext 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/check.d.ts +18 -0
- package/dist/check.js +37 -17
- package/dist/cli.js +2 -2
- package/dist/config/next-config.d.ts +2 -1
- package/dist/config/next-config.js +3 -0
- package/dist/config/server-external-packages.d.ts +4 -0
- package/dist/config/server-external-packages.js +91 -0
- package/dist/deploy.d.ts +7 -0
- package/dist/deploy.js +25 -18
- package/dist/entries/app-rsc-entry.js +54 -10
- package/dist/entries/app-rsc-manifest.js +8 -0
- package/dist/entries/pages-client-entry.js +1 -0
- package/dist/entries/pages-server-entry.js +1 -0
- package/dist/index.js +46 -20
- package/dist/init.js +2 -1
- package/dist/plugins/middleware-server-only.d.ts +8 -6
- package/dist/plugins/middleware-server-only.js +8 -7
- package/dist/plugins/optimize-imports.js +1 -1
- package/dist/routing/app-route-graph.d.ts +6 -2
- package/dist/routing/app-route-graph.js +61 -13
- package/dist/routing/app-router.d.ts +5 -0
- package/dist/routing/app-router.js +5 -0
- package/dist/routing/file-matcher.d.ts +5 -0
- package/dist/routing/file-matcher.js +7 -1
- package/dist/server/app-browser-entry.js +42 -147
- package/dist/server/app-browser-history-controller.d.ts +2 -1
- package/dist/server/app-browser-history-controller.js +6 -2
- package/dist/server/app-browser-server-action-client.d.ts +32 -0
- package/dist/server/app-browser-server-action-client.js +128 -0
- package/dist/server/app-fallback-renderer.d.ts +3 -1
- package/dist/server/app-fallback-renderer.js +6 -2
- package/dist/server/app-page-boundary-render.d.ts +3 -0
- package/dist/server/app-page-boundary-render.js +14 -3
- package/dist/server/app-page-cache-finalizer.d.ts +1 -0
- package/dist/server/app-page-cache-finalizer.js +8 -2
- package/dist/server/app-page-dispatch.d.ts +33 -4
- package/dist/server/app-page-dispatch.js +62 -90
- package/dist/server/app-page-element-builder.d.ts +7 -1
- package/dist/server/app-page-element-builder.js +56 -19
- package/dist/server/app-page-head.d.ts +16 -1
- package/dist/server/app-page-head.js +44 -22
- package/dist/server/app-page-params.d.ts +2 -1
- package/dist/server/app-page-params.js +8 -1
- package/dist/server/app-page-ppr-runtime.d.ts +7 -0
- package/dist/server/app-page-ppr-runtime.js +70 -0
- package/dist/server/app-page-probe.d.ts +1 -0
- package/dist/server/app-page-probe.js +1 -1
- package/dist/server/app-page-render.d.ts +4 -1
- package/dist/server/app-page-render.js +8 -3
- package/dist/server/app-page-request.d.ts +8 -1
- package/dist/server/app-page-request.js +23 -11
- package/dist/server/app-page-route-wiring.d.ts +6 -1
- package/dist/server/app-page-route-wiring.js +30 -8
- package/dist/server/app-page-search-params-observation.d.ts +4 -2
- package/dist/server/app-page-search-params-observation.js +11 -7
- package/dist/server/app-ppr-fallback-shell-render.d.ts +4 -3
- package/dist/server/app-route-handler-dispatch.js +1 -0
- package/dist/server/app-route-handler-execution.js +2 -1
- package/dist/server/app-route-module-loader.d.ts +2 -0
- package/dist/server/app-route-module-loader.js +1 -0
- package/dist/server/app-router-entry.js +7 -6
- package/dist/server/app-rsc-errors.js +7 -1
- package/dist/server/app-rsc-handler.d.ts +2 -1
- package/dist/server/app-rsc-handler.js +6 -9
- package/dist/server/app-rsc-route-matching.d.ts +7 -0
- package/dist/server/app-rsc-route-matching.js +36 -3
- package/dist/server/app-segment-config.d.ts +1 -0
- package/dist/server/app-segment-config.js +32 -2
- package/dist/server/app-server-action-execution.d.ts +4 -0
- package/dist/server/app-server-action-execution.js +41 -10
- package/dist/server/app-ssr-entry.js +4 -1
- package/dist/server/app-static-generation.d.ts +1 -0
- package/dist/server/app-static-generation.js +1 -0
- package/dist/server/headers.d.ts +3 -1
- package/dist/server/headers.js +3 -1
- package/dist/server/prod-server.js +15 -6
- package/dist/server/worker-utils.d.ts +2 -1
- package/dist/server/worker-utils.js +7 -1
- package/dist/shims/error-boundary.d.ts +19 -1
- package/dist/shims/error-boundary.js +11 -1
- package/dist/shims/headers.d.ts +3 -1
- package/dist/shims/headers.js +16 -5
- package/dist/shims/link.js +16 -12
- package/dist/shims/metadata.d.ts +3 -2
- package/dist/shims/metadata.js +8 -4
- package/dist/shims/router.js +13 -2
- package/dist/typegen.js +6 -5
- package/dist/utils/path.d.ts +2 -1
- package/dist/utils/path.js +1 -1
- package/dist/utils/project.d.ts +4 -0
- package/dist/utils/project.js +5 -1
- package/package.json +7 -3
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER } from "./headers.js";
|
|
2
|
+
import { DANGEROUS_URL_BLOCK_MESSAGE, isDangerousScheme } from "../shims/url-safety.js";
|
|
3
|
+
import { AppElementsWire } from "./app-elements-wire.js";
|
|
4
|
+
import "./app-elements.js";
|
|
5
|
+
import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, createServerActionRequestUrl } from "./app-rsc-cache-busting.js";
|
|
6
|
+
import { createServerActionResultFacts, isServerActionResult, normalizeServerActionThrownValue, parseServerActionRevalidationHeader, readInvalidServerActionResponseError, shouldClearClientNavigationCachesForServerActionResult } from "./app-browser-action-result.js";
|
|
7
|
+
import { resolveServerActionRequestState } from "./app-browser-state.js";
|
|
8
|
+
import { applyServerActionResultDecision } from "./app-browser-server-action-navigation.js";
|
|
9
|
+
import { throwOnServerActionNotFound } from "./server-action-not-found.js";
|
|
10
|
+
import { createFromFetch, createTemporaryReferenceSet, encodeReply } from "@vitejs/plugin-rsc/browser";
|
|
11
|
+
//#region src/server/app-browser-server-action-client.ts
|
|
12
|
+
function resolveActionRedirectTarget(response, performHardNavigation) {
|
|
13
|
+
const actionRedirect = response.headers.get(ACTION_REDIRECT_HEADER);
|
|
14
|
+
if (!actionRedirect) return null;
|
|
15
|
+
if (isDangerousScheme(actionRedirect)) {
|
|
16
|
+
console.error(DANGEROUS_URL_BLOCK_MESSAGE);
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
let redirectUrl;
|
|
21
|
+
if (actionRedirect.startsWith("/") || /^[a-z]+:/i.test(actionRedirect)) redirectUrl = new URL(actionRedirect, window.location.href);
|
|
22
|
+
else {
|
|
23
|
+
const baseParsed = new URL(window.location.href);
|
|
24
|
+
let baseDir = baseParsed.pathname;
|
|
25
|
+
if (!baseDir.endsWith("/")) baseDir += "/";
|
|
26
|
+
redirectUrl = new URL(actionRedirect, `${baseParsed.origin}${baseDir}${baseParsed.search}`);
|
|
27
|
+
}
|
|
28
|
+
if (redirectUrl.origin !== window.location.origin) {
|
|
29
|
+
performHardNavigation(actionRedirect);
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
const statusHeader = response.headers.get(ACTION_REDIRECT_STATUS_HEADER);
|
|
33
|
+
return {
|
|
34
|
+
href: redirectUrl.href,
|
|
35
|
+
type: response.headers.get("x-action-redirect-type") ?? "push",
|
|
36
|
+
status: statusHeader ? parseInt(statusHeader, 10) : 307
|
|
37
|
+
};
|
|
38
|
+
} catch {
|
|
39
|
+
performHardNavigation(actionRedirect);
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
var ServerActionRedirectError = class extends Error {
|
|
44
|
+
digest;
|
|
45
|
+
handled = true;
|
|
46
|
+
constructor(target) {
|
|
47
|
+
super("NEXT_REDIRECT");
|
|
48
|
+
const redirectUrl = new URL(target.href, window.location.href);
|
|
49
|
+
const redirectHref = redirectUrl.pathname + redirectUrl.search + redirectUrl.hash;
|
|
50
|
+
const redirectType = target.type === "push" ? "push" : "replace";
|
|
51
|
+
this.digest = `NEXT_REDIRECT;${redirectType};${encodeURIComponent(redirectHref)};${target.status};`;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
async function invokeClientServerAction(id, args, actionInitiation, deps) {
|
|
55
|
+
deps.syncServerActionHttpFallbackHead(null);
|
|
56
|
+
const temporaryReferences = createTemporaryReferenceSet();
|
|
57
|
+
deps.syncCurrentHistoryState(actionInitiation.routerState.previousNextUrl, actionInitiation.routerState.bfcacheIds);
|
|
58
|
+
const body = await encodeReply(args, { temporaryReferences });
|
|
59
|
+
const headers = resolveServerActionRequestState({
|
|
60
|
+
actionId: id,
|
|
61
|
+
basePath: deps.basePath,
|
|
62
|
+
elements: actionInitiation.routerState.elements,
|
|
63
|
+
previousNextUrl: actionInitiation.routerState.previousNextUrl
|
|
64
|
+
}).headers;
|
|
65
|
+
const fetchResponse = await fetch(createServerActionRequestUrl(actionInitiation.path), {
|
|
66
|
+
method: "POST",
|
|
67
|
+
headers,
|
|
68
|
+
body
|
|
69
|
+
});
|
|
70
|
+
throwOnServerActionNotFound(fetchResponse, id);
|
|
71
|
+
const hasActionRedirect = fetchResponse.headers.has(ACTION_REDIRECT_HEADER);
|
|
72
|
+
const actionRedirectTarget = resolveActionRedirectTarget(fetchResponse, (url, historyMode) => deps.performHardNavigation(url, historyMode));
|
|
73
|
+
if (hasActionRedirect && !actionRedirectTarget) return void 0;
|
|
74
|
+
const actionResultFacts = createServerActionResultFacts({
|
|
75
|
+
actionRedirectHref: actionRedirectTarget?.href ?? null,
|
|
76
|
+
actionRedirectType: actionRedirectTarget?.type ?? null,
|
|
77
|
+
clientCompatibilityId: deps.clientRscCompatibilityId,
|
|
78
|
+
compatibilityIdHeader: fetchResponse.headers.get(VINEXT_RSC_COMPATIBILITY_ID_HEADER),
|
|
79
|
+
contentTypeHeader: fetchResponse.headers.get("content-type"),
|
|
80
|
+
currentHref: actionInitiation.href,
|
|
81
|
+
origin: window.location.origin,
|
|
82
|
+
responseUrl: fetchResponse.url
|
|
83
|
+
});
|
|
84
|
+
const fetchResponseIsRsc = actionResultFacts.isRscContentType;
|
|
85
|
+
if (applyServerActionResultDecision(deps.navigationPlanner.classifyServerActionResult(actionResultFacts), () => deps.clearClientNavigationCaches(), (url, historyMode) => deps.performHardNavigation(url, historyMode))) return;
|
|
86
|
+
const revalidation = parseServerActionRevalidationHeader(fetchResponse.headers);
|
|
87
|
+
if (revalidation !== "none") deps.clearClientNavigationCaches();
|
|
88
|
+
const invalidResponseError = await readInvalidServerActionResponseError(fetchResponse.clone(), actionRedirectTarget !== null);
|
|
89
|
+
if (invalidResponseError) throw invalidResponseError;
|
|
90
|
+
if (actionRedirectTarget && !fetchResponseIsRsc) {
|
|
91
|
+
deps.performHardNavigation(actionRedirectTarget.href);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const flightResponse = fetchResponse.status === 303 ? new Response(fetchResponse.body, {
|
|
95
|
+
headers: fetchResponse.headers,
|
|
96
|
+
status: 200,
|
|
97
|
+
statusText: "OK"
|
|
98
|
+
}) : fetchResponse;
|
|
99
|
+
const result = await createFromFetch(Promise.resolve(flightResponse), { temporaryReferences });
|
|
100
|
+
if (revalidation === "none" && shouldClearClientNavigationCachesForServerActionResult(result, revalidation)) deps.clearClientNavigationCaches();
|
|
101
|
+
if (actionRedirectTarget) {
|
|
102
|
+
if (isServerActionResult(result) && result.root !== void 0) {
|
|
103
|
+
deps.renderRedirectPayload(AppElementsWire.decode(result.root), actionRedirectTarget, actionInitiation);
|
|
104
|
+
throw new ServerActionRedirectError(actionRedirectTarget);
|
|
105
|
+
}
|
|
106
|
+
deps.performHardNavigation(actionRedirectTarget.href);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const hasSameUrlRerenderPayload = isServerActionResult(result) && result.root !== void 0;
|
|
110
|
+
deps.syncServerActionHttpFallbackHead(hasSameUrlRerenderPayload ? null : fetchResponse.status);
|
|
111
|
+
if (isServerActionResult(result)) {
|
|
112
|
+
if (result.root !== void 0) {
|
|
113
|
+
const returnValue = result.returnValue && !result.returnValue.ok ? {
|
|
114
|
+
ok: false,
|
|
115
|
+
data: normalizeServerActionThrownValue(result.returnValue.data, fetchResponse.status)
|
|
116
|
+
} : result.returnValue;
|
|
117
|
+
return deps.commitSameUrlNavigatePayload(Promise.resolve(AppElementsWire.decode(result.root)), actionInitiation, returnValue, revalidation);
|
|
118
|
+
}
|
|
119
|
+
if (result.returnValue) {
|
|
120
|
+
if (!result.returnValue.ok) throw normalizeServerActionThrownValue(result.returnValue.data, fetchResponse.status);
|
|
121
|
+
return result.returnValue.data;
|
|
122
|
+
}
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
return deps.commitSameUrlNavigatePayload(Promise.resolve(AppElementsWire.decode(result)), actionInitiation, void 0, revalidation);
|
|
126
|
+
}
|
|
127
|
+
//#endregion
|
|
128
|
+
export { invokeClientServerAction };
|
|
@@ -4,6 +4,7 @@ import { NavigationContext } from "../shims/navigation-context-state.js";
|
|
|
4
4
|
import { AppPageParams } from "./app-page-boundary.js";
|
|
5
5
|
import { AppPageFontPreload } from "./app-page-execution.js";
|
|
6
6
|
import { AppPageMiddlewareContext } from "./app-page-response.js";
|
|
7
|
+
import { ApplyAppPageFileBasedMetadata } from "./app-page-head.js";
|
|
7
8
|
import { AppPageSsrHandler } from "./app-page-stream.js";
|
|
8
9
|
import { AppPageBoundaryRoute } from "./app-page-boundary-render.js";
|
|
9
10
|
import { ReactNode } from "react";
|
|
@@ -27,6 +28,7 @@ type AppFallbackRendererFontProviders = {
|
|
|
27
28
|
getFontStyles: () => string[];
|
|
28
29
|
};
|
|
29
30
|
type AppFallbackRendererOptions<TModule extends AppPageModule = AppPageModule> = {
|
|
31
|
+
applyFileBasedMetadata?: ApplyAppPageFileBasedMetadata;
|
|
30
32
|
clearRequestContext: () => void;
|
|
31
33
|
createRscOnErrorHandler: (request: Request, pathname: string, routePath: string) => AppPageBoundaryOnError;
|
|
32
34
|
fontProviders: AppFallbackRendererFontProviders;
|
|
@@ -73,7 +75,7 @@ type AppFallbackRendererCallContext = {
|
|
|
73
75
|
sourcePageSegments?: readonly string[] | null;
|
|
74
76
|
};
|
|
75
77
|
type AppFallbackRenderer<TModule extends AppPageModule = AppPageModule> = {
|
|
76
|
-
renderErrorBoundary: (route: AppPageBoundaryRoute<TModule> | null, error: unknown, isRscRequest: boolean, request: Request, matchedParams: AppPageParams | undefined, scriptNonce: string | undefined, middlewareContext: AppPageMiddlewareContext, callContext?: AppFallbackRendererCallContext) => Promise<Response | null>;
|
|
78
|
+
renderErrorBoundary: (route: AppPageBoundaryRoute<TModule> | null, error: unknown, isRscRequest: boolean, request: Request, matchedParams: AppPageParams | undefined, scriptNonce: string | undefined, middlewareContext: AppPageMiddlewareContext, callContext?: AppFallbackRendererCallContext, errorOrigin?: "rsc" | "ssr") => Promise<Response | null>;
|
|
77
79
|
renderHttpAccessFallback: (route: AppPageBoundaryRoute<TModule> | null, statusCode: number, isRscRequest: boolean, request: Request, opts: {
|
|
78
80
|
boundaryComponent?: AppPageComponent | null;
|
|
79
81
|
boundaryModule?: TModule | null;
|
|
@@ -7,7 +7,7 @@ const EMPTY_MW_CTX = {
|
|
|
7
7
|
status: null
|
|
8
8
|
};
|
|
9
9
|
function createAppFallbackRenderer(options) {
|
|
10
|
-
const { basePath = "", clearRequestContext, createRscOnErrorHandler: buildRscOnErrorHandler, fontProviders, getNavigationContext, globalErrorModule, loadGlobalNotFoundModule, makeThenableParams, metadataRoutes, resolveChildSegments, rootBoundaries, rscRenderer, sanitizer, ssrLoader, trailingSlash } = options;
|
|
10
|
+
const { applyFileBasedMetadata, basePath = "", clearRequestContext, createRscOnErrorHandler: buildRscOnErrorHandler, fontProviders, getNavigationContext, globalErrorModule, loadGlobalNotFoundModule, makeThenableParams, metadataRoutes, resolveChildSegments, rootBoundaries, rscRenderer, sanitizer, ssrLoader, trailingSlash } = options;
|
|
11
11
|
const { rootForbiddenModule, rootLayouts, rootNotFoundModule, rootUnauthorizedModule } = rootBoundaries;
|
|
12
12
|
const effectiveGlobalErrorModule = globalErrorModule ?? DEFAULT_GLOBAL_ERROR_MODULE;
|
|
13
13
|
const effectiveRootNotFoundModule = rootNotFoundModule ?? DEFAULT_NOT_FOUND_MODULE;
|
|
@@ -23,6 +23,7 @@ function createAppFallbackRenderer(options) {
|
|
|
23
23
|
const globalNotFoundModule = await resolveGlobalNotFoundModule();
|
|
24
24
|
const globalNotFoundComponent = globalNotFoundModule?.default ?? null;
|
|
25
25
|
if (globalNotFoundComponent) return renderAppPageHttpAccessFallback({
|
|
26
|
+
applyFileBasedMetadata,
|
|
26
27
|
boundaryComponent: globalNotFoundComponent,
|
|
27
28
|
boundaryModule: globalNotFoundModule ?? null,
|
|
28
29
|
buildFontLinkHeader: fontProviders.buildFontLinkHeader,
|
|
@@ -57,6 +58,7 @@ function createAppFallbackRenderer(options) {
|
|
|
57
58
|
});
|
|
58
59
|
}
|
|
59
60
|
return renderAppPageHttpAccessFallback({
|
|
61
|
+
applyFileBasedMetadata,
|
|
60
62
|
basePath,
|
|
61
63
|
trailingSlash,
|
|
62
64
|
boundaryComponent: opts?.boundaryComponent ?? null,
|
|
@@ -95,8 +97,9 @@ function createAppFallbackRenderer(options) {
|
|
|
95
97
|
renderNotFound(route, isRscRequest, request, matchedParams, scriptNonce, middlewareContext, callContext) {
|
|
96
98
|
return this.renderHttpAccessFallback(route, 404, isRscRequest, request, { matchedParams }, scriptNonce, middlewareContext, callContext);
|
|
97
99
|
},
|
|
98
|
-
renderErrorBoundary(route, error, isRscRequest, request, matchedParams, scriptNonce, middlewareContext, callContext) {
|
|
100
|
+
renderErrorBoundary(route, error, isRscRequest, request, matchedParams, scriptNonce, middlewareContext, callContext, errorOrigin = "rsc") {
|
|
99
101
|
return renderAppPageErrorBoundary({
|
|
102
|
+
applyFileBasedMetadata,
|
|
100
103
|
basePath,
|
|
101
104
|
trailingSlash,
|
|
102
105
|
buildFontLinkHeader: fontProviders.buildFontLinkHeader,
|
|
@@ -105,6 +108,7 @@ function createAppFallbackRenderer(options) {
|
|
|
105
108
|
return buildRscOnErrorHandler(request, pathname, routePath);
|
|
106
109
|
},
|
|
107
110
|
error,
|
|
111
|
+
errorOrigin,
|
|
108
112
|
getFontLinks: fontProviders.getFontLinks,
|
|
109
113
|
getFontPreloads: fontProviders.getFontPreloads,
|
|
110
114
|
getFontStyles: fontProviders.getFontStyles,
|
|
@@ -4,6 +4,7 @@ import { NavigationContext } from "../shims/navigation-context-state.js";
|
|
|
4
4
|
import { AppPageParams } from "./app-page-boundary.js";
|
|
5
5
|
import { AppPageFontPreload } from "./app-page-execution.js";
|
|
6
6
|
import { AppPageMiddlewareContext } from "./app-page-response.js";
|
|
7
|
+
import { ApplyAppPageFileBasedMetadata } from "./app-page-head.js";
|
|
7
8
|
import { AppPageSsrHandler } from "./app-page-stream.js";
|
|
8
9
|
import { ComponentType, ReactNode } from "react";
|
|
9
10
|
|
|
@@ -27,6 +28,7 @@ type AppPageBoundaryRoute<TModule extends AppPageModule = AppPageModule> = {
|
|
|
27
28
|
unauthorized?: TModule | null;
|
|
28
29
|
};
|
|
29
30
|
type AppPageBoundaryRenderCommonOptions<TModule extends AppPageModule = AppPageModule> = {
|
|
31
|
+
applyFileBasedMetadata?: ApplyAppPageFileBasedMetadata;
|
|
30
32
|
buildFontLinkHeader: (preloads: readonly AppPageFontPreload[] | null | undefined) => string;
|
|
31
33
|
clearRequestContext: () => void;
|
|
32
34
|
createRscOnErrorHandler: (pathname: string, routePath: string) => AppPageBoundaryOnError;
|
|
@@ -73,6 +75,7 @@ type RenderAppPageHttpAccessFallbackOptions<TModule extends AppPageModule = AppP
|
|
|
73
75
|
} & AppPageBoundaryRenderCommonOptions<TModule>;
|
|
74
76
|
type RenderAppPageErrorBoundaryOptions<TModule extends AppPageModule = AppPageModule> = {
|
|
75
77
|
error: unknown;
|
|
78
|
+
errorOrigin?: "rsc" | "ssr";
|
|
76
79
|
matchedParams?: AppPageParams | null;
|
|
77
80
|
route?: AppPageBoundaryRoute<TModule> | null;
|
|
78
81
|
sanitizeErrorForClient: (error: Error) => Error;
|
|
@@ -3,7 +3,7 @@ import { AppElementsWire } from "./app-elements-wire.js";
|
|
|
3
3
|
import "./app-elements.js";
|
|
4
4
|
import DefaultGlobalError from "../shims/default-global-error.js";
|
|
5
5
|
import { isNavigationSignalError } from "../utils/navigation-signal.js";
|
|
6
|
-
import { ErrorBoundary, GlobalErrorBoundary } from "../shims/error-boundary.js";
|
|
6
|
+
import { ErrorBoundary, GlobalErrorBoundary, SerializedErrorBoundary } from "../shims/error-boundary.js";
|
|
7
7
|
import { LayoutSegmentProvider } from "../shims/layout-segment-context.js";
|
|
8
8
|
import { MetadataHead, ViewportHead } from "../shims/metadata.js";
|
|
9
9
|
import { resolveAppPageSpecialError } from "./app-page-execution.js";
|
|
@@ -152,6 +152,7 @@ async function renderAppPageHttpAccessFallback(options) {
|
|
|
152
152
|
const pathname = new URL(options.requestUrl).pathname;
|
|
153
153
|
const routeSegments = resolveHttpAccessFallbackHeadRouteSegments(options.route, layoutModules);
|
|
154
154
|
const { metadata, viewport } = await resolveAppPageHead({
|
|
155
|
+
applyFileBasedMetadata: options.applyFileBasedMetadata,
|
|
155
156
|
basePath: options.basePath ?? "",
|
|
156
157
|
layoutModules,
|
|
157
158
|
layoutTreePositions: resolveHttpAccessFallbackHeadLayoutTreePositions(options.route, layoutModules),
|
|
@@ -214,7 +215,7 @@ async function renderAppPageErrorBoundary(options) {
|
|
|
214
215
|
if (!errorBoundary.component) return null;
|
|
215
216
|
const rawError = options.error instanceof Error ? options.error : new Error(String(options.error));
|
|
216
217
|
rewriteClientHookError(rawError);
|
|
217
|
-
const errorObject = options.sanitizeErrorForClient(rawError);
|
|
218
|
+
const errorObject = options.errorOrigin === "ssr" ? rawError : options.sanitizeErrorForClient(rawError);
|
|
218
219
|
const matchedParams = options.matchedParams ?? options.route?.params ?? {};
|
|
219
220
|
const layoutModules = options.route?.layouts ?? options.rootLayouts;
|
|
220
221
|
const pathname = new URL(options.requestUrl).pathname;
|
|
@@ -224,6 +225,7 @@ async function renderAppPageErrorBoundary(options) {
|
|
|
224
225
|
})];
|
|
225
226
|
if (!errorBoundary.isGlobalError) try {
|
|
226
227
|
const { metadata, viewport } = await resolveAppPageHead({
|
|
228
|
+
applyFileBasedMetadata: options.applyFileBasedMetadata,
|
|
227
229
|
basePath: options.basePath ?? "",
|
|
228
230
|
fallbackOnFileMetadataError: true,
|
|
229
231
|
layoutModules,
|
|
@@ -247,7 +249,16 @@ async function renderAppPageErrorBoundary(options) {
|
|
|
247
249
|
console.error(`[vinext] App page error boundary head resolution failed for ${options.route?.pattern ?? pathname}:`, error);
|
|
248
250
|
}
|
|
249
251
|
const buildElement = (BoundaryComponent) => {
|
|
250
|
-
const
|
|
252
|
+
const serializedError = {
|
|
253
|
+
digest: "digest" in errorObject ? String(errorObject.digest) : void 0,
|
|
254
|
+
message: errorObject.message,
|
|
255
|
+
name: errorObject.name,
|
|
256
|
+
stack: process.env.NODE_ENV !== "production" ? errorObject.stack : void 0
|
|
257
|
+
};
|
|
258
|
+
const boundaryElement = errorBoundary.isGlobalError && BoundaryComponent !== DEFAULT_GLOBAL_ERROR_COMPONENT ? createElement(SerializedErrorBoundary, {
|
|
259
|
+
error: serializedError,
|
|
260
|
+
fallback: BoundaryComponent
|
|
261
|
+
}) : createElement(BoundaryComponent, { error: errorObject });
|
|
251
262
|
return wrapRenderedBoundaryElement({
|
|
252
263
|
element: createElement(Fragment, null, ...headElements, errorBoundary.isGlobalError ? createElement(GlobalErrorBoundary, {
|
|
253
264
|
fallback: DEFAULT_GLOBAL_ERROR_COMPONENT,
|
|
@@ -30,6 +30,7 @@ type FinalizeAppPageHtmlCacheResponseOptions = {
|
|
|
30
30
|
isrRscKey: AppPageRscCacheKeyBuilder;
|
|
31
31
|
isrSet: AppPageCacheSetter;
|
|
32
32
|
interceptionContext?: string | null;
|
|
33
|
+
omitPendingDynamicCacheState?: boolean;
|
|
33
34
|
preserveClientResponseHeaders?: boolean;
|
|
34
35
|
expireSeconds?: number;
|
|
35
36
|
revalidateSeconds: number | null;
|
|
@@ -1,15 +1,21 @@
|
|
|
1
|
+
import { NEXTJS_CACHE_HEADER, VINEXT_CACHE_HEADER } from "./headers.js";
|
|
1
2
|
import { setCacheStateHeaders } from "./cache-headers.js";
|
|
2
3
|
import { applyCdnResponseHeaders } from "./cache-control.js";
|
|
3
4
|
import { buildAppPageCacheValue } from "./isr-cache.js";
|
|
4
5
|
import { readStreamAsText } from "../utils/text-stream.js";
|
|
5
6
|
import { createEmptyAppPageRenderObservationState } from "./app-page-render-observation.js";
|
|
6
7
|
//#region src/server/app-page-cache-finalizer.ts
|
|
7
|
-
function applyPendingDynamicCdnHeaders(headers, tags) {
|
|
8
|
+
function applyPendingDynamicCdnHeaders(headers, tags, options = {}) {
|
|
8
9
|
applyCdnResponseHeaders(headers, {
|
|
9
10
|
cacheControl: headers.get("Cache-Control") ?? "",
|
|
10
11
|
pendingDynamicCheck: true,
|
|
11
12
|
tags
|
|
12
13
|
});
|
|
14
|
+
if (options.omitCacheState === true) {
|
|
15
|
+
headers.delete(VINEXT_CACHE_HEADER);
|
|
16
|
+
headers.delete(NEXTJS_CACHE_HEADER);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
13
19
|
setCacheStateHeaders(headers, "MISS");
|
|
14
20
|
}
|
|
15
21
|
function resolveAppPageCacheWritePolicy(options) {
|
|
@@ -30,7 +36,7 @@ function finalizeAppPageHtmlCacheResponse(response, options) {
|
|
|
30
36
|
const htmlKey = options.isrHtmlKey(options.cleanPathname);
|
|
31
37
|
const rscKey = options.isrRscKey(options.cleanPathname, null, void 0, options.interceptionContext);
|
|
32
38
|
const clientHeaders = new Headers(response.headers);
|
|
33
|
-
if (options.preserveClientResponseHeaders !== true) applyPendingDynamicCdnHeaders(clientHeaders, options.getPageTags());
|
|
39
|
+
if (options.preserveClientResponseHeaders !== true) applyPendingDynamicCdnHeaders(clientHeaders, options.getPageTags(), { omitCacheState: options.omitPendingDynamicCacheState === true });
|
|
34
40
|
const cachePromise = (async () => {
|
|
35
41
|
try {
|
|
36
42
|
const cachedHtml = await readStreamAsText(streamForCache);
|
|
@@ -10,8 +10,10 @@ import { AppPageSsrHandler } from "./app-page-stream.js";
|
|
|
10
10
|
import { AppLayoutParamAccessTracker } from "./app-layout-param-observation.js";
|
|
11
11
|
import { AppRscRenderMode } from "./app-rsc-render-mode.js";
|
|
12
12
|
import { ISRCacheEntry } from "./isr-cache.js";
|
|
13
|
+
import { PprFallbackShellState } from "../shims/ppr-fallback-shell.js";
|
|
13
14
|
import { FetchCacheMode } from "../shims/fetch-cache.js";
|
|
14
15
|
import { AppPagePprFallbackCacheShell } from "./app-ppr-fallback-shell.js";
|
|
16
|
+
import { WarmPprFallbackShellCachesOptions } from "./app-ppr-fallback-shell-render.js";
|
|
15
17
|
import { ValidateAppPageDynamicParamsOptions } from "./app-page-request.js";
|
|
16
18
|
import { ReactNode } from "react";
|
|
17
19
|
import { ReactFormState } from "react-dom/client";
|
|
@@ -32,7 +34,10 @@ type AppPageBackgroundRegenerationErrorContext = {
|
|
|
32
34
|
type AppPageBackgroundRegenerator = (key: string, renderFn: () => Promise<void>, errorContext?: AppPageBackgroundRegenerationErrorContext) => void;
|
|
33
35
|
type AppPageDispatchIntercept<TPage = unknown> = {
|
|
34
36
|
interceptLayouts?: readonly unknown[] | null;
|
|
37
|
+
interceptLayoutSegments?: readonly (readonly string[])[] | null;
|
|
38
|
+
interceptBranchSegments?: readonly string[] | null;
|
|
35
39
|
matchedParams: AppPageParams;
|
|
40
|
+
sourceMatchedParams?: AppPageParams;
|
|
36
41
|
page: TPage;
|
|
37
42
|
slotId?: string | null;
|
|
38
43
|
slotKey: string;
|
|
@@ -42,6 +47,8 @@ type AppPageDispatchIntercept<TPage = unknown> = {
|
|
|
42
47
|
type AppPageDispatchInterceptOptions<TPage = unknown> = {
|
|
43
48
|
interceptionContext: string | null;
|
|
44
49
|
interceptLayouts?: readonly unknown[] | null;
|
|
50
|
+
interceptLayoutSegments?: readonly (readonly string[])[] | null;
|
|
51
|
+
interceptBranchSegments?: readonly string[] | null;
|
|
45
52
|
interceptPage: TPage;
|
|
46
53
|
interceptParams: AppPageParams;
|
|
47
54
|
interceptSlotId?: string | null;
|
|
@@ -80,6 +87,14 @@ type AppPageDispatchRoute = {
|
|
|
80
87
|
unauthorized?: AppPageModule | null;
|
|
81
88
|
unauthorizeds?: readonly (AppPageModule | null | undefined)[];
|
|
82
89
|
};
|
|
90
|
+
type AppPagePprRuntime<TRoute extends AppPageDispatchRoute> = {
|
|
91
|
+
beginFinalRender(state: AppPagePprState): void;
|
|
92
|
+
getState(): AppPagePprState | null;
|
|
93
|
+
run<T>(shell: NonNullable<DispatchAppPageOptions<TRoute>["pprFallbackShell"]>, fn: () => T): T;
|
|
94
|
+
tryServe(options: DispatchAppPageOptions<TRoute>, currentRevalidateSeconds: number | null, isDraftMode: boolean, isForceStatic: boolean, isForceDynamic: boolean): Promise<Response | null>;
|
|
95
|
+
warm(options: WarmPprFallbackShellCachesOptions): Promise<void>;
|
|
96
|
+
};
|
|
97
|
+
type AppPagePprState = PprFallbackShellState;
|
|
83
98
|
type DispatchAppPageOptions<TRoute extends AppPageDispatchRoute> = {
|
|
84
99
|
/** Configured basePath (e.g. "/blog"). Used to prefix redirect Locations. */basePath?: string;
|
|
85
100
|
/**
|
|
@@ -94,7 +109,10 @@ type DispatchAppPageOptions<TRoute extends AppPageDispatchRoute> = {
|
|
|
94
109
|
* `next.config`. Undefined falls back to the React default downstream.
|
|
95
110
|
*/
|
|
96
111
|
reactMaxHeadersLength?: number;
|
|
97
|
-
buildPageElement: (route: TRoute, params: AppPageParams, opts: AppPageDispatchInterceptOptions | undefined, searchParams: URLSearchParams, layoutParamAccess?: AppLayoutParamAccessTracker
|
|
112
|
+
buildPageElement: (route: TRoute, params: AppPageParams, opts: AppPageDispatchInterceptOptions | undefined, searchParams: URLSearchParams, layoutParamAccess?: AppLayoutParamAccessTracker, options?: {
|
|
113
|
+
observeMetadataSearchParamsAccess?: boolean;
|
|
114
|
+
observePageSearchParamsAccess?: boolean;
|
|
115
|
+
}) => Promise<AppPageElement>;
|
|
98
116
|
clientReuseManifest?: ClientReuseManifestParseResult;
|
|
99
117
|
cleanPathname: string;
|
|
100
118
|
displayPathname?: string;
|
|
@@ -147,6 +165,7 @@ type DispatchAppPageOptions<TRoute extends AppPageDispatchRoute> = {
|
|
|
147
165
|
fallbackParamNames: readonly string[];
|
|
148
166
|
routePattern: string;
|
|
149
167
|
};
|
|
168
|
+
pprRuntime?: AppPagePprRuntime<TRoute>;
|
|
150
169
|
/**
|
|
151
170
|
* Set of concrete URL paths that were pre-rendered at build time for this
|
|
152
171
|
* route. When the exact cache entry for a known pregenerated path is absent
|
|
@@ -159,9 +178,9 @@ type DispatchAppPageOptions<TRoute extends AppPageDispatchRoute> = {
|
|
|
159
178
|
staticParamsValidationParams?: AppPageParams;
|
|
160
179
|
rootParams?: RootParams;
|
|
161
180
|
probeLayoutAt: (layoutIndex: number, layoutParamAccess?: AppLayoutParamAccessTracker) => unknown;
|
|
162
|
-
probePage: () => unknown;
|
|
181
|
+
probePage: (searchParams?: URLSearchParams) => unknown;
|
|
163
182
|
expireSeconds?: number;
|
|
164
|
-
renderErrorBoundaryPage: (error: unknown) => Promise<Response | null>;
|
|
183
|
+
renderErrorBoundaryPage: (error: unknown, errorOrigin?: "rsc" | "ssr") => Promise<Response | null>;
|
|
165
184
|
renderHttpAccessFallbackPage: (statusCode: number, opts: {
|
|
166
185
|
boundaryComponent?: unknown;
|
|
167
186
|
boundaryModule?: AppPageModule | null;
|
|
@@ -197,6 +216,16 @@ type DispatchAppPageOptions<TRoute extends AppPageDispatchRoute> = {
|
|
|
197
216
|
}) => void;
|
|
198
217
|
renderMode?: AppRscRenderMode;
|
|
199
218
|
};
|
|
219
|
+
declare function shouldReadAppPageCache(options: {
|
|
220
|
+
isProgressiveActionRender: boolean;
|
|
221
|
+
isDraftMode: boolean;
|
|
222
|
+
isForceDynamic: boolean;
|
|
223
|
+
isProduction: boolean;
|
|
224
|
+
isRscRequest: boolean;
|
|
225
|
+
revalidateSeconds: number | null;
|
|
226
|
+
scriptNonce?: string;
|
|
227
|
+
}): boolean;
|
|
228
|
+
declare function hasSearchParams(searchParams: URLSearchParams | null | undefined): boolean;
|
|
200
229
|
declare function dispatchAppPage<TRoute extends AppPageDispatchRoute>(options: DispatchAppPageOptions<TRoute>): Promise<Response>;
|
|
201
230
|
//#endregion
|
|
202
|
-
export { dispatchAppPage };
|
|
231
|
+
export { AppPageBoundaryOnError, AppPageDispatchRoute, AppPagePprRuntime, AppPagePprState, AppPageRenderableElement, DispatchAppPageOptions, dispatchAppPage, hasSearchParams, shouldReadAppPageCache };
|