vinext 1.4.0 → 1.4.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.
Files changed (156) hide show
  1. package/README.md +21 -21
  2. package/dist/build/action-owner-manifest.js +1 -0
  3. package/dist/build/prerender.js +3 -2
  4. package/dist/check.js +4 -7
  5. package/dist/client/dev-error-overlay-store.d.ts +1 -0
  6. package/dist/client/dev-error-overlay.d.ts +2 -0
  7. package/dist/client/dev-error-overlay.js +19 -3
  8. package/dist/config/config-matchers.js +4 -2
  9. package/dist/config/destination-params.d.ts +2 -1
  10. package/dist/config/destination-params.js +55 -2
  11. package/dist/config/next-config.d.ts +22 -1
  12. package/dist/config/next-config.js +49 -4
  13. package/dist/entries/app-rsc-entry.js +41 -19
  14. package/dist/entries/app-rsc-manifest.js +4 -0
  15. package/dist/entries/pages-server-entry.js +5 -1
  16. package/dist/index.js +44 -32
  17. package/dist/init-cloudflare.js +18 -2
  18. package/dist/plugins/css-target.d.ts +18 -0
  19. package/dist/plugins/css-target.js +93 -0
  20. package/dist/plugins/strip-server-exports.js +2 -1
  21. package/dist/routing/app-route-graph.d.ts +10 -0
  22. package/dist/routing/app-route-graph.js +15 -1
  23. package/dist/server/api-handler.d.ts +2 -0
  24. package/dist/server/api-handler.js +5 -6
  25. package/dist/server/app-action-forwarding.js +3 -1
  26. package/dist/server/app-browser-entry.js +28 -7
  27. package/dist/server/app-browser-rsc-redirect.js +3 -3
  28. package/dist/server/app-browser-server-action-client.js +2 -2
  29. package/dist/server/app-browser-visible-commit.js +2 -2
  30. package/dist/server/app-elements.d.ts +3 -0
  31. package/dist/server/app-elements.js +14 -1
  32. package/dist/server/app-fallback-renderer.js +2 -2
  33. package/dist/server/app-optimistic-routing.d.ts +12 -1
  34. package/dist/server/app-optimistic-routing.js +40 -19
  35. package/dist/server/app-page-boundary-render.js +7 -12
  36. package/dist/server/app-page-cache-render.js +1 -1
  37. package/dist/server/app-page-dispatch.d.ts +10 -2
  38. package/dist/server/app-page-dispatch.js +73 -31
  39. package/dist/server/app-page-element-builder.d.ts +9 -20
  40. package/dist/server/app-page-element-builder.js +40 -52
  41. package/dist/server/app-page-execution.js +9 -6
  42. package/dist/server/app-page-intercept-segment-config.d.ts +56 -0
  43. package/dist/server/app-page-intercept-segment-config.js +90 -0
  44. package/dist/server/app-page-navigation-params.d.ts +23 -0
  45. package/dist/server/app-page-navigation-params.js +49 -0
  46. package/dist/server/app-page-render.d.ts +5 -3
  47. package/dist/server/app-page-render.js +31 -17
  48. package/dist/server/app-page-request.d.ts +24 -2
  49. package/dist/server/app-page-request.js +10 -10
  50. package/dist/server/app-page-response.js +1 -1
  51. package/dist/server/app-page-route-wiring.d.ts +10 -0
  52. package/dist/server/app-page-route-wiring.js +34 -13
  53. package/dist/server/app-page-segment-plan.js +9 -1
  54. package/dist/server/app-page-segment-state.d.ts +9 -0
  55. package/dist/server/app-page-segment-state.js +54 -1
  56. package/dist/server/app-page-stream.d.ts +10 -8
  57. package/dist/server/app-page-stream.js +2 -1
  58. package/dist/server/app-request-stage-independent-entry.js +1 -1
  59. package/dist/server/app-response-stage-entry.js +1 -0
  60. package/dist/server/app-route-handler-execution.js +3 -2
  61. package/dist/server/app-route-handler-runtime.d.ts +1 -1
  62. package/dist/server/app-route-handler-runtime.js +11 -7
  63. package/dist/server/app-route-module-loader.d.ts +2 -0
  64. package/dist/server/app-route-module-loader.js +1 -0
  65. package/dist/server/app-router-entry.js +1 -1
  66. package/dist/server/app-rsc-cache-busting.d.ts +10 -10
  67. package/dist/server/app-rsc-cache-busting.js +27 -68
  68. package/dist/server/app-rsc-error-handler.js +1 -1
  69. package/dist/server/app-rsc-errors.js +4 -1
  70. package/dist/server/app-rsc-handler.d.ts +10 -1
  71. package/dist/server/app-rsc-handler.js +126 -103
  72. package/dist/server/app-rsc-request-normalization.d.ts +10 -12
  73. package/dist/server/app-rsc-request-normalization.js +11 -14
  74. package/dist/server/app-rsc-response-finalizer.d.ts +3 -0
  75. package/dist/server/app-rsc-response-finalizer.js +11 -1
  76. package/dist/server/app-rsc-response-stage.js +4 -12
  77. package/dist/server/app-rsc-route-matching.d.ts +14 -0
  78. package/dist/server/app-rsc-route-matching.js +42 -18
  79. package/dist/server/app-server-action-execution.js +24 -14
  80. package/dist/server/app-ssr-entry.d.ts +6 -0
  81. package/dist/server/app-ssr-entry.js +13 -8
  82. package/dist/server/app-worker-stages.d.ts +12 -3
  83. package/dist/server/app-worker-stages.js +37 -4
  84. package/dist/server/bfcache-identity.d.ts +7 -0
  85. package/dist/server/bfcache-identity.js +10 -1
  86. package/dist/server/cache-control.d.ts +2 -0
  87. package/dist/server/cache-control.js +3 -1
  88. package/dist/server/cacheability-manifest.js +1 -2
  89. package/dist/server/dev-origin-check.d.ts +2 -1
  90. package/dist/server/dev-origin-check.js +2 -1
  91. package/dist/server/dev-server.js +2 -1
  92. package/dist/server/image-local-patterns.d.ts +21 -0
  93. package/dist/server/image-local-patterns.js +59 -0
  94. package/dist/server/image-optimization.d.ts +28 -22
  95. package/dist/server/image-optimization.js +17 -2
  96. package/dist/server/isr-cache.js +4 -1
  97. package/dist/server/metadata-route-response.js +25 -7
  98. package/dist/server/middleware-matcher.js +19 -6
  99. package/dist/server/middleware-runtime.js +6 -2
  100. package/dist/server/multi-stage.d.ts +7 -3
  101. package/dist/server/nitro-public-files-plugin.js +20 -8
  102. package/dist/server/pages-data-route.d.ts +5 -1
  103. package/dist/server/pages-data-route.js +14 -6
  104. package/dist/server/pages-node-compat.d.ts +1 -1
  105. package/dist/server/pages-node-compat.js +2 -0
  106. package/dist/server/pages-page-data.js +2 -1
  107. package/dist/server/pages-page-handler.js +17 -6
  108. package/dist/server/pages-page-response.d.ts +9 -0
  109. package/dist/server/pages-page-response.js +18 -4
  110. package/dist/server/pages-request-pipeline.js +6 -5
  111. package/dist/server/pages-request-stage-entry.js +25 -4
  112. package/dist/server/pages-router-entry.js +3 -2
  113. package/dist/server/prod-server.d.ts +2 -2
  114. package/dist/server/prod-server.js +59 -17
  115. package/dist/server/request-error-log.d.ts +9 -0
  116. package/dist/server/request-error-log.js +13 -0
  117. package/dist/server/request-pipeline.d.ts +14 -4
  118. package/dist/server/request-pipeline.js +33 -15
  119. package/dist/server/response-stage-cacheability.d.ts +7 -0
  120. package/dist/server/response-stage-cacheability.js +6 -5
  121. package/dist/server/rsc-completion-metadata.js +3 -0
  122. package/dist/server/static-file-cache.d.ts +9 -0
  123. package/dist/server/static-file-cache.js +13 -2
  124. package/dist/server/worker-image-optimization.d.ts +2 -2
  125. package/dist/server/worker-image-optimization.js +18 -3
  126. package/dist/server/worker-utils.d.ts +8 -0
  127. package/dist/server/worker-utils.js +19 -2
  128. package/dist/shims/cache-request-state.d.ts +6 -6
  129. package/dist/shims/cache-request-state.js +6 -36
  130. package/dist/shims/cache-runtime.d.ts +3 -1
  131. package/dist/shims/cache-runtime.js +77 -47
  132. package/dist/shims/cache.d.ts +10 -5
  133. package/dist/shims/cache.js +35 -22
  134. package/dist/shims/client-hook-error.d.ts +2 -0
  135. package/dist/shims/client-hook-error.js +16 -1
  136. package/dist/shims/fetch-cache.js +6 -2
  137. package/dist/shims/headers.d.ts +2 -1
  138. package/dist/shims/headers.js +5 -3
  139. package/dist/shims/image-props.d.ts +6 -0
  140. package/dist/shims/image-props.js +39 -3
  141. package/dist/shims/image.js +3 -3
  142. package/dist/shims/internal/cache-context-accessor.d.ts +1 -1
  143. package/dist/shims/link.js +5 -10
  144. package/dist/shims/navigation.js +2 -3
  145. package/dist/shims/request-state-types.d.ts +2 -1
  146. package/dist/shims/slot.d.ts +9 -2
  147. package/dist/shims/slot.js +76 -11
  148. package/dist/shims/unified-request-context.d.ts +6 -0
  149. package/dist/shims/unified-request-context.js +1 -0
  150. package/dist/utils/base-path.d.ts +7 -0
  151. package/dist/utils/base-path.js +11 -1
  152. package/dist/utils/cache-life-profiles.d.ts +33 -0
  153. package/dist/utils/cache-life-profiles.js +111 -0
  154. package/dist/utils/middleware-request-headers.d.ts +14 -3
  155. package/dist/utils/middleware-request-headers.js +54 -7
  156. package/package.json +1 -1
@@ -8,7 +8,7 @@ import { getRepeatedSlashRedirect, isOpenRedirectShaped, sendRepeatedSlashRedire
8
8
  import { readStaticFileSignal } from "./static-file-signal.js";
9
9
  import { canonicalizeRequestPathname, filterInternalHeaders } from "./request-pipeline.js";
10
10
  import { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix, isAssetPrefixBuildAsset } from "../utils/asset-prefix.js";
11
- import { StaticFileCache, contentTypeForPath, etagFromFilenameHash } from "./static-file-cache.js";
11
+ import { StaticFileCache, contentTypeForPath, etagFromFilenameHash, staticFileCacheControl } from "./static-file-cache.js";
12
12
  import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
13
13
  import { resolveRequestHost, resolveRequestProtocol, resolveTrustedForwardedHost, trustProxy, trustedHosts } from "./proxy-trust.js";
14
14
  import { normalizePath } from "./normalize-path.js";
@@ -19,7 +19,7 @@ import { parseHttpDate } from "./http-date.js";
19
19
  import { evaluateStaticPreconditions } from "./http-conditional.js";
20
20
  import { setPagesClientAssets } from "./pages-client-assets.js";
21
21
  import { signalFromNodeResponse } from "./node-response-signal.js";
22
- import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, IMAGE_RESPONSE_CACHE_STATE, INVALID_IMAGE_SOURCE_MESSAGE, isImageOptimizationPath, isImageSourceBodyStatus, isSafeImageContentType, parseImageParams, readImageOptimizationSignal } from "./image-optimization.js";
22
+ import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, IMAGE_RESPONSE_CACHE_STATE, INVALID_IMAGE_SOURCE_MESSAGE, MUTABLE_IMAGE_SOURCE_CACHE_CONTROL, isImageOptimizationPath, isImageSourceBodyStatus, isSafeImageContentType, parseImageParams, readImageOptimizationSignal } from "./image-optimization.js";
23
23
  import { installSocketErrorBackstop } from "./socket-error-backstop.js";
24
24
  import { collectInlineCssManifest } from "../build/inline-css.js";
25
25
  import { readTrustedRevalidationHostname } from "./revalidation-host.js";
@@ -270,6 +270,7 @@ const OMIT_STATIC_RESPONSE_HEADERS = /* @__PURE__ */ new Set([
270
270
  "content-type",
271
271
  VINEXT_PRERENDER_SPECIAL_ERROR_HEADER
272
272
  ]);
273
+ const IMAGE_ERROR_SIGNAL_HEADERS = ["content-type", "content-encoding"];
273
274
  function omitHeadersCaseInsensitive(headersRecord, targets) {
274
275
  const filtered = {};
275
276
  for (const [key, value] of Object.entries(headersRecord)) {
@@ -284,6 +285,16 @@ function readHeaderCaseInsensitive(headersRecord, name) {
284
285
  const value = headersRecord[key];
285
286
  return Array.isArray(value) ? value.join(", ") : value;
286
287
  }
288
+ /** Spell a header's name as `name`, so spreading the record replaces that key. */
289
+ function withHeaderName(headersRecord, name) {
290
+ const lowerName = name.toLowerCase();
291
+ const key = Object.keys(headersRecord).find((entry) => entry !== name && entry.toLowerCase() === lowerName);
292
+ if (key === void 0) return headersRecord;
293
+ const { [key]: value, ...rest } = headersRecord;
294
+ const existing = rest[name];
295
+ rest[name] = existing === void 0 ? value : [existing, value].flat();
296
+ return rest;
297
+ }
287
298
  /** Remove one field name from a Vary header, dropping the header if it empties. */
288
299
  function omitVaryToken(headers, token) {
289
300
  const varyKey = Object.keys(headers).find((key) => key.toLowerCase() === "vary");
@@ -464,6 +475,18 @@ function staticCacheLookupPath(pathname) {
464
475
  if (lookupPath.startsWith("/.vite/") || lookupPath === "/.vite") return null;
465
476
  return lookupPath;
466
477
  }
478
+ /**
479
+ * Give a Node image passthrough Next.js's image Cache-Control instead of the
480
+ * public-file default: a hashed build asset keeps its immutable default, and
481
+ * any other source is revalidated after the default `images.minimumCacheTTL`.
482
+ */
483
+ function withImageSourceCacheControl(headers, sourcePath) {
484
+ if (sourcePath.includes(`/_next/static/`)) return headers;
485
+ return {
486
+ ...headers,
487
+ "Cache-Control": MUTABLE_IMAGE_SOURCE_CACHE_CONTROL
488
+ };
489
+ }
467
490
  /** Pathnames the Pages Router serves from routes rather than the client directory. */
468
491
  function isPagesFilesystemExcludedPath(pathname, phase) {
469
492
  return pathname === "/" || pathname === "/api" || pathname.startsWith("/api/") || phase === "direct" && pathname.startsWith(`/_next/static/`);
@@ -479,12 +502,13 @@ function isPagesFilesystemExcludedPath(pathname, phase) {
479
502
  * Without a cache, falls back to async filesystem probing (still non-blocking,
480
503
  * unlike the old sync existsSync/statSync approach).
481
504
  *
482
- * `extraHeaders` go on every response for the file. `representationHeaders` go
505
+ * `stagedHeaders` go on every response for the file. `representationHeaders` go
483
506
  * only on responses that carry the file's bytes (200 and 206), never on a 304,
484
507
  * 412, 416 or 405.
485
508
  */
486
- async function tryServeStatic(req, res, clientDir, pathname, compress, cache, extraHeaders, statusCode, representationHeaders) {
509
+ async function tryServeStatic(req, res, clientDir, pathname, compress, cache, stagedHeaders, statusCode, representationHeaders) {
487
510
  if (pathname === "/") return false;
511
+ const extraHeaders = stagedHeaders && withHeaderName(stagedHeaders, "Cache-Control");
488
512
  const responseStatus = statusCode ?? 200;
489
513
  const omitBody = isNoBodyResponseStatus(responseStatus);
490
514
  const requestRange = req.method === "GET" || req.method === "HEAD" ? req.headers.range : void 0;
@@ -610,7 +634,7 @@ async function tryServeStatic(req, res, clientDir, pathname, compress, cache, ex
610
634
  const ext = path.extname(resolved.path);
611
635
  const ct = contentTypeForPath(resolved.path);
612
636
  const isHashed = pathname.includes(`/${ASSET_PREFIX_URL_DIR}/`);
613
- const cacheControl = isHashed ? "public, max-age=31536000, immutable" : "public, max-age=3600";
637
+ const cacheControl = staticFileCacheControl(isHashed);
614
638
  const etag = isHashed && etagFromFilenameHash(resolved.path, ext) || `W/"${resolved.size}-${Math.floor(resolved.mtimeMs / 1e3)}"`;
615
639
  const baseHeaders = {
616
640
  "Content-Type": ct,
@@ -798,6 +822,15 @@ async function statIfFile(filePath) {
798
822
  }
799
823
  }
800
824
  /**
825
+ * Node's parser keeps a `#fragment` sent on the request line in `req.url`.
826
+ * Next.js rebuilds request URLs from pathname + search, so neither routing,
827
+ * middleware nor handlers observe one; drop it before anything reads the URL.
828
+ */
829
+ function stripNodeRequestUrlFragment(req) {
830
+ const hashIndex = req.url?.indexOf("#") ?? -1;
831
+ if (hashIndex !== -1) req.url = req.url?.slice(0, hashIndex);
832
+ }
833
+ /**
801
834
  * Convert a Node.js IncomingMessage to a Web Request object.
802
835
  *
803
836
  * When `urlOverride` is provided, it is used as the path + query string
@@ -1163,18 +1196,25 @@ async function startAppRouterServer(options) {
1163
1196
  if (seededRoutes > 0) console.log(`[vinext] Seeded ${seededRoutes} pre-rendered route${seededRoutes !== 1 ? "s" : ""} into memory cache`);
1164
1197
  const staticCache = await StaticFileCache.create(clientDir);
1165
1198
  const serveImageOptimization = async (req, res, request, signal) => {
1166
- const stagedHeaders = omitHeadersCaseInsensitive(mergeResponseHeaders({}, new Response(null, { headers: signal.middlewareHeaders })), OMIT_STATIC_RESPONSE_HEADERS);
1199
+ const signalHeaders = mergeResponseHeaders({}, new Response(null, { headers: signal.middlewareHeaders }));
1200
+ const stagedHeaders = omitHeadersCaseInsensitive(signalHeaders, OMIT_STATIC_RESPONSE_HEADERS);
1167
1201
  for (const [name, value] of Object.entries(stagedHeaders)) res.setHeader(name, value);
1202
+ const sendBadRequest = (body) => {
1203
+ for (const name of IMAGE_ERROR_SIGNAL_HEADERS) {
1204
+ const value = signalHeaders[name];
1205
+ if (value !== void 0) res.setHeader(name, value);
1206
+ }
1207
+ res.writeHead(400);
1208
+ res.end(body);
1209
+ };
1168
1210
  const parsedUrl = new URL(`/${signal.search}`, "http://localhost");
1169
- const params = parseImageParams(parsedUrl, appImageAllowedWidths, imageConfig?.qualities);
1211
+ const params = parseImageParams(parsedUrl, appImageAllowedWidths, imageConfig?.qualities, { localPatterns: imageConfig?.localPatterns });
1170
1212
  if (!params) {
1171
- res.writeHead(400);
1172
- res.end("Bad Request");
1213
+ sendBadRequest("Bad Request");
1173
1214
  return;
1174
1215
  }
1175
1216
  const sendUnsafeContentType = () => {
1176
- res.writeHead(400);
1177
- res.end("The requested resource is not an allowed image type");
1217
+ sendBadRequest("The requested resource is not an allowed image type");
1178
1218
  };
1179
1219
  const isSafeSourcePath = (sourcePath) => isSafeImageContentType(contentTypeForPath(sourcePath), imageConfig?.dangerouslyAllowSVG);
1180
1220
  const imageSecurityHeaders = {
@@ -1182,7 +1222,7 @@ async function startAppRouterServer(options) {
1182
1222
  "X-Content-Type-Options": "nosniff",
1183
1223
  "Content-Disposition": imageConfig?.contentDispositionType === "attachment" ? "attachment" : "inline"
1184
1224
  };
1185
- const serveSource = (sourcePath) => tryServeStatic(req, res, clientDir, sourcePath, false, staticCache, imageSecurityHeaders, void 0, IMAGE_REPRESENTATION_HEADERS);
1225
+ const serveSource = (sourcePath) => tryServeStatic(req, res, clientDir, sourcePath, false, staticCache, withImageSourceCacheControl(imageSecurityHeaders, sourcePath), void 0, IMAGE_REPRESENTATION_HEADERS);
1186
1226
  const sourceUrl = new URL(params.imageUrl, request.url);
1187
1227
  let sourcePathname = null;
1188
1228
  try {
@@ -1211,8 +1251,7 @@ async function startAppRouterServer(options) {
1211
1251
  if (await serveSource(sourcePath)) return;
1212
1252
  }
1213
1253
  }
1214
- res.writeHead(400);
1215
- res.end(INVALID_IMAGE_SOURCE_MESSAGE);
1254
+ sendBadRequest(INVALID_IMAGE_SOURCE_MESSAGE);
1216
1255
  };
1217
1256
  const clientBuildFiles = new Set(Array.isArray(rscModule.__clientBuildFiles) ? rscModule.__clientBuildFiles : []);
1218
1257
  const handleRequestImpl = async (req, res) => {
@@ -1299,6 +1338,7 @@ async function startAppRouterServer(options) {
1299
1338
  }
1300
1339
  };
1301
1340
  const handleRequest = async (req, res) => {
1341
+ stripNodeRequestUrlFragment(req);
1302
1342
  try {
1303
1343
  await ensureInstrumentation();
1304
1344
  } catch (error) {
@@ -1319,7 +1359,7 @@ async function startAppRouterServer(options) {
1319
1359
  },
1320
1360
  getStatus: () => res.statusCode,
1321
1361
  headers,
1322
- isRsc: target.split("?", 1)[0].endsWith(".rsc") || headers.get("RSC") === "1",
1362
+ isRsc: headers.get("RSC") === "1",
1323
1363
  method: req.method ?? "GET",
1324
1364
  target
1325
1365
  });
@@ -1422,6 +1462,7 @@ async function createPagesRouterRequestHandler(options) {
1422
1462
  dangerouslyAllowSVG: vinextConfig.images.dangerouslyAllowSVG,
1423
1463
  dangerouslyAllowLocalIP: vinextConfig.images.dangerouslyAllowLocalIP,
1424
1464
  qualities: vinextConfig.images.qualities,
1465
+ localPatterns: vinextConfig.images.localPatterns,
1425
1466
  contentDispositionType: vinextConfig.images.contentDispositionType,
1426
1467
  contentSecurityPolicy: vinextConfig.images.contentSecurityPolicy
1427
1468
  } : void 0;
@@ -1434,6 +1475,7 @@ async function createPagesRouterRequestHandler(options) {
1434
1475
  });
1435
1476
  const staticCache = await StaticFileCache.create(clientDir);
1436
1477
  const handleRequest = async (req, res) => {
1478
+ stripNodeRequestUrlFragment(req);
1437
1479
  try {
1438
1480
  await ensureInstrumentation();
1439
1481
  } catch (error) {
@@ -1655,7 +1697,7 @@ async function createPagesRouterRequestHandler(options) {
1655
1697
  if (resolvedPathnameHasBasePath && isImageOptimizationPath(requestPathname)) {
1656
1698
  for (const [name, value] of Object.entries(stagedHeaders)) res.setHeader(name, value);
1657
1699
  const parsedUrl = new URL(resolvedUrl, "http://localhost");
1658
- const params = parseImageParams(parsedUrl, allowedImageWidths, pagesImageConfig?.qualities);
1700
+ const params = parseImageParams(parsedUrl, allowedImageWidths, pagesImageConfig?.qualities, { localPatterns: pagesImageConfig?.localPatterns });
1659
1701
  if (!params) {
1660
1702
  res.writeHead(400);
1661
1703
  res.end("Bad Request");
@@ -1671,7 +1713,7 @@ async function createPagesRouterRequestHandler(options) {
1671
1713
  "X-Content-Type-Options": "nosniff",
1672
1714
  "Content-Disposition": pagesImageConfig?.contentDispositionType === "attachment" ? "attachment" : "inline"
1673
1715
  };
1674
- const serveSource = (sourcePath) => tryServeStatic(req, res, clientDir, sourcePath, false, staticCache, imageSecurityHeaders, void 0, IMAGE_REPRESENTATION_HEADERS);
1716
+ const serveSource = (sourcePath) => tryServeStatic(req, res, clientDir, sourcePath, false, staticCache, withImageSourceCacheControl(imageSecurityHeaders, sourcePath), void 0, IMAGE_REPRESENTATION_HEADERS);
1675
1717
  const sourceUrl = new URL(params.imageUrl, webRequest.url);
1676
1718
  let sourcePathname = null;
1677
1719
  try {
@@ -0,0 +1,9 @@
1
+ //#region src/server/request-error-log.d.ts
2
+ /**
3
+ * Logs an error vinext also passes to instrumentation's `onRequestError`, as
4
+ * `next start` logs it. `VINEXT_QUIET=1` turns these logs off, like Next.js'
5
+ * `quiet` server option, so an `onRequestError` that redacts errors before
6
+ * recording them is the only place they reach.
7
+ */
8
+ export declare function logRequestError(...args: unknown[]): void;
9
+ //#endregion
@@ -0,0 +1,13 @@
1
+ //#region src/server/request-error-log.ts
2
+ /**
3
+ * Logs an error vinext also passes to instrumentation's `onRequestError`, as
4
+ * `next start` logs it. `VINEXT_QUIET=1` turns these logs off, like Next.js'
5
+ * `quiet` server option, so an `onRequestError` that redacts errors before
6
+ * recording them is the only place they reach.
7
+ */
8
+ function logRequestError(...args) {
9
+ if (process.env.VINEXT_QUIET === "1") return;
10
+ console.error(...args);
11
+ }
12
+ //#endregion
13
+ export { logRequestError };
@@ -16,6 +16,11 @@ import { getRepeatedSlashRedirect, isOpenRedirectShaped, sendRepeatedSlashRedire
16
16
  export declare function canonicalizeRequestPathname(pathname: string): string;
17
17
  /** Canonicalize only the pathname portion while preserving the raw query. */
18
18
  export declare function canonicalizeRequestUrlPathname(url: string): string;
19
+ /**
20
+ * Whether the pathname has a segment the URL parser resolves as `.` or `..`,
21
+ * literally or percent-encoded. Such a pathname changes when it becomes a URL.
22
+ */
23
+ export declare function hasUrlParserDotSegment(pathname: string): boolean;
19
24
  /**
20
25
  * Shared request pipeline utilities.
21
26
  *
@@ -73,7 +78,6 @@ type ResolvePublicFileRouteOptions = {
73
78
  middlewareContext: StaticFileSignalContext & {
74
79
  requestHeaders?: Headers | null;
75
80
  };
76
- pathname: string;
77
81
  publicFiles: ReadonlySet<string>;
78
82
  request: Request;
79
83
  };
@@ -82,7 +86,7 @@ type ResolvePublicFileRouteOptions = {
82
86
  *
83
87
  * Public files are checked after middleware and before afterFiles/fallback
84
88
  * rewrites. The generated App Router entry provides the public-file set; this
85
- * helper owns the RSC exclusion, existence-first method enforcement, and
89
+ * helper owns existence-first method enforcement and
86
90
  * static-file signaling. Missing mutation targets continue through routing.
87
91
  */
88
92
  export declare function resolvePublicFileRoute(options: ResolvePublicFileRouteOptions): Response | null;
@@ -91,8 +95,8 @@ export declare function normalizeTrailingSlashPathname(pathname: string, trailin
91
95
  * Check if the pathname needs a trailing slash redirect, and return the
92
96
  * redirect Response if so.
93
97
  *
94
- * Follows Next.js behavior:
95
- * - `/api` routes are never redirected
98
+ * Follows Next.js behavior, whose internal trailing-slash redirects cover
99
+ * every path, `/api` included:
96
100
  * - The root path `/` is never redirected
97
101
  * - If `trailingSlash` is true, redirect `/about` → `/about/`
98
102
  * - If `trailingSlash` is true, redirect file-looking `/file.ext/` → `/file.ext`
@@ -200,5 +204,11 @@ export declare function withRequestHeaders(request: Request, headers: Headers):
200
204
  * exists.
201
205
  */
202
206
  export declare function cloneRequestWithUrl(request: Request, url: string): Request;
207
+ /**
208
+ * Drop a `#fragment` the host kept from the request line. Next.js rebuilds
209
+ * request URLs from pathname + search, so neither middleware nor handlers
210
+ * ever observe one, and pathname-keyed cache entries must not either.
211
+ */
212
+ export declare function stripRequestUrlFragment(request: Request): Request;
203
213
  //#endregion
204
214
  export { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, createStaticFileSignal, getRepeatedSlashRedirect, hasBasePath, isOpenRedirectShaped, sendRepeatedSlashRedirect, stripBasePath };
@@ -30,6 +30,16 @@ function canonicalizeRequestUrlPathname(url) {
30
30
  return canonicalizeRequestPathname(pathname) + search;
31
31
  }
32
32
  /**
33
+ * Whether the pathname has a segment the URL parser resolves as `.` or `..`,
34
+ * literally or percent-encoded. Such a pathname changes when it becomes a URL.
35
+ */
36
+ function hasUrlParserDotSegment(pathname) {
37
+ return pathname.split("/").some((segment) => {
38
+ const decodedDots = segment.replaceAll(/%2e/gi, ".");
39
+ return decodedDots === "." || decodedDots === "..";
40
+ });
41
+ }
42
+ /**
33
43
  * Shared request pipeline utilities.
34
44
  *
35
45
  * Extracted from generated entries and server hot paths to keep codegen focused
@@ -84,7 +94,8 @@ function guardProtocolRelativeUrl(rawPathname, search = "") {
84
94
  if (isOpenRedirectShaped(rawPathname)) return notFoundResponse();
85
95
  return null;
86
96
  }
87
- const FILE_LIKE_PATHNAME_RE = /\.[^/]+\/?$/;
97
+ const SLASHED_FILE_PATHNAME_RE = /^\/(?:[^/]+\/)*[^/]+\.\w+\/$/;
98
+ const UNSLASHED_NON_FILE_PATHNAME_RE = /^\/(?:[^/]+\/)*[^/.]+$/;
88
99
  function isWellKnownPathname(pathname) {
89
100
  return pathname === "/.well-known" || pathname.startsWith("/.well-known/");
90
101
  }
@@ -93,37 +104,32 @@ function isWellKnownPathname(pathname) {
93
104
  *
94
105
  * Public files are checked after middleware and before afterFiles/fallback
95
106
  * rewrites. The generated App Router entry provides the public-file set; this
96
- * helper owns the RSC exclusion, existence-first method enforcement, and
107
+ * helper owns existence-first method enforcement and
97
108
  * static-file signaling. Missing mutation targets continue through routing.
98
109
  */
99
110
  function resolvePublicFileRoute(options) {
100
- if (options.pathname.endsWith(".rsc")) return null;
101
111
  if (!options.publicFiles.has(options.cleanPathname)) return null;
102
112
  if (options.request.method !== "GET" && options.request.method !== "HEAD") return methodNotAllowedResponse("GET, HEAD", { headers: options.middlewareContext.headers ?? void 0 });
103
113
  const { requestHeaders } = options.middlewareContext;
104
114
  return createStaticFileSignal(options.cleanPathname, options.middlewareContext, requestHeaders ? buildRequestHeadersFromMiddlewareResponse(options.request.headers, requestHeaders) : null);
105
115
  }
106
116
  function normalizeTrailingSlashPathname(pathname, trailingSlash) {
107
- if (pathname === "/" || pathname === "/api" || pathname.startsWith("/api/")) return null;
108
- const hasTrailing = pathname.endsWith("/");
117
+ if (pathname === "/") return null;
109
118
  if (trailingSlash) {
110
119
  if (isWellKnownPathname(pathname)) return null;
111
- if (FILE_LIKE_PATHNAME_RE.test(pathname)) {
112
- const normalized = removeTrailingSlash(pathname);
113
- return normalized === pathname ? null : normalized;
114
- }
115
- if (!hasTrailing && !pathname.endsWith(".rsc")) return `${pathname}/`;
120
+ if (SLASHED_FILE_PATHNAME_RE.test(pathname)) return pathname.slice(0, -1);
121
+ if (UNSLASHED_NON_FILE_PATHNAME_RE.test(pathname)) return `${pathname}/`;
116
122
  return null;
117
123
  }
118
- if (hasTrailing) return removeTrailingSlash(pathname);
124
+ if (pathname.endsWith("/")) return removeTrailingSlash(pathname);
119
125
  return null;
120
126
  }
121
127
  /**
122
128
  * Check if the pathname needs a trailing slash redirect, and return the
123
129
  * redirect Response if so.
124
130
  *
125
- * Follows Next.js behavior:
126
- * - `/api` routes are never redirected
131
+ * Follows Next.js behavior, whose internal trailing-slash redirects cover
132
+ * every path, `/api` included:
127
133
  * - The root path `/` is never redirected
128
134
  * - If `trailingSlash` is true, redirect `/about` → `/about/`
129
135
  * - If `trailingSlash` is true, redirect file-looking `/file.ext/` → `/file.ext`
@@ -137,7 +143,7 @@ function normalizeTrailingSlashPathname(pathname, trailingSlash) {
137
143
  * @returns A 308 redirect Response, or null if no redirect is needed
138
144
  */
139
145
  function normalizeTrailingSlash(pathname, basePath, trailingSlash, search) {
140
- if (pathname === "/" || pathname === "/api" || pathname.startsWith("/api/")) return null;
146
+ if (pathname === "/") return null;
141
147
  if (isOpenRedirectShaped(pathname)) return notFoundResponse();
142
148
  const normalizedPathname = normalizeTrailingSlashPathname(pathname, trailingSlash);
143
149
  if (normalizedPathname === null) return null;
@@ -463,5 +469,17 @@ function cloneRequestWithUrl(request, url) {
463
469
  }
464
470
  return attachRequestCfMetadata(cloned, request);
465
471
  }
472
+ /**
473
+ * Drop a `#fragment` the host kept from the request line. Next.js rebuilds
474
+ * request URLs from pathname + search, so neither middleware nor handlers
475
+ * ever observe one, and pathname-keyed cache entries must not either.
476
+ */
477
+ function stripRequestUrlFragment(request) {
478
+ const hashIndex = request.url.indexOf("#");
479
+ if (hashIndex === -1) return request;
480
+ const signal = request.signal;
481
+ const stripped = cloneRequestWithUrl(request, request.url.slice(0, hashIndex));
482
+ return attachRequestCfMetadata(new Request(stripped, { signal }), request);
483
+ }
466
484
  //#endregion
467
- export { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, attachRequestCfMetadata, canonicalizeRequestPathname, canonicalizeRequestUrlPathname, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, dropEmptyMiddlewareSetCookie, filterInternalHeaders, getRepeatedSlashRedirect, guardProtocolRelativeUrl, hasBasePath, haveSameHeaders, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, sendRepeatedSlashRedirect, stripBasePath, validateCsrfOrigin, validateServerActionPayload, withRequestHeaders };
485
+ export { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, attachRequestCfMetadata, canonicalizeRequestPathname, canonicalizeRequestUrlPathname, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, dropEmptyMiddlewareSetCookie, filterInternalHeaders, getRepeatedSlashRedirect, guardProtocolRelativeUrl, hasBasePath, hasUrlParserDotSegment, haveSameHeaders, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, sendRepeatedSlashRedirect, stripBasePath, stripRequestUrlFragment, validateCsrfOrigin, validateServerActionPayload, withRequestHeaders };
@@ -23,6 +23,13 @@ export type ResponseStageCacheabilityOptions = {
23
23
  * shared response drops them before admission and storage.
24
24
  */
25
25
  recomposesRequestScopedHeaders?: boolean;
26
+ /**
27
+ * Keeps `X-Vinext-Params` when the request scoped headers are dropped. An
28
+ * intercepted response's params include its target's, which the request
29
+ * stage doesn't match. Its identity holds the interception context, so the
30
+ * stored params are its own.
31
+ */
32
+ keepsParamsHeader?: boolean;
26
33
  /** Generated adapter registration, deferred until the response stage executes. */
27
34
  registerCacheAdapters(): void;
28
35
  request: Request;
@@ -24,13 +24,13 @@ async function withResponseStageCacheability(options, render) {
24
24
  const stripsRequestScopedHeaders = options.recomposesRequestScopedHeaders === true && options.cache === "shared" && !options.probeMode;
25
25
  if (!cacheability) {
26
26
  const rendered = await render(context);
27
- return stripsRequestScopedHeaders ? withoutRequestScopedHeaders(rendered) : rendered;
27
+ return stripsRequestScopedHeaders ? withoutRequestScopedHeaders(rendered, options.keepsParamsHeader === true) : rendered;
28
28
  }
29
29
  if (options.policyHeadersAppliedBeforeRender) cacheability.recordResponseStageCachePolicy(context, options.policyHeaders);
30
30
  const state = Reflect.get(context, CACHEABILITY_REQUEST_STATE);
31
31
  if (options.probeMode && options.forceDynamic && !options.policyHeaders?.length && state) state.patternDynamicReason = "dynamic = \"force-dynamic\"";
32
32
  const renderedResponse = await render(context);
33
- const rendered = stripsRequestScopedHeaders ? withoutRequestScopedHeaders(renderedResponse) : renderedResponse;
33
+ const rendered = stripsRequestScopedHeaders ? withoutRequestScopedHeaders(renderedResponse, options.keepsParamsHeader === true) : renderedResponse;
34
34
  const response = options.policyHeadersAppliedBeforeRender ? rendered : cacheability.applyResponseStageCachePolicy(rendered, context, options.policyHeaders);
35
35
  const complete = (candidate) => cacheability.finalizeWorkerCacheabilityResponse(candidate, context);
36
36
  const route = state?.route;
@@ -40,10 +40,11 @@ async function withResponseStageCacheability(options, render) {
40
40
  }
41
41
  return complete(response);
42
42
  }
43
- function withoutRequestScopedHeaders(response) {
44
- if (!response.headers.has("X-Vinext-Params") && !response.headers.has("X-Vinext-Rendered-Path-And-Search")) return response;
43
+ function withoutRequestScopedHeaders(response, keepsParams) {
44
+ const dropsParams = !keepsParams && response.headers.has("X-Vinext-Params");
45
+ if (!dropsParams && !response.headers.has("X-Vinext-Rendered-Path-And-Search")) return response;
45
46
  const headers = new Headers(response.headers);
46
- headers.delete(VINEXT_PARAMS_HEADER);
47
+ if (dropsParams) headers.delete(VINEXT_PARAMS_HEADER);
47
48
  headers.delete(VINEXT_RENDERED_PATH_AND_SEARCH_HEADER);
48
49
  return preserveFullyBufferedBodyMetadata(response, new Response(response.body, {
49
50
  headers,
@@ -88,9 +88,11 @@ function extractRscCompletionMetadata(buffer) {
88
88
  }
89
89
  function appendRscCompletionMetadata(source, getMetadata) {
90
90
  const reader = source.getReader();
91
+ let cancelled = false;
91
92
  return new ReadableStream({
92
93
  async pull(controller) {
93
94
  const next = await reader.read();
95
+ if (cancelled) return;
94
96
  if (!next.done) {
95
97
  controller.enqueue(escapeFlightChunk(next.value));
96
98
  return;
@@ -100,6 +102,7 @@ function appendRscCompletionMetadata(source, getMetadata) {
100
102
  controller.close();
101
103
  },
102
104
  cancel(reason) {
105
+ cancelled = true;
103
106
  return reader.cancel(reason);
104
107
  }
105
108
  });
@@ -3,6 +3,15 @@
3
3
  export declare const CONTENT_TYPES: Record<string, string>;
4
4
  /** Resolve a path's MIME type with case-insensitive extension matching. */
5
5
  export declare function contentTypeForPath(filePath: string): string;
6
+ /**
7
+ * Default `Cache-Control` for a file in the client build directory. Hashed
8
+ * build assets never change at their URL. Every other file (`public/`) must be
9
+ * revalidated on each reuse, as Next.js's `send` serves it with `max-age=0`:
10
+ * middleware may admit it per request, so a shared cache must not reuse it
11
+ * without asking the origin. A `Cache-Control` set by middleware or config
12
+ * headers replaces this default.
13
+ */
14
+ export declare function staticFileCacheControl(isHashed: boolean): string;
6
15
  /** A servable file variant with pre-computed response headers. */
7
16
  type FileVariant = {
8
17
  /** Absolute file path (used for streaming large files). */
@@ -57,6 +57,17 @@ function contentTypeForPath(filePath) {
57
57
  return CONTENT_TYPES[path.extname(filePath).toLowerCase()] ?? "application/octet-stream";
58
58
  }
59
59
  /**
60
+ * Default `Cache-Control` for a file in the client build directory. Hashed
61
+ * build assets never change at their URL. Every other file (`public/`) must be
62
+ * revalidated on each reuse, as Next.js's `send` serves it with `max-age=0`:
63
+ * middleware may admit it per request, so a shared cache must not reuse it
64
+ * without asking the origin. A `Cache-Control` set by middleware or config
65
+ * headers replaces this default.
66
+ */
67
+ function staticFileCacheControl(isHashed) {
68
+ return isHashed ? "public, max-age=31536000, immutable" : "public, max-age=0";
69
+ }
70
+ /**
60
71
  * Files below this size are buffered in memory at startup for zero-syscall
61
72
  * serving via res.end(buffer). Above this, files stream via createReadStream.
62
73
  * 64KB covers virtually all precompressed assets (a 200KB JS bundle compresses
@@ -98,7 +109,7 @@ var StaticFileCache = class StaticFileCache {
98
109
  if (isHashed && PRECOMPRESSION_TEMP_FILE_RE.test(relativePath)) continue;
99
110
  const ext = path.extname(relativePath);
100
111
  const contentType = contentTypeForPath(relativePath);
101
- const cacheControl = isHashed ? "public, max-age=31536000, immutable" : "public, max-age=3600";
112
+ const cacheControl = staticFileCacheControl(isHashed);
102
113
  const etag = isHashed && etagFromFilenameHash(relativePath, ext) || `W/"${fileInfo.size}-${Math.floor(fileInfo.mtimeMs / 1e3)}"`;
103
114
  const lastModified = new Date(fileInfo.mtimeMs).toUTCString();
104
115
  const baseHeaders = {
@@ -266,4 +277,4 @@ async function* walkFilesWithStats(dir, base = dir) {
266
277
  }
267
278
  }
268
279
  //#endregion
269
- export { CONTENT_TYPES, StaticFileCache, contentTypeForPath, etagFromFilenameHash };
280
+ export { CONTENT_TYPES, StaticFileCache, contentTypeForPath, etagFromFilenameHash, staticFileCacheControl };
@@ -27,8 +27,8 @@ export declare function fetchWorkerImageSource(imageUrl: string, request: Reques
27
27
  export declare function fetchSignalledPublicFile(response: Response, sourceRequest: Request, assets: VinextAssetFetcher): Promise<Response> | null;
28
28
  /**
29
29
  * Serve the App Router image endpoint once the request handler admitted it
30
- * through middleware and rewrites. Middleware's response headers are sent with
31
- * the image, as Next.js does.
30
+ * through middleware and rewrites. Config and middleware response headers are
31
+ * sent with the image, as Next.js does.
32
32
  */
33
33
  export declare function serveWorkerImageOptimization(request: Request, signal: ImageOptimizationSignal, allowedWidths: number[] | undefined, imageConfig: ImageConfig | undefined, options: WorkerImageSourceOptions): Promise<Response>;
34
34
  //#endregion
@@ -78,8 +78,8 @@ function fetchSignalledPublicFile(response, sourceRequest, assets) {
78
78
  }
79
79
  /**
80
80
  * Serve the App Router image endpoint once the request handler admitted it
81
- * through middleware and rewrites. Middleware's response headers are sent with
82
- * the image, as Next.js does.
81
+ * through middleware and rewrites. Config and middleware response headers are
82
+ * sent with the image, as Next.js does.
83
83
  */
84
84
  async function serveWorkerImageOptimization(request, signal, allowedWidths, imageConfig, options) {
85
85
  const imageUrl = new URL(request.url);
@@ -93,7 +93,22 @@ async function serveWorkerImageOptimization(request, signal, allowedWidths, imag
93
93
  });
94
94
  const cookies = signal.middlewareHeaders.getSetCookie();
95
95
  if (cookies.length > 0) middlewareHeaders["set-cookie"] = cookies;
96
- return mergeHeaders(response, middlewareHeaders);
96
+ const merged = mergeHeaders(response, middlewareHeaders);
97
+ if (response.status < 400) return merged;
98
+ const errorHeaders = new Headers(merged.headers);
99
+ let replaced = false;
100
+ for (const name of ["content-type", "content-encoding"]) {
101
+ const value = signal.middlewareHeaders.get(name);
102
+ if (value === null) continue;
103
+ errorHeaders.set(name, value);
104
+ replaced = true;
105
+ }
106
+ if (!replaced) return merged;
107
+ return new Response(merged.body, {
108
+ headers: errorHeaders,
109
+ status: merged.status,
110
+ statusText: merged.statusText
111
+ });
97
112
  }
98
113
  //#endregion
99
114
  export { fetchSignalledPublicFile, fetchWorkerImageSource, serveWorkerImageOptimization };
@@ -1,6 +1,14 @@
1
1
  //#region src/server/worker-utils.d.ts
2
2
  export declare function finalizeMissingStaticAssetResponse(response: Response, missingBuildAsset: boolean): Response;
3
3
  export declare function mergeHeaders(response: Response, extraHeaders: Record<string, string | string[]>, statusOverride?: number): Response;
4
+ /**
5
+ * Merge middleware/config headers into a file served from the asset layer.
6
+ * Its Cache-Control is only the layer's default (Workers Static Assets sends
7
+ * `public, max-age=0, must-revalidate` with every file), so one set by
8
+ * middleware or `headers()` wins, as Next.js's static file server sets its
9
+ * own only when the response has none.
10
+ */
11
+ export declare function mergeStaticFileHeaders(response: Response, extraHeaders: Record<string, string | string[]>, statusOverride?: number): Response;
4
12
  export declare function resolveStaticAssetSignal(signalResponse: Response, options: {
5
13
  /** `requestHeaders` are the post-middleware request headers, when middleware set any. */
6
14
  fetchAsset(path: string, requestHeaders: Headers | null): Promise<Response>;
@@ -82,6 +82,23 @@ function mergeHeaders(response, extraHeaders, statusOverride) {
82
82
  headers: merged
83
83
  });
84
84
  }
85
+ /**
86
+ * Merge middleware/config headers into a file served from the asset layer.
87
+ * Its Cache-Control is only the layer's default (Workers Static Assets sends
88
+ * `public, max-age=0, must-revalidate` with every file), so one set by
89
+ * middleware or `headers()` wins, as Next.js's static file server sets its
90
+ * own only when the response has none.
91
+ */
92
+ function mergeStaticFileHeaders(response, extraHeaders, statusOverride) {
93
+ const merged = mergeHeaders(response, extraHeaders, statusOverride);
94
+ for (const [name, value] of Object.entries(extraHeaders)) {
95
+ if (name.toLowerCase() !== "cache-control") continue;
96
+ const cacheControl = (Array.isArray(value) ? value.join(", ") : value).trim();
97
+ if (!cacheControl) continue;
98
+ merged.headers.set(name, cacheControl);
99
+ }
100
+ return merged;
101
+ }
85
102
  async function resolveStaticAssetSignal(signalResponse, options) {
86
103
  const signal = readStaticFileSignal(signalResponse);
87
104
  if (!signal) return null;
@@ -100,7 +117,7 @@ async function resolveStaticAssetSignal(signalResponse, options) {
100
117
  const requestHeaders = readStaticFileSignalRequestHeaders(signalResponse);
101
118
  cancelResponseBody(signalResponse);
102
119
  const assetResponse = await options.fetchAsset(assetPath, requestHeaders);
103
- return mergeHeaders(assetResponse, extraHeaders, assetResponse.ok && assetResponse.status !== 206 && signalResponse.status !== 200 ? signalResponse.status : void 0);
120
+ return mergeStaticFileHeaders(assetResponse, extraHeaders, assetResponse.ok && assetResponse.status !== 206 && signalResponse.status !== 200 ? signalResponse.status : void 0);
104
121
  }
105
122
  /**
106
123
  * Retarget a Worker asset request without dropping its conditional/range
@@ -116,4 +133,4 @@ function createStaticAssetRequest(assetPath, sourceRequest, headers = null) {
116
133
  });
117
134
  }
118
135
  //#endregion
119
- export { createStaticAssetRequest, finalizeMissingStaticAssetResponse, mergeHeaders, resolveStaticAssetSignal };
136
+ export { createStaticAssetRequest, finalizeMissingStaticAssetResponse, mergeHeaders, mergeStaticFileHeaders, resolveStaticAssetSignal };
@@ -1,10 +1,10 @@
1
+ import { CacheLifeConfig } from "../utils/cache-life-profiles.js";
1
2
  import { _recordUseCacheRootParamRead, _registerCacheContextAccessor, getRegisteredCacheContext } from "./internal/cache-context-accessor.js";
2
3
  //#region src/shims/cache-request-state.d.ts
3
- export type CacheLifeConfig = {
4
- stale?: number;
5
- revalidate?: number;
6
- expire?: number;
7
- };
4
+ /**
5
+ * The built-in profiles merged with next.config `cacheLife`, baked in at
6
+ * build time like Next.js's `process.env.__NEXT_CACHE_LIFE` define.
7
+ */
8
8
  export declare const cacheLifeProfiles: Record<string, CacheLifeConfig>;
9
9
  export type UnstableCacheRevalidationMode = "foreground" | "background";
10
10
  export type ActionRevalidationKind = 0 | 1 | 2;
@@ -121,4 +121,4 @@ export declare function _captureRequestScopedCacheLifeAccessors(): {
121
121
  export declare function runWithDetachedCacheObservations<T>(fn: () => Promise<T>): Promise<T>;
122
122
  export declare function shouldServeStaleUnstableCacheEntry(): boolean;
123
123
  //#endregion
124
- export { _recordUseCacheRootParamRead, _registerCacheContextAccessor, getRegisteredCacheContext };
124
+ export { type CacheLifeConfig, _recordUseCacheRootParamRead, _registerCacheContextAccessor, getRegisteredCacheContext };