@qzsy/vinext 0.1.12 → 0.1.122

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 (77) hide show
  1. package/dist/check.d.ts +8 -0
  2. package/dist/check.js +20 -9
  3. package/dist/cli.js +2 -2
  4. package/dist/deploy.js +10 -11
  5. package/dist/entries/app-rsc-entry.js +37 -8
  6. package/dist/entries/app-rsc-manifest.js +8 -0
  7. package/dist/entries/pages-client-entry.js +1 -0
  8. package/dist/entries/pages-server-entry.js +1 -0
  9. package/dist/index.js +12 -8
  10. package/dist/init.js +2 -1
  11. package/dist/plugins/middleware-server-only.d.ts +8 -6
  12. package/dist/plugins/middleware-server-only.js +8 -7
  13. package/dist/routing/app-route-graph.d.ts +6 -2
  14. package/dist/routing/app-route-graph.js +60 -12
  15. package/dist/routing/app-router.d.ts +5 -0
  16. package/dist/routing/app-router.js +5 -0
  17. package/dist/routing/file-matcher.d.ts +5 -0
  18. package/dist/routing/file-matcher.js +7 -1
  19. package/dist/server/app-browser-history-controller.d.ts +2 -1
  20. package/dist/server/app-browser-history-controller.js +6 -2
  21. package/dist/server/app-fallback-renderer.d.ts +1 -1
  22. package/dist/server/app-fallback-renderer.js +2 -1
  23. package/dist/server/app-page-boundary-render.d.ts +1 -0
  24. package/dist/server/app-page-boundary-render.js +12 -3
  25. package/dist/server/app-page-cache-finalizer.d.ts +1 -0
  26. package/dist/server/app-page-cache-finalizer.js +8 -2
  27. package/dist/server/app-page-dispatch.d.ts +11 -3
  28. package/dist/server/app-page-dispatch.js +54 -15
  29. package/dist/server/app-page-element-builder.d.ts +5 -1
  30. package/dist/server/app-page-element-builder.js +55 -19
  31. package/dist/server/app-page-head.d.ts +12 -0
  32. package/dist/server/app-page-head.js +42 -19
  33. package/dist/server/app-page-params.d.ts +2 -1
  34. package/dist/server/app-page-params.js +8 -1
  35. package/dist/server/app-page-probe.d.ts +1 -0
  36. package/dist/server/app-page-probe.js +1 -1
  37. package/dist/server/app-page-render.d.ts +4 -1
  38. package/dist/server/app-page-render.js +8 -3
  39. package/dist/server/app-page-request.d.ts +8 -1
  40. package/dist/server/app-page-request.js +23 -11
  41. package/dist/server/app-page-route-wiring.d.ts +6 -1
  42. package/dist/server/app-page-route-wiring.js +30 -8
  43. package/dist/server/app-page-search-params-observation.d.ts +4 -2
  44. package/dist/server/app-page-search-params-observation.js +11 -7
  45. package/dist/server/app-route-handler-dispatch.js +1 -0
  46. package/dist/server/app-route-handler-execution.js +2 -1
  47. package/dist/server/app-route-module-loader.d.ts +2 -0
  48. package/dist/server/app-route-module-loader.js +1 -0
  49. package/dist/server/app-router-entry.js +7 -6
  50. package/dist/server/app-rsc-errors.js +7 -1
  51. package/dist/server/app-rsc-handler.js +4 -1
  52. package/dist/server/app-rsc-route-matching.d.ts +7 -0
  53. package/dist/server/app-rsc-route-matching.js +36 -3
  54. package/dist/server/app-segment-config.d.ts +1 -0
  55. package/dist/server/app-segment-config.js +32 -2
  56. package/dist/server/app-server-action-execution.d.ts +4 -0
  57. package/dist/server/app-server-action-execution.js +41 -10
  58. package/dist/server/app-static-generation.d.ts +1 -0
  59. package/dist/server/app-static-generation.js +1 -0
  60. package/dist/server/headers.d.ts +3 -1
  61. package/dist/server/headers.js +3 -1
  62. package/dist/server/prod-server.js +15 -6
  63. package/dist/server/worker-utils.d.ts +2 -1
  64. package/dist/server/worker-utils.js +7 -1
  65. package/dist/shims/error-boundary.d.ts +19 -1
  66. package/dist/shims/error-boundary.js +11 -1
  67. package/dist/shims/headers.d.ts +3 -1
  68. package/dist/shims/headers.js +16 -5
  69. package/dist/shims/metadata.d.ts +3 -2
  70. package/dist/shims/metadata.js +8 -4
  71. package/dist/shims/router.js +13 -2
  72. package/dist/typegen.js +6 -5
  73. package/dist/utils/path.d.ts +2 -1
  74. package/dist/utils/path.js +1 -1
  75. package/dist/utils/project.d.ts +4 -0
  76. package/dist/utils/project.js +5 -1
  77. package/package.json +1 -1
@@ -1,10 +1,12 @@
1
+ import { ThenableParams, ThenableParamsObserver } from "../shims/thenable-params.js";
1
2
  import { AppPageSearchParams } from "./app-page-head.js";
2
- import { ThenableParams } from "../shims/thenable-params.js";
3
3
 
4
4
  //#region src/server/app-page-search-params-observation.d.ts
5
5
  type AppPageSearchParamsObservationOptions = {
6
+ markDynamic?: boolean;
6
7
  observeReactPromiseStatus?: boolean;
7
8
  };
9
+ declare function createAppPageSearchParamsObserver(options?: AppPageSearchParamsObservationOptions): ThenableParamsObserver;
8
10
  declare function makeObservedAppPageSearchParamsThenable(pageSearchParams: AppPageSearchParams, options?: AppPageSearchParamsObservationOptions): ThenableParams<AppPageSearchParams>;
9
11
  //#endregion
10
- export { makeObservedAppPageSearchParamsThenable };
12
+ export { createAppPageSearchParamsObserver, makeObservedAppPageSearchParamsThenable };
@@ -1,15 +1,19 @@
1
- import { markDynamicUsage, markRenderRequestApiUsage, throwIfInsideCacheScope } from "../shims/headers.js";
1
+ import { markDynamicUsage, markRenderRequestApiUsage, throwIfInsideCacheScope, throwIfStaticGenerationAccessError } from "../shims/headers.js";
2
2
  import { makeThenableParams } from "../shims/thenable-params.js";
3
3
  //#region src/server/app-page-search-params-observation.ts
4
- function markAppPageSearchParamsAccess() {
4
+ function markAppPageSearchParamsAccess(markDynamic) {
5
+ throwIfStaticGenerationAccessError();
5
6
  throwIfInsideCacheScope("searchParams");
6
- markDynamicUsage();
7
+ if (markDynamic) markDynamicUsage();
7
8
  markRenderRequestApiUsage("searchParams");
8
9
  }
9
- function makeObservedAppPageSearchParamsThenable(pageSearchParams, options = {}) {
10
- const observer = { observeParamAccess() {
11
- markAppPageSearchParamsAccess();
10
+ function createAppPageSearchParamsObserver(options = {}) {
11
+ return { observeParamAccess() {
12
+ markAppPageSearchParamsAccess(options.markDynamic !== false);
12
13
  } };
14
+ }
15
+ function makeObservedAppPageSearchParamsThenable(pageSearchParams, options = {}) {
16
+ const observer = createAppPageSearchParamsObserver(options);
13
17
  if (options.observeReactPromiseStatus === true) return makeThenableParams(pageSearchParams, {
14
18
  ...observer,
15
19
  observeReactPromiseStatus: true
@@ -17,4 +21,4 @@ function makeObservedAppPageSearchParamsThenable(pageSearchParams, options = {})
17
21
  return makeThenableParams(pageSearchParams, observer);
18
22
  }
19
23
  //#endregion
20
- export { makeObservedAppPageSearchParamsThenable };
24
+ export { createAppPageSearchParamsObserver, makeObservedAppPageSearchParamsThenable };
@@ -24,6 +24,7 @@ function buildRouteHandlerPageCacheTags(pathname, extraTags, routeSegments) {
24
24
  async function runInRouteHandlerRevalidationContext(options, renderFn) {
25
25
  await runWithRequestContext(createRequestContext({
26
26
  headersContext: createStaticGenerationHeadersContext({
27
+ draftModeEnabled: false,
27
28
  draftModeSecret: options.draftModeSecret,
28
29
  dynamicConfig: options.dynamicConfig,
29
30
  routeKind: "route",
@@ -1,4 +1,4 @@
1
- import { setHeadersContext } from "../shims/headers.js";
1
+ import { isDraftModeRequest, setHeadersContext } from "../shims/headers.js";
2
2
  import { isPossibleAppRouteActionRequest } from "./app-action-request.js";
3
3
  import { applyRouteHandlerMiddlewareContext, applyRouteHandlerRevalidateHeader, assertSupportedAppRouteHandlerResponse, buildAppRouteCacheValue, finalizeRouteHandlerResponse, markRouteHandlerCacheMiss } from "./app-route-handler-response.js";
4
4
  import { createTrackedAppRouteRequest, markKnownDynamicAppRoute } from "./app-route-handler-runtime.js";
@@ -8,6 +8,7 @@ import { resolveAppRouteHandlerSpecialError, shouldApplyAppRouteHandlerRevalidat
8
8
  function configureAppRouteStaticGenerationContext(options) {
9
9
  if (options.dynamicConfig === "force-static" || options.dynamicConfig === "error") {
10
10
  setHeadersContext(createStaticGenerationHeadersContext({
11
+ draftModeEnabled: options.draftModeSecret !== void 0 && isDraftModeRequest(options.request, options.draftModeSecret),
11
12
  draftModeSecret: options.draftModeSecret,
12
13
  dynamicConfig: options.dynamicConfig,
13
14
  routeKind: "route",
@@ -36,11 +36,13 @@ type LazyLoadableSlot = {
36
36
  page?: unknown;
37
37
  default?: unknown;
38
38
  layout?: unknown;
39
+ configLayouts?: readonly unknown[];
39
40
  loading?: unknown;
40
41
  error?: unknown;
41
42
  __loadPage?: LazyModuleThunk | null;
42
43
  __loadDefault?: LazyModuleThunk | null;
43
44
  __loadLayout?: LazyModuleThunk | null;
45
+ __loadConfigLayouts?: LazyModuleLoaderArray | null;
44
46
  __loadLoading?: LazyModuleThunk | null;
45
47
  __loadError?: LazyModuleThunk | null; /** Hydrated only after an intercept matches, not with the slot's base modules. */
46
48
  intercepts?: LazyLoadableIntercept[];
@@ -61,6 +61,7 @@ function ensureAppRouteModulesLoaded(route) {
61
61
  pushFieldLoad(loads, slot, "page", slot.__loadPage);
62
62
  pushFieldLoad(loads, slot, "default", slot.__loadDefault);
63
63
  pushFieldLoad(loads, slot, "layout", slot.__loadLayout);
64
+ pushArrayLoads(loads, slot.configLayouts, slot.__loadConfigLayouts);
64
65
  pushFieldLoad(loads, slot, "loading", slot.__loadLoading);
65
66
  pushFieldLoad(loads, slot, "error", slot.__loadError);
66
67
  }
@@ -5,7 +5,7 @@ import { badRequestResponse, notFoundResponse, notFoundStaticAssetResponse } fro
5
5
  import { isOpenRedirectShaped } from "./open-redirect.js";
6
6
  import { bufferRequestBodyForHeaderClone, cloneRequestWithHeaders, filterInternalHeaders } from "./request-pipeline.js";
7
7
  import { assetPrefixPathname, isNextStaticPath } from "../utils/asset-prefix.js";
8
- import { resolveStaticAssetSignal } from "./worker-utils.js";
8
+ import { finalizeMissingStaticAssetResponse, resolveStaticAssetSignal } from "./worker-utils.js";
9
9
  import { readTrustedPrerenderRouteParams, serializePrerenderRouteParamsHeader } from "./prerender-route-params.js";
10
10
  import rscHandler, { __assetPrefix, __basePath } from "virtual:vinext-rsc-entry";
11
11
  import { registerConfiguredCacheAdapters } from "virtual:vinext-cache-adapters";
@@ -24,7 +24,7 @@ async function handleRequest(request, env, ctx) {
24
24
  } catch {
25
25
  return badRequestResponse();
26
26
  }
27
- if (isNextStaticPath(url.pathname, __workerBasePath, __workerAssetPathPrefix)) return notFoundStaticAssetResponse();
27
+ const missingBuildAsset = isNextStaticPath(url.pathname, __workerBasePath, __workerAssetPathPrefix);
28
28
  {
29
29
  request = await bufferRequestBodyForHeaderClone(request);
30
30
  const prerenderRouteParamsPayload = readTrustedPrerenderRouteParams(request);
@@ -36,14 +36,15 @@ async function handleRequest(request, env, ctx) {
36
36
  const handleFn = () => rscHandler(request, ctx);
37
37
  const result = await (ctx ? runWithExecutionContext(ctx, handleFn) : handleFn());
38
38
  if (result instanceof Response) {
39
+ let response = result;
39
40
  if (env?.ASSETS) {
40
41
  const assetFetcher = env.ASSETS;
41
- const assetResponse = await resolveStaticAssetSignal(result, { fetchAsset: (path) => Promise.resolve(assetFetcher.fetch(new Request(new URL(path, request.url)))) });
42
- if (assetResponse) return assetResponse;
42
+ const assetResponse = await resolveStaticAssetSignal(response, { fetchAsset: (path) => Promise.resolve(assetFetcher.fetch(new Request(new URL(path, request.url)))) });
43
+ if (assetResponse) response = assetResponse;
43
44
  }
44
- return result;
45
+ return finalizeMissingStaticAssetResponse(response, missingBuildAsset);
45
46
  }
46
- if (result === null || result === void 0) return notFoundResponse();
47
+ if (result === null || result === void 0) return missingBuildAsset ? notFoundStaticAssetResponse() : notFoundResponse();
47
48
  return new Response(String(result), { status: 200 });
48
49
  }
49
50
  //#endregion
@@ -53,7 +53,13 @@ function createRscOnErrorHandler(options) {
53
53
  }
54
54
  if (options.requestInfo && options.errorContext && error) options.reportRequestError(error instanceof Error ? error : new Error(getThrownValueMessage(error)), options.requestInfo, options.errorContext);
55
55
  if (hasDigest(error)) return String(error.digest);
56
- if (nodeEnv === "production" && error) return errorDigest(getThrownValueMessage(error) + getThrownValueStack(error));
56
+ if (error) {
57
+ const digest = errorDigest(getThrownValueMessage(error) + getThrownValueStack(error));
58
+ if (error instanceof Error) try {
59
+ Object.assign(error, { digest });
60
+ } catch {}
61
+ return digest;
62
+ }
57
63
  };
58
64
  }
59
65
  //#endregion
@@ -208,7 +208,10 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
208
208
  if (isImageOptimizationPath(cleanPathname)) {
209
209
  const imageRedirect = resolveDevImageRedirect(url, [...options.imageConfig?.deviceSizes ?? DEFAULT_DEVICE_SIZES, ...options.imageConfig?.imageSizes ?? DEFAULT_IMAGE_SIZES], options.imageConfig?.qualities, { isDev: options.isDev });
210
210
  if (!imageRedirect) return new Response("Invalid image optimization parameters", { status: 400 });
211
- return Response.redirect(new URL(imageRedirect, url.origin).href, 302);
211
+ return new Response(null, {
212
+ status: 302,
213
+ headers: { Location: imageRedirect }
214
+ });
212
215
  }
213
216
  if (options.handleMetadataRouteRequest) {
214
217
  const metadataRouteResponse = await options.handleMetadataRouteRequest(cleanPathname);
@@ -31,6 +31,8 @@ type AppRscInterceptForMatching = {
31
31
  sourceMatchPattern?: string;
32
32
  sourcePageSegments?: readonly string[];
33
33
  interceptLayouts: readonly unknown[];
34
+ interceptLayoutSegments?: readonly (readonly string[])[];
35
+ interceptBranchSegments?: readonly string[];
34
36
  __loadInterceptLayouts?: readonly (() => Promise<unknown>)[] | null;
35
37
  page: unknown;
36
38
  __pageLoader?: (() => Promise<unknown>) | null;
@@ -46,6 +48,8 @@ type AppRscSiblingInterceptForMatching = {
46
48
  sourcePageSegments?: readonly string[];
47
49
  slotId: string | null;
48
50
  interceptLayouts: readonly unknown[];
51
+ interceptLayoutSegments?: readonly (readonly string[])[];
52
+ interceptBranchSegments?: readonly string[];
49
53
  __loadInterceptLayouts?: readonly (() => Promise<unknown>)[] | null;
50
54
  page: unknown;
51
55
  __pageLoader?: (() => Promise<unknown>) | null;
@@ -59,6 +63,7 @@ type AppRscRouteForMatching = {
59
63
  };
60
64
  type AppRscInterceptMatch = AppRscInterceptLookupEntry & {
61
65
  matchedParams: AppRscRouteParams;
66
+ sourceMatchedParams: AppRscRouteParams;
62
67
  };
63
68
  type AppRscInterceptLoadState = {
64
69
  page: unknown;
@@ -74,6 +79,8 @@ type AppRscInterceptLookupEntry = {
74
79
  sourceMatchPatternParts: string[] | null;
75
80
  sourcePageSegments: readonly string[] | null;
76
81
  interceptLayouts: readonly unknown[];
82
+ interceptLayoutSegments?: readonly (readonly string[])[];
83
+ interceptBranchSegments?: readonly string[];
77
84
  __loadInterceptLayouts?: readonly (() => Promise<unknown>)[] | null;
78
85
  page: unknown;
79
86
  __pageLoader?: (() => Promise<unknown>) | null;
@@ -36,12 +36,13 @@ function createAppRscRouteMatcher(routes) {
36
36
  const sourceRoute = routes[concreteSourceRouteIndex];
37
37
  const matchedSourceParams = matchedSourceRoute && entry.sourceMatchPatternParts !== null ? matchedSourceRoute.params : sourceRoute ? matchAppRscRoutePattern(sourceParts, sourceRoute.patternParts) : null;
38
38
  if (matchedSourceParams === null && entry.sourceMatchPatternParts === null) continue;
39
- const sourceParams = matchedSourceParams ?? createRouteParams();
39
+ const sourceParams = matchedSourceParams && entry.sourceMatchPatternParts !== null ? pickPatternParams(matchedSourceParams, entry.sourceMatchPatternParts) : matchedSourceParams ?? createRouteParams();
40
40
  return {
41
41
  ...entry,
42
42
  page: entry.__loadState.page,
43
43
  sourceRouteIndex: concreteSourceRouteIndex,
44
- matchedParams: mergeMatchedParams(sourceParams, params)
44
+ matchedParams: mergeMatchedParams(sourceParams, params),
45
+ sourceMatchedParams: matchedSourceParams ?? createRouteParams()
45
46
  };
46
47
  }
47
48
  return null;
@@ -64,6 +65,24 @@ function matchInterceptSource(sourceParts, entry) {
64
65
  if (patternParts.length === 0) return true;
65
66
  return matchRoutePatternPrefix(sourceParts, patternParts);
66
67
  }
68
+ function interceptSegmentPrecedence(segment) {
69
+ if (!segment.startsWith(":")) return 0;
70
+ if (segment.endsWith("*")) return 3;
71
+ if (segment.endsWith("+")) return 2;
72
+ return 1;
73
+ }
74
+ function compareInterceptTargetPatterns(a, b) {
75
+ const sharedLength = Math.min(a.targetPatternParts.length, b.targetPatternParts.length);
76
+ for (let index = 0; index < sharedLength; index++) {
77
+ const aSegment = a.targetPatternParts[index];
78
+ const bSegment = b.targetPatternParts[index];
79
+ const precedence = interceptSegmentPrecedence(aSegment) - interceptSegmentPrecedence(bSegment);
80
+ if (precedence !== 0) return precedence;
81
+ if (aSegment !== bSegment) return aSegment.localeCompare(bSegment);
82
+ }
83
+ const lengthDifference = a.targetPatternParts.length - b.targetPatternParts.length;
84
+ return lengthDifference !== 0 ? lengthDifference : a.targetPattern.localeCompare(b.targetPattern);
85
+ }
67
86
  function createInterceptLookup(routes) {
68
87
  const patternToIndex = new Map(routes.map((r, i) => [r.pattern, i]));
69
88
  const interceptLookup = [];
@@ -85,6 +104,8 @@ function createInterceptLookup(routes) {
85
104
  sourceMatchPatternParts,
86
105
  sourcePageSegments: intercept.sourcePageSegments ?? null,
87
106
  interceptLayouts: intercept.interceptLayouts,
107
+ interceptLayoutSegments: intercept.interceptLayoutSegments,
108
+ interceptBranchSegments: intercept.interceptBranchSegments,
88
109
  __loadInterceptLayouts: intercept.__loadInterceptLayouts,
89
110
  page: intercept.page,
90
111
  __pageLoader: intercept.__pageLoader,
@@ -110,6 +131,8 @@ function createInterceptLookup(routes) {
110
131
  sourceMatchPatternParts,
111
132
  sourcePageSegments: intercept.sourcePageSegments ?? null,
112
133
  interceptLayouts: intercept.interceptLayouts,
134
+ interceptLayoutSegments: intercept.interceptLayoutSegments,
135
+ interceptBranchSegments: intercept.interceptBranchSegments,
113
136
  __loadInterceptLayouts: intercept.__loadInterceptLayouts,
114
137
  page: intercept.page,
115
138
  __pageLoader: intercept.__pageLoader,
@@ -122,7 +145,7 @@ function createInterceptLookup(routes) {
122
145
  });
123
146
  }
124
147
  }
125
- return interceptLookup;
148
+ return interceptLookup.sort(compareInterceptTargetPatterns);
126
149
  }
127
150
  function matchAppRscRoutePattern(urlParts, patternParts) {
128
151
  return matchRoutePattern(urlParts, patternParts);
@@ -130,5 +153,15 @@ function matchAppRscRoutePattern(urlParts, patternParts) {
130
153
  function mergeMatchedParams(sourceParams, targetParams) {
131
154
  return Object.assign(createRouteParams(), sourceParams, targetParams);
132
155
  }
156
+ function pickPatternParams(params, patternParts) {
157
+ const picked = createRouteParams();
158
+ for (const patternPart of patternParts) {
159
+ if (!patternPart.startsWith(":")) continue;
160
+ const paramName = patternPart.endsWith("+") || patternPart.endsWith("*") ? patternPart.slice(1, -1) : patternPart.slice(1);
161
+ const value = params[paramName];
162
+ if (value !== void 0) picked[paramName] = value;
163
+ }
164
+ return picked;
165
+ }
133
166
  //#endregion
134
167
  export { SIBLING_PAGE_INTERCEPT_SLOT_KEY, createAppRscRouteMatcher, matchAppRscRoutePattern };
@@ -22,6 +22,7 @@ type ResolveAppPageSegmentConfigOptions = {
22
22
  layouts?: readonly (AppRouteSegmentConfigModule | null | undefined)[];
23
23
  page?: AppRouteSegmentConfigModule | null;
24
24
  parallelPages?: readonly (AppRouteSegmentConfigModule | null | undefined)[];
25
+ parallelSegments?: readonly (AppRouteSegmentConfigModule | null | undefined)[];
25
26
  };
26
27
  /**
27
28
  * Resolve the route segment config that applies to an App page route.
@@ -59,9 +59,13 @@ function resolveAppPageSegmentConfig(options) {
59
59
  let hasOnlyCache = false;
60
60
  let hasOnlyNoStore = false;
61
61
  let hasParentDefaultNoStore = false;
62
+ let hasForceDynamic = false;
62
63
  for (const segment of segments) {
63
64
  if (!segment) continue;
64
- if (isRouteSegmentDynamic(segment.dynamic)) config.dynamicConfig = segment.dynamic;
65
+ if (isRouteSegmentDynamic(segment.dynamic)) {
66
+ if (segment.dynamic === "force-dynamic") hasForceDynamic = true;
67
+ config.dynamicConfig = hasForceDynamic ? "force-dynamic" : segment.dynamic;
68
+ }
65
69
  if (isRouteSegmentRuntime(segment.runtime)) config.runtime = segment.runtime;
66
70
  if (segment.dynamicParams === false) config.dynamicParamsConfig = false;
67
71
  else if (segment.dynamicParams === true && config.dynamicParamsConfig !== false) config.dynamicParamsConfig = true;
@@ -72,7 +76,7 @@ function resolveAppPageSegmentConfig(options) {
72
76
  if (fetchCache === "force-no-store") hasForceNoStore = true;
73
77
  if (fetchCache === "only-cache") hasOnlyCache = true;
74
78
  if (fetchCache === "only-no-store") hasOnlyNoStore = true;
75
- if ((hasForceCache || hasOnlyCache) && (hasForceNoStore || hasOnlyNoStore)) throw new Error(describeFetchCacheConflict(fetchCache));
79
+ if (hasForceCache && hasForceNoStore || !hasForceCache && !hasForceNoStore && hasOnlyCache && hasOnlyNoStore) throw new Error(describeFetchCacheConflict(fetchCache));
76
80
  if (fetchCache === "default-no-store") hasParentDefaultNoStore = true;
77
81
  if (hasForceCache) config.fetchCache = "force-cache";
78
82
  else if (hasForceNoStore) config.fetchCache = "force-no-store";
@@ -82,6 +86,32 @@ function resolveAppPageSegmentConfig(options) {
82
86
  }
83
87
  config.revalidateSeconds = resolveRevalidateSeconds(config.revalidateSeconds, segment.revalidate);
84
88
  }
89
+ for (const segment of options.parallelSegments ?? []) {
90
+ if (!segment) continue;
91
+ if (segment.dynamic === "force-dynamic") {
92
+ hasForceDynamic = true;
93
+ config.dynamicConfig = "force-dynamic";
94
+ } else if (config.dynamicConfig === void 0 && isRouteSegmentDynamic(segment.dynamic)) config.dynamicConfig = segment.dynamic;
95
+ if (segment.dynamicParams === false) config.dynamicParamsConfig = false;
96
+ else if (segment.dynamicParams === true && config.dynamicParamsConfig === void 0) config.dynamicParamsConfig = true;
97
+ if (config.runtime === void 0 && isRouteSegmentRuntime(segment.runtime)) config.runtime = segment.runtime;
98
+ if (isRouteSegmentFetchCache(segment.fetchCache)) {
99
+ const fetchCache = segment.fetchCache;
100
+ if (hasParentDefaultNoStore && (fetchCache === "auto" || isCacheFetchCacheMode(fetchCache))) throw new Error(describeFetchCacheConflict(fetchCache));
101
+ if (fetchCache === "force-cache") hasForceCache = true;
102
+ if (fetchCache === "force-no-store") hasForceNoStore = true;
103
+ if (fetchCache === "only-cache") hasOnlyCache = true;
104
+ if (fetchCache === "only-no-store") hasOnlyNoStore = true;
105
+ if (hasForceCache && hasForceNoStore || !hasForceCache && !hasForceNoStore && hasOnlyCache && hasOnlyNoStore) throw new Error(describeFetchCacheConflict(fetchCache));
106
+ if (fetchCache === "default-no-store") hasParentDefaultNoStore = true;
107
+ if (hasForceCache) config.fetchCache = "force-cache";
108
+ else if (hasForceNoStore) config.fetchCache = "force-no-store";
109
+ else if (hasOnlyCache) config.fetchCache = "only-cache";
110
+ else if (hasOnlyNoStore) config.fetchCache = "only-no-store";
111
+ else if (config.fetchCache === void 0) config.fetchCache = fetchCache;
112
+ }
113
+ config.revalidateSeconds = resolveRevalidateSeconds(config.revalidateSeconds, segment.revalidate);
114
+ }
85
115
  for (const segment of [options.page, ...options.parallelPages ?? []]) {
86
116
  if (!segment) continue;
87
117
  config.dynamicStaleTimeSeconds = resolveDynamicStaleTimeSeconds(config.dynamicStaleTimeSeconds, segment.unstable_dynamicStaleTime);
@@ -76,6 +76,7 @@ type AppServerActionMatch<TRoute extends AppServerActionRoute> = {
76
76
  };
77
77
  type AppServerActionIntercept<TPage = unknown> = {
78
78
  matchedParams: AppPageParams;
79
+ sourceMatchedParams?: AppPageParams;
79
80
  page: TPage;
80
81
  slotId?: string | null;
81
82
  slotKey: string;
@@ -91,6 +92,8 @@ type BuildServerActionPageElementOptions<TRoute extends AppServerActionRoute, TI
91
92
  route: TRoute;
92
93
  searchParams: URLSearchParams;
93
94
  renderMode: AppRscRenderMode;
95
+ observeMetadataSearchParamsAccess?: boolean;
96
+ observePageSearchParamsAccess?: boolean;
94
97
  };
95
98
  type AppServerActionRscModel<TElement> = {
96
99
  /**
@@ -148,6 +151,7 @@ type HandleServerActionRscRequestOptions<TElement, TRoute extends AppServerActio
148
151
  createRscOnErrorHandler: (request: Request, pathname: string, pattern: string) => (error: unknown) => unknown;
149
152
  createTemporaryReferenceSet: () => TTemporaryReferences;
150
153
  decodeReply: (body: string | FormData, options: DecodeServerActionReplyOptions<TTemporaryReferences>) => Promise<unknown[]> | unknown[];
154
+ draftModeSecret: string;
151
155
  /**
152
156
  * Hydrate a route's lazy page/route-handler modules before reading
153
157
  * `route.page` / `route.routeHandler` on action redirect targets and
@@ -5,7 +5,7 @@ import { isExternalUrl } from "../config/config-matchers.js";
5
5
  import { internalServerErrorResponse, payloadTooLargeResponse } from "./http-error-responses.js";
6
6
  import { validateCsrfOrigin, validateServerActionPayload } from "./request-pipeline.js";
7
7
  import { APP_RSC_RENDER_MODE_ACTION_RERENDER_PRESERVE_UI } from "./app-rsc-render-mode.js";
8
- import { headersContextFromRequest, setHeadersContext } from "../shims/headers.js";
8
+ import { headersContextFromRequest, isDraftModeRequest, setHeadersContext } from "../shims/headers.js";
9
9
  import { getAndClearActionRevalidationKind } from "../shims/cache-request-state.js";
10
10
  import { setCurrentFetchCacheMode, setCurrentFetchSoftTags, setCurrentForceDynamicFetchDefault } from "../shims/fetch-cache.js";
11
11
  import { readStreamAsTextWithLimit } from "../utils/text-stream.js";
@@ -20,7 +20,18 @@ import { buildAppPageTags } from "./implicit-tags.js";
20
20
  import { resolveAppPageNavigationParams } from "./app-page-element-builder.js";
21
21
  import { resolveAppPageActionRerenderTarget } from "./app-page-request.js";
22
22
  import { getSetCookieName } from "./cookie-utils.js";
23
+ import { createStaticGenerationHeadersContext } from "./app-static-generation.js";
23
24
  //#region src/server/app-server-action-execution.ts
25
+ function prepareActionPageRerenderContext(options) {
26
+ if (options.dynamicConfig === "force-static" || options.dynamicConfig === "error") setHeadersContext(createStaticGenerationHeadersContext({
27
+ draftModeEnabled: isDraftModeRequest(options.request, options.draftModeSecret),
28
+ draftModeSecret: options.draftModeSecret,
29
+ dynamicConfig: options.dynamicConfig,
30
+ routeKind: "page",
31
+ routePattern: options.routePattern
32
+ }));
33
+ return options.dynamicConfig === "force-static" ? new URLSearchParams() : options.searchParams;
34
+ }
24
35
  /**
25
36
  * Matches Next.js' server action argument cap to prevent stack overflow in
26
37
  * Function.prototype.apply when decoding hostile action payloads.
@@ -623,14 +634,22 @@ async function handleServerActionRscRequest(options) {
623
634
  url: redirectTarget
624
635
  });
625
636
  setHeadersContext(headersContextFromRequest(redirectRenderRequest));
637
+ const redirectDynamicConfig = options.resolveRouteDynamicConfig?.(targetMatch.route);
638
+ const redirectSearchParams = prepareActionPageRerenderContext({
639
+ draftModeSecret: options.draftModeSecret,
640
+ dynamicConfig: redirectDynamicConfig,
641
+ request: redirectRenderRequest,
642
+ routePattern: targetMatch.route.pattern,
643
+ searchParams: redirectTarget.searchParams
644
+ });
626
645
  const redirectNavigationParams = resolveAppPageNavigationParams(targetMatch.route, targetMatch.params, targetPathname, null);
627
646
  options.setNavigationContext({
628
647
  pathname: targetPathname,
629
- searchParams: redirectTarget.searchParams,
648
+ searchParams: redirectSearchParams,
630
649
  params: redirectNavigationParams
631
650
  });
632
651
  setCurrentFetchCacheMode(options.resolveRouteFetchCacheMode?.(targetMatch.route) ?? null);
633
- setCurrentForceDynamicFetchDefault(options.resolveRouteDynamicConfig?.(targetMatch.route) === "force-dynamic");
652
+ setCurrentForceDynamicFetchDefault(redirectDynamicConfig === "force-dynamic");
634
653
  setCurrentFetchSoftTags(buildServerActionPageTags(targetMatch.route, targetPathname));
635
654
  const element = options.buildPageElement({
636
655
  cleanPathname: targetPathname,
@@ -640,8 +659,10 @@ async function handleServerActionRscRequest(options) {
640
659
  params: targetMatch.params,
641
660
  request: redirectRenderRequest,
642
661
  route: targetMatch.route,
643
- searchParams: redirectTarget.searchParams,
644
- renderMode: APP_RSC_RENDER_MODE_ACTION_RERENDER_PRESERVE_UI
662
+ searchParams: redirectSearchParams,
663
+ renderMode: APP_RSC_RENDER_MODE_ACTION_RERENDER_PRESERVE_UI,
664
+ observeMetadataSearchParamsAccess: redirectDynamicConfig !== "force-static",
665
+ observePageSearchParamsAccess: redirectDynamicConfig !== "force-static"
645
666
  });
646
667
  const onRenderError = options.createRscOnErrorHandler(redirectRenderRequest, targetPathname, targetMatch.route.pattern);
647
668
  return createServerActionRscResponse(await options.renderToReadableStream({
@@ -702,14 +723,22 @@ async function handleServerActionRscRequest(options) {
702
723
  toInterceptOpts: options.toInterceptOpts
703
724
  });
704
725
  const resolvedActionNavigationParams = resolveAppPageNavigationParams(actionRerenderTarget.route, actionRerenderTarget.navigationParams, options.cleanPathname, actionRerenderTarget.interceptOpts);
726
+ await options.ensureRouteLoaded?.(actionRerenderTarget.route);
727
+ const actionRerenderDynamicConfig = options.resolveRouteDynamicConfig?.(actionRerenderTarget.route);
728
+ const actionRerenderSearchParams = prepareActionPageRerenderContext({
729
+ draftModeSecret: options.draftModeSecret,
730
+ dynamicConfig: actionRerenderDynamicConfig,
731
+ request: options.request,
732
+ routePattern: actionRerenderTarget.route.pattern,
733
+ searchParams: options.searchParams
734
+ });
705
735
  options.setNavigationContext({
706
736
  pathname: options.cleanPathname,
707
- searchParams: options.searchParams,
737
+ searchParams: actionRerenderSearchParams,
708
738
  params: resolvedActionNavigationParams
709
739
  });
710
- await options.ensureRouteLoaded?.(actionRerenderTarget.route);
711
740
  setCurrentFetchCacheMode(options.resolveRouteFetchCacheMode?.(actionRerenderTarget.route) ?? null);
712
- setCurrentForceDynamicFetchDefault(options.resolveRouteDynamicConfig?.(actionRerenderTarget.route) === "force-dynamic");
741
+ setCurrentForceDynamicFetchDefault(actionRerenderDynamicConfig === "force-dynamic");
713
742
  setCurrentFetchSoftTags(buildServerActionPageTags(actionRerenderTarget.route, options.cleanPathname));
714
743
  element = options.buildPageElement({
715
744
  cleanPathname: options.cleanPathname,
@@ -719,8 +748,10 @@ async function handleServerActionRscRequest(options) {
719
748
  params: actionRerenderTarget.params,
720
749
  request: options.request,
721
750
  route: actionRerenderTarget.route,
722
- searchParams: options.searchParams,
723
- renderMode: APP_RSC_RENDER_MODE_ACTION_RERENDER_PRESERVE_UI
751
+ searchParams: actionRerenderSearchParams,
752
+ renderMode: APP_RSC_RENDER_MODE_ACTION_RERENDER_PRESERVE_UI,
753
+ observeMetadataSearchParamsAccess: actionRerenderDynamicConfig !== "force-static",
754
+ observePageSearchParamsAccess: actionRerenderDynamicConfig !== "force-static"
724
755
  });
725
756
  errorPattern = actionRerenderTarget.route.pattern;
726
757
  } else {
@@ -3,6 +3,7 @@ import { HeadersContext } from "../shims/headers.js";
3
3
  //#region src/server/app-static-generation.d.ts
4
4
  type AppStaticGenerationRouteKind = "page" | "route";
5
5
  type CreateStaticGenerationHeadersContextOptions = {
6
+ draftModeEnabled?: boolean;
6
7
  draftModeSecret?: string;
7
8
  dynamicConfig?: string;
8
9
  routeKind: AppStaticGenerationRouteKind;
@@ -9,6 +9,7 @@ function createStaticGenerationHeadersContext(options) {
9
9
  const context = {
10
10
  headers: new Headers(),
11
11
  cookies: /* @__PURE__ */ new Map(),
12
+ draftModeEnabled: options.draftModeEnabled,
12
13
  draftModeSecret: options.draftModeSecret
13
14
  };
14
15
  if (options.dynamicConfig === "force-static") context.forceStatic = true;
@@ -97,6 +97,8 @@ declare const NEXT_ROUTER_STATE_TREE_HEADER = "Next-Router-State-Tree";
97
97
  declare const NEXT_ROUTER_PREFETCH_HEADER = "Next-Router-Prefetch";
98
98
  declare const NEXT_ROUTER_SEGMENT_PREFETCH_HEADER = "Next-Router-Segment-Prefetch";
99
99
  declare const NEXT_URL_HEADER = "Next-Url";
100
+ declare const NEXT_REQUEST_ID_HEADER = "x-nextjs-request-id";
101
+ declare const NEXT_HTML_REQUEST_ID_HEADER = "x-nextjs-html-request-id";
100
102
  /** Lowercase flight header variants used in middleware forwarding. */
101
103
  declare const FLIGHT_HEADERS: readonly string[];
102
104
  /**
@@ -111,4 +113,4 @@ declare const INTERNAL_HEADERS: string[];
111
113
  /** Vinext-only internal headers stripped alongside Next.js protocol internals. */
112
114
  declare const VINEXT_INTERNAL_HEADERS: string[];
113
115
  //#endregion
114
- export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_OVERRIDE_HEADERS, MIDDLEWARE_REQUEST_HEADER_PREFIX, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_PAGES_STATIC_PATHS_PATH, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_STATIC_PARAMS_PATH, VINEXT_REVALIDATE_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_STATIC_FILE_HEADER, VINEXT_TIMING_HEADER };
116
+ export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_OVERRIDE_HEADERS, MIDDLEWARE_REQUEST_HEADER_PREFIX, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_HTML_REQUEST_ID_HEADER, NEXT_REQUEST_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_PAGES_STATIC_PATHS_PATH, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_STATIC_PARAMS_PATH, VINEXT_REVALIDATE_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_STATIC_FILE_HEADER, VINEXT_TIMING_HEADER };
@@ -101,6 +101,8 @@ const NEXT_ROUTER_STATE_TREE_HEADER = "Next-Router-State-Tree";
101
101
  const NEXT_ROUTER_PREFETCH_HEADER = "Next-Router-Prefetch";
102
102
  const NEXT_ROUTER_SEGMENT_PREFETCH_HEADER = "Next-Router-Segment-Prefetch";
103
103
  const NEXT_URL_HEADER = "Next-Url";
104
+ const NEXT_REQUEST_ID_HEADER = "x-nextjs-request-id";
105
+ const NEXT_HTML_REQUEST_ID_HEADER = "x-nextjs-html-request-id";
104
106
  /** Lowercase flight header variants used in middleware forwarding. */
105
107
  const FLIGHT_HEADERS = [
106
108
  "rsc",
@@ -133,4 +135,4 @@ const INTERNAL_HEADERS = [
133
135
  /** Vinext-only internal headers stripped alongside Next.js protocol internals. */
134
136
  const VINEXT_INTERNAL_HEADERS = [VINEXT_PRERENDER_ROUTE_PARAMS_HEADER];
135
137
  //#endregion
136
- export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_OVERRIDE_HEADERS, MIDDLEWARE_REQUEST_HEADER_PREFIX, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_PAGES_STATIC_PATHS_PATH, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_STATIC_PARAMS_PATH, VINEXT_REVALIDATE_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_STATIC_FILE_HEADER, VINEXT_TIMING_HEADER };
138
+ export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_OVERRIDE_HEADERS, MIDDLEWARE_REQUEST_HEADER_PREFIX, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_HTML_REQUEST_ID_HEADER, NEXT_REQUEST_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_PAGES_STATIC_PATHS_PATH, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_STATIC_PARAMS_PATH, VINEXT_REVALIDATE_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_STATIC_FILE_HEADER, VINEXT_TIMING_HEADER };
@@ -819,13 +819,12 @@ async function startAppRouterServer(options) {
819
819
  return;
820
820
  }
821
821
  }
822
+ let missingBuildAsset = false;
822
823
  {
823
824
  const assetLookupPath = resolveAppRouterAssetPath(pathname, appAssetPathPrefix, appRouterAssetPrefix);
824
825
  if (assetLookupPath) {
825
826
  if (await tryServeStatic(req, res, clientDir, assetLookupPath, compress, staticCache)) return;
826
- res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
827
- res.end("Not Found");
828
- return;
827
+ missingBuildAsset = true;
829
828
  }
830
829
  }
831
830
  if (isImageOptimizationPath(pathname)) {
@@ -868,6 +867,12 @@ async function startAppRouterServer(options) {
868
867
  await sendWebResponse(notFoundResponse({ headers: toWebHeaders(staticResponseHeaders) }), req, res, compress);
869
868
  return;
870
869
  }
870
+ if (missingBuildAsset && response.status === 404) {
871
+ cancelResponseBody(response);
872
+ res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
873
+ res.end("Not Found");
874
+ return;
875
+ }
871
876
  await sendWebResponse(response, req, res, compress);
872
877
  } catch (e) {
873
878
  console.error("[vinext] Server error:", e);
@@ -1000,11 +1005,9 @@ async function startPagesRouterServer(options) {
1000
1005
  }
1001
1006
  const staticLookupPath = stripBasePath(pathname, basePath);
1002
1007
  const pagesAssetLookup = resolveAppRouterAssetPath(pathname, pagesAssetPathPrefix, assetPrefix);
1008
+ const missingBuildAsset = pagesAssetLookup !== null;
1003
1009
  if (pagesAssetLookup) {
1004
1010
  if (await tryServeStatic(req, res, clientDir, pagesAssetLookup, compress, staticCache)) return;
1005
- res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
1006
- res.end("Not Found");
1007
- return;
1008
1011
  }
1009
1012
  if (isImageOptimizationPath(pathname) || isImageOptimizationPath(staticLookupPath)) {
1010
1013
  const params = parseImageParams(new URL(rawUrl, "http://localhost"), allowedImageWidths, pagesImageConfig?.qualities);
@@ -1089,6 +1092,12 @@ async function startPagesRouterServer(options) {
1089
1092
  if (result.type === "handled") return;
1090
1093
  if (result.type === "response") {
1091
1094
  const { response } = result;
1095
+ if (missingBuildAsset && response.status === 404) {
1096
+ cancelResponseBody(response);
1097
+ res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
1098
+ res.end("Not Found");
1099
+ return;
1100
+ }
1092
1101
  if (isVinextStreamedHtmlResponse(response) || !response.body || result.defaultContentType === void 0) {
1093
1102
  await sendWebResponse(response, req, res, compress);
1094
1103
  return;
@@ -1,7 +1,8 @@
1
1
  //#region src/server/worker-utils.d.ts
2
+ declare function finalizeMissingStaticAssetResponse(response: Response, missingBuildAsset: boolean): Response;
2
3
  declare function mergeHeaders(response: Response, extraHeaders: Record<string, string | string[]>, statusOverride?: number): Response;
3
4
  declare function resolveStaticAssetSignal(signalResponse: Response, options: {
4
5
  fetchAsset(path: string): Promise<Response>;
5
6
  }): Promise<Response | null>;
6
7
  //#endregion
7
- export { mergeHeaders, resolveStaticAssetSignal };
8
+ export { finalizeMissingStaticAssetResponse, mergeHeaders, resolveStaticAssetSignal };
@@ -1,4 +1,5 @@
1
1
  import { VINEXT_STATIC_FILE_HEADER } from "./headers.js";
2
+ import { notFoundStaticAssetResponse } from "./http-error-responses.js";
2
3
  //#region src/server/worker-utils.ts
3
4
  /**
4
5
  * Shared utilities for Cloudflare Worker entries.
@@ -29,6 +30,11 @@ function cancelResponseBody(response) {
29
30
  if (!body || body.locked) return;
30
31
  body.cancel().catch(() => {});
31
32
  }
33
+ function finalizeMissingStaticAssetResponse(response, missingBuildAsset) {
34
+ if (!missingBuildAsset || response.status !== 404) return response;
35
+ cancelResponseBody(response);
36
+ return notFoundStaticAssetResponse();
37
+ }
32
38
  function buildHeaderRecord(response, omitNames = []) {
33
39
  const omitted = new Set(omitNames.map((name) => name.toLowerCase()));
34
40
  const headers = {};
@@ -96,4 +102,4 @@ async function resolveStaticAssetSignal(signalResponse, options) {
96
102
  return mergeHeaders(assetResponse, extraHeaders, assetResponse.ok && signalResponse.status !== 200 ? signalResponse.status : void 0);
97
103
  }
98
104
  //#endregion
99
- export { mergeHeaders, resolveStaticAssetSignal };
105
+ export { finalizeMissingStaticAssetResponse, mergeHeaders, resolveStaticAssetSignal };