vinext 1.4.1 → 1.4.3
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 +5 -2
- package/dist/build/action-owner-manifest.js +1 -0
- package/dist/build/prerender-paths.d.ts +11 -1
- package/dist/build/prerender-paths.js +36 -10
- package/dist/check.js +1 -1
- package/dist/cli.js +1 -1
- package/dist/client/dev-error-overlay.d.ts +2 -1
- package/dist/client/dev-error-overlay.js +8 -3
- package/dist/config/config-matchers.js +8 -4
- package/dist/config/destination-params.js +5 -2
- package/dist/config/next-config.d.ts +25 -1
- package/dist/config/next-config.js +31 -1
- package/dist/entries/app-rsc-entry.js +65 -30
- package/dist/entries/app-rsc-manifest.js +4 -0
- package/dist/entries/pages-server-entry.js +19 -4
- package/dist/index.js +37 -37
- package/dist/init-cloudflare.js +18 -2
- package/dist/init-platform.js +2 -2
- package/dist/init.js +42 -5
- package/dist/plugins/css-target.d.ts +18 -0
- package/dist/plugins/css-target.js +93 -0
- package/dist/routing/app-route-graph.d.ts +10 -0
- package/dist/routing/app-route-graph.js +15 -1
- package/dist/server/api-handler.d.ts +2 -0
- package/dist/server/api-handler.js +10 -8
- package/dist/server/app-action-forwarding.js +3 -1
- package/dist/server/app-browser-entry.js +37 -41
- package/dist/server/app-browser-error.d.ts +1 -1
- package/dist/server/app-browser-error.js +13 -4
- package/dist/server/app-browser-supplemental-refresh.d.ts +13 -0
- package/dist/server/app-browser-supplemental-refresh.js +36 -4
- package/dist/server/app-elements.d.ts +3 -0
- package/dist/server/app-elements.js +14 -1
- package/dist/server/app-fallback-renderer.js +2 -2
- package/dist/server/app-middleware.d.ts +2 -0
- package/dist/server/app-middleware.js +1 -0
- package/dist/server/app-optimistic-routing.d.ts +12 -1
- package/dist/server/app-optimistic-routing.js +38 -16
- package/dist/server/app-page-boundary-render.js +4 -2
- package/dist/server/app-page-cache-render.js +1 -1
- package/dist/server/app-page-dispatch.d.ts +11 -1
- package/dist/server/app-page-dispatch.js +78 -31
- package/dist/server/app-page-element-builder.d.ts +11 -20
- package/dist/server/app-page-element-builder.js +39 -50
- package/dist/server/app-page-intercept-segment-config.d.ts +56 -0
- package/dist/server/app-page-intercept-segment-config.js +90 -0
- package/dist/server/app-page-navigation-params.d.ts +23 -0
- package/dist/server/app-page-navigation-params.js +49 -0
- package/dist/server/app-page-render.d.ts +5 -2
- package/dist/server/app-page-render.js +17 -3
- package/dist/server/app-page-request.d.ts +24 -2
- package/dist/server/app-page-request.js +10 -10
- package/dist/server/app-page-route-wiring.d.ts +17 -5
- package/dist/server/app-page-route-wiring.js +19 -13
- package/dist/server/app-page-segment-plan.js +9 -1
- package/dist/server/app-page-segment-state.d.ts +9 -0
- package/dist/server/app-page-segment-state.js +54 -1
- package/dist/server/app-pages-bridge.d.ts +6 -0
- package/dist/server/app-pages-bridge.js +8 -3
- package/dist/server/app-request-stage-independent-entry.js +2 -3
- package/dist/server/app-response-stage-entry.js +1 -0
- 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 +2 -3
- package/dist/server/app-rsc-cache-busting.d.ts +8 -0
- package/dist/server/app-rsc-cache-busting.js +13 -1
- package/dist/server/app-rsc-combined-handler.js +2 -1
- package/dist/server/app-rsc-handler.d.ts +14 -1
- package/dist/server/app-rsc-handler.js +111 -62
- package/dist/server/app-rsc-response-finalizer.d.ts +18 -3
- package/dist/server/app-rsc-response-finalizer.js +61 -7
- package/dist/server/app-rsc-route-matching.d.ts +14 -0
- package/dist/server/app-rsc-route-matching.js +42 -18
- package/dist/server/app-server-action-execution.js +16 -10
- package/dist/server/app-ssr-entry.js +8 -0
- package/dist/server/cache-control.d.ts +2 -0
- package/dist/server/cache-control.js +3 -1
- package/dist/server/config-headers.d.ts +7 -0
- package/dist/server/config-headers.js +20 -1
- package/dist/server/image-animation.d.ts +9 -0
- package/dist/server/image-animation.js +118 -0
- package/dist/server/image-local-patterns.d.ts +21 -0
- package/dist/server/image-local-patterns.js +59 -0
- package/dist/server/image-optimization.d.ts +48 -27
- package/dist/server/image-optimization.js +316 -22
- package/dist/server/image-source-reader.d.ts +34 -0
- package/dist/server/image-source-reader.js +126 -0
- package/dist/server/metadata-route-response.d.ts +6 -1
- package/dist/server/metadata-route-response.js +46 -10
- package/dist/server/middleware-matcher.d.ts +1 -0
- package/dist/server/middleware-matcher.js +1 -1
- package/dist/server/middleware-runtime.d.ts +5 -0
- package/dist/server/middleware-runtime.js +48 -15
- package/dist/server/middleware.d.ts +1 -1
- package/dist/server/middleware.js +2 -1
- package/dist/server/nitro-public-files-plugin.js +20 -8
- package/dist/server/nitro-request-target-plugin.d.ts +17 -0
- package/dist/server/nitro-request-target-plugin.js +22 -0
- package/dist/server/open-redirect.d.ts +2 -1
- package/dist/server/open-redirect.js +2 -1
- package/dist/server/pages-client-assets.d.ts +1 -0
- package/dist/server/pages-data-route.d.ts +5 -1
- package/dist/server/pages-data-route.js +14 -6
- package/dist/server/pages-i18n.d.ts +7 -0
- package/dist/server/pages-i18n.js +14 -1
- package/dist/server/pages-node-compat.js +7 -3
- package/dist/server/pages-page-handler.js +4 -4
- package/dist/server/pages-request-pipeline.d.ts +9 -0
- package/dist/server/pages-request-pipeline.js +35 -15
- package/dist/server/pages-request-stage-entry.js +8 -2
- package/dist/server/pages-router-entry.js +3 -0
- package/dist/server/prod-server.d.ts +1 -1
- package/dist/server/prod-server.js +64 -62
- package/dist/server/request-pipeline.d.ts +5 -0
- package/dist/server/request-pipeline.js +11 -1
- package/dist/server/response-stage-cacheability.d.ts +7 -0
- package/dist/server/response-stage-cacheability.js +6 -5
- package/dist/server/srvx-middleware-origin.d.ts +13 -0
- package/dist/server/srvx-middleware-origin.js +19 -0
- package/dist/server/srvx-request-target.d.ts +12 -0
- package/dist/server/srvx-request-target.js +20 -0
- package/dist/server/static-file-cache.js +10 -22
- package/dist/server/worker-image-optimization.d.ts +10 -3
- package/dist/server/worker-image-optimization.js +43 -9
- package/dist/server/worker-revalidation-context.js +1 -0
- package/dist/server/worker-utils.d.ts +8 -0
- package/dist/server/worker-utils.js +19 -2
- package/dist/shims/error-boundary.d.ts +6 -2
- package/dist/shims/error-boundary.js +4 -2
- package/dist/shims/image-props.d.ts +6 -0
- package/dist/shims/image-props.js +39 -3
- package/dist/shims/image.js +3 -3
- package/dist/shims/request-context.d.ts +3 -1
- package/dist/shims/slot.d.ts +3 -0
- package/dist/shims/slot.js +67 -9
- package/dist/shims/url-utils.js +6 -1
- package/dist/utils/base-path.d.ts +7 -0
- package/dist/utils/base-path.js +11 -1
- package/dist/utils/client-entry-manifest.d.ts +5 -0
- package/dist/utils/client-entry-manifest.js +1 -0
- package/dist/utils/client-runtime-metadata.d.ts +5 -0
- package/dist/utils/client-runtime-metadata.js +33 -7
- package/package.json +1 -1
|
@@ -14,5 +14,7 @@ export declare function handleApiRoute(runner: ModuleImporter, req: IncomingMess
|
|
|
14
14
|
allowedRevalidateHeaderKeys?: readonly string[];
|
|
15
15
|
trailingSlash?: boolean;
|
|
16
16
|
skipTrailingSlashRedirect?: boolean;
|
|
17
|
+
/** Edge API request pathname, with any basePath the dev server stripped restored. */
|
|
18
|
+
requestPathname?: string;
|
|
17
19
|
}): Promise<boolean>;
|
|
18
20
|
//#endregion
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { hasBasePath } from "../utils/base-path.js";
|
|
2
1
|
import { matchRoute } from "../routing/pages-router.js";
|
|
3
2
|
import "./server-globals.js";
|
|
4
3
|
import { resolveRequestHost, resolveRequestProtocol } from "./proxy-trust.js";
|
|
@@ -94,7 +93,7 @@ function readEdgeRequestBody(req) {
|
|
|
94
93
|
req.on("error", (error) => controller.error(error));
|
|
95
94
|
} });
|
|
96
95
|
}
|
|
97
|
-
function createEdgeApiRequest(req, res, url, params,
|
|
96
|
+
function createEdgeApiRequest(req, res, url, params, requestPathname) {
|
|
98
97
|
const headers = new Headers();
|
|
99
98
|
for (const [name, value] of Object.entries(req.headers)) {
|
|
100
99
|
if (name.startsWith(":")) continue;
|
|
@@ -104,8 +103,7 @@ function createEdgeApiRequest(req, res, url, params, nextConfig) {
|
|
|
104
103
|
const proto = resolveRequestProtocol(req);
|
|
105
104
|
const host = resolveRequestHost(req, "localhost");
|
|
106
105
|
const requestUrl = new URL(req.url ?? url, `${proto}://${host}`);
|
|
107
|
-
|
|
108
|
-
if (basePath && !hasBasePath(requestUrl.pathname, basePath)) requestUrl.pathname = `${basePath}${requestUrl.pathname}`;
|
|
106
|
+
if (requestPathname !== void 0) requestUrl.pathname = requestPathname;
|
|
109
107
|
const query = mergeRouteParamsIntoQuery(parseQueryString(url), params);
|
|
110
108
|
requestUrl.search = urlQueryToSearchParams(query).toString();
|
|
111
109
|
const signal = signalFromNodeResponse(res);
|
|
@@ -188,10 +186,14 @@ function enhanceApiObjects(req, res, query, body, trustedRevalidateOrigin, allow
|
|
|
188
186
|
return this;
|
|
189
187
|
},
|
|
190
188
|
json(data) {
|
|
191
|
-
this.setHeader("Content-Type", "application/json");
|
|
189
|
+
this.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
192
190
|
this.end(JSON.stringify(data));
|
|
193
191
|
},
|
|
194
192
|
send(data) {
|
|
193
|
+
if (data === null || data === void 0) {
|
|
194
|
+
this.end();
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
195
197
|
if (this.statusCode === 204 || this.statusCode === 304) {
|
|
196
198
|
this.removeHeader("Content-Type");
|
|
197
199
|
this.removeHeader("Content-Length");
|
|
@@ -210,8 +212,8 @@ function enhanceApiObjects(req, res, query, body, trustedRevalidateOrigin, allow
|
|
|
210
212
|
this.end(data);
|
|
211
213
|
return;
|
|
212
214
|
}
|
|
213
|
-
if (typeof data === "object"
|
|
214
|
-
this.setHeader("Content-Type", "application/json");
|
|
215
|
+
if (typeof data === "object" || typeof data === "number" || typeof data === "boolean") {
|
|
216
|
+
this.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
215
217
|
this.end(JSON.stringify(data));
|
|
216
218
|
} else {
|
|
217
219
|
if (!this.getHeader("Content-Type")) this.setHeader("Content-Type", "text/plain");
|
|
@@ -250,7 +252,7 @@ async function handleApiRoute(runner, req, res, url, apiRoutes, nextConfig) {
|
|
|
250
252
|
try {
|
|
251
253
|
const apiModule = await importModule(runner, route.filePath);
|
|
252
254
|
if (isEdgeApiRouteModule(apiModule)) {
|
|
253
|
-
const nextRequest = new NextRequest(createEdgeApiRequest(req, res, url, params, nextConfig), nextConfig ? { nextConfig: {
|
|
255
|
+
const nextRequest = new NextRequest(createEdgeApiRequest(req, res, url, params, nextConfig?.requestPathname), nextConfig ? { nextConfig: {
|
|
254
256
|
basePath: nextConfig.basePath,
|
|
255
257
|
i18n: nextConfig.i18n ?? void 0,
|
|
256
258
|
trailingSlash: nextConfig.trailingSlash,
|
|
@@ -6,6 +6,7 @@ import { RequestCookies, ResponseCookies } from "../shims/server.js";
|
|
|
6
6
|
import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js";
|
|
7
7
|
import { getSetCookieName } from "./cookie-utils.js";
|
|
8
8
|
import "../shims/internal/cookies.js";
|
|
9
|
+
import { isStreamedActionRedirectResponse, markStreamedActionRedirectResponse } from "./app-rsc-response-finalizer.js";
|
|
9
10
|
import { createServerActionNotFoundResponse, getServerActionNotFoundMessage } from "./server-action-not-found.js";
|
|
10
11
|
//#region src/server/app-action-forwarding.ts
|
|
11
12
|
const ACTION_FORWARD_FORBIDDEN_HEADERS = /* @__PURE__ */ new Set([
|
|
@@ -71,11 +72,12 @@ function filterActionForwardResponse(response, sourceMiddlewareHeaders, sourceMi
|
|
|
71
72
|
for (const [key, value] of response.headers) if (!ACTION_FORWARD_FORBIDDEN_HEADERS.has(key.toLowerCase())) headers.set(key, value);
|
|
72
73
|
for (const setCookie of response.headers.getSetCookie()) headers.append("set-cookie", setCookie);
|
|
73
74
|
const preserveResponseStatus = response.headers.get("x-nextjs-action-not-found") === "1" || response.headers.has("x-action-redirect");
|
|
74
|
-
|
|
75
|
+
const forwardedResponse = new Response(response.body, {
|
|
75
76
|
headers,
|
|
76
77
|
status: preserveResponseStatus ? response.status : sourceMiddlewareStatus ?? response.status,
|
|
77
78
|
statusText: response.statusText
|
|
78
79
|
});
|
|
80
|
+
return isStreamedActionRedirectResponse(response) ? markStreamedActionRedirectResponse(forwardedResponse) : forwardedResponse;
|
|
79
81
|
}
|
|
80
82
|
function emptyActionForwardResponse(middlewareContext) {
|
|
81
83
|
const headers = new Headers(middlewareContext.headers ?? void 0);
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import { addBasePathToPathname, stripBasePath } from "../utils/base-path.js";
|
|
2
1
|
import { VINEXT_MOUNTED_LAYOUTS_HEADER, VINEXT_PARAMS_HEADER, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_REDIRECT_TYPE_HEADER } from "./headers.js";
|
|
3
|
-
import { splitPathnameForRouteMatch } from "../routing/utils.js";
|
|
4
2
|
import { AppRouterContext } from "../shims/internal/app-router-context.js";
|
|
5
|
-
import { matchRoutePattern } from "../routing/route-pattern.js";
|
|
6
3
|
import { installWindowNext, setWindowNextInternalSourcePage } from "../client/window-next.js";
|
|
7
4
|
import { retryScrollTo, scrollToHashTargetOnNextFrame } from "../shims/hash-scroll.js";
|
|
8
5
|
import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, createRscRequestHeaders, createRscRequestUrl, formatRscRequestPath, isRscCompatibilityIdCompatible } from "./app-rsc-cache-busting.js";
|
|
@@ -23,16 +20,16 @@ import { navigationPlanner } from "./navigation-planner.js";
|
|
|
23
20
|
import { decodeRedirectError, isRedirectError } from "../shims/navigation-errors.js";
|
|
24
21
|
import { beginAppRouterScrollIntent, consumeAppRouterScrollIntent } from "../shims/app-router-scroll-state.js";
|
|
25
22
|
import { getBfcacheIdMapContext, getNavigationContext, setNavigationContext } from "../shims/navigation-context-state.js";
|
|
26
|
-
import { DYNAMIC_NAVIGATION_CACHE_TTL, PREFETCH_CACHE_TTL, __basePath, appRouterInstance, commitClientNavigationState, consumePrefetchResponse, consumePrefetchResponseForNavigation, createCachedRscResponseSnapshot, createClientNavigationRenderSnapshot, createSnapshotPathAndSearch, deletePrefetchResponseSnapshot, disableNavigationResponsePrefetchCacheReuse, getClientNavigationRenderContext, getMountedSlotsHeader, getPrefetchCache, hasPrefetchCacheEntryForNavigation, invalidateDynamicPrefetchCache, invalidatePrefetchCache, invalidateVisitedResponsePrefetches, parseRenderedPathAndSearchHeader, preloadHybridClientRouteOwner, pushHistoryStateWithoutNotify, replaceClientParamsWithoutNotify, replaceHistoryStateWithoutNotify, resolveLoadedHybridClientRewriteHref, resolvePrefetchCacheEntryMountedSlotsHeader, restoreRscResponse, saveScrollPosition, seedPrefetchResponseSnapshot, setClientParams, setMountedSlotsHeader, setPendingPathname, trackAppRouterNavigationInFlight, useRouter } from "../shims/navigation.js";
|
|
23
|
+
import { DYNAMIC_NAVIGATION_CACHE_TTL, PREFETCH_CACHE_TTL, __basePath, activateNavigationSnapshot, appRouterInstance, commitClientNavigationState, consumePrefetchResponse, consumePrefetchResponseForNavigation, createCachedRscResponseSnapshot, createClientNavigationRenderSnapshot, createSnapshotPathAndSearch, deletePrefetchResponseSnapshot, disableNavigationResponsePrefetchCacheReuse, getClientNavigationRenderContext, getMountedSlotsHeader, getPrefetchCache, hasPrefetchCacheEntryForNavigation, invalidateDynamicPrefetchCache, invalidatePrefetchCache, invalidateVisitedResponsePrefetches, parseRenderedPathAndSearchHeader, preloadHybridClientRouteOwner, pushHistoryStateWithoutNotify, replaceClientParamsWithoutNotify, replaceHistoryStateWithoutNotify, resolveLoadedHybridClientRewriteHref, resolvePrefetchCacheEntryMountedSlotsHeader, restoreRscResponse, saveScrollPosition, seedPrefetchResponseSnapshot, setClientParams, setMountedSlotsHeader, setPendingPathname, trackAppRouterNavigationInFlight, useRouter } from "../shims/navigation.js";
|
|
27
24
|
import { AppRouterScrollCommitProvider } from "../shims/app-router-scroll.js";
|
|
28
25
|
import { chunksToReadableStream, createProgressiveRscStream, getVinextBrowserGlobal } from "./app-browser-stream.js";
|
|
29
26
|
import { createMountedLayoutsHeader, holdsSkippedLayouts } from "./app-mounted-layouts.js";
|
|
30
|
-
import { BfcacheIdentityMapContext, bindAppElementsRenderedSearch, setAppElementsRenderedSearch } from "../shims/slot.js";
|
|
27
|
+
import { BfcacheIdentityMapContext, bindAppElementsRenderedSearch, isBfcacheIdentityRetained, setAppElementsRenderedSearch } from "../shims/slot.js";
|
|
31
28
|
import { COMMITTED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN, FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN, VISITED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN, isCacheRestorableAppPayloadMetadata, isCompleteAppPayloadMetadata, resolveInterceptionContextFromPreviousNextUrl } from "./app-browser-state.js";
|
|
32
29
|
import { clearHardNavigationLoopGuard, createAppBrowserNavigationController, createBasePathStrippedPathAndSearch, navigateDocumentDirectly } from "./app-browser-navigation-controller.js";
|
|
33
30
|
import { createAppRouteTree } from "./app-route-tree.js";
|
|
34
31
|
import { shouldRecoverSamePathSearchCommitOnResponseCompletion } from "./app-browser-navigation-response.js";
|
|
35
|
-
import { createSupplementalRefreshCoordinator, mergeRefreshedParallelSlot, requireCompleteSupplementalRefresh, resolvePersistedSourcePageRefreshes, resolveServerActionSupplementalRefresh, resolveSupplementalRefreshes } from "./app-browser-supplemental-refresh.js";
|
|
32
|
+
import { createSupplementalRefreshCoordinator, mergeRefreshedParallelSlot, requireCompleteSupplementalRefresh, resolvePersistedRefreshInterceptions, resolvePersistedSourcePageRefreshes, resolveServerActionSupplementalRefresh, resolveSupplementalRefreshes } from "./app-browser-supplemental-refresh.js";
|
|
36
33
|
import { createAppBrowserNavigationAbortCoordinator } from "./app-browser-navigation-abort.js";
|
|
37
34
|
import { consumeInitialFormState, createVinextHydrateRootOptions, hydrateRootInTransition, resolveFetchedHydrationLocation } from "./app-browser-hydration.js";
|
|
38
35
|
import { AppBrowserHistoryController } from "./app-browser-history-controller.js";
|
|
@@ -195,6 +192,12 @@ const RSC_HMR_SETTLE_DELAY_MS = 150;
|
|
|
195
192
|
const DEFAULT_GLOBAL_ERROR_COMPONENT = DefaultGlobalError;
|
|
196
193
|
let latestRscHmrUpdateId = 0;
|
|
197
194
|
let synchronousPopstateScrollRestoreNavigationId = null;
|
|
195
|
+
let pendingHistoryRestoreCommit = null;
|
|
196
|
+
function flushPendingHistoryRestoreCommit() {
|
|
197
|
+
const commit = pendingHistoryRestoreCommit;
|
|
198
|
+
pendingHistoryRestoreCommit = null;
|
|
199
|
+
commit?.();
|
|
200
|
+
}
|
|
198
201
|
function waitForRscHmrSettle(delayMs = RSC_HMR_SETTLE_DELAY_MS) {
|
|
199
202
|
return new Promise((resolve) => {
|
|
200
203
|
window.setTimeout(resolve, delayMs);
|
|
@@ -212,6 +215,10 @@ function restoreHistoryStateSnapshot(historyState, navId, onApprovedBeforeCommit
|
|
|
212
215
|
beforeCommit: () => {
|
|
213
216
|
onApprovedBeforeCommit?.();
|
|
214
217
|
beforeCommit();
|
|
218
|
+
if (!restoreCopiedExternalHistoryEntry) {
|
|
219
|
+
activateNavigationSnapshot();
|
|
220
|
+
pendingHistoryRestoreCommit = () => commitClientNavigationState(navId);
|
|
221
|
+
}
|
|
215
222
|
},
|
|
216
223
|
navId,
|
|
217
224
|
state,
|
|
@@ -223,7 +230,8 @@ function restoreHistoryStateSnapshot(historyState, navId, onApprovedBeforeCommit
|
|
|
223
230
|
clientNavigationCacheGeneration += 1;
|
|
224
231
|
revokeHistoryRestoreNavigationReuse(visitedResponseCache);
|
|
225
232
|
disableNavigationResponsePrefetchCacheReuse();
|
|
226
|
-
commitClientNavigationState(navId, { releaseSnapshot: false });
|
|
233
|
+
if (restoreCopiedExternalHistoryEntry) commitClientNavigationState(navId, { releaseSnapshot: false });
|
|
234
|
+
else flushPendingHistoryRestoreCommit();
|
|
227
235
|
return true;
|
|
228
236
|
}
|
|
229
237
|
function getBrowserRouterState() {
|
|
@@ -267,36 +275,6 @@ function clearDynamicClientNavigationCaches() {
|
|
|
267
275
|
optimisticRouteTemplates.clear();
|
|
268
276
|
historyController.invalidateRestorableClientState();
|
|
269
277
|
}
|
|
270
|
-
function getMatchedUrlPathname(matchedUrl) {
|
|
271
|
-
return new URL(matchedUrl, "https://vinext.local").pathname;
|
|
272
|
-
}
|
|
273
|
-
function resolvePersistedRefreshInterceptions(state, routeManifest, refreshUrl, requestInterceptionContext) {
|
|
274
|
-
if (routeManifest === null) return [];
|
|
275
|
-
const refreshPathname = stripBasePath(refreshUrl.pathname, __basePath);
|
|
276
|
-
const refreshPathParts = splitPathnameForRouteMatch(refreshPathname);
|
|
277
|
-
const resolved = [];
|
|
278
|
-
for (const binding of state.slotBindings) {
|
|
279
|
-
if (binding.state !== "active" || !binding.activeRouteId) continue;
|
|
280
|
-
if (!binding.interceptionId || !binding.interceptionSourceMatchedUrl) continue;
|
|
281
|
-
const activeRoute = AppElementsWire.parseElementKey(binding.activeRouteId);
|
|
282
|
-
if (activeRoute?.kind !== "route") continue;
|
|
283
|
-
const match = routeManifest.segmentGraph.interceptions.get(binding.interceptionId);
|
|
284
|
-
if (!match) continue;
|
|
285
|
-
const retainedInterception = state.interception?.slotId === binding.slotId && matchRoutePattern(splitPathnameForRouteMatch(getMatchedUrlPathname(state.interception.targetMatchedUrl)), match.targetPatternParts) !== null ? state.interception : null;
|
|
286
|
-
const targetPathname = retainedInterception?.targetMatchedUrl ?? activeRoute.path;
|
|
287
|
-
const targetParts = splitPathnameForRouteMatch(getMatchedUrlPathname(targetPathname));
|
|
288
|
-
if (requestInterceptionContext !== null && state.interception?.slotId === binding.slotId && matchRoutePattern(refreshPathParts, match.targetPatternParts) !== null && matchRoutePattern(targetParts, match.targetPatternParts) !== null) continue;
|
|
289
|
-
const targetUrl = new URL(targetPathname, refreshUrl);
|
|
290
|
-
targetUrl.pathname = addBasePathToPathname(targetUrl.pathname, __basePath);
|
|
291
|
-
targetUrl.search = refreshUrl.search;
|
|
292
|
-
resolved.push({
|
|
293
|
-
interception: match,
|
|
294
|
-
interceptionContext: retainedInterception?.sourceMatchedUrl ?? binding.interceptionSourceMatchedUrl,
|
|
295
|
-
targetPathname: `${targetUrl.pathname}${targetUrl.search}`
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
return resolved;
|
|
299
|
-
}
|
|
300
278
|
async function fetchPersistedInterceptedSlotRefresh(options) {
|
|
301
279
|
const headers = createRscRequestHeaders({
|
|
302
280
|
interceptionContext: options.interceptionContext,
|
|
@@ -471,7 +449,13 @@ async function commitSameUrlNavigatePayload(nextElements, actionInitiation, retu
|
|
|
471
449
|
if (revalidation !== "none" && routeManifestLoaded) {
|
|
472
450
|
const refreshUrl = new URL(actionInitiation.href);
|
|
473
451
|
const requestInterceptionContext = resolveInterceptionContextFromPreviousNextUrl(actionInitiation.routerState.previousNextUrl, __basePath);
|
|
474
|
-
const persistedInterceptions = resolvePersistedRefreshInterceptions(
|
|
452
|
+
const persistedInterceptions = resolvePersistedRefreshInterceptions({
|
|
453
|
+
basePath: __basePath,
|
|
454
|
+
refreshUrl,
|
|
455
|
+
requestInterceptionContext,
|
|
456
|
+
routeManifest: getBrowserRouteManifest(),
|
|
457
|
+
state: actionInitiation.routerState
|
|
458
|
+
});
|
|
475
459
|
const sourcePageRefreshes = resolvePersistedSourcePageRefreshes({
|
|
476
460
|
basePath: __basePath,
|
|
477
461
|
refreshUrl,
|
|
@@ -1083,7 +1067,7 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
1083
1067
|
const formState = consumeInitialFormState(getVinextBrowserGlobal());
|
|
1084
1068
|
const hydrateRootOptions = import.meta.env.DEV && devErrorOverlay ? createVinextHydrateRootOptions({
|
|
1085
1069
|
formState,
|
|
1086
|
-
onCaughtError: invalidateOnCaughtError(createDevOnCaughtError(devErrorOverlay.devOnCaughtError, onUncaughtError)),
|
|
1070
|
+
onCaughtError: invalidateOnCaughtError(createDevOnCaughtError(devErrorOverlay.devOnCaughtError, onUncaughtError, devErrorOverlay.reportRootErrorBoundaryCatch)),
|
|
1087
1071
|
onUncaughtError
|
|
1088
1072
|
}) : createVinextHydrateRootOptions({
|
|
1089
1073
|
formState,
|
|
@@ -1184,7 +1168,13 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
1184
1168
|
const requestInterceptionContext = requestState.interceptionContext;
|
|
1185
1169
|
const requestPreviousNextUrl = requestState.previousNextUrl;
|
|
1186
1170
|
const shouldSupplementVisibleBranches = navigationKind === "refresh" || navigationKind === "traverse" && !reuseCurrentBfcacheIds;
|
|
1187
|
-
const persistedRefreshInterceptions = navigationKind === "refresh" ? resolvePersistedRefreshInterceptions(
|
|
1171
|
+
const persistedRefreshInterceptions = navigationKind === "refresh" ? resolvePersistedRefreshInterceptions({
|
|
1172
|
+
basePath: __basePath,
|
|
1173
|
+
refreshUrl: url,
|
|
1174
|
+
requestInterceptionContext,
|
|
1175
|
+
routeManifest: getBrowserRouteManifest(),
|
|
1176
|
+
state: navigationInitiationState
|
|
1177
|
+
}) : [];
|
|
1188
1178
|
const persistedSourcePageRefreshes = shouldSupplementVisibleBranches ? resolvePersistedSourcePageRefreshes({
|
|
1189
1179
|
activeRoutePaths: navigationKind === "traverse" ? readHistoryStateActiveRoutePaths(activeTraversalIntent?.historyState) ?? [] : void 0,
|
|
1190
1180
|
basePath: __basePath,
|
|
@@ -1393,7 +1383,10 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
1393
1383
|
currentElements: navigationInitiationState.elements,
|
|
1394
1384
|
currentLayoutIds: navigationInitiationState.layoutIds,
|
|
1395
1385
|
currentParams: navigationInitiationState.navigationSnapshot.params,
|
|
1386
|
+
isBfcacheIdentityRetained,
|
|
1396
1387
|
routeManifest,
|
|
1388
|
+
shownElements: getBrowserRouterState().elements,
|
|
1389
|
+
targetElements: optimisticPayload.elements,
|
|
1397
1390
|
targetRouteParams: optimisticPayload.routeParams,
|
|
1398
1391
|
targetUrlParts: optimisticPayload.urlParts,
|
|
1399
1392
|
template: optimisticPayload.template
|
|
@@ -1635,7 +1628,10 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
1635
1628
|
discardPendingNavigation: () => {
|
|
1636
1629
|
if (browserNavigationController.discardPendingNavigation(historyController.readCurrentTreeSnapshot())) navigationAbortCoordinator.abortActive();
|
|
1637
1630
|
},
|
|
1638
|
-
flushCommittingNavigationUrl: () =>
|
|
1631
|
+
flushCommittingNavigationUrl: () => {
|
|
1632
|
+
flushPendingHistoryRestoreCommit();
|
|
1633
|
+
browserNavigationController.flushCommittingNavigationUrl();
|
|
1634
|
+
}
|
|
1639
1635
|
});
|
|
1640
1636
|
const handlePopstate = createPopstateRestoreHandler({
|
|
1641
1637
|
getActiveNavigationId: browserNavigationController.getActiveNavigationId.bind(browserNavigationController),
|
|
@@ -8,7 +8,7 @@ export declare function createOnUncaughtError(options?: {
|
|
|
8
8
|
coerceToError?: boolean;
|
|
9
9
|
}): HydrateRootErrorHandler;
|
|
10
10
|
export declare function createProdOnCaughtError(onImplicitRootError: HydrateRootErrorHandler): HydrateRootErrorHandler;
|
|
11
|
-
export declare function createDevOnCaughtError(onCaughtError: HydrateRootErrorHandler, onImplicitRootError: HydrateRootErrorHandler): HydrateRootErrorHandler;
|
|
11
|
+
export declare function createDevOnCaughtError(onCaughtError: HydrateRootErrorHandler, onImplicitRootError: HydrateRootErrorHandler, onRootErrorBoundaryCatch?: () => void): HydrateRootErrorHandler;
|
|
12
12
|
export declare function createProdOnRecoverableError(onReportedError: () => void): (error: unknown) => void;
|
|
13
13
|
//#endregion
|
|
14
14
|
export {};
|
|
@@ -2,10 +2,18 @@ import { isUnknownRecord } from "../utils/record.js";
|
|
|
2
2
|
import { isNavigationSignalError } from "../utils/navigation-signal.js";
|
|
3
3
|
import { isBailoutToCSRError } from "../shims/navigation-errors.js";
|
|
4
4
|
//#region src/server/app-browser-error.ts
|
|
5
|
-
function
|
|
6
|
-
if (!isUnknownRecord(errorInfo.errorBoundary)) return
|
|
5
|
+
function getErrorBoundaryProps(errorInfo) {
|
|
6
|
+
if (!isUnknownRecord(errorInfo.errorBoundary)) return {};
|
|
7
7
|
const props = errorInfo.errorBoundary.props;
|
|
8
|
-
return isUnknownRecord(props)
|
|
8
|
+
return isUnknownRecord(props) ? props : {};
|
|
9
|
+
}
|
|
10
|
+
function isImplicitRootErrorBoundary(errorInfo) {
|
|
11
|
+
return getErrorBoundaryProps(errorInfo).isImplicitRootErrorBoundary === true;
|
|
12
|
+
}
|
|
13
|
+
function isRootErrorBoundary(errorInfo) {
|
|
14
|
+
const props = getErrorBoundaryProps(errorInfo);
|
|
15
|
+
if (props.isGlobalErrorBoundary === true) return true;
|
|
16
|
+
return props.isImplicitRootErrorBoundary === true && props.wrapsGlobalErrorBoundary !== true;
|
|
9
17
|
}
|
|
10
18
|
function logCaughtError(error, errorInfo) {
|
|
11
19
|
console.error(error);
|
|
@@ -38,8 +46,9 @@ function createProdOnCaughtError(onImplicitRootError) {
|
|
|
38
46
|
logCaughtError(error, errorInfo);
|
|
39
47
|
};
|
|
40
48
|
}
|
|
41
|
-
function createDevOnCaughtError(onCaughtError, onImplicitRootError) {
|
|
49
|
+
function createDevOnCaughtError(onCaughtError, onImplicitRootError, onRootErrorBoundaryCatch) {
|
|
42
50
|
return (error, errorInfo) => {
|
|
51
|
+
if (!isNavigationSignalError(error) && isRootErrorBoundary(errorInfo)) onRootErrorBoundaryCatch?.();
|
|
43
52
|
if (isImplicitRootErrorBoundary(errorInfo)) {
|
|
44
53
|
onImplicitRootError(error, errorInfo);
|
|
45
54
|
return;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BrowserRouteManifest, BrowserRouteManifestInterception } from "../routing/browser-route-manifest.js";
|
|
1
2
|
import { AppElements } from "./app-elements-wire.js";
|
|
2
3
|
import "./app-elements.js";
|
|
3
4
|
import { AppRouterState } from "./app-browser-state.js";
|
|
@@ -31,6 +32,18 @@ export declare function resolvePersistedSourcePageRefreshes(options: {
|
|
|
31
32
|
refreshUrl: URL;
|
|
32
33
|
state: Pick<AppRouterState, "previousNextUrl" | "slotBindings">;
|
|
33
34
|
}): string[];
|
|
35
|
+
export type PersistedRefreshInterception = {
|
|
36
|
+
interception: BrowserRouteManifestInterception;
|
|
37
|
+
interceptionContext: string;
|
|
38
|
+
targetPathname: string;
|
|
39
|
+
};
|
|
40
|
+
export declare function resolvePersistedRefreshInterceptions(options: {
|
|
41
|
+
basePath: string;
|
|
42
|
+
refreshUrl: URL;
|
|
43
|
+
requestInterceptionContext: string | null;
|
|
44
|
+
routeManifest: BrowserRouteManifest | null;
|
|
45
|
+
state: Pick<AppRouterState, "interception" | "slotBindings">;
|
|
46
|
+
}): PersistedRefreshInterception[];
|
|
34
47
|
export declare function createSupplementalRefreshCoordinator(): {
|
|
35
48
|
abortAll(): void;
|
|
36
49
|
begin(options: {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { normalizePathnameForRouteMatch } from "../routing/utils.js";
|
|
1
|
+
import { removeTrailingSlash, restoreBasePath, stripBasePath } from "../utils/base-path.js";
|
|
2
|
+
import { normalizePathnameForRouteMatch, splitPathnameForRouteMatch } from "../routing/utils.js";
|
|
3
3
|
import { normalizePath } from "./normalize-path.js";
|
|
4
|
+
import { matchRoutePattern } from "../routing/route-pattern.js";
|
|
4
5
|
import { APP_BFCACHE_SEGMENT_IDENTITIES_KEY, APP_INTERCEPTION_CONTEXT_KEY, APP_INTERCEPTION_KEY, APP_SLOT_BINDINGS_KEY, AppElementsWire, normalizeAppElementsSlotBindings } from "./app-elements-wire.js";
|
|
5
6
|
import "./app-elements.js";
|
|
6
7
|
import { isNestedBfcacheSlotSegmentIdFor } from "./bfcache-identity.js";
|
|
@@ -131,7 +132,7 @@ function resolvePersistedSourcePageRefreshes(options) {
|
|
|
131
132
|
});
|
|
132
133
|
for (const activeRoutePath of activeRoutePaths) {
|
|
133
134
|
if (activeRoutePath !== refreshRoutePathname && (refreshRoutePathname === "/" || activeRoutePath.startsWith(refreshRoutePathname + "/"))) continue;
|
|
134
|
-
const pathname =
|
|
135
|
+
const pathname = restoreBasePath(activeRoutePath, options.basePath);
|
|
135
136
|
if (sourceUrlsByPathname.has(pathname)) continue;
|
|
136
137
|
const sourceUrl = new URL(pathname, options.refreshUrl);
|
|
137
138
|
sourceUrl.search = options.refreshUrl.search;
|
|
@@ -145,6 +146,37 @@ function resolvePersistedSourcePageRefreshes(options) {
|
|
|
145
146
|
}
|
|
146
147
|
return resolved;
|
|
147
148
|
}
|
|
149
|
+
function getMatchedUrlPathname(matchedUrl) {
|
|
150
|
+
return new URL(matchedUrl, "https://vinext.local").pathname;
|
|
151
|
+
}
|
|
152
|
+
function resolvePersistedRefreshInterceptions(options) {
|
|
153
|
+
const { basePath, refreshUrl, requestInterceptionContext, routeManifest, state } = options;
|
|
154
|
+
if (routeManifest === null) return [];
|
|
155
|
+
const refreshPathname = stripBasePath(refreshUrl.pathname, basePath);
|
|
156
|
+
const refreshPathParts = splitPathnameForRouteMatch(refreshPathname);
|
|
157
|
+
const resolved = [];
|
|
158
|
+
for (const binding of state.slotBindings) {
|
|
159
|
+
if (binding.state !== "active" || !binding.activeRouteId) continue;
|
|
160
|
+
if (!binding.interceptionId || !binding.interceptionSourceMatchedUrl) continue;
|
|
161
|
+
const activeRoute = AppElementsWire.parseElementKey(binding.activeRouteId);
|
|
162
|
+
if (activeRoute?.kind !== "route") continue;
|
|
163
|
+
const match = routeManifest.segmentGraph.interceptions.get(binding.interceptionId);
|
|
164
|
+
if (!match) continue;
|
|
165
|
+
const retainedInterception = state.interception?.slotId === binding.slotId && matchRoutePattern(splitPathnameForRouteMatch(getMatchedUrlPathname(state.interception.targetMatchedUrl)), match.targetPatternParts) !== null ? state.interception : null;
|
|
166
|
+
const targetPathname = retainedInterception?.targetMatchedUrl ?? activeRoute.path;
|
|
167
|
+
const targetParts = splitPathnameForRouteMatch(getMatchedUrlPathname(targetPathname));
|
|
168
|
+
if (requestInterceptionContext !== null && state.interception?.slotId === binding.slotId && matchRoutePattern(refreshPathParts, match.targetPatternParts) !== null && matchRoutePattern(targetParts, match.targetPatternParts) !== null) continue;
|
|
169
|
+
const targetUrl = new URL(targetPathname, refreshUrl);
|
|
170
|
+
targetUrl.pathname = restoreBasePath(targetUrl.pathname, basePath);
|
|
171
|
+
targetUrl.search = refreshUrl.search;
|
|
172
|
+
resolved.push({
|
|
173
|
+
interception: match,
|
|
174
|
+
interceptionContext: retainedInterception?.sourceMatchedUrl ?? binding.interceptionSourceMatchedUrl,
|
|
175
|
+
targetPathname: `${targetUrl.pathname}${targetUrl.search}`
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
return resolved;
|
|
179
|
+
}
|
|
148
180
|
function createSupplementalRefreshCoordinator() {
|
|
149
181
|
const controllers = /* @__PURE__ */ new Set();
|
|
150
182
|
return {
|
|
@@ -202,4 +234,4 @@ async function resolveSupplementalRefreshes(options) {
|
|
|
202
234
|
}
|
|
203
235
|
}
|
|
204
236
|
//#endregion
|
|
205
|
-
export { SupplementalRefreshError, createSupplementalRefreshCoordinator, mergeRefreshedParallelSlot, requireCompleteSupplementalRefresh, resolvePersistedSourcePageRefreshes, resolveServerActionSupplementalRefresh, resolveSupplementalRefreshes };
|
|
237
|
+
export { SupplementalRefreshError, createSupplementalRefreshCoordinator, mergeRefreshedParallelSlot, requireCompleteSupplementalRefresh, resolvePersistedRefreshInterceptions, resolvePersistedSourcePageRefreshes, resolveServerActionSupplementalRefresh, resolveSupplementalRefreshes };
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { APP_ARTIFACT_COMPATIBILITY_KEY, APP_BFCACHE_SEGMENT_IDENTITIES_KEY, APP_CACHE_ENTRY_REUSE_PROOF_KEY, APP_DYNAMIC_STALE_TIME_KEY, APP_INTERCEPTION_CONTEXT_KEY, APP_INTERCEPTION_KEY, APP_LAYOUT_IDS_KEY, APP_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_SEGMENTS_KEY, APP_STATIC_SIBLINGS_KEY, APP_UNMATCHED_SLOT_WIRE_VALUE, AppElementValue, AppElements, AppElementsBfcacheSegmentIdentities, AppElementsInterception, AppElementsSlotBinding, AppElementsWire, AppOutgoingElements, AppWireElements, UNMATCHED_SLOT, buildOutgoingAppPayload, compareAppElementsSlotIds, isAppElementsRecord, normalizeAppElements, normalizeAppElementsSlotBindings, readAppElementsMetadata } from "./app-elements-wire.js";
|
|
2
2
|
//#region src/server/app-elements.d.ts
|
|
3
3
|
export declare const APP_PREFETCH_LOADING_SHELL_MARKER_KEY = "__prefetchLoadingShell";
|
|
4
|
+
type AppElementsStateKeyRebinder = (stateKey: string) => string;
|
|
5
|
+
export declare function setOptimisticStateKeyRebinder(elements: Record<string, unknown>, rebind: AppElementsStateKeyRebinder): void;
|
|
6
|
+
export declare function readOptimisticStateKeyRebinder(elements: AppElements): AppElementsStateKeyRebinder | null;
|
|
4
7
|
/** Prefix of the element that streams a route's generateMetadata() tags in the body. */
|
|
5
8
|
export declare const APP_STREAMING_METADATA_BODY_ID_PREFIX = "__vinext_streaming_metadata_body:";
|
|
6
9
|
export declare function getMountedSlotIds(elements: AppElements): string[];
|
|
@@ -2,6 +2,19 @@ import { normalizeMountedSlotsHeader } from "./app-mounted-slots-header.js";
|
|
|
2
2
|
import { APP_ARTIFACT_COMPATIBILITY_KEY, APP_BFCACHE_SEGMENT_IDENTITIES_KEY, APP_CACHE_ENTRY_REUSE_PROOF_KEY, APP_DYNAMIC_STALE_TIME_KEY, APP_INTERCEPTION_CONTEXT_KEY, APP_INTERCEPTION_KEY, APP_LAYOUT_IDS_KEY, APP_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_SEGMENTS_KEY, APP_STATIC_SIBLINGS_KEY, APP_UNMATCHED_SLOT_WIRE_VALUE, AppElementsWire, UNMATCHED_SLOT, buildOutgoingAppPayload, compareAppElementsSlotIds, isAppElementsRecord, normalizeAppElements, normalizeAppElementsSlotBindings, readAppElementsMetadata } from "./app-elements-wire.js";
|
|
3
3
|
//#region src/server/app-elements.ts
|
|
4
4
|
const APP_PREFETCH_LOADING_SHELL_MARKER_KEY = "__prefetchLoadingShell";
|
|
5
|
+
/**
|
|
6
|
+
* An optimistic shell's rebinder from the learned URL's segment state keys to
|
|
7
|
+
* the target's. Only the browser sets it, on the elements it builds a shell
|
|
8
|
+
* from, so it never reaches the wire.
|
|
9
|
+
*/
|
|
10
|
+
const APP_OPTIMISTIC_STATE_KEY_REBINDER_KEY = "__optimisticStateKeyRebinder";
|
|
11
|
+
function setOptimisticStateKeyRebinder(elements, rebind) {
|
|
12
|
+
elements[APP_OPTIMISTIC_STATE_KEY_REBINDER_KEY] = rebind;
|
|
13
|
+
}
|
|
14
|
+
function readOptimisticStateKeyRebinder(elements) {
|
|
15
|
+
const rebind = Reflect.get(elements, APP_OPTIMISTIC_STATE_KEY_REBINDER_KEY);
|
|
16
|
+
return typeof rebind === "function" ? rebind : null;
|
|
17
|
+
}
|
|
5
18
|
/** Prefix of the element that streams a route's generateMetadata() tags in the body. */
|
|
6
19
|
const APP_STREAMING_METADATA_BODY_ID_PREFIX = "__vinext_streaming_metadata_body:";
|
|
7
20
|
function getMountedSlotIds(elements) {
|
|
@@ -18,4 +31,4 @@ function resolveVisitedResponseInterceptionContext(requestInterceptionContext, p
|
|
|
18
31
|
return payloadInterceptionContext ?? requestInterceptionContext;
|
|
19
32
|
}
|
|
20
33
|
//#endregion
|
|
21
|
-
export { APP_ARTIFACT_COMPATIBILITY_KEY, APP_BFCACHE_SEGMENT_IDENTITIES_KEY, APP_CACHE_ENTRY_REUSE_PROOF_KEY, APP_DYNAMIC_STALE_TIME_KEY, APP_INTERCEPTION_CONTEXT_KEY, APP_INTERCEPTION_KEY, APP_LAYOUT_IDS_KEY, APP_PREFETCH_LOADING_SHELL_MARKER_KEY, APP_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_SEGMENTS_KEY, APP_STATIC_SIBLINGS_KEY, APP_STREAMING_METADATA_BODY_ID_PREFIX, APP_UNMATCHED_SLOT_WIRE_VALUE, AppElementsWire, UNMATCHED_SLOT, buildOutgoingAppPayload, compareAppElementsSlotIds, getMountedSlotIds, getMountedSlotIdsHeader, isAppElementsRecord, normalizeAppElements, normalizeAppElementsSlotBindings, readAppElementsMetadata, resolveVisitedResponseInterceptionContext };
|
|
34
|
+
export { APP_ARTIFACT_COMPATIBILITY_KEY, APP_BFCACHE_SEGMENT_IDENTITIES_KEY, APP_CACHE_ENTRY_REUSE_PROOF_KEY, APP_DYNAMIC_STALE_TIME_KEY, APP_INTERCEPTION_CONTEXT_KEY, APP_INTERCEPTION_KEY, APP_LAYOUT_IDS_KEY, APP_PREFETCH_LOADING_SHELL_MARKER_KEY, APP_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_SEGMENTS_KEY, APP_STATIC_SIBLINGS_KEY, APP_STREAMING_METADATA_BODY_ID_PREFIX, APP_UNMATCHED_SLOT_WIRE_VALUE, AppElementsWire, UNMATCHED_SLOT, buildOutgoingAppPayload, compareAppElementsSlotIds, getMountedSlotIds, getMountedSlotIdsHeader, isAppElementsRecord, normalizeAppElements, normalizeAppElementsSlotBindings, readAppElementsMetadata, readOptimisticStateKeyRebinder, resolveVisitedResponseInterceptionContext, setOptimisticStateKeyRebinder };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { runOutsideRequestScopes } from "../shims/internal/als-registry.js";
|
|
2
2
|
import { shareIsolatePromise } from "./isolate-shared-promise.js";
|
|
3
|
-
import { DEFAULT_GLOBAL_ERROR_MODULE } from "./default-global-error-module.js";
|
|
4
|
-
import { shouldServeStreamingMetadata } from "./streaming-metadata.js";
|
|
5
3
|
import { renderAppPageErrorBoundary, renderAppPageHttpAccessFallback } from "./app-page-boundary-render.js";
|
|
4
|
+
import { DEFAULT_GLOBAL_ERROR_MODULE } from "./default-global-error-module.js";
|
|
6
5
|
import { DEFAULT_GLOBAL_NOT_FOUND_MODULE } from "./default-global-not-found-module.js";
|
|
7
6
|
import { DEFAULT_NOT_FOUND_MODULE } from "./default-http-access-fallback-modules.js";
|
|
7
|
+
import { shouldServeStreamingMetadata } from "./streaming-metadata.js";
|
|
8
8
|
//#region src/server/app-fallback-renderer.ts
|
|
9
9
|
const EMPTY_MW_CTX = {
|
|
10
10
|
headers: null,
|
|
@@ -26,6 +26,8 @@ export type ApplyAppMiddlewareOptions = {
|
|
|
26
26
|
middlewareRequest?: Request;
|
|
27
27
|
module: MiddlewareModule;
|
|
28
28
|
request: Request;
|
|
29
|
+
/** Forwarded to `executeMiddleware`. */
|
|
30
|
+
restoreLocaleRootTrailingSlash?: boolean;
|
|
29
31
|
validateExternalRewriteRequest?: () => Promise<Response | null>;
|
|
30
32
|
/**
|
|
31
33
|
* Forwarded to `executeMiddleware` so the NextRequest exposes a NextURL with
|
|
@@ -203,6 +203,7 @@ async function applyAppMiddleware(options) {
|
|
|
203
203
|
},
|
|
204
204
|
requestBodyAlreadyIsolated: true,
|
|
205
205
|
request: middlewareRequest,
|
|
206
|
+
restoreLocaleRootTrailingSlash: options.restoreLocaleRootTrailingSlash,
|
|
206
207
|
skipTrailingSlashRedirect: options.skipTrailingSlashRedirect,
|
|
207
208
|
trailingSlash: options.trailingSlash
|
|
208
209
|
});
|
|
@@ -10,6 +10,8 @@ export type OptimisticRouteTemplate = {
|
|
|
10
10
|
omittedBfcacheSegmentIds: readonly string[];
|
|
11
11
|
omittedLayoutIds: readonly string[];
|
|
12
12
|
pageElementIds: readonly string[];
|
|
13
|
+
/** The navigation params of the URL the template was learned from. */
|
|
14
|
+
params: Readonly<Record<string, string | string[]>>;
|
|
13
15
|
routeId: string;
|
|
14
16
|
};
|
|
15
17
|
type OptimisticNavigationPayload = {
|
|
@@ -48,7 +50,7 @@ export declare function createOptimisticRouteTemplate(options: {
|
|
|
48
50
|
mountedSlotsHeader: string | null;
|
|
49
51
|
routeManifest: BrowserRouteManifest;
|
|
50
52
|
}): OptimisticRouteTemplate | null;
|
|
51
|
-
export declare function createOptimisticRouteElements(template: OptimisticRouteTemplate): AppElements;
|
|
53
|
+
export declare function createOptimisticRouteElements(template: OptimisticRouteTemplate, params?: Readonly<Record<string, string | string[]>>): AppElements;
|
|
52
54
|
/**
|
|
53
55
|
* The optimistic shell reveals the prefetch's streamed metadata, such as its
|
|
54
56
|
* title. React won't hide revealed content again in a transition, so while the
|
|
@@ -77,7 +79,16 @@ export declare function canCommitOptimisticRouteTemplate(options: {
|
|
|
77
79
|
currentElements: AppElements;
|
|
78
80
|
currentLayoutIds: readonly string[];
|
|
79
81
|
currentParams: Readonly<Record<string, string | string[]>>;
|
|
82
|
+
/** Whether a hidden BFCache entry retains a segment identity. */
|
|
83
|
+
isBfcacheIdentityRetained?: (identity: string) => boolean;
|
|
80
84
|
routeManifest: BrowserRouteManifest;
|
|
85
|
+
/**
|
|
86
|
+
* The elements on screen, when a shell earlier in this navigation replaced
|
|
87
|
+
* the current ones. Defaults to `currentElements`.
|
|
88
|
+
*/
|
|
89
|
+
shownElements?: AppElements;
|
|
90
|
+
/** The shell's elements for the target. */
|
|
91
|
+
targetElements?: AppElements;
|
|
81
92
|
targetRouteParams: Readonly<Record<string, string | string[]>>;
|
|
82
93
|
targetUrlParts: readonly string[];
|
|
83
94
|
template: OptimisticRouteTemplate;
|
|
@@ -4,10 +4,10 @@ import { decodeMatchedParams, splitPathnameForRouteMatch } from "../routing/util
|
|
|
4
4
|
import { extractRawRoutePatternParams, matchRoutePattern } from "../routing/route-pattern.js";
|
|
5
5
|
import { buildOptimisticRouteTrie, matchOptimisticRouteTrie } from "../routing/optimistic-route-trie.js";
|
|
6
6
|
import { stripRscCacheBustingSearchParam } from "./app-rsc-cache-busting.js";
|
|
7
|
-
import { AppElementsWire } from "./app-elements-wire.js";
|
|
8
|
-
import "./app-elements.js";
|
|
7
|
+
import { APP_BFCACHE_SEGMENT_IDENTITIES_KEY, AppElementsWire } from "./app-elements-wire.js";
|
|
8
|
+
import { setOptimisticStateKeyRebinder } from "./app-elements.js";
|
|
9
9
|
import { createNestedBfcacheSlotSegmentId, deriveBfcacheSegmentIdentity, isNestedBfcacheSlotSegmentId } from "./bfcache-identity.js";
|
|
10
|
-
import { canonicalizeAppPageParams, resolveAppPagePatternStateKey, resolveAppPageSemanticSegmentStateKey } from "./app-page-segment-state.js";
|
|
10
|
+
import { APP_PAGE_DEFAULT_SEGMENT_KEY, APP_PAGE_SEGMENT_KEY, canonicalizeAppPageParams, createAppPageSegmentStateKeyRebinder, resolveAppPagePatternStateKey, resolveAppPageSemanticSegmentStateKey } from "./app-page-segment-state.js";
|
|
11
11
|
import { Suspense, createElement, isValidElement } from "react";
|
|
12
12
|
//#region src/server/app-optimistic-routing.ts
|
|
13
13
|
const routeTrieCache = /* @__PURE__ */ new WeakMap();
|
|
@@ -114,6 +114,18 @@ function resolveTargetBfcacheSegmentIdentity(options) {
|
|
|
114
114
|
const segmentParams = binding.state === "active" && binding.slotPatternParts !== void 0 && binding.slotPatternParts.length > 0 && !binding.slotParamNames?.every((name) => routeParamNames.has(name)) ? matchRoutePattern(options.targetUrlParts, binding.slotPatternParts) ?? options.targetRouteParams : options.targetRouteParams;
|
|
115
115
|
const ownerLayout = binding.ownerLayoutId ? options.routeManifest.segmentGraph.layouts.get(binding.ownerLayoutId) : void 0;
|
|
116
116
|
const ownerStateKey = ownerLayout ? resolveAppPagePatternStateKey(ownerLayout.patternParts, options.targetRouteParams) : "";
|
|
117
|
+
const deriveLevelIdentity = (segmentKeys) => {
|
|
118
|
+
const identityKey = JSON.stringify(segmentKeys);
|
|
119
|
+
return deriveBfcacheSegmentIdentity({
|
|
120
|
+
activeRouteGraphId: null,
|
|
121
|
+
boundSegmentKey: ownerStateKey ? JSON.stringify([ownerStateKey, identityKey]) : identityKey,
|
|
122
|
+
interceptionTargetRouteGraphId: null,
|
|
123
|
+
kind: "slot",
|
|
124
|
+
ownerLayoutGraphId: binding.ownerLayoutId,
|
|
125
|
+
slotGraphId: binding.slotId,
|
|
126
|
+
state: binding.state
|
|
127
|
+
});
|
|
128
|
+
};
|
|
117
129
|
const boundSegmentKeys = [];
|
|
118
130
|
let level = 0;
|
|
119
131
|
for (const segment of binding.routeSegments ?? []) {
|
|
@@ -125,17 +137,9 @@ function resolveTargetBfcacheSegmentIdentity(options) {
|
|
|
125
137
|
segment
|
|
126
138
|
}, segmentParams));
|
|
127
139
|
if (createNestedBfcacheSlotSegmentId(binding.slotId, level) !== options.segmentId) continue;
|
|
128
|
-
|
|
129
|
-
return deriveBfcacheSegmentIdentity({
|
|
130
|
-
activeRouteGraphId: null,
|
|
131
|
-
boundSegmentKey: ownerStateKey ? JSON.stringify([ownerStateKey, identityKey]) : identityKey,
|
|
132
|
-
interceptionTargetRouteGraphId: null,
|
|
133
|
-
kind: "slot",
|
|
134
|
-
ownerLayoutGraphId: binding.ownerLayoutId,
|
|
135
|
-
slotGraphId: binding.slotId,
|
|
136
|
-
state: binding.state
|
|
137
|
-
});
|
|
140
|
+
return deriveLevelIdentity(boundSegmentKeys);
|
|
138
141
|
}
|
|
142
|
+
if (level > 0 && createNestedBfcacheSlotSegmentId(binding.slotId, level + 1) === options.segmentId) return deriveLevelIdentity([...boundSegmentKeys, binding.state === "default" ? APP_PAGE_DEFAULT_SEGMENT_KEY : APP_PAGE_SEGMENT_KEY]);
|
|
139
143
|
if (level === 0 && createNestedBfcacheSlotSegmentId(binding.slotId, 1) === options.segmentId) return deriveBfcacheSegmentIdentity({
|
|
140
144
|
activeRouteGraphId: null,
|
|
141
145
|
boundSegmentKey: ownerStateKey ? JSON.stringify([ownerStateKey, ""]) : "",
|
|
@@ -188,18 +192,31 @@ function createOptimisticRouteTemplate(options) {
|
|
|
188
192
|
if (options.allowLoadingShell && (routeElement === void 0 || routeElement === null)) return null;
|
|
189
193
|
const pageElementIds = getPageElementIds(options.elements, match.route);
|
|
190
194
|
if (pageElementIds.length === 0) return null;
|
|
195
|
+
const urlParts = hrefToRouteParts(options.href, options.basePath);
|
|
196
|
+
if (urlParts === null) return null;
|
|
191
197
|
return {
|
|
192
198
|
elements: options.elements,
|
|
193
199
|
mountedSlotsHeader: options.mountedSlotsHeader,
|
|
194
200
|
omittedBfcacheSegmentIds: options.elements["__prefetchLoadingShell"] === "LoadingBoundary" ? getOmittedBfcacheSegmentIds(options.elements) : [],
|
|
195
201
|
omittedLayoutIds: options.elements["__prefetchLoadingShell"] === "LoadingBoundary" ? metadata.layoutIds.filter((layoutId) => !Object.hasOwn(options.elements, layoutId)) : [],
|
|
196
202
|
pageElementIds,
|
|
203
|
+
params: resolveOptimisticNavigationParams({
|
|
204
|
+
match,
|
|
205
|
+
rawUrlParts: urlParts.raw,
|
|
206
|
+
routeManifest: options.routeManifest,
|
|
207
|
+
urlParts: urlParts.normalized
|
|
208
|
+
}).navigationParams,
|
|
197
209
|
routeId: match.route.id
|
|
198
210
|
};
|
|
199
211
|
}
|
|
200
|
-
function createOptimisticRouteElements(template) {
|
|
212
|
+
function createOptimisticRouteElements(template, params = template.params) {
|
|
201
213
|
const elements = { ...template.elements };
|
|
202
214
|
for (const pageElementId of template.pageElementIds) elements[pageElementId] = createElement(OptimisticRouteSegment);
|
|
215
|
+
const reify = createAppPageSegmentStateKeyRebinder(template.params, params);
|
|
216
|
+
if (reify === null) return elements;
|
|
217
|
+
setOptimisticStateKeyRebinder(elements, reify);
|
|
218
|
+
const identities = AppElementsWire.readMetadata(template.elements).bfcacheSegmentIdentities;
|
|
219
|
+
if (Object.keys(identities).length > 0) elements[APP_BFCACHE_SEGMENT_IDENTITIES_KEY] = Object.fromEntries(Object.entries(identities).map(([id, identity]) => [id, reify(identity)]));
|
|
203
220
|
return elements;
|
|
204
221
|
}
|
|
205
222
|
function findStreamingMetadataBody(elements) {
|
|
@@ -244,9 +261,14 @@ function keepOptimisticStreamingMetadata(elements, optimisticElements, navigatio
|
|
|
244
261
|
* loading boundary does not re-trigger.
|
|
245
262
|
*/
|
|
246
263
|
function canCommitOptimisticRouteTemplate(options) {
|
|
264
|
+
const currentIdentities = AppElementsWire.readMetadata(options.currentElements).bfcacheSegmentIdentities;
|
|
265
|
+
const { isBfcacheIdentityRetained, targetElements } = options;
|
|
266
|
+
if (isBfcacheIdentityRetained && targetElements) {
|
|
267
|
+
const shownIdentities = new Set(Object.values(options.shownElements ? AppElementsWire.readMetadata(options.shownElements).bfcacheSegmentIdentities : currentIdentities));
|
|
268
|
+
for (const identity of Object.values(AppElementsWire.readMetadata(targetElements).bfcacheSegmentIdentities)) if (!shownIdentities.has(identity) && isBfcacheIdentityRetained(identity)) return false;
|
|
269
|
+
}
|
|
247
270
|
if (options.template.omittedLayoutIds.length === 0 && options.template.omittedBfcacheSegmentIds.length === 0) return true;
|
|
248
271
|
const currentLayoutIds = new Set(options.currentLayoutIds);
|
|
249
|
-
const currentIdentities = AppElementsWire.readMetadata(options.currentElements).bfcacheSegmentIdentities;
|
|
250
272
|
for (const layoutId of options.template.omittedLayoutIds) {
|
|
251
273
|
if (!currentLayoutIds.has(layoutId)) continue;
|
|
252
274
|
if (!Object.hasOwn(options.currentElements, layoutId)) continue;
|
|
@@ -302,7 +324,7 @@ function resolveOptimisticNavigationPayload(options) {
|
|
|
302
324
|
urlParts: urlParts.normalized
|
|
303
325
|
});
|
|
304
326
|
return {
|
|
305
|
-
elements: createOptimisticRouteElements(template),
|
|
327
|
+
elements: createOptimisticRouteElements(template, navigationParams),
|
|
306
328
|
params: navigationParams,
|
|
307
329
|
routeParams,
|
|
308
330
|
template,
|
|
@@ -20,9 +20,9 @@ import { hasAppPageDynamicMetadata, prepareAppPageHead, resolveActiveParallelRou
|
|
|
20
20
|
import { renderAppPageBoundaryResponse, resolveAppPageErrorBoundary, resolveAppPageHttpAccessBoundaryModule, wrapAppPageBoundaryElement } from "./app-page-boundary.js";
|
|
21
21
|
import { createRouteNotFoundHeadPlanOptions, recoverAppPageRouteHead, resolveHttpAccessFallbackMetadata, resolveHttpAccessFallbackViewport } from "./app-page-http-access-fallback-metadata.js";
|
|
22
22
|
import "./app-rsc-route-matching.js";
|
|
23
|
-
import {
|
|
24
|
-
import { resolveSlotParamOverrides } from "./app-page-element-builder.js";
|
|
23
|
+
import { resolveSlotParamOverrides } from "./app-page-navigation-params.js";
|
|
25
24
|
import { createAppPageFontData, createAppPageRscErrorTracker, createAppPageSsrErrorHandler, renderAppPageHtmlResponse } from "./app-page-stream.js";
|
|
25
|
+
import { createAppPageLayoutEntries, createAppPageRouteBodyMetadata, createAppPageSourcePage } from "./app-page-route-wiring.js";
|
|
26
26
|
import { Fragment, Suspense, createElement } from "react";
|
|
27
27
|
//#region src/server/app-page-boundary-render.ts
|
|
28
28
|
async function AppPageBoundaryStreamedMetadata(props) {
|
|
@@ -85,8 +85,10 @@ function wrapRenderedBoundaryElement(options) {
|
|
|
85
85
|
renderErrorBoundary(GlobalErrorComponent, children) {
|
|
86
86
|
return createElement(GlobalErrorBoundary, {
|
|
87
87
|
fallback: DEFAULT_GLOBAL_ERROR_COMPONENT,
|
|
88
|
+
wrapsGlobalErrorBoundary: true,
|
|
88
89
|
children: createElement(ErrorBoundary, {
|
|
89
90
|
fallback: GlobalErrorComponent,
|
|
91
|
+
isGlobalErrorBoundary: true,
|
|
90
92
|
children
|
|
91
93
|
})
|
|
92
94
|
});
|