vinext 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -5
- package/dist/build/assets-ignore.d.ts +32 -0
- package/dist/build/assets-ignore.js +48 -0
- package/dist/build/client-build-config.d.ts +33 -1
- package/dist/build/client-build-config.js +66 -1
- package/dist/check.js +4 -3
- package/dist/cli.js +2 -0
- package/dist/client/navigation-runtime.d.ts +11 -2
- package/dist/client/navigation-runtime.js +1 -1
- package/dist/client/vinext-next-data.d.ts +2 -1
- package/dist/client/window-next.d.ts +6 -4
- package/dist/config/config-matchers.d.ts +31 -5
- package/dist/config/config-matchers.js +50 -3
- package/dist/config/next-config.d.ts +29 -3
- package/dist/config/next-config.js +32 -2
- package/dist/deploy.js +47 -304
- package/dist/entries/app-rsc-entry.d.ts +8 -2
- package/dist/entries/app-rsc-entry.js +61 -5
- package/dist/entries/app-rsc-manifest.js +20 -2
- package/dist/entries/pages-client-entry.js +1 -1
- package/dist/entries/pages-server-entry.js +16 -7
- package/dist/index.d.ts +0 -2
- package/dist/index.js +233 -280
- package/dist/plugins/dynamic-preload-metadata.d.ts +13 -0
- package/dist/plugins/dynamic-preload-metadata.js +415 -0
- package/dist/plugins/og-assets.js +2 -2
- package/dist/plugins/optimize-imports.d.ts +8 -4
- package/dist/plugins/optimize-imports.js +16 -12
- package/dist/plugins/postcss.js +18 -14
- package/dist/plugins/require-context.d.ts +6 -0
- package/dist/plugins/require-context.js +184 -0
- package/dist/plugins/sass.d.ts +53 -24
- package/dist/plugins/sass.js +249 -1
- package/dist/plugins/wasm-module-import.d.ts +15 -0
- package/dist/plugins/wasm-module-import.js +50 -0
- package/dist/routing/app-route-graph.d.ts +35 -2
- package/dist/routing/app-route-graph.js +179 -8
- package/dist/routing/file-matcher.js +1 -1
- package/dist/routing/route-pattern.d.ts +2 -1
- package/dist/routing/route-pattern.js +16 -1
- package/dist/server/api-handler.js +4 -0
- package/dist/server/app-browser-entry.js +155 -215
- package/dist/server/app-browser-error.d.ts +4 -1
- package/dist/server/app-browser-error.js +7 -1
- package/dist/server/app-browser-history-controller.d.ts +104 -0
- package/dist/server/app-browser-history-controller.js +210 -0
- package/dist/server/app-browser-interception-context.d.ts +2 -1
- package/dist/server/app-browser-interception-context.js +15 -2
- package/dist/server/app-browser-navigation-controller.d.ts +13 -2
- package/dist/server/app-browser-navigation-controller.js +83 -4
- package/dist/server/app-browser-popstate.d.ts +12 -3
- package/dist/server/app-browser-popstate.js +19 -4
- package/dist/server/app-browser-rsc-redirect.d.ts +11 -2
- package/dist/server/app-browser-rsc-redirect.js +30 -8
- package/dist/server/app-browser-state.d.ts +3 -0
- package/dist/server/app-browser-state.js +10 -10
- package/dist/server/app-browser-visible-commit.js +10 -8
- package/dist/server/app-fallback-renderer.d.ts +2 -1
- package/dist/server/app-fallback-renderer.js +3 -1
- package/dist/server/app-history-state.d.ts +45 -1
- package/dist/server/app-history-state.js +109 -1
- package/dist/server/app-middleware.js +1 -0
- package/dist/server/app-optimistic-routing.js +22 -1
- package/dist/server/app-page-boundary-render.d.ts +2 -1
- package/dist/server/app-page-boundary-render.js +45 -21
- package/dist/server/app-page-cache.js +9 -7
- package/dist/server/app-page-dispatch.d.ts +14 -0
- package/dist/server/app-page-dispatch.js +21 -6
- package/dist/server/app-page-element-builder.d.ts +23 -2
- package/dist/server/app-page-element-builder.js +58 -17
- package/dist/server/app-page-execution.d.ts +1 -1
- package/dist/server/app-page-execution.js +32 -17
- package/dist/server/app-page-render.d.ts +7 -1
- package/dist/server/app-page-render.js +11 -16
- package/dist/server/app-page-request.d.ts +9 -6
- package/dist/server/app-page-request.js +14 -10
- package/dist/server/app-page-response.d.ts +2 -2
- package/dist/server/app-page-response.js +2 -2
- package/dist/server/app-page-route-wiring.d.ts +3 -1
- package/dist/server/app-page-route-wiring.js +10 -8
- package/dist/server/app-page-stream.d.ts +37 -7
- package/dist/server/app-page-stream.js +36 -6
- package/dist/server/app-pages-bridge.d.ts +16 -0
- package/dist/server/app-pages-bridge.js +23 -3
- package/dist/server/app-route-handler-cache.d.ts +1 -0
- package/dist/server/app-route-handler-cache.js +1 -0
- package/dist/server/app-route-handler-dispatch.d.ts +1 -0
- package/dist/server/app-route-handler-dispatch.js +2 -0
- package/dist/server/app-route-handler-execution.d.ts +1 -0
- package/dist/server/app-route-handler-execution.js +1 -0
- package/dist/server/app-route-handler-response.js +11 -10
- package/dist/server/app-route-handler-runtime.d.ts +1 -0
- package/dist/server/app-route-handler-runtime.js +15 -3
- package/dist/server/app-rsc-handler.d.ts +1 -0
- package/dist/server/app-rsc-handler.js +5 -4
- package/dist/server/app-rsc-response-finalizer.js +1 -1
- package/dist/server/app-rsc-route-matching.d.ts +20 -1
- package/dist/server/app-rsc-route-matching.js +29 -4
- package/dist/server/app-server-action-execution.d.ts +22 -1
- package/dist/server/app-server-action-execution.js +73 -12
- package/dist/server/app-ssr-entry.d.ts +6 -0
- package/dist/server/app-ssr-entry.js +19 -3
- package/dist/server/app-ssr-stream.js +9 -1
- package/dist/server/dev-lockfile.js +2 -1
- package/dist/server/dev-server.d.ts +1 -1
- package/dist/server/dev-server.js +97 -43
- package/dist/server/headers.d.ts +8 -1
- package/dist/server/headers.js +8 -1
- package/dist/server/instrumentation-runtime.d.ts +6 -0
- package/dist/server/instrumentation-runtime.js +8 -0
- package/dist/server/isr-cache.d.ts +37 -1
- package/dist/server/isr-cache.js +85 -1
- package/dist/server/isr-decision.d.ts +79 -0
- package/dist/server/isr-decision.js +70 -0
- package/dist/server/metadata-route-response.js +5 -3
- package/dist/server/middleware-runtime.d.ts +13 -0
- package/dist/server/middleware-runtime.js +11 -7
- package/dist/server/middleware.js +1 -0
- package/dist/server/navigation-planner.d.ts +62 -1
- package/dist/server/navigation-planner.js +193 -3
- package/dist/server/navigation-trace.d.ts +12 -2
- package/dist/server/navigation-trace.js +11 -1
- package/dist/server/normalize-path.d.ts +0 -8
- package/dist/server/normalize-path.js +3 -1
- package/dist/server/otel-tracer-extension.d.ts +45 -0
- package/dist/server/otel-tracer-extension.js +89 -0
- package/dist/server/pages-api-route.d.ts +14 -3
- package/dist/server/pages-api-route.js +6 -1
- package/dist/server/pages-asset-tags.d.ts +15 -4
- package/dist/server/pages-asset-tags.js +18 -12
- package/dist/server/pages-data-route.js +5 -1
- package/dist/server/pages-node-compat.d.ts +5 -11
- package/dist/server/pages-node-compat.js +175 -118
- package/dist/server/pages-page-data.d.ts +38 -7
- package/dist/server/pages-page-data.js +64 -18
- package/dist/server/pages-page-handler.d.ts +10 -2
- package/dist/server/pages-page-handler.js +49 -20
- package/dist/server/pages-page-response.d.ts +55 -2
- package/dist/server/pages-page-response.js +74 -6
- package/dist/server/pages-readiness.d.ts +36 -0
- package/dist/server/pages-readiness.js +21 -0
- package/dist/server/pages-request-pipeline.d.ts +113 -0
- package/dist/server/pages-request-pipeline.js +230 -0
- package/dist/server/pages-revalidate.d.ts +15 -0
- package/dist/server/pages-revalidate.js +19 -0
- package/dist/server/prod-server.d.ts +45 -3
- package/dist/server/prod-server.js +182 -234
- package/dist/server/socket-error-backstop.d.ts +19 -1
- package/dist/server/socket-error-backstop.js +77 -4
- package/dist/shims/app-router-scroll.js +22 -4
- package/dist/shims/cache-runtime.js +39 -2
- package/dist/shims/dynamic-preload-chunks.d.ts +8 -0
- package/dist/shims/dynamic-preload-chunks.js +77 -0
- package/dist/shims/dynamic.d.ts +4 -0
- package/dist/shims/dynamic.js +4 -2
- package/dist/shims/error-boundary.d.ts +17 -7
- package/dist/shims/error-boundary.js +8 -1
- package/dist/shims/error.js +37 -11
- package/dist/shims/fetch-cache.d.ts +22 -1
- package/dist/shims/fetch-cache.js +28 -1
- package/dist/shims/hash-scroll.d.ts +1 -0
- package/dist/shims/hash-scroll.js +3 -1
- package/dist/shims/head.js +6 -1
- package/dist/shims/headers.d.ts +16 -2
- package/dist/shims/headers.js +37 -1
- package/dist/shims/image-config.js +7 -1
- package/dist/shims/internal/app-route-detection.d.ts +6 -3
- package/dist/shims/internal/app-route-detection.js +10 -6
- package/dist/shims/internal/app-router-context.d.ts +5 -0
- package/dist/shims/internal/link-status-registry.d.ts +43 -0
- package/dist/shims/internal/link-status-registry.js +42 -0
- package/dist/shims/internal/route-pattern-for-warning.d.ts +27 -0
- package/dist/shims/internal/route-pattern-for-warning.js +40 -0
- package/dist/shims/internal/utils.d.ts +1 -0
- package/dist/shims/link.js +20 -6
- package/dist/shims/metadata.d.ts +6 -2
- package/dist/shims/metadata.js +32 -14
- package/dist/shims/navigation.d.ts +9 -18
- package/dist/shims/navigation.js +96 -23
- package/dist/shims/router-state.d.ts +1 -0
- package/dist/shims/router-state.js +2 -0
- package/dist/shims/router.d.ts +6 -3
- package/dist/shims/router.js +156 -22
- package/dist/shims/script-nonce-context.d.ts +1 -1
- package/dist/shims/script-nonce-context.js +11 -3
- package/dist/shims/server.d.ts +17 -1
- package/dist/shims/server.js +31 -6
- package/dist/shims/slot.js +1 -1
- package/dist/shims/unified-request-context.js +1 -0
- package/dist/typegen.js +1 -0
- package/dist/utils/client-build-manifest.d.ts +8 -1
- package/dist/utils/client-build-manifest.js +41 -6
- package/dist/utils/client-entry-manifest.d.ts +11 -0
- package/dist/utils/client-entry-manifest.js +29 -0
- package/dist/utils/client-runtime-metadata.d.ts +45 -0
- package/dist/utils/client-runtime-metadata.js +63 -0
- package/dist/utils/hash.d.ts +17 -1
- package/dist/utils/hash.js +36 -1
- package/dist/utils/lazy-chunks.d.ts +27 -1
- package/dist/utils/lazy-chunks.js +65 -1
- package/dist/utils/manifest-paths.d.ts +20 -2
- package/dist/utils/manifest-paths.js +38 -3
- package/dist/utils/path.d.ts +2 -1
- package/dist/utils/path.js +5 -1
- package/package.json +6 -2
|
@@ -2,7 +2,7 @@ import { createRequestContext, runWithRequestContext } from "../shims/unified-re
|
|
|
2
2
|
import { hasBasePath } from "../utils/base-path.js";
|
|
3
3
|
import { getRequestExecutionContext } from "../shims/request-context.js";
|
|
4
4
|
import { ACTION_REVALIDATED_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER } from "./headers.js";
|
|
5
|
-
import { isExternalUrl, matchRedirect, matchRewrite, proxyExternalRequest, requestContextFromRequest, sanitizeDestination } from "../config/config-matchers.js";
|
|
5
|
+
import { isExternalUrl, matchRedirect, matchRewrite, preserveRedirectDestinationQuery, proxyExternalRequest, requestContextFromRequest, sanitizeDestination } from "../config/config-matchers.js";
|
|
6
6
|
import { notFoundResponse } from "./http-error-responses.js";
|
|
7
7
|
import { applyConfigHeadersToResponse, cloneRequestWithHeaders, cloneRequestWithUrl, filterInternalHeaders, normalizeTrailingSlash, resolvePublicFileRoute, validateImageUrl } from "./request-pipeline.js";
|
|
8
8
|
import { headersContextFromRequest } from "../shims/headers.js";
|
|
@@ -14,12 +14,12 @@ import { isImageOptimizationPath } from "./image-optimization.js";
|
|
|
14
14
|
import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js";
|
|
15
15
|
import "./app-page-response.js";
|
|
16
16
|
import { prerenderRouteParamsPayloadMatchesRoute, readTrustedPrerenderRouteParams, serializePrerenderRouteParamsHeader } from "./prerender-route-params.js";
|
|
17
|
-
import { pickRootParams, setRootParams } from "../shims/root-params.js";
|
|
18
|
-
import { flattenErrorCauses } from "../utils/error-cause.js";
|
|
19
17
|
import { applyAppMiddleware } from "./app-middleware.js";
|
|
20
18
|
import { buildPageCacheTags } from "./implicit-tags.js";
|
|
21
19
|
import { buildPostMwRequestContext } from "./app-post-middleware-context.js";
|
|
20
|
+
import { pickRootParams, setRootParams } from "../shims/root-params.js";
|
|
22
21
|
import { handleAppPrerenderEndpoint } from "./app-prerender-endpoints.js";
|
|
22
|
+
import { flattenErrorCauses } from "../utils/error-cause.js";
|
|
23
23
|
import { finalizeAppRscResponse } from "./app-rsc-response-finalizer.js";
|
|
24
24
|
import { normalizeRscRequest } from "./app-rsc-request-normalization.js";
|
|
25
25
|
import { handleMetadataRouteRequest } from "./metadata-route-response.js";
|
|
@@ -113,7 +113,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
|
|
|
113
113
|
const redirect = matchRedirect(matchPathname(stripRscSuffix(pathname)), options.configRedirects, preMiddlewareRequestContext, basePathState);
|
|
114
114
|
if (redirect) {
|
|
115
115
|
const destination = sanitizeDestination(redirectDestinationWithBasePath(redirect.destination, options.basePath));
|
|
116
|
-
const location = isRscRequest && request.headers.get("RSC") === "1" ? await createRscRedirectLocation(destination, request) : destination;
|
|
116
|
+
const location = isRscRequest && request.headers.get("RSC") === "1" ? await createRscRedirectLocation(destination, request) : preserveRedirectDestinationQuery(destination, url.search);
|
|
117
117
|
return new Response(null, {
|
|
118
118
|
status: redirect.permanent ? 308 : 307,
|
|
119
119
|
headers: { Location: location }
|
|
@@ -302,6 +302,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
|
|
|
302
302
|
const pageResponse = await options.dispatchMatchedPage({
|
|
303
303
|
clientReuseManifest,
|
|
304
304
|
cleanPathname,
|
|
305
|
+
displayPathname: canonicalPathname,
|
|
305
306
|
formState,
|
|
306
307
|
actionError,
|
|
307
308
|
actionFailed,
|
|
@@ -3,9 +3,9 @@ import { hasBasePath, stripBasePath } from "../utils/base-path.js";
|
|
|
3
3
|
import "./headers.js";
|
|
4
4
|
import { normalizePath } from "./normalize-path.js";
|
|
5
5
|
import { applyConfigHeadersToResponse } from "./request-pipeline.js";
|
|
6
|
+
import { applyCdnResponseHeaders } from "./cache-control.js";
|
|
6
7
|
import { VINEXT_RSC_VARY_HEADER } from "./app-rsc-cache-busting.js";
|
|
7
8
|
import { normalizeDefaultLocalePathname } from "./pages-i18n.js";
|
|
8
|
-
import { applyCdnResponseHeaders } from "./cache-control.js";
|
|
9
9
|
import { mergeVaryHeader } from "./middleware-response-headers.js";
|
|
10
10
|
//#region src/server/app-rsc-response-finalizer.ts
|
|
11
11
|
/**
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { RoutePatternParams } from "../routing/route-pattern.js";
|
|
2
2
|
|
|
3
3
|
//#region src/server/app-rsc-route-matching.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Sentinel slot key used for sibling-style interception entries.
|
|
6
|
+
* When a matched intercept carries this key, the render layer replaces the
|
|
7
|
+
* route's main page element instead of a parallel slot.
|
|
8
|
+
*/
|
|
9
|
+
declare const SIBLING_PAGE_INTERCEPT_SLOT_KEY = "__vinext_page_intercept";
|
|
4
10
|
type AppRscRouteParams = RoutePatternParams;
|
|
5
11
|
type AppRscInterceptForMatching = {
|
|
6
12
|
targetPattern: string;
|
|
@@ -25,15 +31,27 @@ type AppRscInterceptForMatching = {
|
|
|
25
31
|
sourceMatchPattern?: string;
|
|
26
32
|
interceptLayouts: readonly unknown[];
|
|
27
33
|
page: unknown;
|
|
34
|
+
__pageLoader?: (() => Promise<unknown>) | null;
|
|
28
35
|
params: readonly string[];
|
|
29
36
|
};
|
|
30
37
|
type AppRscSlotForMatching = {
|
|
31
38
|
id?: string | null;
|
|
32
39
|
intercepts?: readonly AppRscInterceptForMatching[];
|
|
33
40
|
};
|
|
41
|
+
type AppRscSiblingInterceptForMatching = {
|
|
42
|
+
targetPattern: string;
|
|
43
|
+
sourceMatchPattern: string | null;
|
|
44
|
+
slotId: string | null;
|
|
45
|
+
interceptLayouts: readonly unknown[];
|
|
46
|
+
page: unknown;
|
|
47
|
+
__pageLoader?: (() => Promise<unknown>) | null;
|
|
48
|
+
params: readonly string[];
|
|
49
|
+
};
|
|
34
50
|
type AppRscRouteForMatching = {
|
|
51
|
+
pattern: string;
|
|
35
52
|
patternParts: string[];
|
|
36
53
|
slots?: Record<string, AppRscSlotForMatching>;
|
|
54
|
+
siblingIntercepts?: AppRscSiblingInterceptForMatching[];
|
|
37
55
|
};
|
|
38
56
|
type AppRscInterceptMatch = AppRscInterceptLookupEntry & {
|
|
39
57
|
matchedParams: AppRscRouteParams;
|
|
@@ -47,6 +65,7 @@ type AppRscInterceptLookupEntry = {
|
|
|
47
65
|
sourceMatchPatternParts: string[] | null;
|
|
48
66
|
interceptLayouts: readonly unknown[];
|
|
49
67
|
page: unknown;
|
|
68
|
+
__pageLoader?: (() => Promise<unknown>) | null;
|
|
50
69
|
params: readonly string[];
|
|
51
70
|
slotId: string | null;
|
|
52
71
|
};
|
|
@@ -59,4 +78,4 @@ declare function createAppRscRouteMatcher<Route extends AppRscRouteForMatching>(
|
|
|
59
78
|
};
|
|
60
79
|
declare function matchAppRscRoutePattern(urlParts: string[], patternParts: string[]): AppRscRouteParams | null;
|
|
61
80
|
//#endregion
|
|
62
|
-
export { createAppRscRouteMatcher, matchAppRscRoutePattern };
|
|
81
|
+
export { SIBLING_PAGE_INTERCEPT_SLOT_KEY, createAppRscRouteMatcher, matchAppRscRoutePattern };
|
|
@@ -2,6 +2,12 @@ import { splitPathnameForRouteMatch } from "../routing/utils.js";
|
|
|
2
2
|
import { buildRouteTrie, trieMatch } from "../routing/route-trie.js";
|
|
3
3
|
import { matchRoutePattern, matchRoutePatternPrefix } from "../routing/route-pattern.js";
|
|
4
4
|
//#region src/server/app-rsc-route-matching.ts
|
|
5
|
+
/**
|
|
6
|
+
* Sentinel slot key used for sibling-style interception entries.
|
|
7
|
+
* When a matched intercept carries this key, the render layer replaces the
|
|
8
|
+
* route's main page element instead of a parallel slot.
|
|
9
|
+
*/
|
|
10
|
+
const SIBLING_PAGE_INTERCEPT_SLOT_KEY = "__vinext_page_intercept";
|
|
5
11
|
function createRouteParams() {
|
|
6
12
|
return Object.create(null);
|
|
7
13
|
}
|
|
@@ -54,17 +60,18 @@ function matchInterceptSource(sourceParts, entry) {
|
|
|
54
60
|
return matchRoutePatternPrefix(sourceParts, patternParts);
|
|
55
61
|
}
|
|
56
62
|
function createInterceptLookup(routes) {
|
|
63
|
+
const patternToIndex = new Map(routes.map((r, i) => [r.pattern, i]));
|
|
57
64
|
const interceptLookup = [];
|
|
58
65
|
for (let routeIndex = 0; routeIndex < routes.length; routeIndex++) {
|
|
59
66
|
const route = routes[routeIndex];
|
|
60
|
-
if (
|
|
61
|
-
for (const [slotKey, slotModule] of Object.entries(route.slots)) {
|
|
67
|
+
if (route.slots) for (const [slotKey, slotModule] of Object.entries(route.slots)) {
|
|
62
68
|
if (!slotModule.intercepts) continue;
|
|
63
69
|
for (const intercept of slotModule.intercepts) {
|
|
64
70
|
const sourceMatchPattern = intercept.sourceMatchPattern ?? null;
|
|
65
71
|
const sourceMatchPatternParts = sourceMatchPattern ? sourceMatchPattern.split("/").filter(Boolean) : null;
|
|
72
|
+
const ownerRouteIndex = sourceMatchPattern !== null ? patternToIndex.get(sourceMatchPattern) ?? routeIndex : routeIndex;
|
|
66
73
|
interceptLookup.push({
|
|
67
|
-
sourceRouteIndex:
|
|
74
|
+
sourceRouteIndex: ownerRouteIndex,
|
|
68
75
|
slotKey,
|
|
69
76
|
slotId: typeof slotModule.id === "string" ? slotModule.id : null,
|
|
70
77
|
targetPattern: intercept.targetPattern,
|
|
@@ -73,10 +80,28 @@ function createInterceptLookup(routes) {
|
|
|
73
80
|
sourceMatchPatternParts,
|
|
74
81
|
interceptLayouts: intercept.interceptLayouts,
|
|
75
82
|
page: intercept.page,
|
|
83
|
+
__pageLoader: intercept.__pageLoader,
|
|
76
84
|
params: intercept.params
|
|
77
85
|
});
|
|
78
86
|
}
|
|
79
87
|
}
|
|
88
|
+
if (route.siblingIntercepts) for (const intercept of route.siblingIntercepts) {
|
|
89
|
+
const sourceMatchPattern = intercept.sourceMatchPattern ?? null;
|
|
90
|
+
const sourceMatchPatternParts = sourceMatchPattern ? sourceMatchPattern.split("/").filter(Boolean) : null;
|
|
91
|
+
interceptLookup.push({
|
|
92
|
+
sourceRouteIndex: routeIndex,
|
|
93
|
+
slotKey: SIBLING_PAGE_INTERCEPT_SLOT_KEY,
|
|
94
|
+
slotId: typeof intercept.slotId === "string" ? intercept.slotId : null,
|
|
95
|
+
targetPattern: intercept.targetPattern,
|
|
96
|
+
targetPatternParts: intercept.targetPattern.split("/").filter(Boolean),
|
|
97
|
+
sourceMatchPattern,
|
|
98
|
+
sourceMatchPatternParts,
|
|
99
|
+
interceptLayouts: intercept.interceptLayouts,
|
|
100
|
+
page: intercept.page,
|
|
101
|
+
__pageLoader: intercept.__pageLoader,
|
|
102
|
+
params: intercept.params
|
|
103
|
+
});
|
|
104
|
+
}
|
|
80
105
|
}
|
|
81
106
|
return interceptLookup;
|
|
82
107
|
}
|
|
@@ -87,4 +112,4 @@ function mergeMatchedParams(sourceParams, targetParams) {
|
|
|
87
112
|
return Object.assign(createRouteParams(), sourceParams, targetParams);
|
|
88
113
|
}
|
|
89
114
|
//#endregion
|
|
90
|
-
export { createAppRscRouteMatcher, matchAppRscRoutePattern };
|
|
115
|
+
export { SIBLING_PAGE_INTERCEPT_SLOT_KEY, createAppRscRouteMatcher, matchAppRscRoutePattern };
|
|
@@ -31,6 +31,17 @@ type AppServerActionRoute = {
|
|
|
31
31
|
pattern: string;
|
|
32
32
|
routeHandler?: unknown;
|
|
33
33
|
routeSegments?: readonly string[];
|
|
34
|
+
params?: readonly string[] | null;
|
|
35
|
+
slots?: Readonly<Record<string, {
|
|
36
|
+
default?: {
|
|
37
|
+
default?: unknown;
|
|
38
|
+
} | null;
|
|
39
|
+
page?: {
|
|
40
|
+
default?: unknown;
|
|
41
|
+
} | null;
|
|
42
|
+
slotPatternParts?: readonly string[] | null;
|
|
43
|
+
slotParamNames?: readonly string[] | null;
|
|
44
|
+
}>> | null;
|
|
34
45
|
};
|
|
35
46
|
/**
|
|
36
47
|
* Side-effect headers captured during a progressive (no-JS) server action's
|
|
@@ -108,6 +119,15 @@ type HandleProgressiveServerActionRequestOptions = {
|
|
|
108
119
|
decodeFormState: AppServerActionFormStateDecoder;
|
|
109
120
|
getAndClearPendingCookies: () => string[];
|
|
110
121
|
getDraftModeCookieHeader: () => string | null | undefined;
|
|
122
|
+
/**
|
|
123
|
+
* Whether the posted-to route resolves to an App Router *page* (as opposed to
|
|
124
|
+
* a route handler or no match). Multipart form POSTs to a page are always
|
|
125
|
+
* server-action attempts in Next.js, so a body that decodes to no action must
|
|
126
|
+
* surface as 404 action-not-found rather than rendering the page. Route
|
|
127
|
+
* handlers (which run *after* this dispatch in vinext) legitimately receive
|
|
128
|
+
* raw multipart POSTs, so they must still fall through. See issue #1340.
|
|
129
|
+
*/
|
|
130
|
+
hasPageRoute: boolean;
|
|
111
131
|
maxActionBodySize: number;
|
|
112
132
|
middlewareHeaders: Headers | null;
|
|
113
133
|
readFormDataWithLimit: ReadFormDataWithLimit;
|
|
@@ -143,7 +163,8 @@ type HandleServerActionRscRequestOptions<TElement, TRoute extends AppServerActio
|
|
|
143
163
|
isRscRequest: boolean;
|
|
144
164
|
loadServerAction: (actionId: string) => Promise<unknown>;
|
|
145
165
|
matchRoute: (pathname: string) => AppServerActionMatch<TRoute> | null;
|
|
146
|
-
maxActionBodySize: number;
|
|
166
|
+
maxActionBodySize: number; /** Verbatim `serverActions.bodySizeLimit` config string (e.g. "2mb") for the body-exceeded error. */
|
|
167
|
+
maxActionBodySizeLabel: string;
|
|
147
168
|
middlewareHeaders: Headers | null;
|
|
148
169
|
middlewareStatus: number | null | undefined;
|
|
149
170
|
mountedSlotsHeader: string | null;
|
|
@@ -15,6 +15,7 @@ import { applyEdgeRuntimeHeader } from "./app-page-response.js";
|
|
|
15
15
|
import { getNextErrorDigest, parseNextHttpErrorDigest, parseNextRedirectDigest } from "./next-error-digest.js";
|
|
16
16
|
import { createServerActionNotFoundResponse, getServerActionNotFoundMessage, isServerActionNotFoundError } from "./server-action-not-found.js";
|
|
17
17
|
import { deferUntilStreamConsumed } from "./app-page-stream.js";
|
|
18
|
+
import { resolveAppPageNavigationParams } from "./app-page-element-builder.js";
|
|
18
19
|
import { resolveAppPageActionRerenderTarget } from "./app-page-request.js";
|
|
19
20
|
import { buildPageCacheTags } from "./implicit-tags.js";
|
|
20
21
|
import { getSetCookieName } from "./cookie-utils.js";
|
|
@@ -110,6 +111,15 @@ function isRequestBodyTooLarge(error) {
|
|
|
110
111
|
return error instanceof Error && error.message === "Request body too large";
|
|
111
112
|
}
|
|
112
113
|
/**
|
|
114
|
+
* Build the error thrown when a server-action request body exceeds the
|
|
115
|
+
* configured size limit. Matches Next.js' `Body exceeded {limit} limit.`
|
|
116
|
+
* message + docs link (action-handler.ts) verbatim — including the original
|
|
117
|
+
* config string (e.g. "2mb") — so it reads identically in logs.
|
|
118
|
+
*/
|
|
119
|
+
function createBodyExceededError(limitLabel) {
|
|
120
|
+
return /* @__PURE__ */ new Error(`Body exceeded ${limitLabel} limit.\nTo configure the body size limit for Server Actions, see: https://nextjs.org/docs/app/api-reference/next-config-js/serverActions#bodysizelimit`);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
113
123
|
* Collapse repeated `cookies().set(name, ...)` / `cookies().delete(name)`
|
|
114
124
|
* calls down to the last value per name, matching Next.js'
|
|
115
125
|
* `MutableRequestCookiesAdapter` semantics. Next.js stores response cookies in
|
|
@@ -325,7 +335,13 @@ async function handleProgressiveServerActionRequest(options) {
|
|
|
325
335
|
return payloadResponse;
|
|
326
336
|
}
|
|
327
337
|
const action = await options.decodeAction(body);
|
|
328
|
-
if (!isAppServerActionFunction(action))
|
|
338
|
+
if (!isAppServerActionFunction(action)) {
|
|
339
|
+
if (options.hasPageRoute) return createActionNotFoundResponse(null, {
|
|
340
|
+
clearRequestContext: options.clearRequestContext,
|
|
341
|
+
getAndClearPendingCookies: options.getAndClearPendingCookies
|
|
342
|
+
});
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
329
345
|
let actionRedirect = null;
|
|
330
346
|
let actionError = void 0;
|
|
331
347
|
let actionFailed = false;
|
|
@@ -411,23 +427,66 @@ async function handleProgressiveServerActionRequest(options) {
|
|
|
411
427
|
return internalServerErrorResponse(process.env.NODE_ENV === "production" ? void 0 : "Server action parsing failed: " + getServerActionFailureMessage(error));
|
|
412
428
|
}
|
|
413
429
|
}
|
|
430
|
+
/**
|
|
431
|
+
* Render the response for a fetch (client-invoked) server action whose request
|
|
432
|
+
* body exceeds the configured `serverActions.bodySizeLimit`.
|
|
433
|
+
*
|
|
434
|
+
* Next.js does not return a bare 413 here: it throws the body-exceeded error
|
|
435
|
+
* before the action runs, then — for fetch actions — emits a Flight response
|
|
436
|
+
* with status 500 carrying the rejected action result, so the nearest client
|
|
437
|
+
* error boundary catches it (see action-handler.ts, the `isFetchAction` branch
|
|
438
|
+
* of the generic error path). vinext mirrors that by rendering a Flight stream
|
|
439
|
+
* with `returnValue: { ok: false }` and no page root (the action never ran, so
|
|
440
|
+
* nothing was revalidated and the page render is skipped). A bare 413 plain
|
|
441
|
+
* response would bypass the boundary and surface the wrong status/content-type.
|
|
442
|
+
*/
|
|
443
|
+
async function renderFetchActionBodyExceededResponse(options) {
|
|
444
|
+
const error = createBodyExceededError(options.maxActionBodySizeLabel);
|
|
445
|
+
console.error("[vinext] Server action error:", error);
|
|
446
|
+
options.reportRequestError(normalizeError(error), {
|
|
447
|
+
path: options.cleanPathname,
|
|
448
|
+
method: options.request.method,
|
|
449
|
+
headers: Object.fromEntries(options.request.headers.entries())
|
|
450
|
+
}, {
|
|
451
|
+
routerKind: "App Router",
|
|
452
|
+
routePath: options.cleanPathname,
|
|
453
|
+
routeType: "action"
|
|
454
|
+
});
|
|
455
|
+
getAndClearActionRevalidationKind();
|
|
456
|
+
options.getAndClearPendingCookies();
|
|
457
|
+
const returnValue = {
|
|
458
|
+
ok: false,
|
|
459
|
+
data: options.sanitizeErrorForClient(error)
|
|
460
|
+
};
|
|
461
|
+
const temporaryReferences = options.createTemporaryReferenceSet();
|
|
462
|
+
const onRenderError = options.createRscOnErrorHandler(options.request, options.cleanPathname, options.cleanPathname);
|
|
463
|
+
const rscStream = await options.renderToReadableStream({ returnValue }, {
|
|
464
|
+
temporaryReferences,
|
|
465
|
+
onError: onRenderError
|
|
466
|
+
});
|
|
467
|
+
const headers = new Headers({
|
|
468
|
+
"Content-Type": VINEXT_RSC_CONTENT_TYPE,
|
|
469
|
+
Vary: VINEXT_RSC_VARY_HEADER
|
|
470
|
+
});
|
|
471
|
+
applyEdgeRuntimeHeader(headers, options.isEdgeRuntime);
|
|
472
|
+
mergeMiddlewareResponseHeaders(headers, options.middlewareHeaders);
|
|
473
|
+
applyRscCompatibilityIdHeader(headers);
|
|
474
|
+
return createServerActionRscResponse(rscStream, {
|
|
475
|
+
status: 500,
|
|
476
|
+
headers
|
|
477
|
+
}, options.clearRequestContext);
|
|
478
|
+
}
|
|
414
479
|
async function handleServerActionRscRequest(options) {
|
|
415
480
|
if (options.request.method.toUpperCase() !== "POST" || !options.actionId) return null;
|
|
416
481
|
const csrfResponse = validateCsrfOrigin(options.request, options.allowedOrigins);
|
|
417
482
|
if (csrfResponse) return csrfResponse;
|
|
418
|
-
if (parseInt(options.request.headers.get("content-length") || "0", 10) > options.maxActionBodySize)
|
|
419
|
-
options.clearRequestContext();
|
|
420
|
-
return payloadTooLargeResponse();
|
|
421
|
-
}
|
|
483
|
+
if (parseInt(options.request.headers.get("content-length") || "0", 10) > options.maxActionBodySize) return renderFetchActionBodyExceededResponse(options);
|
|
422
484
|
try {
|
|
423
485
|
let body;
|
|
424
486
|
try {
|
|
425
487
|
body = options.contentType.startsWith("multipart/form-data") ? await options.readFormDataWithLimit(options.request, options.maxActionBodySize) : await options.readBodyWithLimit(options.request, options.maxActionBodySize);
|
|
426
488
|
} catch (error) {
|
|
427
|
-
if (isRequestBodyTooLarge(error))
|
|
428
|
-
options.clearRequestContext();
|
|
429
|
-
return payloadTooLargeResponse();
|
|
430
|
-
}
|
|
489
|
+
if (isRequestBodyTooLarge(error)) return renderFetchActionBodyExceededResponse(options);
|
|
431
490
|
throw error;
|
|
432
491
|
}
|
|
433
492
|
const payloadResponse = await validateServerActionPayload(body);
|
|
@@ -533,10 +592,11 @@ async function handleServerActionRscRequest(options) {
|
|
|
533
592
|
url: redirectTarget
|
|
534
593
|
});
|
|
535
594
|
setHeadersContext(headersContextFromRequest(redirectRenderRequest));
|
|
595
|
+
const redirectNavigationParams = resolveAppPageNavigationParams(targetMatch.route, targetMatch.params, targetPathname, null);
|
|
536
596
|
options.setNavigationContext({
|
|
537
597
|
pathname: targetPathname,
|
|
538
598
|
searchParams: redirectTarget.searchParams,
|
|
539
|
-
params:
|
|
599
|
+
params: redirectNavigationParams
|
|
540
600
|
});
|
|
541
601
|
setCurrentFetchCacheMode(options.resolveRouteFetchCacheMode?.(targetMatch.route) ?? null);
|
|
542
602
|
setCurrentFetchSoftTags(buildServerActionPageTags(targetMatch.route, targetPathname));
|
|
@@ -599,7 +659,7 @@ async function handleServerActionRscRequest(options) {
|
|
|
599
659
|
let errorPattern = match ? match.route.pattern : options.cleanPathname;
|
|
600
660
|
if (match) {
|
|
601
661
|
const { route: actionRoute, params: actionParams } = match;
|
|
602
|
-
const actionRerenderTarget = resolveAppPageActionRerenderTarget({
|
|
662
|
+
const actionRerenderTarget = await resolveAppPageActionRerenderTarget({
|
|
603
663
|
cleanPathname: options.cleanPathname,
|
|
604
664
|
currentParams: actionParams,
|
|
605
665
|
currentRoute: actionRoute,
|
|
@@ -609,10 +669,11 @@ async function handleServerActionRscRequest(options) {
|
|
|
609
669
|
isRscRequest: options.isRscRequest,
|
|
610
670
|
toInterceptOpts: options.toInterceptOpts
|
|
611
671
|
});
|
|
672
|
+
const resolvedActionNavigationParams = resolveAppPageNavigationParams(actionRerenderTarget.route, actionRerenderTarget.navigationParams, options.cleanPathname, actionRerenderTarget.interceptOpts);
|
|
612
673
|
options.setNavigationContext({
|
|
613
674
|
pathname: options.cleanPathname,
|
|
614
675
|
searchParams: options.searchParams,
|
|
615
|
-
params:
|
|
676
|
+
params: resolvedActionNavigationParams
|
|
616
677
|
});
|
|
617
678
|
await options.ensureRouteLoaded?.(actionRerenderTarget.route);
|
|
618
679
|
setCurrentFetchCacheMode(options.resolveRouteFetchCacheMode?.(actionRerenderTarget.route) ?? null);
|
|
@@ -30,6 +30,12 @@ declare function handleSsr(rscStream: ReadableStream<Uint8Array>, navContext: Na
|
|
|
30
30
|
* SSR head. Undefined or empty disables emission entirely.
|
|
31
31
|
*/
|
|
32
32
|
clientTraceMetadata?: readonly string[];
|
|
33
|
+
/**
|
|
34
|
+
* Maximum total length (in characters) of the preload `Link` header React
|
|
35
|
+
* emits during SSR. `0` disables emission. From `reactMaxHeadersLength` in
|
|
36
|
+
* `next.config`. Undefined falls back to React's own default.
|
|
37
|
+
*/
|
|
38
|
+
reactMaxHeadersLength?: number;
|
|
33
39
|
rootParams?: RootParams; /** Dev-only: original server error to surface in the browser overlay. */
|
|
34
40
|
initialDevServerError?: unknown;
|
|
35
41
|
/** When true, wait for the full React tree (including Suspense boundaries)
|
|
@@ -12,18 +12,25 @@ import { getClientTraceMetadataHTML } from "./client-trace-metadata.js";
|
|
|
12
12
|
import { BfcacheStateKeyMapContext, ElementsContext, Slot } from "../shims/slot.js";
|
|
13
13
|
import { createSsrErrorMetaRenderer } from "./app-ssr-error-meta.js";
|
|
14
14
|
import { createNavigationRuntimeRscMetadataScript, createRscEmbedTransform, createTickBufferedTransform } from "./app-ssr-stream.js";
|
|
15
|
-
import { BeforeInteractiveContext } from "../shims/before-interactive-context.js";
|
|
16
|
-
import { runWithRootParamsScope } from "../shims/root-params.js";
|
|
17
15
|
import { createBfcacheSegmentStateKeyMap, createInitialBfcacheIdMap } from "./app-browser-state.js";
|
|
18
16
|
import { RSC_FORM_STATE_GLOBAL } from "./app-browser-hydration.js";
|
|
19
17
|
import { createClientReferencePreloader } from "./app-client-reference-preloader.js";
|
|
20
18
|
import { deferUntilStreamConsumed } from "./app-page-stream.js";
|
|
19
|
+
import { runWithRootParamsScope } from "../shims/root-params.js";
|
|
20
|
+
import { BeforeInteractiveContext } from "../shims/before-interactive-context.js";
|
|
21
21
|
import { createInitialDevServerErrorScript } from "./dev-initial-server-error.js";
|
|
22
22
|
import { Fragment, createElement, use } from "react";
|
|
23
23
|
import { renderToReadableStream, renderToStaticMarkup } from "react-dom/server.edge";
|
|
24
24
|
import { createFromReadableStream } from "@vitejs/plugin-rsc/ssr";
|
|
25
25
|
import clientReferences from "virtual:vite-rsc/client-references";
|
|
26
26
|
//#region src/server/app-ssr-entry.ts
|
|
27
|
+
/**
|
|
28
|
+
* Default cap for the preload `Link` header, matching Next.js's
|
|
29
|
+
* `defaultConfig.reactMaxHeadersLength`. Used when no config value threads
|
|
30
|
+
* through (e.g. error-boundary renders) so React's internal cap agrees with
|
|
31
|
+
* the response-layer combine cap.
|
|
32
|
+
*/
|
|
33
|
+
const DEFAULT_REACT_MAX_HEADERS_LENGTH = 6e3;
|
|
27
34
|
const clientReferencePreloader = createClientReferencePreloader({
|
|
28
35
|
getReferences() {
|
|
29
36
|
return clientReferences;
|
|
@@ -175,7 +182,15 @@ async function handleSsr(rscStream, navContext, fontData, options) {
|
|
|
175
182
|
const ssrRoot = withScriptNonce(createElement(BeforeInteractiveContext.Provider, { value: registerBeforeInteractiveInlineScript }, ssrTree), options?.scriptNonce);
|
|
176
183
|
const bootstrapModuleUrl = extractBootstrapModuleUrl(await import.meta.viteRsc.loadBootstrapScriptContent("index"));
|
|
177
184
|
const errorMetaRenderer = createSsrErrorMetaRenderer({ basePath: options?.basePath });
|
|
185
|
+
let reactLinkHeader = "";
|
|
186
|
+
const maxHeadersLength = options?.reactMaxHeadersLength ?? DEFAULT_REACT_MAX_HEADERS_LENGTH;
|
|
187
|
+
const captureHeaders = maxHeadersLength > 0;
|
|
178
188
|
const htmlStream = await renderToReadableStream(ssrRoot, {
|
|
189
|
+
onHeaders: captureHeaders ? (headers) => {
|
|
190
|
+
const link = headers.get("Link");
|
|
191
|
+
if (link) reactLinkHeader = link;
|
|
192
|
+
} : void 0,
|
|
193
|
+
maxHeadersLength: captureHeaders ? maxHeadersLength : void 0,
|
|
179
194
|
bootstrapModules: bootstrapModuleUrl ? [bootstrapModuleUrl] : void 0,
|
|
180
195
|
formState: options?.formState ?? null,
|
|
181
196
|
nonce: options?.scriptNonce,
|
|
@@ -210,7 +225,8 @@ async function handleSsr(rscStream, navContext, fontData, options) {
|
|
|
210
225
|
return {
|
|
211
226
|
htmlStream: deferUntilStreamConsumed(htmlStream.pipeThrough(createTickBufferedTransform(rscEmbed, getInsertedHTML, getBeforeInteractiveHeadHTML, inlineCssManifest, inlineCssFontStyles, inlineCssFontStyleFallbackHTML, options?.scriptNonce)), cleanup),
|
|
212
227
|
metadataReady: Promise.resolve(),
|
|
213
|
-
capturedRscData: options?.capturedRscDataRef?.value ?? null
|
|
228
|
+
capturedRscData: options?.capturedRscDataRef?.value ?? null,
|
|
229
|
+
linkHeader: reactLinkHeader
|
|
214
230
|
};
|
|
215
231
|
} catch (error) {
|
|
216
232
|
cleanup();
|
|
@@ -89,6 +89,12 @@ function fixPreloadAs(html) {
|
|
|
89
89
|
const LINK_TAG_RE = /<link\b[^>]*>/gi;
|
|
90
90
|
const HTML_REWRITE_EXCLUDED_REGION_RE = /<!--[\s\S]*?-->|<(script|style|textarea|title)\b[^>]*>[\s\S]*?<\/\1\s*>/gi;
|
|
91
91
|
const HTML_REWRITE_EXCLUDED_REGION_START_RE = /<!--|<(script|style|textarea|title)\b[^>]*>/gi;
|
|
92
|
+
const CLOSE_TAG_RES = {
|
|
93
|
+
script: /<\/script\s*>/i,
|
|
94
|
+
style: /<\/style\s*>/i,
|
|
95
|
+
textarea: /<\/textarea\s*>/i,
|
|
96
|
+
title: /<\/title\s*>/i
|
|
97
|
+
};
|
|
92
98
|
function getHtmlAttribute(tag, name) {
|
|
93
99
|
const attrRe = /\s([^\s"'=<>`]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
|
|
94
100
|
let match;
|
|
@@ -141,7 +147,9 @@ function findTrailingOpenHtmlRewriteExcludedRegionStart(html) {
|
|
|
141
147
|
}
|
|
142
148
|
const tagName = match[1]?.toLowerCase();
|
|
143
149
|
if (!tagName) continue;
|
|
144
|
-
const
|
|
150
|
+
const closeTagRe = CLOSE_TAG_RES[tagName];
|
|
151
|
+
if (!closeTagRe) continue;
|
|
152
|
+
const close = closeTagRe.exec(html.slice(HTML_REWRITE_EXCLUDED_REGION_START_RE.lastIndex));
|
|
145
153
|
if (!close) return start;
|
|
146
154
|
HTML_REWRITE_EXCLUDED_REGION_START_RE.lastIndex += close.index + close[0].length;
|
|
147
155
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizePathSeparators } from "../utils/path.js";
|
|
1
2
|
import fs from "node:fs";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
//#region src/server/dev-lockfile.ts
|
|
@@ -98,7 +99,7 @@ function formatAlreadyRunningError(opts) {
|
|
|
98
99
|
if (!existing) return [
|
|
99
100
|
"Another vinext dev server appears to be running in this directory.",
|
|
100
101
|
"",
|
|
101
|
-
`Stale lock file: ${path.relative(cwd, lockfilePath)}`,
|
|
102
|
+
`Stale lock file: ${normalizePathSeparators(path.relative(cwd, lockfilePath))}`,
|
|
102
103
|
"Remove it manually if no server is running, then re-run `vinext dev`."
|
|
103
104
|
].join("\n");
|
|
104
105
|
const killCommand = process.platform === "win32" ? `taskkill /PID ${existing.pid} /F` : `kill ${existing.pid}`;
|
|
@@ -36,7 +36,7 @@ declare function parseCookieLocale(req: IncomingMessage, i18nConfig: NextI18nCon
|
|
|
36
36
|
* 4. Render the component to HTML
|
|
37
37
|
* 5. Wrap in _document shell and send response
|
|
38
38
|
*/
|
|
39
|
-
declare function createSSRHandler(server: ViteDevServer, runner: ModuleImporter, routes: Route[], pagesDir: string, i18nConfig?: NextI18nConfig | null, fileMatcher?: ValidFileMatcher, basePath?: string, trailingSlash?: boolean, hasMiddleware?: boolean,
|
|
39
|
+
declare function createSSRHandler(server: ViteDevServer, runner: ModuleImporter, routes: Route[], pagesDir: string, i18nConfig?: NextI18nConfig | null, fileMatcher?: ValidFileMatcher, basePath?: string, trailingSlash?: boolean, hasMiddleware?: boolean, hasRewrites?: boolean,
|
|
40
40
|
/**
|
|
41
41
|
* Allow-list of OpenTelemetry propagation keys to emit as `<meta>` tags
|
|
42
42
|
* in the SSR head. Sourced from `experimental.clientTraceMetadata` in
|