vinext 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/dist/build/client-build-config.d.ts +4 -6
  2. package/dist/build/client-build-config.js +27 -7
  3. package/dist/build/prerender.js +1 -2
  4. package/dist/check.d.ts +18 -0
  5. package/dist/check.js +37 -17
  6. package/dist/cli.js +2 -2
  7. package/dist/config/next-config.d.ts +2 -1
  8. package/dist/config/next-config.js +3 -0
  9. package/dist/config/server-external-packages.d.ts +4 -0
  10. package/dist/config/server-external-packages.js +91 -0
  11. package/dist/deploy.d.ts +7 -0
  12. package/dist/deploy.js +25 -18
  13. package/dist/entries/app-rsc-entry.d.ts +2 -1
  14. package/dist/entries/app-rsc-entry.js +89 -20
  15. package/dist/entries/app-rsc-manifest.js +8 -0
  16. package/dist/entries/pages-client-entry.js +1 -0
  17. package/dist/entries/pages-server-entry.js +1 -0
  18. package/dist/index.js +87 -34
  19. package/dist/init.js +2 -1
  20. package/dist/plugins/middleware-server-only.d.ts +8 -6
  21. package/dist/plugins/middleware-server-only.js +8 -7
  22. package/dist/plugins/og-assets.js +6 -6
  23. package/dist/plugins/optimize-imports.js +1 -1
  24. package/dist/plugins/remove-console.js +1 -1
  25. package/dist/plugins/strip-server-exports.js +2 -1
  26. package/dist/routing/app-route-graph.d.ts +6 -2
  27. package/dist/routing/app-route-graph.js +61 -13
  28. package/dist/routing/app-router.d.ts +5 -0
  29. package/dist/routing/app-router.js +5 -0
  30. package/dist/routing/file-matcher.d.ts +5 -0
  31. package/dist/routing/file-matcher.js +7 -1
  32. package/dist/server/app-action-request.d.ts +4 -0
  33. package/dist/server/app-action-request.js +9 -0
  34. package/dist/server/app-bfcache-identity.d.ts +12 -2
  35. package/dist/server/app-bfcache-identity.js +52 -23
  36. package/dist/server/app-browser-entry.js +42 -147
  37. package/dist/server/app-browser-history-controller.d.ts +2 -1
  38. package/dist/server/app-browser-history-controller.js +6 -2
  39. package/dist/server/app-browser-server-action-client.d.ts +32 -0
  40. package/dist/server/app-browser-server-action-client.js +128 -0
  41. package/dist/server/app-fallback-renderer.d.ts +3 -1
  42. package/dist/server/app-fallback-renderer.js +6 -2
  43. package/dist/server/app-history-state.d.ts +1 -2
  44. package/dist/server/app-history-state.js +1 -1
  45. package/dist/server/app-page-boundary-render.d.ts +3 -0
  46. package/dist/server/app-page-boundary-render.js +14 -3
  47. package/dist/server/app-page-cache-finalizer.d.ts +1 -0
  48. package/dist/server/app-page-cache-finalizer.js +8 -2
  49. package/dist/server/app-page-dispatch.d.ts +33 -4
  50. package/dist/server/app-page-dispatch.js +62 -90
  51. package/dist/server/app-page-element-builder.d.ts +7 -1
  52. package/dist/server/app-page-element-builder.js +56 -19
  53. package/dist/server/app-page-head.d.ts +16 -1
  54. package/dist/server/app-page-head.js +44 -22
  55. package/dist/server/app-page-method.js +1 -1
  56. package/dist/server/app-page-params.d.ts +2 -1
  57. package/dist/server/app-page-params.js +8 -1
  58. package/dist/server/app-page-ppr-runtime.d.ts +7 -0
  59. package/dist/server/app-page-ppr-runtime.js +70 -0
  60. package/dist/server/app-page-probe.d.ts +1 -0
  61. package/dist/server/app-page-probe.js +1 -1
  62. package/dist/server/app-page-render.d.ts +4 -1
  63. package/dist/server/app-page-render.js +8 -3
  64. package/dist/server/app-page-request.d.ts +8 -1
  65. package/dist/server/app-page-request.js +23 -11
  66. package/dist/server/app-page-route-wiring.d.ts +6 -1
  67. package/dist/server/app-page-route-wiring.js +30 -8
  68. package/dist/server/app-page-search-params-observation.d.ts +4 -2
  69. package/dist/server/app-page-search-params-observation.js +11 -7
  70. package/dist/server/app-page-stream.js +2 -1
  71. package/dist/server/app-ppr-fallback-shell-render.d.ts +4 -3
  72. package/dist/server/app-route-handler-cache.js +1 -1
  73. package/dist/server/app-route-handler-dispatch.js +3 -2
  74. package/dist/server/app-route-handler-execution.js +5 -3
  75. package/dist/server/app-route-handler-policy.d.ts +1 -1
  76. package/dist/server/app-route-handler-policy.js +1 -6
  77. package/dist/server/app-route-module-loader.d.ts +2 -0
  78. package/dist/server/app-route-module-loader.js +1 -0
  79. package/dist/server/app-router-entry.js +7 -6
  80. package/dist/server/app-rsc-errors.d.ts +12 -1
  81. package/dist/server/app-rsc-errors.js +29 -3
  82. package/dist/server/app-rsc-handler.d.ts +15 -13
  83. package/dist/server/app-rsc-handler.js +28 -33
  84. package/dist/server/app-rsc-response-finalizer.js +5 -1
  85. package/dist/server/app-rsc-route-matching.d.ts +7 -0
  86. package/dist/server/app-rsc-route-matching.js +36 -3
  87. package/dist/server/app-segment-config.d.ts +1 -0
  88. package/dist/server/app-segment-config.js +32 -2
  89. package/dist/server/app-server-action-execution.d.ts +4 -0
  90. package/dist/server/app-server-action-execution.js +42 -11
  91. package/dist/server/app-ssr-entry.js +11 -6
  92. package/dist/server/app-static-generation.d.ts +1 -0
  93. package/dist/server/app-static-generation.js +1 -0
  94. package/dist/server/headers.d.ts +3 -1
  95. package/dist/server/headers.js +3 -1
  96. package/dist/server/metadata-route-response.d.ts +1 -1
  97. package/dist/server/prod-server.js +15 -6
  98. package/dist/server/worker-utils.d.ts +2 -1
  99. package/dist/server/worker-utils.js +7 -1
  100. package/dist/shims/dynamic.js +1 -1
  101. package/dist/shims/error-boundary.d.ts +19 -1
  102. package/dist/shims/error-boundary.js +11 -1
  103. package/dist/shims/headers.d.ts +3 -1
  104. package/dist/shims/headers.js +16 -5
  105. package/dist/shims/link.js +16 -12
  106. package/dist/shims/metadata.d.ts +3 -2
  107. package/dist/shims/metadata.js +8 -4
  108. package/dist/shims/router.js +13 -2
  109. package/dist/typegen.js +6 -5
  110. package/dist/utils/path.d.ts +2 -1
  111. package/dist/utils/path.js +1 -1
  112. package/dist/utils/project.d.ts +4 -0
  113. package/dist/utils/project.js +5 -1
  114. package/package.json +7 -3
@@ -3,7 +3,7 @@ import { AppElementsWire } from "./app-elements-wire.js";
3
3
  import "./app-elements.js";
4
4
  import DefaultGlobalError from "../shims/default-global-error.js";
5
5
  import { isNavigationSignalError } from "../utils/navigation-signal.js";
6
- import { ErrorBoundary, GlobalErrorBoundary } from "../shims/error-boundary.js";
6
+ import { ErrorBoundary, GlobalErrorBoundary, SerializedErrorBoundary } from "../shims/error-boundary.js";
7
7
  import { LayoutSegmentProvider } from "../shims/layout-segment-context.js";
8
8
  import { MetadataHead, ViewportHead } from "../shims/metadata.js";
9
9
  import { resolveAppPageSpecialError } from "./app-page-execution.js";
@@ -152,6 +152,7 @@ async function renderAppPageHttpAccessFallback(options) {
152
152
  const pathname = new URL(options.requestUrl).pathname;
153
153
  const routeSegments = resolveHttpAccessFallbackHeadRouteSegments(options.route, layoutModules);
154
154
  const { metadata, viewport } = await resolveAppPageHead({
155
+ applyFileBasedMetadata: options.applyFileBasedMetadata,
155
156
  basePath: options.basePath ?? "",
156
157
  layoutModules,
157
158
  layoutTreePositions: resolveHttpAccessFallbackHeadLayoutTreePositions(options.route, layoutModules),
@@ -214,7 +215,7 @@ async function renderAppPageErrorBoundary(options) {
214
215
  if (!errorBoundary.component) return null;
215
216
  const rawError = options.error instanceof Error ? options.error : new Error(String(options.error));
216
217
  rewriteClientHookError(rawError);
217
- const errorObject = options.sanitizeErrorForClient(rawError);
218
+ const errorObject = options.errorOrigin === "ssr" ? rawError : options.sanitizeErrorForClient(rawError);
218
219
  const matchedParams = options.matchedParams ?? options.route?.params ?? {};
219
220
  const layoutModules = options.route?.layouts ?? options.rootLayouts;
220
221
  const pathname = new URL(options.requestUrl).pathname;
@@ -224,6 +225,7 @@ async function renderAppPageErrorBoundary(options) {
224
225
  })];
225
226
  if (!errorBoundary.isGlobalError) try {
226
227
  const { metadata, viewport } = await resolveAppPageHead({
228
+ applyFileBasedMetadata: options.applyFileBasedMetadata,
227
229
  basePath: options.basePath ?? "",
228
230
  fallbackOnFileMetadataError: true,
229
231
  layoutModules,
@@ -247,7 +249,16 @@ async function renderAppPageErrorBoundary(options) {
247
249
  console.error(`[vinext] App page error boundary head resolution failed for ${options.route?.pattern ?? pathname}:`, error);
248
250
  }
249
251
  const buildElement = (BoundaryComponent) => {
250
- const boundaryElement = createElement(BoundaryComponent, { error: errorObject });
252
+ const serializedError = {
253
+ digest: "digest" in errorObject ? String(errorObject.digest) : void 0,
254
+ message: errorObject.message,
255
+ name: errorObject.name,
256
+ stack: process.env.NODE_ENV !== "production" ? errorObject.stack : void 0
257
+ };
258
+ const boundaryElement = errorBoundary.isGlobalError && BoundaryComponent !== DEFAULT_GLOBAL_ERROR_COMPONENT ? createElement(SerializedErrorBoundary, {
259
+ error: serializedError,
260
+ fallback: BoundaryComponent
261
+ }) : createElement(BoundaryComponent, { error: errorObject });
251
262
  return wrapRenderedBoundaryElement({
252
263
  element: createElement(Fragment, null, ...headElements, errorBoundary.isGlobalError ? createElement(GlobalErrorBoundary, {
253
264
  fallback: DEFAULT_GLOBAL_ERROR_COMPONENT,
@@ -30,6 +30,7 @@ type FinalizeAppPageHtmlCacheResponseOptions = {
30
30
  isrRscKey: AppPageRscCacheKeyBuilder;
31
31
  isrSet: AppPageCacheSetter;
32
32
  interceptionContext?: string | null;
33
+ omitPendingDynamicCacheState?: boolean;
33
34
  preserveClientResponseHeaders?: boolean;
34
35
  expireSeconds?: number;
35
36
  revalidateSeconds: number | null;
@@ -1,15 +1,21 @@
1
+ import { NEXTJS_CACHE_HEADER, VINEXT_CACHE_HEADER } from "./headers.js";
1
2
  import { setCacheStateHeaders } from "./cache-headers.js";
2
3
  import { applyCdnResponseHeaders } from "./cache-control.js";
3
4
  import { buildAppPageCacheValue } from "./isr-cache.js";
4
5
  import { readStreamAsText } from "../utils/text-stream.js";
5
6
  import { createEmptyAppPageRenderObservationState } from "./app-page-render-observation.js";
6
7
  //#region src/server/app-page-cache-finalizer.ts
7
- function applyPendingDynamicCdnHeaders(headers, tags) {
8
+ function applyPendingDynamicCdnHeaders(headers, tags, options = {}) {
8
9
  applyCdnResponseHeaders(headers, {
9
10
  cacheControl: headers.get("Cache-Control") ?? "",
10
11
  pendingDynamicCheck: true,
11
12
  tags
12
13
  });
14
+ if (options.omitCacheState === true) {
15
+ headers.delete(VINEXT_CACHE_HEADER);
16
+ headers.delete(NEXTJS_CACHE_HEADER);
17
+ return;
18
+ }
13
19
  setCacheStateHeaders(headers, "MISS");
14
20
  }
15
21
  function resolveAppPageCacheWritePolicy(options) {
@@ -30,7 +36,7 @@ function finalizeAppPageHtmlCacheResponse(response, options) {
30
36
  const htmlKey = options.isrHtmlKey(options.cleanPathname);
31
37
  const rscKey = options.isrRscKey(options.cleanPathname, null, void 0, options.interceptionContext);
32
38
  const clientHeaders = new Headers(response.headers);
33
- if (options.preserveClientResponseHeaders !== true) applyPendingDynamicCdnHeaders(clientHeaders, options.getPageTags());
39
+ if (options.preserveClientResponseHeaders !== true) applyPendingDynamicCdnHeaders(clientHeaders, options.getPageTags(), { omitCacheState: options.omitPendingDynamicCacheState === true });
34
40
  const cachePromise = (async () => {
35
41
  try {
36
42
  const cachedHtml = await readStreamAsText(streamForCache);
@@ -10,8 +10,10 @@ import { AppPageSsrHandler } from "./app-page-stream.js";
10
10
  import { AppLayoutParamAccessTracker } from "./app-layout-param-observation.js";
11
11
  import { AppRscRenderMode } from "./app-rsc-render-mode.js";
12
12
  import { ISRCacheEntry } from "./isr-cache.js";
13
+ import { PprFallbackShellState } from "../shims/ppr-fallback-shell.js";
13
14
  import { FetchCacheMode } from "../shims/fetch-cache.js";
14
15
  import { AppPagePprFallbackCacheShell } from "./app-ppr-fallback-shell.js";
16
+ import { WarmPprFallbackShellCachesOptions } from "./app-ppr-fallback-shell-render.js";
15
17
  import { ValidateAppPageDynamicParamsOptions } from "./app-page-request.js";
16
18
  import { ReactNode } from "react";
17
19
  import { ReactFormState } from "react-dom/client";
@@ -32,7 +34,10 @@ type AppPageBackgroundRegenerationErrorContext = {
32
34
  type AppPageBackgroundRegenerator = (key: string, renderFn: () => Promise<void>, errorContext?: AppPageBackgroundRegenerationErrorContext) => void;
33
35
  type AppPageDispatchIntercept<TPage = unknown> = {
34
36
  interceptLayouts?: readonly unknown[] | null;
37
+ interceptLayoutSegments?: readonly (readonly string[])[] | null;
38
+ interceptBranchSegments?: readonly string[] | null;
35
39
  matchedParams: AppPageParams;
40
+ sourceMatchedParams?: AppPageParams;
36
41
  page: TPage;
37
42
  slotId?: string | null;
38
43
  slotKey: string;
@@ -42,6 +47,8 @@ type AppPageDispatchIntercept<TPage = unknown> = {
42
47
  type AppPageDispatchInterceptOptions<TPage = unknown> = {
43
48
  interceptionContext: string | null;
44
49
  interceptLayouts?: readonly unknown[] | null;
50
+ interceptLayoutSegments?: readonly (readonly string[])[] | null;
51
+ interceptBranchSegments?: readonly string[] | null;
45
52
  interceptPage: TPage;
46
53
  interceptParams: AppPageParams;
47
54
  interceptSlotId?: string | null;
@@ -80,6 +87,14 @@ type AppPageDispatchRoute = {
80
87
  unauthorized?: AppPageModule | null;
81
88
  unauthorizeds?: readonly (AppPageModule | null | undefined)[];
82
89
  };
90
+ type AppPagePprRuntime<TRoute extends AppPageDispatchRoute> = {
91
+ beginFinalRender(state: AppPagePprState): void;
92
+ getState(): AppPagePprState | null;
93
+ run<T>(shell: NonNullable<DispatchAppPageOptions<TRoute>["pprFallbackShell"]>, fn: () => T): T;
94
+ tryServe(options: DispatchAppPageOptions<TRoute>, currentRevalidateSeconds: number | null, isDraftMode: boolean, isForceStatic: boolean, isForceDynamic: boolean): Promise<Response | null>;
95
+ warm(options: WarmPprFallbackShellCachesOptions): Promise<void>;
96
+ };
97
+ type AppPagePprState = PprFallbackShellState;
83
98
  type DispatchAppPageOptions<TRoute extends AppPageDispatchRoute> = {
84
99
  /** Configured basePath (e.g. "/blog"). Used to prefix redirect Locations. */basePath?: string;
85
100
  /**
@@ -94,7 +109,10 @@ type DispatchAppPageOptions<TRoute extends AppPageDispatchRoute> = {
94
109
  * `next.config`. Undefined falls back to the React default downstream.
95
110
  */
96
111
  reactMaxHeadersLength?: number;
97
- buildPageElement: (route: TRoute, params: AppPageParams, opts: AppPageDispatchInterceptOptions | undefined, searchParams: URLSearchParams, layoutParamAccess?: AppLayoutParamAccessTracker) => Promise<AppPageElement>;
112
+ buildPageElement: (route: TRoute, params: AppPageParams, opts: AppPageDispatchInterceptOptions | undefined, searchParams: URLSearchParams, layoutParamAccess?: AppLayoutParamAccessTracker, options?: {
113
+ observeMetadataSearchParamsAccess?: boolean;
114
+ observePageSearchParamsAccess?: boolean;
115
+ }) => Promise<AppPageElement>;
98
116
  clientReuseManifest?: ClientReuseManifestParseResult;
99
117
  cleanPathname: string;
100
118
  displayPathname?: string;
@@ -147,6 +165,7 @@ type DispatchAppPageOptions<TRoute extends AppPageDispatchRoute> = {
147
165
  fallbackParamNames: readonly string[];
148
166
  routePattern: string;
149
167
  };
168
+ pprRuntime?: AppPagePprRuntime<TRoute>;
150
169
  /**
151
170
  * Set of concrete URL paths that were pre-rendered at build time for this
152
171
  * route. When the exact cache entry for a known pregenerated path is absent
@@ -159,9 +178,9 @@ type DispatchAppPageOptions<TRoute extends AppPageDispatchRoute> = {
159
178
  staticParamsValidationParams?: AppPageParams;
160
179
  rootParams?: RootParams;
161
180
  probeLayoutAt: (layoutIndex: number, layoutParamAccess?: AppLayoutParamAccessTracker) => unknown;
162
- probePage: () => unknown;
181
+ probePage: (searchParams?: URLSearchParams) => unknown;
163
182
  expireSeconds?: number;
164
- renderErrorBoundaryPage: (error: unknown) => Promise<Response | null>;
183
+ renderErrorBoundaryPage: (error: unknown, errorOrigin?: "rsc" | "ssr") => Promise<Response | null>;
165
184
  renderHttpAccessFallbackPage: (statusCode: number, opts: {
166
185
  boundaryComponent?: unknown;
167
186
  boundaryModule?: AppPageModule | null;
@@ -197,6 +216,16 @@ type DispatchAppPageOptions<TRoute extends AppPageDispatchRoute> = {
197
216
  }) => void;
198
217
  renderMode?: AppRscRenderMode;
199
218
  };
219
+ declare function shouldReadAppPageCache(options: {
220
+ isProgressiveActionRender: boolean;
221
+ isDraftMode: boolean;
222
+ isForceDynamic: boolean;
223
+ isProduction: boolean;
224
+ isRscRequest: boolean;
225
+ revalidateSeconds: number | null;
226
+ scriptNonce?: string;
227
+ }): boolean;
228
+ declare function hasSearchParams(searchParams: URLSearchParams | null | undefined): boolean;
200
229
  declare function dispatchAppPage<TRoute extends AppPageDispatchRoute>(options: DispatchAppPageOptions<TRoute>): Promise<Response>;
201
230
  //#endregion
202
- export { dispatchAppPage };
231
+ export { AppPageBoundaryOnError, AppPageDispatchRoute, AppPagePprRuntime, AppPagePprState, AppPageRenderableElement, DispatchAppPageOptions, dispatchAppPage, hasSearchParams, shouldReadAppPageCache };
@@ -3,8 +3,7 @@ import { getRequestExecutionContext } from "../shims/request-context.js";
3
3
  import { AppElementsWire } from "./app-elements-wire.js";
4
4
  import { shouldSuppressLoadingBoundaries } from "./app-rsc-render-mode.js";
5
5
  import "./app-elements.js";
6
- import { beginPprFallbackShellFinalRender, createPprFallbackShellState, getPprFallbackShellState, runWithPprFallbackShellState } from "../shims/ppr-fallback-shell.js";
7
- import { consumeDynamicUsage, consumeInvalidDynamicUsageError, getAndClearPendingCookies, getDraftModeCookieHeader, isDraftModeRequest, markDynamicUsage, peekRenderRequestApiUsage, setHeadersContext } from "../shims/headers.js";
6
+ import { consumeDynamicUsage, consumeInvalidDynamicUsageError, getAndClearPendingCookies, getDraftModeCookieHeader, getHeadersContext, isDraftModeRequest, markDynamicUsage, peekDynamicUsage, peekRenderRequestApiUsage, setHeadersContext } from "../shims/headers.js";
8
7
  import { _consumeRequestScopedCacheLife, _peekRequestScopedCacheLife } from "../shims/cache-request-state.js";
9
8
  import { ensureFetchPatch, getCollectedFetchTags, peekDynamicFetchObservations, runWithFetchDedupe, setCurrentFetchCacheMode, setCurrentFetchSoftTags, setCurrentForceDynamicFetchDefault } from "../shims/fetch-cache.js";
10
9
  import { VINEXT_RSC_CONTENT_TYPE, VINEXT_RSC_VARY_HEADER, applyRscCompatibilityIdHeader } from "./app-rsc-cache-busting.js";
@@ -108,6 +107,7 @@ async function runAppPageRevalidationContext(options, renderFn) {
108
107
  const { createStaticGenerationHeadersContext } = await import("./app-static-generation.js");
109
108
  return runWithRequestContext(createRequestContext({
110
109
  headersContext: createStaticGenerationHeadersContext({
110
+ draftModeEnabled: false,
111
111
  draftModeSecret: options.draftModeSecret,
112
112
  dynamicConfig: options.dynamicConfig,
113
113
  routeKind: "page",
@@ -132,6 +132,8 @@ function toInterceptOptions(interceptionContext, intercept) {
132
132
  return {
133
133
  interceptionContext,
134
134
  interceptLayouts: intercept.interceptLayouts,
135
+ interceptLayoutSegments: intercept.interceptLayoutSegments,
136
+ interceptBranchSegments: intercept.interceptBranchSegments,
135
137
  interceptPage: intercept.page,
136
138
  interceptParams: intercept.matchedParams,
137
139
  interceptSlotId: intercept.slotId ?? null,
@@ -140,75 +142,10 @@ function toInterceptOptions(interceptionContext, intercept) {
140
142
  interceptSourcePageSegments: intercept.sourcePageSegments ?? null
141
143
  };
142
144
  }
143
- /**
144
- * Request-phase fallback-shell gate. Callers must run the exact cache read and
145
- * static-param validation before this classification step.
146
- */
147
- function classifyPprFallbackShellEligibility(options, currentRevalidateSeconds, isDraftMode, isForceStatic, isForceDynamic) {
148
- if (options.renderedConcreteUrlPaths?.has(options.cleanPathname) === true) return { kind: "skip-known-pregenerated-route" };
149
- const fallbackShells = options.pprFallbackCacheShells;
150
- if (!fallbackShells || fallbackShells.length === 0) return { kind: "skip-no-fallback-shells" };
151
- if (options.isRscRequest) return { kind: "skip-rsc-request" };
152
- if (options.request.method !== "GET") return { kind: "skip-non-get" };
153
- if (!isForceStatic && hasSearchParams(options.searchParams)) return { kind: "skip-search-params" };
154
- if (!shouldReadAppPageCache({
155
- isDraftMode,
156
- isForceDynamic,
157
- isProgressiveActionRender: options.isProgressiveActionRender === true,
158
- isProduction: options.isProduction,
159
- isRscRequest: false,
160
- revalidateSeconds: currentRevalidateSeconds,
161
- scriptNonce: options.scriptNonce
162
- })) return { kind: "skip-cache-disabled" };
163
- return {
164
- kind: "probe-fallback-shells",
165
- fallbackShells
166
- };
167
- }
168
- async function probePprFallbackShellCache(options, route, fallbackShells, currentRevalidateSeconds) {
169
- const { readAppPageFallbackShellCacheResponse } = await import("./app-page-cache.js");
170
- const { rewriteAppPprFallbackShellHtmlNavigation } = await import("./app-ppr-fallback-shell.js");
171
- for (const fallbackShell of fallbackShells) {
172
- const fallbackShellResponse = await readAppPageFallbackShellCacheResponse({
173
- clearRequestContext: options.clearRequestContext,
174
- expireSeconds: options.expireSeconds,
175
- fallbackPathname: fallbackShell.pathname,
176
- isEdgeRuntime: options.isEdgeRuntime,
177
- isrDebug: options.isrDebug,
178
- isrGet: options.isrGet,
179
- isrHtmlKey: options.isrHtmlKey,
180
- middlewareHeaders: options.middlewareContext.headers,
181
- middlewareStatus: options.middlewareContext.status,
182
- revalidateSeconds: currentRevalidateSeconds ?? 0,
183
- rewriteHtml(html) {
184
- return rewriteAppPprFallbackShellHtmlNavigation({
185
- html,
186
- params: options.params,
187
- pathname: options.cleanPathname,
188
- searchParams: options.searchParams
189
- });
190
- }
191
- });
192
- if (fallbackShellResponse) return fallbackShellResponse;
193
- }
194
- return null;
195
- }
196
- async function tryServePprFallbackShell(options, route, currentRevalidateSeconds, isDraftMode, isForceStatic, isForceDynamic) {
197
- const decision = classifyPprFallbackShellEligibility(options, currentRevalidateSeconds, isDraftMode, isForceStatic, isForceDynamic);
198
- switch (decision.kind) {
199
- case "skip-known-pregenerated-route":
200
- case "skip-no-fallback-shells":
201
- case "skip-rsc-request":
202
- case "skip-non-get":
203
- case "skip-search-params":
204
- case "skip-cache-disabled": return null;
205
- case "probe-fallback-shells": return await probePprFallbackShellCache(options, route, decision.fallbackShells, currentRevalidateSeconds);
206
- }
207
- }
208
145
  async function dispatchAppPage(options) {
209
146
  const dispatch = () => runWithFetchDedupe(() => dispatchAppPageInner(options));
210
- if (!options.pprFallbackShell) return await dispatch();
211
- return await runWithPprFallbackShellState(createPprFallbackShellState(options.pprFallbackShell), dispatch);
147
+ if (!options.pprFallbackShell || !options.pprRuntime) return await dispatch();
148
+ return await options.pprRuntime.run(options.pprFallbackShell, dispatch);
212
149
  }
213
150
  async function dispatchAppPageInner(options) {
214
151
  const route = options.route;
@@ -218,7 +155,11 @@ async function dispatchAppPageInner(options) {
218
155
  const isDynamicError = dynamicConfig === "error";
219
156
  const isForceDynamic = dynamicConfig === "force-dynamic";
220
157
  const isDraftMode = isDraftModeRequest(options.request, options.draftModeSecret);
158
+ const requestHeadersContext = getHeadersContext();
159
+ const hasRequestSearchParams = !isForceStatic && hasSearchParams(options.searchParams);
160
+ const pageSearchParams = isForceStatic ? new URLSearchParams() : options.searchParams;
221
161
  const layoutParamAccess = createAppLayoutParamAccessTracker();
162
+ const hasActiveLoadingBoundary = shouldSuppressLoadingBoundaries(options.renderMode ?? "navigation") ? false : Boolean(route.loading?.default);
222
163
  setCurrentFetchSoftTags(buildAppPageTags(options.cleanPathname, [], route.routeSegments));
223
164
  setCurrentFetchCacheMode(options.fetchCache ?? null);
224
165
  setCurrentForceDynamicFetchDefault(isForceDynamic);
@@ -238,9 +179,10 @@ async function dispatchAppPageInner(options) {
238
179
  options.clearRequestContext();
239
180
  return methodResponse;
240
181
  }
241
- if ((isForceStatic || isDynamicError) && !isDraftMode) {
182
+ if (isForceStatic || isDynamicError) {
242
183
  const { createStaticGenerationHeadersContext } = await import("./app-static-generation.js");
243
184
  setHeadersContext(createStaticGenerationHeadersContext({
185
+ draftModeEnabled: isDraftMode,
244
186
  draftModeSecret: options.draftModeSecret,
245
187
  dynamicConfig,
246
188
  routeKind: "page",
@@ -266,7 +208,7 @@ async function dispatchAppPageInner(options) {
266
208
  const cachedPageResponse = await readAppPageCacheResponse({
267
209
  cleanPathname: options.cleanPathname,
268
210
  clearRequestContext: options.clearRequestContext,
269
- hasRequestSearchParams: !isForceStatic && hasSearchParams(options.searchParams),
211
+ hasRequestSearchParams,
270
212
  isEdgeRuntime: options.isEdgeRuntime,
271
213
  isRscRequest: options.isRscRequest,
272
214
  isrDebug: options.isrDebug,
@@ -300,19 +242,23 @@ async function dispatchAppPageInner(options) {
300
242
  });
301
243
  revalidationTarget.navigationParams = resolveAppPageNavigationParams(revalidationTarget.route, revalidationTarget.navigationParams, options.cleanPathname, revalidationTarget.interceptOpts);
302
244
  await options.ensureRouteLoaded?.(revalidationTarget.route);
245
+ const revalidationDynamicConfig = options.resolveRouteDynamicConfig?.(revalidationTarget.route) ?? (revalidationTarget.route === route ? dynamicConfig : void 0);
303
246
  return runAppPageRevalidationContext({
304
247
  cleanPathname: options.cleanPathname,
305
248
  displayPathname: options.displayPathname,
306
249
  currentFetchCacheMode: options.resolveRouteFetchCacheMode?.(revalidationTarget.route) ?? (revalidationTarget.route === route ? options.fetchCache ?? null : null),
307
250
  draftModeSecret: options.draftModeSecret,
308
- dynamicConfig: options.resolveRouteDynamicConfig?.(revalidationTarget.route) ?? (revalidationTarget.route === route ? dynamicConfig : void 0),
251
+ dynamicConfig: revalidationDynamicConfig,
309
252
  params: revalidationTarget.navigationParams,
310
253
  routePattern: revalidationTarget.route.pattern,
311
254
  routeSegments: revalidationTarget.route.routeSegments,
312
255
  setNavigationContext: options.setNavigationContext
313
256
  }, async () => {
314
257
  const { renderAppPageCacheArtifacts } = await import("./app-page-cache-render.js");
315
- const revalidatedElement = await options.buildPageElement(revalidationTarget.route, revalidationTarget.params, revalidationTarget.interceptOpts, new URLSearchParams());
258
+ const revalidatedElement = await options.buildPageElement(revalidationTarget.route, revalidationTarget.params, revalidationTarget.interceptOpts, new URLSearchParams(), void 0, {
259
+ observeMetadataSearchParamsAccess: revalidationDynamicConfig !== "force-static",
260
+ observePageSearchParamsAccess: revalidationDynamicConfig !== "force-static"
261
+ });
316
262
  const revalidatedOnError = options.createRscOnErrorHandler(options.cleanPathname, revalidationTarget.route.pattern);
317
263
  const rendered = await renderAppPageCacheArtifacts({
318
264
  basePath: options.basePath,
@@ -365,14 +311,29 @@ async function dispatchAppPageInner(options) {
365
311
  });
366
312
  if (dynamicParamsResponse) return dynamicParamsResponse;
367
313
  }
368
- const fallbackShellResponse = await tryServePprFallbackShell(options, route, currentRevalidateSeconds, isDraftMode, isForceStatic, isForceDynamic);
314
+ const fallbackShellResponse = options.pprRuntime ? await options.pprRuntime.tryServe(options, currentRevalidateSeconds, isDraftMode, isForceStatic, isForceDynamic) : null;
369
315
  if (fallbackShellResponse) return fallbackShellResponse;
316
+ let interceptDynamicConfig;
317
+ let interceptDynamicConfigResolved = false;
370
318
  const interceptResult = await resolveAppPageIntercept({
371
319
  async buildPageElement(interceptRoute, interceptParams, interceptOpts, interceptSearchParams, interceptLayoutParamAccess) {
372
- await options.ensureRouteLoaded?.(interceptRoute);
320
+ const sourceDynamicConfig = interceptDynamicConfigResolved ? interceptDynamicConfig : options.resolveRouteDynamicConfig?.(interceptRoute);
321
+ if (sourceDynamicConfig === "force-static" || sourceDynamicConfig === "error") {
322
+ const { createStaticGenerationHeadersContext } = await import("./app-static-generation.js");
323
+ setHeadersContext(createStaticGenerationHeadersContext({
324
+ draftModeEnabled: isDraftMode,
325
+ draftModeSecret: options.draftModeSecret,
326
+ dynamicConfig: sourceDynamicConfig,
327
+ routeKind: "page",
328
+ routePattern: interceptRoute.pattern
329
+ }));
330
+ } else setHeadersContext(requestHeadersContext);
373
331
  setCurrentFetchCacheMode(options.resolveRouteFetchCacheMode?.(interceptRoute) ?? null);
374
- setCurrentForceDynamicFetchDefault(options.resolveRouteDynamicConfig?.(interceptRoute) === "force-dynamic");
375
- return options.buildPageElement(interceptRoute, interceptParams, interceptOpts, interceptSearchParams, interceptLayoutParamAccess);
332
+ setCurrentForceDynamicFetchDefault(sourceDynamicConfig === "force-dynamic");
333
+ return options.buildPageElement(interceptRoute, interceptParams, interceptOpts, interceptSearchParams, interceptLayoutParamAccess, {
334
+ observeMetadataSearchParamsAccess: sourceDynamicConfig !== "force-static",
335
+ observePageSearchParamsAccess: sourceDynamicConfig !== "force-static"
336
+ });
376
337
  },
377
338
  cleanPathname: options.cleanPathname,
378
339
  currentRoute: route,
@@ -404,6 +365,12 @@ async function dispatchAppPageInner(options) {
404
365
  headers: interceptHeaders
405
366
  });
406
367
  },
368
+ async resolveSearchParams(sourceRoute, searchParams) {
369
+ await options.ensureRouteLoaded?.(sourceRoute);
370
+ interceptDynamicConfig = options.resolveRouteDynamicConfig?.(sourceRoute);
371
+ interceptDynamicConfigResolved = true;
372
+ return interceptDynamicConfig === "force-static" ? new URLSearchParams() : searchParams;
373
+ },
407
374
  searchParams: options.searchParams,
408
375
  setNavigationContext: options.setNavigationContext,
409
376
  toInterceptOpts(intercept) {
@@ -414,12 +381,15 @@ async function dispatchAppPageInner(options) {
414
381
  const buildCurrentPageElement = () => buildAppPageElement({
415
382
  buildPageElement() {
416
383
  if (options.actionFailed) throw options.actionError;
417
- return options.buildPageElement(route, options.params, interceptResult.interceptOpts, options.searchParams, layoutParamAccess);
384
+ return options.buildPageElement(route, options.params, interceptResult.interceptOpts, pageSearchParams, layoutParamAccess, {
385
+ observeMetadataSearchParamsAccess: !isForceStatic,
386
+ observePageSearchParamsAccess: !isForceStatic
387
+ });
418
388
  },
419
389
  async probePageSpecialError() {
420
390
  if (!shouldSuppressLoadingBoundaries(options.renderMode ?? "navigation") && route.loading?.default) return null;
421
391
  return resolveAppPageSpecialError(await probeAppPageThrownError({
422
- probePage: options.probePage,
392
+ probePage: () => options.probePage(pageSearchParams),
423
393
  runWithSuppressedHookWarning(probe) {
424
394
  return options.runWithSuppressedHookWarning(probe);
425
395
  }
@@ -433,12 +403,11 @@ async function dispatchAppPageInner(options) {
433
403
  },
434
404
  resolveSpecialError: resolveAppPageSpecialError
435
405
  });
436
- const fallbackShellState = getPprFallbackShellState();
406
+ const fallbackShellState = options.pprRuntime?.getState() ?? null;
437
407
  if (fallbackShellState && process.env.VINEXT_PRERENDER === "1" && !options.isRscRequest) {
438
408
  const warmupBuildResult = await buildCurrentPageElement();
439
409
  if (warmupBuildResult.response) return warmupBuildResult.response;
440
- const { warmPprFallbackShellCaches } = await import("./app-ppr-fallback-shell-render.js");
441
- await warmPprFallbackShellCaches({
410
+ await options.pprRuntime.warm({
442
411
  element: warmupBuildResult.element,
443
412
  onError: options.createRscOnErrorHandler(options.cleanPathname, route.pattern),
444
413
  renderToReadableStream: options.renderToReadableStream,
@@ -451,11 +420,11 @@ async function dispatchAppPageInner(options) {
451
420
  const navigationParams = resolveAppPageNavigationParams(route, options.params, options.cleanPathname, interceptResult.interceptOpts);
452
421
  options.setNavigationContext({
453
422
  pathname: options.displayPathname ?? options.cleanPathname,
454
- searchParams: options.searchParams,
423
+ searchParams: pageSearchParams,
455
424
  params: navigationParams
456
425
  });
457
426
  const layoutClassifications = getEffectiveLayoutClassifications(route, options.debugClassification);
458
- const activeFallbackShellState = getPprFallbackShellState();
427
+ const activeFallbackShellState = options.pprRuntime?.getState() ?? null;
459
428
  const pprFallbackShellSignal = activeFallbackShellState?.abortController.signal;
460
429
  const pprFallbackShellReactSignal = activeFallbackShellState?.reactAbortController.signal;
461
430
  return renderAppPageLifecycle({
@@ -465,6 +434,7 @@ async function dispatchAppPageInner(options) {
465
434
  cleanPathname: options.cleanPathname,
466
435
  clearRequestContext: options.clearRequestContext,
467
436
  consumeDynamicUsage,
437
+ peekDynamicUsage,
468
438
  consumeInvalidDynamicUsageError,
469
439
  consumeRenderObservationState: consumeAppPageRenderObservationState,
470
440
  createRscOnErrorHandler(pathname, routePath) {
@@ -487,7 +457,8 @@ async function dispatchAppPageInner(options) {
487
457
  return _peekRequestScopedCacheLife();
488
458
  },
489
459
  handlerStart: options.handlerStart,
490
- hasLoadingBoundary: shouldSuppressLoadingBoundaries(options.renderMode ?? "navigation") ? false : Boolean(route.loading?.default),
460
+ hasLoadingBoundary: hasActiveLoadingBoundary,
461
+ omitPendingDynamicCacheState: !options.isRscRequest && hasRequestSearchParams,
491
462
  formState: options.formState ?? null,
492
463
  isProgressiveActionRender: options.isProgressiveActionRender === true,
493
464
  isDynamicError,
@@ -512,7 +483,7 @@ async function dispatchAppPageInner(options) {
512
483
  pprFallbackShellSignal,
513
484
  pprFallbackShellReactSignal,
514
485
  abortPprFallbackShell: activeFallbackShellState ? () => {
515
- beginPprFallbackShellFinalRender(activeFallbackShellState);
486
+ options.pprRuntime.beginFinalRender(activeFallbackShellState);
516
487
  } : void 0,
517
488
  layoutParamAccess,
518
489
  rootParams: options.rootParams,
@@ -526,8 +497,9 @@ async function dispatchAppPageInner(options) {
526
497
  return options.probeLayoutAt(layoutIndex, layoutParamAccess);
527
498
  },
528
499
  probePage() {
529
- return options.probePage();
500
+ return options.probePage(pageSearchParams);
530
501
  },
502
+ probePageBeforeRender: options.isRscRequest,
531
503
  classification: {
532
504
  getLayoutId(index) {
533
505
  const treePosition = route.layoutTreePositions?.[index] ?? 0;
@@ -556,8 +528,8 @@ async function dispatchAppPageInner(options) {
556
528
  revalidateSeconds: currentRevalidateSeconds,
557
529
  mountedSlotsHeader: options.mountedSlotsHeader,
558
530
  renderMode: options.renderMode ?? "navigation",
559
- renderErrorBoundaryResponse(renderError) {
560
- return options.renderErrorBoundaryPage(renderError);
531
+ renderErrorBoundaryResponse(renderError, errorOrigin) {
532
+ return options.renderErrorBoundaryPage(renderError, errorOrigin);
561
533
  },
562
534
  renderLayoutSpecialError(specialError, layoutIndex) {
563
535
  return renderLayoutSpecialError(options, specialError, layoutIndex);
@@ -674,4 +646,4 @@ async function renderPageSpecialError(options, specialError) {
674
646
  });
675
647
  }
676
648
  //#endregion
677
- export { dispatchAppPage };
649
+ export { dispatchAppPage, hasSearchParams, shouldReadAppPageCache };
@@ -1,6 +1,7 @@
1
1
  import { MetadataFileRoute } from "./metadata-routes.js";
2
2
  import { AppElements } from "./app-elements-wire.js";
3
3
  import { AppPageParams } from "./app-page-boundary.js";
4
+ import { ApplyAppPageFileBasedMetadata } from "./app-page-head.js";
4
5
  import { AppLayoutParamAccessTracker } from "./app-layout-param-observation.js";
5
6
  import { AppRscRenderMode } from "./app-rsc-render-mode.js";
6
7
  import { AppPageErrorModule, AppPageModule, AppPageRouteWiringRoute } from "./app-page-route-wiring.js";
@@ -19,6 +20,8 @@ type AppPageBuildRoute<TModule extends AppPageModule = AppPageModule, TErrorModu
19
20
  type AppPageInterceptOptions<TModule extends AppPageModule = AppPageModule> = {
20
21
  interceptionContext?: string | null;
21
22
  interceptLayouts?: readonly (TModule | null | undefined)[] | null;
23
+ interceptLayoutSegments?: readonly (readonly string[])[] | null;
24
+ interceptBranchSegments?: readonly string[] | null;
22
25
  interceptPage?: TModule | null;
23
26
  interceptParams?: AppPageParams | null;
24
27
  interceptSlotId?: string | null;
@@ -32,9 +35,12 @@ type AppPagePageRequest<TModule extends AppPageModule = AppPageModule> = {
32
35
  isRscRequest: boolean; /** The incoming HTTP request (available but unused by this module). */
33
36
  request: Request; /** Normalized x-vinext-mounted-slots header value. */
34
37
  mountedSlotsHeader: string | null; /** Semantic RSC payload mode for this page render. */
35
- renderMode?: AppRscRenderMode;
38
+ renderMode?: AppRscRenderMode; /** Observe page `searchParams` access for cache-safety classification. */
39
+ observePageSearchParamsAccess?: boolean; /** Observe page metadata `searchParams` access for cache-safety classification. */
40
+ observeMetadataSearchParamsAccess?: boolean;
36
41
  };
37
42
  type BuildPageElementsOptions<TModule extends AppPageModule = AppPageModule, TErrorModule extends AppPageErrorModule = AppPageErrorModule> = {
43
+ applyFileBasedMetadata?: ApplyAppPageFileBasedMetadata;
38
44
  route: AppPageBuildRoute<TModule, TErrorModule>;
39
45
  params: AppPageParams;
40
46
  routePath: string;