vinext 0.1.0 → 0.1.2
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 +2 -5
- package/dist/build/assets-ignore.d.ts +32 -0
- package/dist/build/assets-ignore.js +48 -0
- package/dist/build/client-build-config.d.ts +33 -1
- package/dist/build/client-build-config.js +66 -1
- package/dist/check.js +4 -3
- package/dist/cli.js +2 -0
- package/dist/client/navigation-runtime.d.ts +11 -2
- package/dist/client/navigation-runtime.js +1 -1
- package/dist/client/vinext-next-data.d.ts +2 -1
- package/dist/client/window-next.d.ts +6 -4
- package/dist/config/config-matchers.d.ts +31 -5
- package/dist/config/config-matchers.js +50 -3
- package/dist/config/next-config.d.ts +29 -3
- package/dist/config/next-config.js +32 -2
- package/dist/deploy.js +47 -304
- package/dist/entries/app-rsc-entry.d.ts +8 -2
- package/dist/entries/app-rsc-entry.js +61 -5
- package/dist/entries/app-rsc-manifest.js +20 -2
- package/dist/entries/pages-client-entry.js +1 -1
- package/dist/entries/pages-server-entry.js +16 -7
- package/dist/index.d.ts +0 -2
- package/dist/index.js +233 -280
- package/dist/plugins/dynamic-preload-metadata.d.ts +13 -0
- package/dist/plugins/dynamic-preload-metadata.js +415 -0
- package/dist/plugins/og-assets.js +2 -2
- package/dist/plugins/optimize-imports.d.ts +8 -4
- package/dist/plugins/optimize-imports.js +16 -12
- package/dist/plugins/postcss.js +18 -14
- package/dist/plugins/require-context.d.ts +6 -0
- package/dist/plugins/require-context.js +184 -0
- package/dist/plugins/sass.d.ts +53 -24
- package/dist/plugins/sass.js +249 -1
- package/dist/plugins/wasm-module-import.d.ts +15 -0
- package/dist/plugins/wasm-module-import.js +50 -0
- package/dist/routing/app-route-graph.d.ts +35 -2
- package/dist/routing/app-route-graph.js +179 -8
- package/dist/routing/file-matcher.js +1 -1
- package/dist/routing/route-pattern.d.ts +2 -1
- package/dist/routing/route-pattern.js +16 -1
- package/dist/server/api-handler.js +4 -0
- package/dist/server/app-browser-entry.js +155 -215
- package/dist/server/app-browser-error.d.ts +4 -1
- package/dist/server/app-browser-error.js +7 -1
- package/dist/server/app-browser-history-controller.d.ts +104 -0
- package/dist/server/app-browser-history-controller.js +210 -0
- package/dist/server/app-browser-interception-context.d.ts +2 -1
- package/dist/server/app-browser-interception-context.js +15 -2
- package/dist/server/app-browser-navigation-controller.d.ts +13 -2
- package/dist/server/app-browser-navigation-controller.js +83 -4
- package/dist/server/app-browser-popstate.d.ts +12 -3
- package/dist/server/app-browser-popstate.js +19 -4
- package/dist/server/app-browser-rsc-redirect.d.ts +11 -2
- package/dist/server/app-browser-rsc-redirect.js +30 -8
- package/dist/server/app-browser-state.d.ts +3 -0
- package/dist/server/app-browser-state.js +10 -10
- package/dist/server/app-browser-visible-commit.js +10 -8
- package/dist/server/app-fallback-renderer.d.ts +2 -1
- package/dist/server/app-fallback-renderer.js +3 -1
- package/dist/server/app-history-state.d.ts +45 -1
- package/dist/server/app-history-state.js +109 -1
- package/dist/server/app-middleware.js +1 -0
- package/dist/server/app-optimistic-routing.js +22 -1
- package/dist/server/app-page-boundary-render.d.ts +2 -1
- package/dist/server/app-page-boundary-render.js +45 -21
- package/dist/server/app-page-cache.js +9 -7
- package/dist/server/app-page-dispatch.d.ts +14 -0
- package/dist/server/app-page-dispatch.js +21 -6
- package/dist/server/app-page-element-builder.d.ts +23 -2
- package/dist/server/app-page-element-builder.js +58 -17
- package/dist/server/app-page-execution.d.ts +1 -1
- package/dist/server/app-page-execution.js +32 -17
- package/dist/server/app-page-render.d.ts +7 -1
- package/dist/server/app-page-render.js +11 -16
- package/dist/server/app-page-request.d.ts +9 -6
- package/dist/server/app-page-request.js +14 -10
- package/dist/server/app-page-response.d.ts +2 -2
- package/dist/server/app-page-response.js +2 -2
- package/dist/server/app-page-route-wiring.d.ts +3 -1
- package/dist/server/app-page-route-wiring.js +10 -8
- package/dist/server/app-page-stream.d.ts +37 -7
- package/dist/server/app-page-stream.js +36 -6
- package/dist/server/app-pages-bridge.d.ts +16 -0
- package/dist/server/app-pages-bridge.js +23 -3
- package/dist/server/app-route-handler-cache.d.ts +1 -0
- package/dist/server/app-route-handler-cache.js +1 -0
- package/dist/server/app-route-handler-dispatch.d.ts +1 -0
- package/dist/server/app-route-handler-dispatch.js +2 -0
- package/dist/server/app-route-handler-execution.d.ts +1 -0
- package/dist/server/app-route-handler-execution.js +1 -0
- package/dist/server/app-route-handler-response.js +11 -10
- package/dist/server/app-route-handler-runtime.d.ts +1 -0
- package/dist/server/app-route-handler-runtime.js +15 -3
- package/dist/server/app-rsc-handler.d.ts +1 -0
- package/dist/server/app-rsc-handler.js +5 -4
- package/dist/server/app-rsc-response-finalizer.js +1 -1
- package/dist/server/app-rsc-route-matching.d.ts +20 -1
- package/dist/server/app-rsc-route-matching.js +29 -4
- package/dist/server/app-server-action-execution.d.ts +22 -1
- package/dist/server/app-server-action-execution.js +73 -12
- package/dist/server/app-ssr-entry.d.ts +6 -0
- package/dist/server/app-ssr-entry.js +19 -3
- package/dist/server/app-ssr-stream.js +9 -1
- package/dist/server/dev-lockfile.js +2 -1
- package/dist/server/dev-server.d.ts +1 -1
- package/dist/server/dev-server.js +97 -43
- package/dist/server/headers.d.ts +8 -1
- package/dist/server/headers.js +8 -1
- package/dist/server/instrumentation-runtime.d.ts +6 -0
- package/dist/server/instrumentation-runtime.js +8 -0
- package/dist/server/isr-cache.d.ts +37 -1
- package/dist/server/isr-cache.js +85 -1
- package/dist/server/isr-decision.d.ts +79 -0
- package/dist/server/isr-decision.js +70 -0
- package/dist/server/metadata-route-response.js +5 -3
- package/dist/server/middleware-runtime.d.ts +13 -0
- package/dist/server/middleware-runtime.js +11 -7
- package/dist/server/middleware.js +1 -0
- package/dist/server/navigation-planner.d.ts +62 -1
- package/dist/server/navigation-planner.js +193 -3
- package/dist/server/navigation-trace.d.ts +12 -2
- package/dist/server/navigation-trace.js +11 -1
- package/dist/server/normalize-path.d.ts +0 -8
- package/dist/server/normalize-path.js +3 -1
- package/dist/server/otel-tracer-extension.d.ts +45 -0
- package/dist/server/otel-tracer-extension.js +89 -0
- package/dist/server/pages-api-route.d.ts +14 -3
- package/dist/server/pages-api-route.js +6 -1
- package/dist/server/pages-asset-tags.d.ts +15 -4
- package/dist/server/pages-asset-tags.js +18 -12
- package/dist/server/pages-data-route.js +5 -1
- package/dist/server/pages-node-compat.d.ts +5 -11
- package/dist/server/pages-node-compat.js +175 -118
- package/dist/server/pages-page-data.d.ts +38 -7
- package/dist/server/pages-page-data.js +64 -18
- package/dist/server/pages-page-handler.d.ts +10 -2
- package/dist/server/pages-page-handler.js +49 -20
- package/dist/server/pages-page-response.d.ts +55 -2
- package/dist/server/pages-page-response.js +74 -6
- package/dist/server/pages-readiness.d.ts +36 -0
- package/dist/server/pages-readiness.js +21 -0
- package/dist/server/pages-request-pipeline.d.ts +113 -0
- package/dist/server/pages-request-pipeline.js +230 -0
- package/dist/server/pages-revalidate.d.ts +15 -0
- package/dist/server/pages-revalidate.js +19 -0
- package/dist/server/prod-server.d.ts +45 -3
- package/dist/server/prod-server.js +182 -234
- package/dist/server/socket-error-backstop.d.ts +19 -1
- package/dist/server/socket-error-backstop.js +77 -4
- package/dist/shims/app-router-scroll.js +22 -4
- package/dist/shims/cache-runtime.js +39 -2
- package/dist/shims/dynamic-preload-chunks.d.ts +8 -0
- package/dist/shims/dynamic-preload-chunks.js +77 -0
- package/dist/shims/dynamic.d.ts +4 -0
- package/dist/shims/dynamic.js +4 -2
- package/dist/shims/error-boundary.d.ts +17 -7
- package/dist/shims/error-boundary.js +8 -1
- package/dist/shims/error.js +37 -11
- package/dist/shims/fetch-cache.d.ts +22 -1
- package/dist/shims/fetch-cache.js +28 -1
- package/dist/shims/hash-scroll.d.ts +1 -0
- package/dist/shims/hash-scroll.js +3 -1
- package/dist/shims/head.js +6 -1
- package/dist/shims/headers.d.ts +16 -2
- package/dist/shims/headers.js +37 -1
- package/dist/shims/image-config.js +7 -1
- package/dist/shims/internal/app-route-detection.d.ts +6 -3
- package/dist/shims/internal/app-route-detection.js +10 -6
- package/dist/shims/internal/app-router-context.d.ts +5 -0
- package/dist/shims/internal/link-status-registry.d.ts +43 -0
- package/dist/shims/internal/link-status-registry.js +42 -0
- package/dist/shims/internal/route-pattern-for-warning.d.ts +27 -0
- package/dist/shims/internal/route-pattern-for-warning.js +40 -0
- package/dist/shims/internal/utils.d.ts +1 -0
- package/dist/shims/link.js +20 -6
- package/dist/shims/metadata.d.ts +6 -2
- package/dist/shims/metadata.js +32 -14
- package/dist/shims/navigation.d.ts +9 -18
- package/dist/shims/navigation.js +96 -23
- package/dist/shims/router-state.d.ts +1 -0
- package/dist/shims/router-state.js +2 -0
- package/dist/shims/router.d.ts +6 -3
- package/dist/shims/router.js +156 -22
- package/dist/shims/script-nonce-context.d.ts +1 -1
- package/dist/shims/script-nonce-context.js +11 -3
- package/dist/shims/server.d.ts +17 -1
- package/dist/shims/server.js +31 -6
- package/dist/shims/slot.js +1 -1
- package/dist/shims/unified-request-context.js +1 -0
- package/dist/typegen.js +1 -0
- package/dist/utils/client-build-manifest.d.ts +8 -1
- package/dist/utils/client-build-manifest.js +41 -6
- package/dist/utils/client-entry-manifest.d.ts +11 -0
- package/dist/utils/client-entry-manifest.js +29 -0
- package/dist/utils/client-runtime-metadata.d.ts +45 -0
- package/dist/utils/client-runtime-metadata.js +63 -0
- package/dist/utils/hash.d.ts +17 -1
- package/dist/utils/hash.js +36 -1
- package/dist/utils/lazy-chunks.d.ts +27 -1
- package/dist/utils/lazy-chunks.js +65 -1
- package/dist/utils/manifest-paths.d.ts +20 -2
- package/dist/utils/manifest-paths.js +38 -3
- package/dist/utils/path.d.ts +2 -1
- package/dist/utils/path.js +5 -1
- package/package.json +6 -2
|
@@ -6,6 +6,7 @@ type AppPageParams = Record<string, string | string[]>;
|
|
|
6
6
|
type GenerateStaticParams = (args: {
|
|
7
7
|
params: AppPageParams;
|
|
8
8
|
}) => unknown;
|
|
9
|
+
type Awaitable<T> = T | Promise<T>;
|
|
9
10
|
type GenerateStaticParamsModule = {
|
|
10
11
|
generateStaticParams?: GenerateStaticParams | null;
|
|
11
12
|
};
|
|
@@ -39,6 +40,7 @@ type BuildAppPageElementResult<TElement> = {
|
|
|
39
40
|
type AppPageInterceptMatch<TPage = unknown> = {
|
|
40
41
|
matchedParams: AppPageParams;
|
|
41
42
|
page: TPage;
|
|
43
|
+
__pageLoader?: (() => Promise<TPage>) | null;
|
|
42
44
|
slotId?: string | null;
|
|
43
45
|
slotKey: string;
|
|
44
46
|
sourceRouteIndex: number;
|
|
@@ -48,7 +50,7 @@ type ResolveAppPageInterceptMatchOptions<TRoute, TPage, TInterceptOpts> = {
|
|
|
48
50
|
currentRoute: TRoute;
|
|
49
51
|
findIntercept: (pathname: string) => AppPageInterceptMatch<TPage> | null;
|
|
50
52
|
getRouteParamNames: (route: TRoute) => readonly string[];
|
|
51
|
-
getSourceRoute: (sourceRouteIndex: number) => TRoute | undefined
|
|
53
|
+
getSourceRoute: (sourceRouteIndex: number) => Awaitable<TRoute | undefined>;
|
|
52
54
|
isRscRequest: boolean;
|
|
53
55
|
toInterceptOpts: (intercept: AppPageInterceptMatch<TPage>) => TInterceptOpts;
|
|
54
56
|
};
|
|
@@ -64,7 +66,7 @@ type ResolveAppPageInterceptionRerenderTargetOptions<TRoute, TPage, TInterceptOp
|
|
|
64
66
|
currentRoute: TRoute;
|
|
65
67
|
findIntercept: (pathname: string) => AppPageInterceptMatch<TPage> | null;
|
|
66
68
|
getRouteParamNames: (route: TRoute) => readonly string[];
|
|
67
|
-
getSourceRoute: (sourceRouteIndex: number) => TRoute | undefined
|
|
69
|
+
getSourceRoute: (sourceRouteIndex: number) => Awaitable<TRoute | undefined>;
|
|
68
70
|
isRscRequest: boolean;
|
|
69
71
|
toInterceptOpts: (intercept: AppPageInterceptMatch<TPage>) => TInterceptOpts;
|
|
70
72
|
};
|
|
@@ -82,9 +84,10 @@ type ResolveAppPageInterceptOptions<TRoute, TPage, TInterceptOpts, TElement> = {
|
|
|
82
84
|
currentRoute: TRoute;
|
|
83
85
|
findIntercept: (pathname: string) => AppPageInterceptMatch<TPage> | null;
|
|
84
86
|
getRouteParamNames: (route: TRoute) => readonly string[];
|
|
85
|
-
getSourceRoute: (sourceRouteIndex: number) => TRoute | undefined
|
|
87
|
+
getSourceRoute: (sourceRouteIndex: number) => Awaitable<TRoute | undefined>;
|
|
86
88
|
isRscRequest: boolean;
|
|
87
89
|
layoutParamAccess?: AppLayoutParamAccessTracker;
|
|
90
|
+
resolveNavigationParams: (route: TRoute, params: AppPageParams, pathname: string, interceptOpts: TInterceptOpts) => AppPageParams;
|
|
88
91
|
renderInterceptResponse: (route: TRoute, element: TElement) => Promise<Response> | Response;
|
|
89
92
|
searchParams: URLSearchParams;
|
|
90
93
|
setNavigationContext: (context: {
|
|
@@ -116,9 +119,9 @@ declare function validateAppPageDynamicParams(options: ValidateAppPageDynamicPar
|
|
|
116
119
|
* `setNavigationContext` + element build + Response wrap) and the server-action
|
|
117
120
|
* POST path (entries/app-rsc-entry.ts), which runs its own response pipeline.
|
|
118
121
|
*/
|
|
119
|
-
declare function resolveAppPageInterceptMatch<TRoute, TPage, TInterceptOpts>(options: ResolveAppPageInterceptMatchOptions<TRoute, TPage, TInterceptOpts>): ResolveAppPageInterceptMatchResult<TRoute, TInterceptOpts> | null
|
|
120
|
-
declare function resolveAppPageInterceptionRerenderTarget<TRoute, TPage, TInterceptOpts>(options: ResolveAppPageInterceptionRerenderTargetOptions<TRoute, TPage, TInterceptOpts>): ResolveAppPageInterceptionRerenderTargetResult<TRoute, TInterceptOpts
|
|
121
|
-
declare function resolveAppPageActionRerenderTarget<TRoute, TPage, TInterceptOpts>(options: ResolveAppPageActionRerenderTargetOptions<TRoute, TPage, TInterceptOpts>): ResolveAppPageActionRerenderTargetResult<TRoute, TInterceptOpts
|
|
122
|
+
declare function resolveAppPageInterceptMatch<TRoute, TPage, TInterceptOpts>(options: ResolveAppPageInterceptMatchOptions<TRoute, TPage, TInterceptOpts>): Promise<ResolveAppPageInterceptMatchResult<TRoute, TInterceptOpts> | null>;
|
|
123
|
+
declare function resolveAppPageInterceptionRerenderTarget<TRoute, TPage, TInterceptOpts>(options: ResolveAppPageInterceptionRerenderTargetOptions<TRoute, TPage, TInterceptOpts>): Promise<ResolveAppPageInterceptionRerenderTargetResult<TRoute, TInterceptOpts>>;
|
|
124
|
+
declare function resolveAppPageActionRerenderTarget<TRoute, TPage, TInterceptOpts>(options: ResolveAppPageActionRerenderTargetOptions<TRoute, TPage, TInterceptOpts>): Promise<ResolveAppPageActionRerenderTargetResult<TRoute, TInterceptOpts>>;
|
|
122
125
|
declare function resolveAppPageIntercept<TRoute, TPage, TInterceptOpts, TElement>(options: ResolveAppPageInterceptOptions<TRoute, TPage, TInterceptOpts, TElement>): Promise<ResolveAppPageInterceptResult<TInterceptOpts>>;
|
|
123
126
|
declare function buildAppPageElement<TElement>(options: BuildAppPageElementOptions<TElement>): Promise<BuildAppPageElementResult<TElement>>;
|
|
124
127
|
//#endregion
|
|
@@ -90,8 +90,8 @@ async function validateAppPageDynamicParams(options) {
|
|
|
90
90
|
* `setNavigationContext` + element build + Response wrap) and the server-action
|
|
91
91
|
* POST path (entries/app-rsc-entry.ts), which runs its own response pipeline.
|
|
92
92
|
*/
|
|
93
|
-
function resolveAppPageInterceptMatch(options) {
|
|
94
|
-
const interceptState = resolveAppPageInterceptState(options);
|
|
93
|
+
async function resolveAppPageInterceptMatch(options) {
|
|
94
|
+
const interceptState = await resolveAppPageInterceptState(options);
|
|
95
95
|
if (interceptState.kind !== "source-route") return null;
|
|
96
96
|
return {
|
|
97
97
|
interceptOpts: options.toInterceptOpts(interceptState.intercept),
|
|
@@ -100,11 +100,12 @@ function resolveAppPageInterceptMatch(options) {
|
|
|
100
100
|
sourceRoute: interceptState.sourceRoute
|
|
101
101
|
};
|
|
102
102
|
}
|
|
103
|
-
function resolveAppPageInterceptState(options) {
|
|
103
|
+
async function resolveAppPageInterceptState(options) {
|
|
104
104
|
if (!options.isRscRequest) return { kind: "none" };
|
|
105
105
|
const intercept = options.findIntercept(options.cleanPathname);
|
|
106
106
|
if (!intercept) return { kind: "none" };
|
|
107
|
-
|
|
107
|
+
if (intercept.__pageLoader && intercept.page == null) intercept.page = await intercept.__pageLoader();
|
|
108
|
+
const sourceRoute = await options.getSourceRoute(intercept.sourceRouteIndex);
|
|
108
109
|
if (!sourceRoute) return { kind: "none" };
|
|
109
110
|
if (sourceRoute === options.currentRoute) return {
|
|
110
111
|
kind: "current-route",
|
|
@@ -116,8 +117,8 @@ function resolveAppPageInterceptState(options) {
|
|
|
116
117
|
sourceRoute
|
|
117
118
|
};
|
|
118
119
|
}
|
|
119
|
-
function resolveAppPageInterceptionRerenderTarget(options) {
|
|
120
|
-
const interceptState = resolveAppPageInterceptState({
|
|
120
|
+
async function resolveAppPageInterceptionRerenderTarget(options) {
|
|
121
|
+
const interceptState = await resolveAppPageInterceptState({
|
|
121
122
|
cleanPathname: options.cleanPathname,
|
|
122
123
|
currentRoute: options.currentRoute,
|
|
123
124
|
findIntercept: options.findIntercept,
|
|
@@ -143,7 +144,7 @@ function resolveAppPageActionRerenderTarget(options) {
|
|
|
143
144
|
return resolveAppPageInterceptionRerenderTarget(options);
|
|
144
145
|
}
|
|
145
146
|
async function resolveAppPageIntercept(options) {
|
|
146
|
-
const interceptState = resolveAppPageInterceptState({
|
|
147
|
+
const interceptState = await resolveAppPageInterceptState({
|
|
147
148
|
cleanPathname: options.cleanPathname,
|
|
148
149
|
currentRoute: options.currentRoute,
|
|
149
150
|
findIntercept: options.findIntercept,
|
|
@@ -153,15 +154,18 @@ async function resolveAppPageIntercept(options) {
|
|
|
153
154
|
toInterceptOpts: options.toInterceptOpts
|
|
154
155
|
});
|
|
155
156
|
if (interceptState.kind === "source-route") {
|
|
157
|
+
const renderRoute = interceptState.sourceRoute;
|
|
158
|
+
const interceptOpts = options.toInterceptOpts(interceptState.intercept);
|
|
159
|
+
const renderParams = pickRouteParams(interceptState.intercept.matchedParams, options.getRouteParamNames(interceptState.sourceRoute));
|
|
156
160
|
options.setNavigationContext({
|
|
157
|
-
params: interceptState.intercept.matchedParams,
|
|
161
|
+
params: options.resolveNavigationParams(renderRoute, interceptState.intercept.matchedParams, options.cleanPathname, interceptOpts),
|
|
158
162
|
pathname: options.cleanPathname,
|
|
159
163
|
searchParams: options.searchParams
|
|
160
164
|
});
|
|
161
|
-
const interceptElement = await options.buildPageElement(
|
|
165
|
+
const interceptElement = await options.buildPageElement(renderRoute, renderParams, interceptOpts, options.searchParams, options.layoutParamAccess);
|
|
162
166
|
return {
|
|
163
167
|
interceptOpts: void 0,
|
|
164
|
-
response: await options.renderInterceptResponse(
|
|
168
|
+
response: await options.renderInterceptResponse(renderRoute, interceptElement)
|
|
165
169
|
};
|
|
166
170
|
}
|
|
167
171
|
return {
|
|
@@ -45,8 +45,8 @@ type BuildAppPageRscResponseOptions = {
|
|
|
45
45
|
timing?: AppPageResponseTiming;
|
|
46
46
|
};
|
|
47
47
|
type BuildAppPageHtmlResponseOptions = {
|
|
48
|
-
draftCookie?: string | null;
|
|
49
|
-
|
|
48
|
+
draftCookie?: string | null; /** Combined preload `Link` header value (React hints + font preloads), already capped. */
|
|
49
|
+
linkHeader?: string;
|
|
50
50
|
isEdgeRuntime?: boolean;
|
|
51
51
|
middlewareContext: AppPageMiddlewareContext;
|
|
52
52
|
policy: AppPageResponsePolicy;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_PARAMS_HEADER, VINEXT_TIMING_HEADER } from "./headers.js";
|
|
2
2
|
import { setCacheStateHeaders } from "./cache-headers.js";
|
|
3
|
-
import { VINEXT_RSC_CONTENT_TYPE, VINEXT_RSC_VARY_HEADER, applyRscCompatibilityIdHeader } from "./app-rsc-cache-busting.js";
|
|
4
3
|
import { NO_STORE_CACHE_CONTROL, STATIC_CACHE_CONTROL, buildRevalidateCacheControl } from "./cache-control.js";
|
|
4
|
+
import { VINEXT_RSC_CONTENT_TYPE, VINEXT_RSC_VARY_HEADER, applyRscCompatibilityIdHeader } from "./app-rsc-cache-busting.js";
|
|
5
5
|
import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js";
|
|
6
6
|
//#region src/server/app-page-response.ts
|
|
7
7
|
function applyTimingHeader(headers, timing) {
|
|
@@ -108,7 +108,7 @@ function buildAppPageHtmlResponse(body, options) {
|
|
|
108
108
|
if (options.policy.cacheControl) headers.set("Cache-Control", options.policy.cacheControl);
|
|
109
109
|
if (options.policy.cacheState) setCacheStateHeaders(headers, options.policy.cacheState);
|
|
110
110
|
if (options.draftCookie) headers.append("Set-Cookie", options.draftCookie);
|
|
111
|
-
if (options.
|
|
111
|
+
if (options.linkHeader) headers.set("Link", options.linkHeader);
|
|
112
112
|
mergeMiddlewareResponseHeaders(headers, options.middlewareContext.headers);
|
|
113
113
|
applyTimingHeader(headers, options.timing);
|
|
114
114
|
return new Response(body, {
|
|
@@ -113,6 +113,7 @@ type BuildAppPageRouteElementOptions<TModule extends AppPageModule = AppPageModu
|
|
|
113
113
|
resolvedMetadata: Metadata | null;
|
|
114
114
|
resolvedMetadataPathname?: string;
|
|
115
115
|
resolvedViewport: Viewport;
|
|
116
|
+
trailingSlash?: boolean;
|
|
116
117
|
rootForbiddenModule?: TModule | null;
|
|
117
118
|
rootNotFoundModule?: TModule | null;
|
|
118
119
|
rootUnauthorizedModule?: TModule | null;
|
|
@@ -142,6 +143,7 @@ declare function createAppPageLayoutEntries<TModule extends AppPageModule, TErro
|
|
|
142
143
|
forbiddens?: readonly (TModule | null | undefined)[] | null;
|
|
143
144
|
unauthorizeds?: readonly (TModule | null | undefined)[] | null;
|
|
144
145
|
}): AppPageLayoutEntry<TModule, TErrorModule>[];
|
|
146
|
+
declare function createAppPageRouteBodyMetadata(metadata: Metadata | null, pathname: string, metadataPlacement: "body" | "head", trailingSlash?: boolean): ReactNode;
|
|
145
147
|
declare function buildAppPageElements<TModule extends AppPageModule, TErrorModule extends AppPageErrorModule>(options: BuildAppPageElementsOptions<TModule, TErrorModule>): AppElements;
|
|
146
148
|
//#endregion
|
|
147
|
-
export { AppPageErrorModule, AppPageModule, AppPageRouteWiringRoute, AppPageSlotOverride, buildAppPageElements, createAppPageLayoutEntries, createAppPageTreePath, probeAppPageLayoutWithTracking, resolveAppPageChildSegments };
|
|
149
|
+
export { AppPageErrorModule, AppPageModule, AppPageRouteWiringRoute, AppPageSlotOverride, buildAppPageElements, createAppPageLayoutEntries, createAppPageRouteBodyMetadata, createAppPageTreePath, probeAppPageLayoutWithTracking, resolveAppPageChildSegments };
|
|
@@ -139,21 +139,22 @@ function createAppPageSlotBindings(route, layoutEntries, resolveSlotOverride, op
|
|
|
139
139
|
}
|
|
140
140
|
return normalizeAppElementsSlotBindings(bindings, { layoutIds: layoutEntries.map((entry) => entry.id) });
|
|
141
141
|
}
|
|
142
|
-
function createAppPageRouteHead(metadata, viewport, pathname, metadataPlacement) {
|
|
142
|
+
function createAppPageRouteHead(metadata, viewport, pathname, metadataPlacement, trailingSlash) {
|
|
143
143
|
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
144
144
|
/* @__PURE__ */ jsx("meta", { charSet: "utf-8" }),
|
|
145
145
|
metadata && metadataPlacement === "head" ? /* @__PURE__ */ jsx(MetadataHead, {
|
|
146
146
|
metadata,
|
|
147
|
-
pathname
|
|
147
|
+
pathname,
|
|
148
|
+
trailingSlash
|
|
148
149
|
}) : null,
|
|
149
150
|
/* @__PURE__ */ jsx(ViewportHead, { viewport })
|
|
150
151
|
] });
|
|
151
152
|
}
|
|
152
|
-
function createAppPageRouteBodyMetadata(metadata, pathname, metadataPlacement) {
|
|
153
|
+
function createAppPageRouteBodyMetadata(metadata, pathname, metadataPlacement, trailingSlash) {
|
|
153
154
|
if (!metadata || metadataPlacement !== "body") return null;
|
|
154
155
|
return /* @__PURE__ */ jsx("div", {
|
|
155
156
|
hidden: true,
|
|
156
|
-
dangerouslySetInnerHTML: { __html: renderMetadataToHtml(metadata, pathname) }
|
|
157
|
+
dangerouslySetInnerHTML: { __html: renderMetadataToHtml(metadata, pathname, { trailingSlash }) }
|
|
157
158
|
});
|
|
158
159
|
}
|
|
159
160
|
function buildAppPageElements(options) {
|
|
@@ -329,7 +330,7 @@ function buildAppPageElements(options) {
|
|
|
329
330
|
}
|
|
330
331
|
let routeChildren = /* @__PURE__ */ jsx(LayoutSegmentProvider, {
|
|
331
332
|
segmentMap: { children: [] },
|
|
332
|
-
children: /* @__PURE__ */ jsx(
|
|
333
|
+
children: /* @__PURE__ */ jsx(Slot, { id: pageId })
|
|
333
334
|
});
|
|
334
335
|
if (isPrefetchLoadingShell) if (routeLoadingComponent === null) routeChildren = null;
|
|
335
336
|
else routeChildren = /* @__PURE__ */ jsx(routeLoadingComponent, {});
|
|
@@ -339,6 +340,7 @@ function buildAppPageElements(options) {
|
|
|
339
340
|
fallback: /* @__PURE__ */ jsx(routeLoadingComponent, {}),
|
|
340
341
|
children: routeChildren
|
|
341
342
|
}, routeResetKey);
|
|
343
|
+
routeChildren = /* @__PURE__ */ jsx(AppRouterScrollTarget, { children: routeChildren });
|
|
342
344
|
}
|
|
343
345
|
const lastLayoutErrorModule = errorEntries.length > 0 ? errorEntries[errorEntries.length - 1].errorModule : null;
|
|
344
346
|
const notFoundComponent = getDefaultExport(options.route.notFound) ?? getDefaultExport(options.rootNotFoundModule);
|
|
@@ -430,12 +432,12 @@ function buildAppPageElements(options) {
|
|
|
430
432
|
children: routeChildren
|
|
431
433
|
});
|
|
432
434
|
elements[routeId] = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
433
|
-
createAppPageRouteHead(options.resolvedMetadata, options.resolvedViewport, options.resolvedMetadataPathname ?? options.routePath, metadataPlacement),
|
|
435
|
+
createAppPageRouteHead(options.resolvedMetadata, options.resolvedViewport, options.resolvedMetadataPathname ?? options.routePath, metadataPlacement, options.trailingSlash),
|
|
434
436
|
routeChildren,
|
|
435
|
-
createAppPageRouteBodyMetadata(options.resolvedMetadata, options.resolvedMetadataPathname ?? options.routePath, metadataPlacement)
|
|
437
|
+
createAppPageRouteBodyMetadata(options.resolvedMetadata, options.resolvedMetadataPathname ?? options.routePath, metadataPlacement, options.trailingSlash)
|
|
436
438
|
] });
|
|
437
439
|
registerAppElementRenderDependencies(elements, renderDependenciesByElementId);
|
|
438
440
|
return elements;
|
|
439
441
|
}
|
|
440
442
|
//#endregion
|
|
441
|
-
export { buildAppPageElements, createAppPageLayoutEntries, createAppPageTreePath, probeAppPageLayoutWithTracking, resolveAppPageChildSegments };
|
|
443
|
+
export { buildAppPageElements, createAppPageLayoutEntries, createAppPageRouteBodyMetadata, createAppPageTreePath, probeAppPageLayoutWithTracking, resolveAppPageChildSegments };
|
|
@@ -18,8 +18,30 @@ type AppSsrRenderResult = {
|
|
|
18
18
|
htmlStream: ReadableStream<Uint8Array>;
|
|
19
19
|
metadataReady: Promise<void>;
|
|
20
20
|
capturedRscData: Promise<ArrayBuffer> | null;
|
|
21
|
+
/**
|
|
22
|
+
* Preload `Link` header value emitted by React during SSR (via `onHeaders`),
|
|
23
|
+
* already capped to `reactMaxHeadersLength`. Empty/undefined when React
|
|
24
|
+
* emitted no preload headers (or emission was disabled with `0`).
|
|
25
|
+
*/
|
|
26
|
+
linkHeader?: string;
|
|
21
27
|
};
|
|
22
28
|
declare function isAppSsrRenderResult(value: unknown): value is AppSsrRenderResult;
|
|
29
|
+
/**
|
|
30
|
+
* Combine the React-emitted preload `Link` header with vinext's font preload
|
|
31
|
+
* `Link` header, capping the result to `reactMaxHeadersLength`.
|
|
32
|
+
*
|
|
33
|
+
* React already caps its own portion, but vinext emits font preloads through a
|
|
34
|
+
* separate channel. Mirroring Next.js — where every preload flows through a
|
|
35
|
+
* single capped `onHeaders` callback — we cap the *combined* header here,
|
|
36
|
+
* keeping only whole entries that fit and dropping the rest once the limit is
|
|
37
|
+
* exceeded. `0` disables emission entirely (matches React); `undefined` falls
|
|
38
|
+
* back to the React default of 6000.
|
|
39
|
+
*
|
|
40
|
+
* React's hints (scripts/modules/styles) come first so that under a tight cap
|
|
41
|
+
* the render-critical entries survive and trailing font preloads are dropped
|
|
42
|
+
* first.
|
|
43
|
+
*/
|
|
44
|
+
declare function buildAppPageLinkHeader(reactLinkHeader: string | undefined, fontLinkHeader: string | undefined, maxHeadersLength: number | undefined): string;
|
|
23
45
|
type AppPageSsrHandler = {
|
|
24
46
|
handleSsr: (rscStream: ReadableStream<Uint8Array>, navigationContext: NavigationContext | null, fontData: AppPageFontData, options?: {
|
|
25
47
|
formState?: ReactFormState | null;
|
|
@@ -30,6 +52,12 @@ type AppPageSsrHandler = {
|
|
|
30
52
|
* in the SSR head. Sourced from `experimental.clientTraceMetadata`.
|
|
31
53
|
*/
|
|
32
54
|
clientTraceMetadata?: readonly string[];
|
|
55
|
+
/**
|
|
56
|
+
* Maximum total length (in characters) of the preload `Link` header
|
|
57
|
+
* emitted during SSR. `0` disables emission. From `reactMaxHeadersLength`
|
|
58
|
+
* in `next.config`.
|
|
59
|
+
*/
|
|
60
|
+
reactMaxHeadersLength?: number;
|
|
33
61
|
rootParams?: RootParams;
|
|
34
62
|
sideStream?: ReadableStream<Uint8Array>;
|
|
35
63
|
capturedRscDataRef?: {
|
|
@@ -52,6 +80,12 @@ type RenderAppPageHtmlStreamOptions = {
|
|
|
52
80
|
* the SSR head. Undefined or empty disables emission.
|
|
53
81
|
*/
|
|
54
82
|
clientTraceMetadata?: readonly string[];
|
|
83
|
+
/**
|
|
84
|
+
* Maximum total length (in characters) of the preload `Link` header emitted
|
|
85
|
+
* during SSR. `0` disables emission. From `reactMaxHeadersLength` in
|
|
86
|
+
* `next.config`.
|
|
87
|
+
*/
|
|
88
|
+
reactMaxHeadersLength?: number;
|
|
55
89
|
rootParams?: RootParams;
|
|
56
90
|
ssrHandler: AppPageSsrHandler;
|
|
57
91
|
/** Pre-split side stream for fused embed+capture (#981). When set,
|
|
@@ -74,7 +108,8 @@ type AppPageHtmlStreamRecoveryResult = {
|
|
|
74
108
|
htmlStream: ReadableStream<Uint8Array> | null;
|
|
75
109
|
response: Response | null;
|
|
76
110
|
metadataReady: Promise<void>;
|
|
77
|
-
capturedRscData: Promise<ArrayBuffer> | null;
|
|
111
|
+
capturedRscData: Promise<ArrayBuffer> | null; /** React-emitted preload `Link` header (already capped). */
|
|
112
|
+
linkHeader?: string;
|
|
78
113
|
};
|
|
79
114
|
type RenderAppPageHtmlStreamWithRecoveryOptions<TSpecialError> = {
|
|
80
115
|
onShellRendered?: () => void;
|
|
@@ -94,10 +129,6 @@ type AppPageRscErrorTracker = {
|
|
|
94
129
|
getCapturedSpecialError: () => unknown;
|
|
95
130
|
onRenderError: (error: unknown, requestInfo: unknown, errorContext: unknown) => unknown;
|
|
96
131
|
};
|
|
97
|
-
type ShouldRerenderAppPageWithGlobalErrorOptions = {
|
|
98
|
-
capturedError: unknown;
|
|
99
|
-
hasLocalBoundary: boolean;
|
|
100
|
-
};
|
|
101
132
|
declare function createAppPageFontData(options: CreateAppPageFontDataOptions): AppPageFontData;
|
|
102
133
|
declare function renderAppPageHtmlStream(options: RenderAppPageHtmlStreamOptions): Promise<AppSsrRenderResult>;
|
|
103
134
|
/**
|
|
@@ -111,6 +142,5 @@ declare function deferUntilStreamConsumed(stream: ReadableStream<Uint8Array>, on
|
|
|
111
142
|
declare function renderAppPageHtmlResponse(options: RenderAppPageHtmlResponseOptions): Promise<Response>;
|
|
112
143
|
declare function renderAppPageHtmlStreamWithRecovery<TSpecialError>(options: RenderAppPageHtmlStreamWithRecoveryOptions<TSpecialError>): Promise<AppPageHtmlStreamRecoveryResult>;
|
|
113
144
|
declare function createAppPageRscErrorTracker(baseOnError: (error: unknown, requestInfo: unknown, errorContext: unknown) => unknown): AppPageRscErrorTracker;
|
|
114
|
-
declare function shouldRerenderAppPageWithGlobalError(options: ShouldRerenderAppPageWithGlobalErrorOptions): boolean;
|
|
115
145
|
//#endregion
|
|
116
|
-
export { AppPageFontData, AppPageSsrHandler, AppSsrRenderResult, createAppPageFontData, createAppPageRscErrorTracker, deferUntilStreamConsumed, isAppSsrRenderResult, renderAppPageHtmlResponse, renderAppPageHtmlStream, renderAppPageHtmlStreamWithRecovery
|
|
146
|
+
export { AppPageFontData, AppPageSsrHandler, AppSsrRenderResult, buildAppPageLinkHeader, createAppPageFontData, createAppPageRscErrorTracker, deferUntilStreamConsumed, isAppSsrRenderResult, renderAppPageHtmlResponse, renderAppPageHtmlStream, renderAppPageHtmlStreamWithRecovery };
|
|
@@ -14,6 +14,37 @@ function normalizeAppSsrRenderResult(raw, fallbackCapturedRscData = null) {
|
|
|
14
14
|
capturedRscData: fallbackCapturedRscData
|
|
15
15
|
};
|
|
16
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Combine the React-emitted preload `Link` header with vinext's font preload
|
|
19
|
+
* `Link` header, capping the result to `reactMaxHeadersLength`.
|
|
20
|
+
*
|
|
21
|
+
* React already caps its own portion, but vinext emits font preloads through a
|
|
22
|
+
* separate channel. Mirroring Next.js — where every preload flows through a
|
|
23
|
+
* single capped `onHeaders` callback — we cap the *combined* header here,
|
|
24
|
+
* keeping only whole entries that fit and dropping the rest once the limit is
|
|
25
|
+
* exceeded. `0` disables emission entirely (matches React); `undefined` falls
|
|
26
|
+
* back to the React default of 6000.
|
|
27
|
+
*
|
|
28
|
+
* React's hints (scripts/modules/styles) come first so that under a tight cap
|
|
29
|
+
* the render-critical entries survive and trailing font preloads are dropped
|
|
30
|
+
* first.
|
|
31
|
+
*/
|
|
32
|
+
function buildAppPageLinkHeader(reactLinkHeader, fontLinkHeader, maxHeadersLength) {
|
|
33
|
+
const limit = typeof maxHeadersLength === "number" ? maxHeadersLength : 6e3;
|
|
34
|
+
if (limit <= 0) return "";
|
|
35
|
+
const entries = [];
|
|
36
|
+
for (const source of [reactLinkHeader, fontLinkHeader]) {
|
|
37
|
+
if (!source) continue;
|
|
38
|
+
for (const entry of source.split(", ")) if (entry.length > 0) entries.push(entry);
|
|
39
|
+
}
|
|
40
|
+
let header = "";
|
|
41
|
+
for (const entry of entries) {
|
|
42
|
+
const next = header.length === 0 ? entry : `${header}, ${entry}`;
|
|
43
|
+
if (next.length > limit) break;
|
|
44
|
+
header = next;
|
|
45
|
+
}
|
|
46
|
+
return header;
|
|
47
|
+
}
|
|
17
48
|
function createAppPageFontData(options) {
|
|
18
49
|
return {
|
|
19
50
|
links: options.getLinks(),
|
|
@@ -27,6 +58,7 @@ async function renderAppPageHtmlStream(options) {
|
|
|
27
58
|
scriptNonce: options.scriptNonce,
|
|
28
59
|
basePath: options.basePath,
|
|
29
60
|
clientTraceMetadata: options.clientTraceMetadata,
|
|
61
|
+
reactMaxHeadersLength: options.reactMaxHeadersLength,
|
|
30
62
|
rootParams: options.rootParams,
|
|
31
63
|
sideStream: options.sideStream,
|
|
32
64
|
capturedRscDataRef: options.capturedRscDataRef,
|
|
@@ -89,13 +121,14 @@ async function renderAppPageHtmlResponse(options) {
|
|
|
89
121
|
}
|
|
90
122
|
async function renderAppPageHtmlStreamWithRecovery(options) {
|
|
91
123
|
try {
|
|
92
|
-
const { htmlStream, metadataReady, capturedRscData } = normalizeAppSsrRenderResult(await options.renderHtmlStream());
|
|
124
|
+
const { htmlStream, metadataReady, capturedRscData, linkHeader } = normalizeAppSsrRenderResult(await options.renderHtmlStream());
|
|
93
125
|
options.onShellRendered?.();
|
|
94
126
|
return {
|
|
95
127
|
htmlStream,
|
|
96
128
|
response: null,
|
|
97
129
|
metadataReady,
|
|
98
|
-
capturedRscData
|
|
130
|
+
capturedRscData,
|
|
131
|
+
linkHeader
|
|
99
132
|
};
|
|
100
133
|
} catch (error) {
|
|
101
134
|
const specialError = options.resolveSpecialError(error);
|
|
@@ -133,8 +166,5 @@ function createAppPageRscErrorTracker(baseOnError) {
|
|
|
133
166
|
}
|
|
134
167
|
};
|
|
135
168
|
}
|
|
136
|
-
function shouldRerenderAppPageWithGlobalError(options) {
|
|
137
|
-
return Boolean(options.capturedError) && !options.hasLocalBoundary;
|
|
138
|
-
}
|
|
139
169
|
//#endregion
|
|
140
|
-
export { createAppPageFontData, createAppPageRscErrorTracker, deferUntilStreamConsumed, isAppSsrRenderResult, renderAppPageHtmlResponse, renderAppPageHtmlStream, renderAppPageHtmlStreamWithRecovery
|
|
170
|
+
export { buildAppPageLinkHeader, createAppPageFontData, createAppPageRscErrorTracker, deferUntilStreamConsumed, isAppSsrRenderResult, renderAppPageHtmlResponse, renderAppPageHtmlStream, renderAppPageHtmlStreamWithRecovery };
|
|
@@ -10,6 +10,22 @@ type RenderPagesFallbackDependencies = {
|
|
|
10
10
|
buildRequestHeaders: (requestHeaders: Headers, middlewareRequestHeaders: Headers) => Headers | null;
|
|
11
11
|
decodePathParams: (pathname: string) => string;
|
|
12
12
|
applyRouteHandlerMiddlewareContext: (response: Response, middlewareContext: AppMiddlewareContext) => Response;
|
|
13
|
+
/**
|
|
14
|
+
* Returns the `__prerender_bypass` Set-Cookie header emitted by a
|
|
15
|
+
* `draftMode().enable()`/`disable()` call inside middleware, if any. Reading
|
|
16
|
+
* it clears it. Mirrors how App Router route handlers and page renders surface
|
|
17
|
+
* the middleware-enabled draft cookie so the same flow works when the request
|
|
18
|
+
* falls through to a Pages Router route.
|
|
19
|
+
*
|
|
20
|
+
* Note: this closes the draft-mode flow for production (Cloudflare Workers /
|
|
21
|
+
* Node), where middleware runs inline in the same RSC handler context that
|
|
22
|
+
* builds this fallback. In hybrid *dev*, middleware runs in a separate Vite
|
|
23
|
+
* Pages SSR runner and `draftMode()` inside middleware is not yet permitted
|
|
24
|
+
* there (it throws a scope error before any cookie is set), so this getter
|
|
25
|
+
* returns `null` and no cookie is appended. That dev limitation is pre-existing
|
|
26
|
+
* and tracked separately from #1520.
|
|
27
|
+
*/
|
|
28
|
+
getDraftModeCookieHeader: () => string | null | undefined;
|
|
13
29
|
};
|
|
14
30
|
type RenderPagesFallbackOptions = {
|
|
15
31
|
isRscRequest: boolean;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
async function renderPagesFallback(options, dependencies) {
|
|
6
6
|
const { isRscRequest, middlewareContext, request, url } = options;
|
|
7
|
-
const { loadPagesEntry, buildRequestHeaders, decodePathParams, applyRouteHandlerMiddlewareContext } = dependencies;
|
|
7
|
+
const { loadPagesEntry, buildRequestHeaders, decodePathParams, applyRouteHandlerMiddlewareContext, getDraftModeCookieHeader } = dependencies;
|
|
8
8
|
if (isRscRequest) return null;
|
|
9
9
|
const pagesEntry = await loadPagesEntry();
|
|
10
10
|
const pagesRequestHeaders = middlewareContext.requestHeaders ? buildRequestHeaders(request.headers, middlewareContext.requestHeaders) : null;
|
|
@@ -24,11 +24,31 @@ async function renderPagesFallback(options, dependencies) {
|
|
|
24
24
|
const pagesPathname = url.pathname;
|
|
25
25
|
if (pagesPathname.startsWith("/api/") || pagesPathname === "/api") {
|
|
26
26
|
if (typeof pagesEntry.handleApiRoute !== "function") return null;
|
|
27
|
-
|
|
27
|
+
const pagesApiResponse = await pagesEntry.handleApiRoute(pagesRequest, pagesUrl);
|
|
28
|
+
const draftCookie = getDraftModeCookieHeader();
|
|
29
|
+
return applyDraftModeCookie(applyRouteHandlerMiddlewareContext(pagesApiResponse, middlewareContext), draftCookie);
|
|
28
30
|
}
|
|
29
31
|
if (typeof pagesEntry.renderPage !== "function") return null;
|
|
30
32
|
const pagesRes = await pagesEntry.renderPage(pagesRequest, pagesUrl, {}, void 0, middlewareContext.requestHeaders);
|
|
31
|
-
|
|
33
|
+
if (pagesRes.status === 404) return null;
|
|
34
|
+
return applyDraftModeCookie(pagesRes, getDraftModeCookieHeader());
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Append a middleware-emitted `__prerender_bypass` Set-Cookie header to a Pages
|
|
38
|
+
* Router fallback response. Returns the response unchanged when there is no
|
|
39
|
+
* draft cookie to add. App Router route handlers/page renders surface this same
|
|
40
|
+
* cookie via `finalizeRouteHandlerResponse`/the page response builder; this
|
|
41
|
+
* keeps draft-mode parity for requests that fall through to the Pages Router.
|
|
42
|
+
*/
|
|
43
|
+
function applyDraftModeCookie(response, draftCookie) {
|
|
44
|
+
if (!draftCookie) return response;
|
|
45
|
+
const headers = new Headers(response.headers);
|
|
46
|
+
headers.append("Set-Cookie", draftCookie);
|
|
47
|
+
return new Response(response.body, {
|
|
48
|
+
status: response.status,
|
|
49
|
+
statusText: response.statusText,
|
|
50
|
+
headers
|
|
51
|
+
});
|
|
32
52
|
}
|
|
33
53
|
//#endregion
|
|
34
54
|
export { renderPagesFallback };
|
|
@@ -18,6 +18,7 @@ type ReadAppRouteHandlerCacheOptions = {
|
|
|
18
18
|
getCollectedFetchTags: () => string[];
|
|
19
19
|
handlerFn: AppRouteHandlerFunction;
|
|
20
20
|
i18n?: NextI18nConfig | null;
|
|
21
|
+
trailingSlash?: boolean;
|
|
21
22
|
isAutoHead: boolean;
|
|
22
23
|
isrDebug?: AppRouteDebugLogger;
|
|
23
24
|
isrGet: RouteHandlerCacheGetter;
|
|
@@ -39,6 +39,7 @@ async function readAppRouteHandlerCacheResponse(options) {
|
|
|
39
39
|
dynamicConfig: options.dynamicConfig,
|
|
40
40
|
handlerFn: options.handlerFn,
|
|
41
41
|
i18n: options.i18n,
|
|
42
|
+
trailingSlash: options.trailingSlash,
|
|
42
43
|
markDynamicUsage: options.markDynamicUsage,
|
|
43
44
|
params: options.params === null ? null : makeThenableParams(options.params),
|
|
44
45
|
request: new Request(options.requestUrl, { method: "GET" }),
|
|
@@ -30,6 +30,7 @@ type DispatchAppRouteHandlerOptions = {
|
|
|
30
30
|
isrGet: RouteHandlerCacheGetter;
|
|
31
31
|
isrRouteKey: (pathname: string) => string;
|
|
32
32
|
isrSet: RouteHandlerCacheSetter;
|
|
33
|
+
trailingSlash?: boolean;
|
|
33
34
|
middlewareContext: RouteHandlerMiddlewareContext;
|
|
34
35
|
middlewareRequestHeaders?: Headers | null;
|
|
35
36
|
/**
|
|
@@ -77,6 +77,7 @@ async function dispatchAppRouteHandler(options) {
|
|
|
77
77
|
getCollectedFetchTags,
|
|
78
78
|
handlerFn: resolvedHandlerFn,
|
|
79
79
|
i18n: options.i18n,
|
|
80
|
+
trailingSlash: options.trailingSlash,
|
|
80
81
|
isAutoHead,
|
|
81
82
|
isrDebug: options.isrDebug,
|
|
82
83
|
isrGet: options.isrGet,
|
|
@@ -127,6 +128,7 @@ async function dispatchAppRouteHandler(options) {
|
|
|
127
128
|
handler,
|
|
128
129
|
handlerFn: resolvedHandlerFn,
|
|
129
130
|
i18n: options.i18n,
|
|
131
|
+
trailingSlash: options.trailingSlash,
|
|
130
132
|
isAutoHead,
|
|
131
133
|
isProduction,
|
|
132
134
|
isrDebug: options.isrDebug,
|
|
@@ -42,6 +42,7 @@ type RunAppRouteHandlerOptions = {
|
|
|
42
42
|
dynamicConfig?: string;
|
|
43
43
|
handlerFn: AppRouteHandlerFunction;
|
|
44
44
|
i18n?: NextI18nConfig | null;
|
|
45
|
+
trailingSlash?: boolean;
|
|
45
46
|
markDynamicUsage: MarkAppRouteDynamicUsageFn;
|
|
46
47
|
middlewareRequestHeaders?: Headers | null;
|
|
47
48
|
/**
|
|
@@ -21,6 +21,7 @@ async function runAppRouteHandler(options) {
|
|
|
21
21
|
const trackedRequest = createTrackedAppRouteRequest(options.request, {
|
|
22
22
|
basePath: options.basePath,
|
|
23
23
|
i18n: options.i18n,
|
|
24
|
+
trailingSlash: options.trailingSlash,
|
|
24
25
|
middlewareHeaders: options.middlewareRequestHeaders,
|
|
25
26
|
onDynamicAccess() {
|
|
26
27
|
options.markDynamicUsage();
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import "./headers.js";
|
|
2
2
|
import { processMiddlewareHeaders } from "./request-pipeline.js";
|
|
3
3
|
import { setCacheStateHeaders } from "./cache-headers.js";
|
|
4
|
-
import {
|
|
4
|
+
import { applyCdnResponseHeaders } from "./cache-control.js";
|
|
5
|
+
import { buildAppRouteMissIsrCacheControl, decideIsr } from "./isr-decision.js";
|
|
5
6
|
import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js";
|
|
6
7
|
import { getSetCookieName } from "./cookie-utils.js";
|
|
7
8
|
//#region src/server/app-route-handler-response.ts
|
|
@@ -11,11 +12,6 @@ function hasMiddlewareHeader(headers) {
|
|
|
11
12
|
for (const key of headers.keys()) if (key.startsWith("x-middleware-")) return true;
|
|
12
13
|
return false;
|
|
13
14
|
}
|
|
14
|
-
function buildRouteHandlerCacheControl(cacheState, revalidateSeconds, expireSeconds) {
|
|
15
|
-
if (revalidateSeconds === 0) return NEVER_CACHE_CONTROL;
|
|
16
|
-
if (revalidateSeconds === Infinity) return STATIC_CACHE_CONTROL;
|
|
17
|
-
return buildCachedRevalidateCacheControl(cacheState, revalidateSeconds, expireSeconds);
|
|
18
|
-
}
|
|
19
15
|
function applyRouteHandlerMiddlewareContext(response, middlewareContext) {
|
|
20
16
|
if (!middlewareContext.headers && middlewareContext.status == null) return response;
|
|
21
17
|
const responseHeaders = new Headers(response.headers);
|
|
@@ -35,9 +31,14 @@ function buildRouteHandlerCachedResponse(cachedValue, options) {
|
|
|
35
31
|
for (const [key, value] of Object.entries(cachedValue.headers)) if (Array.isArray(value)) for (const entry of value) headers.append(key, entry);
|
|
36
32
|
else headers.set(key, value);
|
|
37
33
|
setCacheStateHeaders(headers, options.cacheState);
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
const { cacheControl } = decideIsr({
|
|
35
|
+
cacheState: options.cacheState,
|
|
36
|
+
kind: "app-route",
|
|
37
|
+
revalidateSeconds: options.revalidateSeconds,
|
|
38
|
+
expireSeconds: options.expireSeconds,
|
|
39
|
+
cacheControlMeta: options.cacheControl
|
|
40
|
+
});
|
|
41
|
+
applyCdnResponseHeaders(headers, { cacheControl });
|
|
41
42
|
return new Response(options.isHead ? null : cachedValue.body, {
|
|
42
43
|
status: cachedValue.status,
|
|
43
44
|
headers
|
|
@@ -45,7 +46,7 @@ function buildRouteHandlerCachedResponse(cachedValue, options) {
|
|
|
45
46
|
}
|
|
46
47
|
function applyRouteHandlerRevalidateHeader(response, revalidateSeconds, expireSeconds, tags) {
|
|
47
48
|
applyCdnResponseHeaders(response.headers, {
|
|
48
|
-
cacheControl:
|
|
49
|
+
cacheControl: buildAppRouteMissIsrCacheControl(revalidateSeconds, expireSeconds),
|
|
49
50
|
tags
|
|
50
51
|
});
|
|
51
52
|
}
|
|
@@ -24,6 +24,7 @@ type AppRouteRequestMode = "auto" | "force-static" | "error";
|
|
|
24
24
|
type TrackedAppRouteRequestOptions = {
|
|
25
25
|
basePath?: string;
|
|
26
26
|
i18n?: NextI18nConfig | null;
|
|
27
|
+
trailingSlash?: boolean;
|
|
27
28
|
middlewareHeaders?: Headers | null;
|
|
28
29
|
onDynamicAccess?: (access: AppRouteDynamicRequestAccess) => void;
|
|
29
30
|
requestMode?: AppRouteRequestMode;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { addBasePathToPathname } from "../utils/base-path.js";
|
|
1
2
|
import { buildRequestHeadersFromMiddlewareResponse } from "./middleware-request-headers.js";
|
|
2
3
|
import { NextRequest, RequestCookies, sealRequestCookies, sealRequestHeaders } from "../shims/server.js";
|
|
3
4
|
//#region src/server/app-route-handler-runtime.ts
|
|
@@ -43,10 +44,11 @@ function bindMethodIfNeeded(value, target) {
|
|
|
43
44
|
return typeof value === "function" ? value.bind(target) : value;
|
|
44
45
|
}
|
|
45
46
|
function buildNextConfig(options) {
|
|
46
|
-
if (!options.basePath && !options.i18n) return null;
|
|
47
|
+
if (!options.basePath && !options.i18n && !options.trailingSlash) return null;
|
|
47
48
|
return {
|
|
48
49
|
basePath: options.basePath,
|
|
49
|
-
i18n: options.i18n ?? void 0
|
|
50
|
+
i18n: options.i18n ?? void 0,
|
|
51
|
+
trailingSlash: options.trailingSlash
|
|
50
52
|
};
|
|
51
53
|
}
|
|
52
54
|
function rebuildRequestWithHeaders(input, headers) {
|
|
@@ -155,7 +157,17 @@ function createTrackedAppRouteRequest(request, options = {}) {
|
|
|
155
157
|
}
|
|
156
158
|
} });
|
|
157
159
|
};
|
|
158
|
-
const wrapRequest = (
|
|
160
|
+
const wrapRequest = (rawInput) => {
|
|
161
|
+
let input = rawInput;
|
|
162
|
+
if (options.basePath) {
|
|
163
|
+
const inputUrl = new URL(rawInput.url);
|
|
164
|
+
const prefixedPathname = addBasePathToPathname(inputUrl.pathname, options.basePath);
|
|
165
|
+
if (prefixedPathname !== inputUrl.pathname) {
|
|
166
|
+
inputUrl.pathname = prefixedPathname;
|
|
167
|
+
const bodySource = rawInput.body && !rawInput.bodyUsed ? rawInput.clone() : rawInput;
|
|
168
|
+
input = new Request(inputUrl, bodySource);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
159
171
|
const requestHeaders = options.middlewareHeaders ? buildRequestHeadersFromMiddlewareResponse(input.headers, options.middlewareHeaders) : null;
|
|
160
172
|
const requestWithOverrides = requestHeaders ? rebuildRequestWithHeaders(input, requestHeaders) : input;
|
|
161
173
|
const nextRequest = requestWithOverrides instanceof NextRequest ? requestWithOverrides : new NextRequest(requestWithOverrides, { nextConfig: nextConfig ?? void 0 });
|
|
@@ -30,6 +30,7 @@ type AppRscRouteMatch<TRoute> = {
|
|
|
30
30
|
type DispatchMatchedPageOptions<TRoute> = {
|
|
31
31
|
clientReuseManifest: ClientReuseManifestParseResult;
|
|
32
32
|
cleanPathname: string;
|
|
33
|
+
displayPathname: string;
|
|
33
34
|
formState: ReactFormState | null;
|
|
34
35
|
actionError?: unknown;
|
|
35
36
|
actionFailed?: boolean;
|