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
|
@@ -3,8 +3,7 @@ import { getRequestExecutionContext } from "../shims/request-context.js";
|
|
|
3
3
|
import { AppElementsWire } from "./app-elements-wire.js";
|
|
4
4
|
import { shouldSuppressLoadingBoundaries } from "./app-rsc-render-mode.js";
|
|
5
5
|
import "./app-elements.js";
|
|
6
|
-
import {
|
|
7
|
-
import { consumeDynamicUsage, consumeInvalidDynamicUsageError, getAndClearPendingCookies, getDraftModeCookieHeader, isDraftModeRequest, markDynamicUsage, peekRenderRequestApiUsage, setHeadersContext } from "../shims/headers.js";
|
|
6
|
+
import { consumeDynamicUsage, consumeInvalidDynamicUsageError, getAndClearPendingCookies, getDraftModeCookieHeader, getHeadersContext, isDraftModeRequest, markDynamicUsage, peekDynamicUsage, peekRenderRequestApiUsage, setHeadersContext } from "../shims/headers.js";
|
|
8
7
|
import { _consumeRequestScopedCacheLife, _peekRequestScopedCacheLife } from "../shims/cache-request-state.js";
|
|
9
8
|
import { ensureFetchPatch, getCollectedFetchTags, peekDynamicFetchObservations, runWithFetchDedupe, setCurrentFetchCacheMode, setCurrentFetchSoftTags, setCurrentForceDynamicFetchDefault } from "../shims/fetch-cache.js";
|
|
10
9
|
import { VINEXT_RSC_CONTENT_TYPE, VINEXT_RSC_VARY_HEADER, applyRscCompatibilityIdHeader } from "./app-rsc-cache-busting.js";
|
|
@@ -108,6 +107,7 @@ async function runAppPageRevalidationContext(options, renderFn) {
|
|
|
108
107
|
const { createStaticGenerationHeadersContext } = await import("./app-static-generation.js");
|
|
109
108
|
return runWithRequestContext(createRequestContext({
|
|
110
109
|
headersContext: createStaticGenerationHeadersContext({
|
|
110
|
+
draftModeEnabled: false,
|
|
111
111
|
draftModeSecret: options.draftModeSecret,
|
|
112
112
|
dynamicConfig: options.dynamicConfig,
|
|
113
113
|
routeKind: "page",
|
|
@@ -132,6 +132,8 @@ function toInterceptOptions(interceptionContext, intercept) {
|
|
|
132
132
|
return {
|
|
133
133
|
interceptionContext,
|
|
134
134
|
interceptLayouts: intercept.interceptLayouts,
|
|
135
|
+
interceptLayoutSegments: intercept.interceptLayoutSegments,
|
|
136
|
+
interceptBranchSegments: intercept.interceptBranchSegments,
|
|
135
137
|
interceptPage: intercept.page,
|
|
136
138
|
interceptParams: intercept.matchedParams,
|
|
137
139
|
interceptSlotId: intercept.slotId ?? null,
|
|
@@ -140,75 +142,10 @@ function toInterceptOptions(interceptionContext, intercept) {
|
|
|
140
142
|
interceptSourcePageSegments: intercept.sourcePageSegments ?? null
|
|
141
143
|
};
|
|
142
144
|
}
|
|
143
|
-
/**
|
|
144
|
-
* Request-phase fallback-shell gate. Callers must run the exact cache read and
|
|
145
|
-
* static-param validation before this classification step.
|
|
146
|
-
*/
|
|
147
|
-
function classifyPprFallbackShellEligibility(options, currentRevalidateSeconds, isDraftMode, isForceStatic, isForceDynamic) {
|
|
148
|
-
if (options.renderedConcreteUrlPaths?.has(options.cleanPathname) === true) return { kind: "skip-known-pregenerated-route" };
|
|
149
|
-
const fallbackShells = options.pprFallbackCacheShells;
|
|
150
|
-
if (!fallbackShells || fallbackShells.length === 0) return { kind: "skip-no-fallback-shells" };
|
|
151
|
-
if (options.isRscRequest) return { kind: "skip-rsc-request" };
|
|
152
|
-
if (options.request.method !== "GET") return { kind: "skip-non-get" };
|
|
153
|
-
if (!isForceStatic && hasSearchParams(options.searchParams)) return { kind: "skip-search-params" };
|
|
154
|
-
if (!shouldReadAppPageCache({
|
|
155
|
-
isDraftMode,
|
|
156
|
-
isForceDynamic,
|
|
157
|
-
isProgressiveActionRender: options.isProgressiveActionRender === true,
|
|
158
|
-
isProduction: options.isProduction,
|
|
159
|
-
isRscRequest: false,
|
|
160
|
-
revalidateSeconds: currentRevalidateSeconds,
|
|
161
|
-
scriptNonce: options.scriptNonce
|
|
162
|
-
})) return { kind: "skip-cache-disabled" };
|
|
163
|
-
return {
|
|
164
|
-
kind: "probe-fallback-shells",
|
|
165
|
-
fallbackShells
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
async function probePprFallbackShellCache(options, route, fallbackShells, currentRevalidateSeconds) {
|
|
169
|
-
const { readAppPageFallbackShellCacheResponse } = await import("./app-page-cache.js");
|
|
170
|
-
const { rewriteAppPprFallbackShellHtmlNavigation } = await import("./app-ppr-fallback-shell.js");
|
|
171
|
-
for (const fallbackShell of fallbackShells) {
|
|
172
|
-
const fallbackShellResponse = await readAppPageFallbackShellCacheResponse({
|
|
173
|
-
clearRequestContext: options.clearRequestContext,
|
|
174
|
-
expireSeconds: options.expireSeconds,
|
|
175
|
-
fallbackPathname: fallbackShell.pathname,
|
|
176
|
-
isEdgeRuntime: options.isEdgeRuntime,
|
|
177
|
-
isrDebug: options.isrDebug,
|
|
178
|
-
isrGet: options.isrGet,
|
|
179
|
-
isrHtmlKey: options.isrHtmlKey,
|
|
180
|
-
middlewareHeaders: options.middlewareContext.headers,
|
|
181
|
-
middlewareStatus: options.middlewareContext.status,
|
|
182
|
-
revalidateSeconds: currentRevalidateSeconds ?? 0,
|
|
183
|
-
rewriteHtml(html) {
|
|
184
|
-
return rewriteAppPprFallbackShellHtmlNavigation({
|
|
185
|
-
html,
|
|
186
|
-
params: options.params,
|
|
187
|
-
pathname: options.cleanPathname,
|
|
188
|
-
searchParams: options.searchParams
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
if (fallbackShellResponse) return fallbackShellResponse;
|
|
193
|
-
}
|
|
194
|
-
return null;
|
|
195
|
-
}
|
|
196
|
-
async function tryServePprFallbackShell(options, route, currentRevalidateSeconds, isDraftMode, isForceStatic, isForceDynamic) {
|
|
197
|
-
const decision = classifyPprFallbackShellEligibility(options, currentRevalidateSeconds, isDraftMode, isForceStatic, isForceDynamic);
|
|
198
|
-
switch (decision.kind) {
|
|
199
|
-
case "skip-known-pregenerated-route":
|
|
200
|
-
case "skip-no-fallback-shells":
|
|
201
|
-
case "skip-rsc-request":
|
|
202
|
-
case "skip-non-get":
|
|
203
|
-
case "skip-search-params":
|
|
204
|
-
case "skip-cache-disabled": return null;
|
|
205
|
-
case "probe-fallback-shells": return await probePprFallbackShellCache(options, route, decision.fallbackShells, currentRevalidateSeconds);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
145
|
async function dispatchAppPage(options) {
|
|
209
146
|
const dispatch = () => runWithFetchDedupe(() => dispatchAppPageInner(options));
|
|
210
|
-
if (!options.pprFallbackShell) return await dispatch();
|
|
211
|
-
return await
|
|
147
|
+
if (!options.pprFallbackShell || !options.pprRuntime) return await dispatch();
|
|
148
|
+
return await options.pprRuntime.run(options.pprFallbackShell, dispatch);
|
|
212
149
|
}
|
|
213
150
|
async function dispatchAppPageInner(options) {
|
|
214
151
|
const route = options.route;
|
|
@@ -218,7 +155,11 @@ async function dispatchAppPageInner(options) {
|
|
|
218
155
|
const isDynamicError = dynamicConfig === "error";
|
|
219
156
|
const isForceDynamic = dynamicConfig === "force-dynamic";
|
|
220
157
|
const isDraftMode = isDraftModeRequest(options.request, options.draftModeSecret);
|
|
158
|
+
const requestHeadersContext = getHeadersContext();
|
|
159
|
+
const hasRequestSearchParams = !isForceStatic && hasSearchParams(options.searchParams);
|
|
160
|
+
const pageSearchParams = isForceStatic ? new URLSearchParams() : options.searchParams;
|
|
221
161
|
const layoutParamAccess = createAppLayoutParamAccessTracker();
|
|
162
|
+
const hasActiveLoadingBoundary = shouldSuppressLoadingBoundaries(options.renderMode ?? "navigation") ? false : Boolean(route.loading?.default);
|
|
222
163
|
setCurrentFetchSoftTags(buildAppPageTags(options.cleanPathname, [], route.routeSegments));
|
|
223
164
|
setCurrentFetchCacheMode(options.fetchCache ?? null);
|
|
224
165
|
setCurrentForceDynamicFetchDefault(isForceDynamic);
|
|
@@ -238,9 +179,10 @@ async function dispatchAppPageInner(options) {
|
|
|
238
179
|
options.clearRequestContext();
|
|
239
180
|
return methodResponse;
|
|
240
181
|
}
|
|
241
|
-
if (
|
|
182
|
+
if (isForceStatic || isDynamicError) {
|
|
242
183
|
const { createStaticGenerationHeadersContext } = await import("./app-static-generation.js");
|
|
243
184
|
setHeadersContext(createStaticGenerationHeadersContext({
|
|
185
|
+
draftModeEnabled: isDraftMode,
|
|
244
186
|
draftModeSecret: options.draftModeSecret,
|
|
245
187
|
dynamicConfig,
|
|
246
188
|
routeKind: "page",
|
|
@@ -266,7 +208,7 @@ async function dispatchAppPageInner(options) {
|
|
|
266
208
|
const cachedPageResponse = await readAppPageCacheResponse({
|
|
267
209
|
cleanPathname: options.cleanPathname,
|
|
268
210
|
clearRequestContext: options.clearRequestContext,
|
|
269
|
-
hasRequestSearchParams
|
|
211
|
+
hasRequestSearchParams,
|
|
270
212
|
isEdgeRuntime: options.isEdgeRuntime,
|
|
271
213
|
isRscRequest: options.isRscRequest,
|
|
272
214
|
isrDebug: options.isrDebug,
|
|
@@ -300,19 +242,23 @@ async function dispatchAppPageInner(options) {
|
|
|
300
242
|
});
|
|
301
243
|
revalidationTarget.navigationParams = resolveAppPageNavigationParams(revalidationTarget.route, revalidationTarget.navigationParams, options.cleanPathname, revalidationTarget.interceptOpts);
|
|
302
244
|
await options.ensureRouteLoaded?.(revalidationTarget.route);
|
|
245
|
+
const revalidationDynamicConfig = options.resolveRouteDynamicConfig?.(revalidationTarget.route) ?? (revalidationTarget.route === route ? dynamicConfig : void 0);
|
|
303
246
|
return runAppPageRevalidationContext({
|
|
304
247
|
cleanPathname: options.cleanPathname,
|
|
305
248
|
displayPathname: options.displayPathname,
|
|
306
249
|
currentFetchCacheMode: options.resolveRouteFetchCacheMode?.(revalidationTarget.route) ?? (revalidationTarget.route === route ? options.fetchCache ?? null : null),
|
|
307
250
|
draftModeSecret: options.draftModeSecret,
|
|
308
|
-
dynamicConfig:
|
|
251
|
+
dynamicConfig: revalidationDynamicConfig,
|
|
309
252
|
params: revalidationTarget.navigationParams,
|
|
310
253
|
routePattern: revalidationTarget.route.pattern,
|
|
311
254
|
routeSegments: revalidationTarget.route.routeSegments,
|
|
312
255
|
setNavigationContext: options.setNavigationContext
|
|
313
256
|
}, async () => {
|
|
314
257
|
const { renderAppPageCacheArtifacts } = await import("./app-page-cache-render.js");
|
|
315
|
-
const revalidatedElement = await options.buildPageElement(revalidationTarget.route, revalidationTarget.params, revalidationTarget.interceptOpts, new URLSearchParams()
|
|
258
|
+
const revalidatedElement = await options.buildPageElement(revalidationTarget.route, revalidationTarget.params, revalidationTarget.interceptOpts, new URLSearchParams(), void 0, {
|
|
259
|
+
observeMetadataSearchParamsAccess: revalidationDynamicConfig !== "force-static",
|
|
260
|
+
observePageSearchParamsAccess: revalidationDynamicConfig !== "force-static"
|
|
261
|
+
});
|
|
316
262
|
const revalidatedOnError = options.createRscOnErrorHandler(options.cleanPathname, revalidationTarget.route.pattern);
|
|
317
263
|
const rendered = await renderAppPageCacheArtifacts({
|
|
318
264
|
basePath: options.basePath,
|
|
@@ -365,14 +311,29 @@ async function dispatchAppPageInner(options) {
|
|
|
365
311
|
});
|
|
366
312
|
if (dynamicParamsResponse) return dynamicParamsResponse;
|
|
367
313
|
}
|
|
368
|
-
const fallbackShellResponse = await
|
|
314
|
+
const fallbackShellResponse = options.pprRuntime ? await options.pprRuntime.tryServe(options, currentRevalidateSeconds, isDraftMode, isForceStatic, isForceDynamic) : null;
|
|
369
315
|
if (fallbackShellResponse) return fallbackShellResponse;
|
|
316
|
+
let interceptDynamicConfig;
|
|
317
|
+
let interceptDynamicConfigResolved = false;
|
|
370
318
|
const interceptResult = await resolveAppPageIntercept({
|
|
371
319
|
async buildPageElement(interceptRoute, interceptParams, interceptOpts, interceptSearchParams, interceptLayoutParamAccess) {
|
|
372
|
-
|
|
320
|
+
const sourceDynamicConfig = interceptDynamicConfigResolved ? interceptDynamicConfig : options.resolveRouteDynamicConfig?.(interceptRoute);
|
|
321
|
+
if (sourceDynamicConfig === "force-static" || sourceDynamicConfig === "error") {
|
|
322
|
+
const { createStaticGenerationHeadersContext } = await import("./app-static-generation.js");
|
|
323
|
+
setHeadersContext(createStaticGenerationHeadersContext({
|
|
324
|
+
draftModeEnabled: isDraftMode,
|
|
325
|
+
draftModeSecret: options.draftModeSecret,
|
|
326
|
+
dynamicConfig: sourceDynamicConfig,
|
|
327
|
+
routeKind: "page",
|
|
328
|
+
routePattern: interceptRoute.pattern
|
|
329
|
+
}));
|
|
330
|
+
} else setHeadersContext(requestHeadersContext);
|
|
373
331
|
setCurrentFetchCacheMode(options.resolveRouteFetchCacheMode?.(interceptRoute) ?? null);
|
|
374
|
-
setCurrentForceDynamicFetchDefault(
|
|
375
|
-
return options.buildPageElement(interceptRoute, interceptParams, interceptOpts, interceptSearchParams, interceptLayoutParamAccess
|
|
332
|
+
setCurrentForceDynamicFetchDefault(sourceDynamicConfig === "force-dynamic");
|
|
333
|
+
return options.buildPageElement(interceptRoute, interceptParams, interceptOpts, interceptSearchParams, interceptLayoutParamAccess, {
|
|
334
|
+
observeMetadataSearchParamsAccess: sourceDynamicConfig !== "force-static",
|
|
335
|
+
observePageSearchParamsAccess: sourceDynamicConfig !== "force-static"
|
|
336
|
+
});
|
|
376
337
|
},
|
|
377
338
|
cleanPathname: options.cleanPathname,
|
|
378
339
|
currentRoute: route,
|
|
@@ -404,6 +365,12 @@ async function dispatchAppPageInner(options) {
|
|
|
404
365
|
headers: interceptHeaders
|
|
405
366
|
});
|
|
406
367
|
},
|
|
368
|
+
async resolveSearchParams(sourceRoute, searchParams) {
|
|
369
|
+
await options.ensureRouteLoaded?.(sourceRoute);
|
|
370
|
+
interceptDynamicConfig = options.resolveRouteDynamicConfig?.(sourceRoute);
|
|
371
|
+
interceptDynamicConfigResolved = true;
|
|
372
|
+
return interceptDynamicConfig === "force-static" ? new URLSearchParams() : searchParams;
|
|
373
|
+
},
|
|
407
374
|
searchParams: options.searchParams,
|
|
408
375
|
setNavigationContext: options.setNavigationContext,
|
|
409
376
|
toInterceptOpts(intercept) {
|
|
@@ -414,12 +381,15 @@ async function dispatchAppPageInner(options) {
|
|
|
414
381
|
const buildCurrentPageElement = () => buildAppPageElement({
|
|
415
382
|
buildPageElement() {
|
|
416
383
|
if (options.actionFailed) throw options.actionError;
|
|
417
|
-
return options.buildPageElement(route, options.params, interceptResult.interceptOpts,
|
|
384
|
+
return options.buildPageElement(route, options.params, interceptResult.interceptOpts, pageSearchParams, layoutParamAccess, {
|
|
385
|
+
observeMetadataSearchParamsAccess: !isForceStatic,
|
|
386
|
+
observePageSearchParamsAccess: !isForceStatic
|
|
387
|
+
});
|
|
418
388
|
},
|
|
419
389
|
async probePageSpecialError() {
|
|
420
390
|
if (!shouldSuppressLoadingBoundaries(options.renderMode ?? "navigation") && route.loading?.default) return null;
|
|
421
391
|
return resolveAppPageSpecialError(await probeAppPageThrownError({
|
|
422
|
-
probePage: options.probePage,
|
|
392
|
+
probePage: () => options.probePage(pageSearchParams),
|
|
423
393
|
runWithSuppressedHookWarning(probe) {
|
|
424
394
|
return options.runWithSuppressedHookWarning(probe);
|
|
425
395
|
}
|
|
@@ -433,12 +403,11 @@ async function dispatchAppPageInner(options) {
|
|
|
433
403
|
},
|
|
434
404
|
resolveSpecialError: resolveAppPageSpecialError
|
|
435
405
|
});
|
|
436
|
-
const fallbackShellState =
|
|
406
|
+
const fallbackShellState = options.pprRuntime?.getState() ?? null;
|
|
437
407
|
if (fallbackShellState && process.env.VINEXT_PRERENDER === "1" && !options.isRscRequest) {
|
|
438
408
|
const warmupBuildResult = await buildCurrentPageElement();
|
|
439
409
|
if (warmupBuildResult.response) return warmupBuildResult.response;
|
|
440
|
-
|
|
441
|
-
await warmPprFallbackShellCaches({
|
|
410
|
+
await options.pprRuntime.warm({
|
|
442
411
|
element: warmupBuildResult.element,
|
|
443
412
|
onError: options.createRscOnErrorHandler(options.cleanPathname, route.pattern),
|
|
444
413
|
renderToReadableStream: options.renderToReadableStream,
|
|
@@ -451,11 +420,11 @@ async function dispatchAppPageInner(options) {
|
|
|
451
420
|
const navigationParams = resolveAppPageNavigationParams(route, options.params, options.cleanPathname, interceptResult.interceptOpts);
|
|
452
421
|
options.setNavigationContext({
|
|
453
422
|
pathname: options.displayPathname ?? options.cleanPathname,
|
|
454
|
-
searchParams:
|
|
423
|
+
searchParams: pageSearchParams,
|
|
455
424
|
params: navigationParams
|
|
456
425
|
});
|
|
457
426
|
const layoutClassifications = getEffectiveLayoutClassifications(route, options.debugClassification);
|
|
458
|
-
const activeFallbackShellState =
|
|
427
|
+
const activeFallbackShellState = options.pprRuntime?.getState() ?? null;
|
|
459
428
|
const pprFallbackShellSignal = activeFallbackShellState?.abortController.signal;
|
|
460
429
|
const pprFallbackShellReactSignal = activeFallbackShellState?.reactAbortController.signal;
|
|
461
430
|
return renderAppPageLifecycle({
|
|
@@ -465,6 +434,7 @@ async function dispatchAppPageInner(options) {
|
|
|
465
434
|
cleanPathname: options.cleanPathname,
|
|
466
435
|
clearRequestContext: options.clearRequestContext,
|
|
467
436
|
consumeDynamicUsage,
|
|
437
|
+
peekDynamicUsage,
|
|
468
438
|
consumeInvalidDynamicUsageError,
|
|
469
439
|
consumeRenderObservationState: consumeAppPageRenderObservationState,
|
|
470
440
|
createRscOnErrorHandler(pathname, routePath) {
|
|
@@ -487,7 +457,8 @@ async function dispatchAppPageInner(options) {
|
|
|
487
457
|
return _peekRequestScopedCacheLife();
|
|
488
458
|
},
|
|
489
459
|
handlerStart: options.handlerStart,
|
|
490
|
-
hasLoadingBoundary:
|
|
460
|
+
hasLoadingBoundary: hasActiveLoadingBoundary,
|
|
461
|
+
omitPendingDynamicCacheState: !options.isRscRequest && hasRequestSearchParams,
|
|
491
462
|
formState: options.formState ?? null,
|
|
492
463
|
isProgressiveActionRender: options.isProgressiveActionRender === true,
|
|
493
464
|
isDynamicError,
|
|
@@ -512,7 +483,7 @@ async function dispatchAppPageInner(options) {
|
|
|
512
483
|
pprFallbackShellSignal,
|
|
513
484
|
pprFallbackShellReactSignal,
|
|
514
485
|
abortPprFallbackShell: activeFallbackShellState ? () => {
|
|
515
|
-
|
|
486
|
+
options.pprRuntime.beginFinalRender(activeFallbackShellState);
|
|
516
487
|
} : void 0,
|
|
517
488
|
layoutParamAccess,
|
|
518
489
|
rootParams: options.rootParams,
|
|
@@ -526,8 +497,9 @@ async function dispatchAppPageInner(options) {
|
|
|
526
497
|
return options.probeLayoutAt(layoutIndex, layoutParamAccess);
|
|
527
498
|
},
|
|
528
499
|
probePage() {
|
|
529
|
-
return options.probePage();
|
|
500
|
+
return options.probePage(pageSearchParams);
|
|
530
501
|
},
|
|
502
|
+
probePageBeforeRender: options.isRscRequest,
|
|
531
503
|
classification: {
|
|
532
504
|
getLayoutId(index) {
|
|
533
505
|
const treePosition = route.layoutTreePositions?.[index] ?? 0;
|
|
@@ -556,8 +528,8 @@ async function dispatchAppPageInner(options) {
|
|
|
556
528
|
revalidateSeconds: currentRevalidateSeconds,
|
|
557
529
|
mountedSlotsHeader: options.mountedSlotsHeader,
|
|
558
530
|
renderMode: options.renderMode ?? "navigation",
|
|
559
|
-
renderErrorBoundaryResponse(renderError) {
|
|
560
|
-
return options.renderErrorBoundaryPage(renderError);
|
|
531
|
+
renderErrorBoundaryResponse(renderError, errorOrigin) {
|
|
532
|
+
return options.renderErrorBoundaryPage(renderError, errorOrigin);
|
|
561
533
|
},
|
|
562
534
|
renderLayoutSpecialError(specialError, layoutIndex) {
|
|
563
535
|
return renderLayoutSpecialError(options, specialError, layoutIndex);
|
|
@@ -674,4 +646,4 @@ async function renderPageSpecialError(options, specialError) {
|
|
|
674
646
|
});
|
|
675
647
|
}
|
|
676
648
|
//#endregion
|
|
677
|
-
export { dispatchAppPage };
|
|
649
|
+
export { dispatchAppPage, hasSearchParams, shouldReadAppPageCache };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { MetadataFileRoute } from "./metadata-routes.js";
|
|
2
2
|
import { AppElements } from "./app-elements-wire.js";
|
|
3
3
|
import { AppPageParams } from "./app-page-boundary.js";
|
|
4
|
+
import { ApplyAppPageFileBasedMetadata } from "./app-page-head.js";
|
|
4
5
|
import { AppLayoutParamAccessTracker } from "./app-layout-param-observation.js";
|
|
5
6
|
import { AppRscRenderMode } from "./app-rsc-render-mode.js";
|
|
6
7
|
import { AppPageErrorModule, AppPageModule, AppPageRouteWiringRoute } from "./app-page-route-wiring.js";
|
|
@@ -19,6 +20,8 @@ type AppPageBuildRoute<TModule extends AppPageModule = AppPageModule, TErrorModu
|
|
|
19
20
|
type AppPageInterceptOptions<TModule extends AppPageModule = AppPageModule> = {
|
|
20
21
|
interceptionContext?: string | null;
|
|
21
22
|
interceptLayouts?: readonly (TModule | null | undefined)[] | null;
|
|
23
|
+
interceptLayoutSegments?: readonly (readonly string[])[] | null;
|
|
24
|
+
interceptBranchSegments?: readonly string[] | null;
|
|
22
25
|
interceptPage?: TModule | null;
|
|
23
26
|
interceptParams?: AppPageParams | null;
|
|
24
27
|
interceptSlotId?: string | null;
|
|
@@ -32,9 +35,12 @@ type AppPagePageRequest<TModule extends AppPageModule = AppPageModule> = {
|
|
|
32
35
|
isRscRequest: boolean; /** The incoming HTTP request (available but unused by this module). */
|
|
33
36
|
request: Request; /** Normalized x-vinext-mounted-slots header value. */
|
|
34
37
|
mountedSlotsHeader: string | null; /** Semantic RSC payload mode for this page render. */
|
|
35
|
-
renderMode?: AppRscRenderMode;
|
|
38
|
+
renderMode?: AppRscRenderMode; /** Observe page `searchParams` access for cache-safety classification. */
|
|
39
|
+
observePageSearchParamsAccess?: boolean; /** Observe page metadata `searchParams` access for cache-safety classification. */
|
|
40
|
+
observeMetadataSearchParamsAccess?: boolean;
|
|
36
41
|
};
|
|
37
42
|
type BuildPageElementsOptions<TModule extends AppPageModule = AppPageModule, TErrorModule extends AppPageErrorModule = AppPageErrorModule> = {
|
|
43
|
+
applyFileBasedMetadata?: ApplyAppPageFileBasedMetadata;
|
|
38
44
|
route: AppPageBuildRoute<TModule, TErrorModule>;
|
|
39
45
|
params: AppPageParams;
|
|
40
46
|
routePath: string;
|
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
import { matchRoutePattern } from "../routing/route-pattern.js";
|
|
2
2
|
import { AppElementsWire } from "./app-elements-wire.js";
|
|
3
|
-
import { APP_RSC_RENDER_MODE_NAVIGATION
|
|
3
|
+
import { APP_RSC_RENDER_MODE_NAVIGATION } from "./app-rsc-render-mode.js";
|
|
4
4
|
import "./app-elements.js";
|
|
5
5
|
import { createAppPageRenderIdentity } from "./app-page-render-identity.js";
|
|
6
6
|
import { makeThenableParams } from "../shims/thenable-params.js";
|
|
7
|
+
import { resolveAppPageBranchParams } from "./app-page-params.js";
|
|
7
8
|
import { resolveActiveParallelRouteHeadInputs, resolveAppPageHead } from "./app-page-head.js";
|
|
8
|
-
import { makeObservedAppPageSearchParamsThenable } from "./app-page-search-params-observation.js";
|
|
9
|
+
import { createAppPageSearchParamsObserver, makeObservedAppPageSearchParamsThenable } from "./app-page-search-params-observation.js";
|
|
9
10
|
import { buildAppPageElements, createAppPageSourcePage, createAppPageTreePath } from "./app-page-route-wiring.js";
|
|
10
11
|
import { DEFAULT_GLOBAL_ERROR_MODULE } from "./default-global-error-module.js";
|
|
11
12
|
import "./app-rsc-route-matching.js";
|
|
12
13
|
import { shouldServeStreamingMetadata } from "./streaming-metadata.js";
|
|
13
14
|
import { createElement } from "react";
|
|
14
15
|
//#region src/server/app-page-element-builder.ts
|
|
16
|
+
function resolveInterceptLayoutParams(branchSegments, layoutSegments, params) {
|
|
17
|
+
return resolveAppPageBranchParams(branchSegments, layoutSegments.length, params, layoutSegments);
|
|
18
|
+
}
|
|
19
|
+
const REACT_CLIENT_REFERENCE = Symbol.for("react.client.reference");
|
|
20
|
+
function isReactOwnedPageComponent(component) {
|
|
21
|
+
if (typeof component !== "function") return true;
|
|
22
|
+
const candidate = component;
|
|
23
|
+
return candidate.$$typeof === REACT_CLIENT_REFERENCE || candidate.prototype?.isReactComponent != null;
|
|
24
|
+
}
|
|
15
25
|
/**
|
|
16
26
|
* Build the App Router element tree for a matched route.
|
|
17
27
|
*
|
|
@@ -30,7 +40,8 @@ import { createElement } from "react";
|
|
|
30
40
|
*/
|
|
31
41
|
async function buildPageElements(options) {
|
|
32
42
|
const { route, params, routePath, displayPathname = routePath, pageRequest, globalErrorModule, rootNotFoundModule, rootForbiddenModule, rootUnauthorizedModule, metadataRoutes } = options;
|
|
33
|
-
const
|
|
43
|
+
const slotParamOverrides = resolveSlotParamOverrides(route, routePath);
|
|
44
|
+
const { opts, searchParams, isRscRequest, mountedSlotsHeader, renderMode = APP_RSC_RENDER_MODE_NAVIGATION, observeMetadataSearchParamsAccess = false, observePageSearchParamsAccess = false } = pageRequest;
|
|
34
45
|
const pageModule = route.page;
|
|
35
46
|
const isSiblingIntercept = opts?.interceptSlotKey === "__vinext_page_intercept" && !!opts?.interceptPage;
|
|
36
47
|
const effectivePageModule = isSiblingIntercept ? opts.interceptPage : pageModule;
|
|
@@ -64,44 +75,68 @@ async function buildPageElements(options) {
|
|
|
64
75
|
};
|
|
65
76
|
}
|
|
66
77
|
const { hasDynamicMetadata, metadata: resolvedMetadata, pageSearchParams, viewport: resolvedViewport } = await resolveAppPageHead({
|
|
78
|
+
applyFileBasedMetadata: options.applyFileBasedMetadata,
|
|
67
79
|
basePath: options.basePath ?? "",
|
|
68
80
|
layoutModules: route.layouts,
|
|
69
81
|
layoutTreePositions: route.layoutTreePositions,
|
|
70
82
|
metadataRoutes,
|
|
71
|
-
pageModule: effectivePageModule ?? null,
|
|
72
|
-
parallelRoutes: resolveActiveParallelRouteHeadInputs({
|
|
83
|
+
pageModule: isSiblingIntercept ? null : effectivePageModule ?? null,
|
|
84
|
+
parallelRoutes: [...resolveActiveParallelRouteHeadInputs({
|
|
85
|
+
interceptBranchSegments: opts?.interceptBranchSegments ?? null,
|
|
73
86
|
interceptLayouts: opts?.interceptLayouts ?? null,
|
|
87
|
+
interceptLayoutSegments: opts?.interceptLayoutSegments ?? null,
|
|
74
88
|
interceptPage: opts?.interceptPage ?? null,
|
|
75
89
|
interceptParams: opts?.interceptParams ?? null,
|
|
76
90
|
interceptSlotKey: opts?.interceptSlotKey ?? null,
|
|
91
|
+
layoutTreePositions: route.layoutTreePositions,
|
|
77
92
|
params,
|
|
78
93
|
routeSegments: route.routeSegments ?? [],
|
|
94
|
+
slotParams: slotParamOverrides,
|
|
79
95
|
slots: route.slots ?? null
|
|
80
|
-
}),
|
|
96
|
+
}), ...isSiblingIntercept ? [{
|
|
97
|
+
layoutModules: opts?.interceptLayouts ?? [],
|
|
98
|
+
layoutParams: (opts?.interceptLayoutSegments ?? []).map((segments) => resolveInterceptLayoutParams(opts?.interceptBranchSegments ?? segments, segments, effectiveParams)),
|
|
99
|
+
pageModule: effectivePageModule ?? null,
|
|
100
|
+
params: effectiveParams,
|
|
101
|
+
routeSegments: opts?.interceptSourcePageSegments ?? route.routeSegments ?? []
|
|
102
|
+
}] : []],
|
|
81
103
|
params: effectiveParams,
|
|
82
104
|
routePath: route.pattern,
|
|
83
105
|
routeSegments: route.routeSegments ?? null,
|
|
84
|
-
searchParams
|
|
106
|
+
searchParams,
|
|
107
|
+
searchParamsObserver: observeMetadataSearchParamsAccess ? createAppPageSearchParamsObserver() : void 0
|
|
85
108
|
});
|
|
86
109
|
const pageProps = { params: makeThenableParams(effectiveParams) };
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
110
|
+
const hasRequestSearchParams = Object.keys(pageSearchParams).length > 0;
|
|
111
|
+
const createPageElement = (PageComponent, props) => {
|
|
112
|
+
if (isReactOwnedPageComponent(PageComponent)) {
|
|
113
|
+
const invocationProps = { ...props };
|
|
114
|
+
if (searchParams) invocationProps.searchParams = observePageSearchParamsAccess ? makeObservedAppPageSearchParamsThenable(pageSearchParams, { markDynamic: hasRequestSearchParams }) : makeThenableParams(pageSearchParams);
|
|
115
|
+
return createElement(PageComponent, invocationProps);
|
|
116
|
+
}
|
|
117
|
+
const ServerPageComponent = PageComponent;
|
|
118
|
+
const PageInvoker = () => {
|
|
119
|
+
const invocationProps = { ...props };
|
|
120
|
+
if (searchParams) invocationProps.searchParams = observePageSearchParamsAccess ? makeObservedAppPageSearchParamsThenable(pageSearchParams) : makeThenableParams(pageSearchParams);
|
|
121
|
+
return ServerPageComponent(invocationProps);
|
|
122
|
+
};
|
|
123
|
+
return createElement(PageInvoker);
|
|
124
|
+
};
|
|
125
|
+
const pageSearchParamsThenable = searchParams ? makeThenableParams(pageSearchParams) : void 0;
|
|
92
126
|
const mountedSlotIds = mountedSlotsHeader ? new Set(mountedSlotsHeader.split(" ")) : null;
|
|
93
127
|
const slotOverrides = buildSlotOverrides(route, params, routePath, opts);
|
|
94
128
|
const metadataPlacement = hasDynamicMetadata && shouldServeStreamingMetadata(pageRequest.request.headers.get("user-agent") ?? "", options.htmlLimitedBots) ? "body" : "head";
|
|
95
|
-
let siblingInterceptElement = isSiblingIntercept && EffectivePageComponent ?
|
|
96
|
-
if (isSiblingIntercept && siblingInterceptElement !== null && opts?.interceptLayouts?.length) {
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
|
|
129
|
+
let siblingInterceptElement = isSiblingIntercept && EffectivePageComponent ? createPageElement(EffectivePageComponent, pageProps) : null;
|
|
130
|
+
if (isSiblingIntercept && siblingInterceptElement !== null && opts?.interceptLayouts?.length) for (let i = opts.interceptLayouts.length - 1; i >= 0; i--) {
|
|
131
|
+
const LayoutComponent = opts.interceptLayouts[i]?.default;
|
|
132
|
+
if (LayoutComponent) {
|
|
133
|
+
const interceptLayoutSegments = opts.interceptLayoutSegments?.[i] ?? [];
|
|
134
|
+
siblingInterceptElement = createElement(LayoutComponent, { params: makeThenableParams(resolveInterceptLayoutParams(opts.interceptBranchSegments ?? interceptLayoutSegments, interceptLayoutSegments, effectiveParams)) }, siblingInterceptElement);
|
|
101
135
|
}
|
|
102
136
|
}
|
|
103
137
|
return buildAppPageElements({
|
|
104
|
-
element: isSiblingIntercept ? siblingInterceptElement : EffectivePageComponent ?
|
|
138
|
+
element: isSiblingIntercept ? siblingInterceptElement : EffectivePageComponent ? createPageElement(EffectivePageComponent, pageProps) : null,
|
|
139
|
+
createPageElement,
|
|
105
140
|
globalErrorModule: globalErrorModule ?? DEFAULT_GLOBAL_ERROR_MODULE,
|
|
106
141
|
isRscRequest,
|
|
107
142
|
layoutParamAccess: options.layoutParamAccess,
|
|
@@ -142,7 +177,9 @@ async function buildPageElements(options) {
|
|
|
142
177
|
function buildSlotOverrides(route, routeParams, routePath, opts) {
|
|
143
178
|
const overrides = {};
|
|
144
179
|
if (opts && opts.interceptSlotKey && opts.interceptPage && opts.interceptSlotKey !== "__vinext_page_intercept") overrides[opts.interceptSlotKey] = {
|
|
180
|
+
branchSegments: opts.interceptBranchSegments ?? null,
|
|
145
181
|
layoutModules: opts.interceptLayouts || null,
|
|
182
|
+
layoutSegments: opts.interceptLayoutSegments ?? null,
|
|
146
183
|
pageModule: opts.interceptPage,
|
|
147
184
|
params: opts.interceptParams || routeParams
|
|
148
185
|
};
|
|
@@ -1,31 +1,45 @@
|
|
|
1
1
|
import { MetadataFileRoute } from "./metadata-routes.js";
|
|
2
2
|
import { AppPageParams } from "./app-page-boundary.js";
|
|
3
|
+
import { ThenableParamsObserver } from "../shims/thenable-params.js";
|
|
3
4
|
import { Metadata, Viewport } from "../shims/metadata.js";
|
|
5
|
+
import { applyFileBasedMetadata } from "./file-based-metadata.js";
|
|
4
6
|
|
|
5
7
|
//#region src/server/app-page-head.d.ts
|
|
6
8
|
type AppPageSearchParams = Record<string, string | string[]>;
|
|
7
9
|
type AppPageHeadModule = Record<string, unknown>;
|
|
10
|
+
type ApplyAppPageFileBasedMetadata = typeof applyFileBasedMetadata;
|
|
8
11
|
type AppPageHeadParallelRoute<TModule extends AppPageHeadModule = AppPageHeadModule> = {
|
|
12
|
+
layoutParams?: readonly AppPageParams[] | null;
|
|
9
13
|
layoutModule?: TModule | null;
|
|
10
14
|
layoutModules?: readonly (TModule | null | undefined)[] | null;
|
|
15
|
+
layoutTreePositions?: readonly number[] | null;
|
|
11
16
|
pageModule?: TModule | null;
|
|
12
17
|
params?: AppPageParams | null;
|
|
13
18
|
routeSegments?: readonly string[] | null;
|
|
14
19
|
};
|
|
15
20
|
type AppPageHeadSlot<TModule extends AppPageHeadModule = AppPageHeadModule> = {
|
|
21
|
+
configLayouts?: readonly (TModule | null | undefined)[] | null;
|
|
22
|
+
configLayoutTreePositions?: readonly number[] | null;
|
|
16
23
|
layout?: TModule | null;
|
|
24
|
+
layoutIndex?: number;
|
|
17
25
|
page?: TModule | null;
|
|
26
|
+
routeSegments?: readonly string[] | null;
|
|
18
27
|
};
|
|
19
28
|
type ResolveActiveParallelRouteHeadInputsOptions<TModule extends AppPageHeadModule = AppPageHeadModule> = {
|
|
20
29
|
interceptLayouts?: readonly (TModule | null | undefined)[] | null;
|
|
30
|
+
interceptBranchSegments?: readonly string[] | null;
|
|
31
|
+
interceptLayoutSegments?: readonly (readonly string[])[] | null;
|
|
21
32
|
interceptPage?: TModule | null;
|
|
22
33
|
interceptParams?: AppPageParams | null;
|
|
23
34
|
interceptSlotKey?: string | null;
|
|
35
|
+
layoutTreePositions?: readonly number[] | null;
|
|
24
36
|
params: AppPageParams;
|
|
25
37
|
routeSegments: readonly string[];
|
|
38
|
+
slotParams?: Readonly<Record<string, AppPageParams>> | null;
|
|
26
39
|
slots?: Record<string, AppPageHeadSlot<TModule>> | null;
|
|
27
40
|
};
|
|
28
41
|
type ResolveAppPageHeadOptions<TModule extends AppPageHeadModule = AppPageHeadModule> = {
|
|
42
|
+
applyFileBasedMetadata?: ApplyAppPageFileBasedMetadata;
|
|
29
43
|
/**
|
|
30
44
|
* Configured next.config `basePath`. Threaded into `applyFileBasedMetadata`
|
|
31
45
|
* so file-based metadata route URLs (icon, opengraph-image, manifest, ...)
|
|
@@ -43,6 +57,7 @@ type ResolveAppPageHeadOptions<TModule extends AppPageHeadModule = AppPageHeadMo
|
|
|
43
57
|
routePath: string;
|
|
44
58
|
routeSegments?: readonly string[] | null;
|
|
45
59
|
searchParams?: URLSearchParams | null;
|
|
60
|
+
searchParamsObserver?: ThenableParamsObserver;
|
|
46
61
|
};
|
|
47
62
|
type ResolveAppPageHeadResult = {
|
|
48
63
|
hasDynamicMetadata: boolean;
|
|
@@ -59,4 +74,4 @@ declare function resolveActiveParallelRouteHeadInputs<TModule extends AppPageHea
|
|
|
59
74
|
declare function collectAppPageSearchParams(searchParams: URLSearchParams | null | undefined): AppPageSearchParamsCollection;
|
|
60
75
|
declare function resolveAppPageHead<TModule extends AppPageHeadModule>(options: ResolveAppPageHeadOptions<TModule>): Promise<ResolveAppPageHeadResult>;
|
|
61
76
|
//#endregion
|
|
62
|
-
export { AppPageSearchParams, collectAppPageSearchParams, resolveActiveParallelRouteHeadInputs, resolveAppPageHead };
|
|
77
|
+
export { AppPageSearchParams, ApplyAppPageFileBasedMetadata, collectAppPageSearchParams, resolveActiveParallelRouteHeadInputs, resolveAppPageHead };
|