vinext 0.2.0 → 1.0.0-beta.0

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 (168) hide show
  1. package/README.md +50 -29
  2. package/dist/build/client-build-config.d.ts +13 -92
  3. package/dist/build/client-build-config.js +17 -95
  4. package/dist/build/css-url-assets.d.ts +3 -1
  5. package/dist/build/css-url-assets.js +18 -1
  6. package/dist/build/inject-pregenerated-paths.d.ts +3 -0
  7. package/dist/build/inject-pregenerated-paths.js +4 -1
  8. package/dist/build/prerender-paths.d.ts +23 -0
  9. package/dist/build/prerender-paths.js +296 -0
  10. package/dist/build/prerender-server-entry.d.ts +1 -0
  11. package/dist/build/prerender-server-entry.js +49 -0
  12. package/dist/build/prerender-server-pool.d.ts +44 -0
  13. package/dist/build/prerender-server-pool.js +194 -0
  14. package/dist/build/prerender.d.ts +12 -1
  15. package/dist/build/prerender.js +87 -23
  16. package/dist/build/run-prerender.js +2 -1
  17. package/dist/cache/cache-adapters-virtual.d.ts +7 -1
  18. package/dist/cache/cache-adapters-virtual.js +26 -2
  19. package/dist/check.d.ts +4 -6
  20. package/dist/check.js +13 -9
  21. package/dist/cli.js +34 -54
  22. package/dist/client/vinext-next-data.d.ts +4 -1
  23. package/dist/config/config-matchers.js +33 -11
  24. package/dist/config/next-config.d.ts +36 -6
  25. package/dist/config/next-config.js +17 -4
  26. package/dist/config/prerender.d.ts +11 -1
  27. package/dist/config/prerender.js +19 -1
  28. package/dist/config/tsconfig-paths.js +5 -1
  29. package/dist/entries/app-browser-entry.js +5 -1
  30. package/dist/entries/app-rsc-entry.d.ts +3 -2
  31. package/dist/entries/app-rsc-entry.js +4 -0
  32. package/dist/entries/pages-client-entry.js +14 -3
  33. package/dist/entries/pages-server-entry.js +18 -4
  34. package/dist/image/image-adapters-virtual.js +1 -0
  35. package/dist/index.d.ts +1 -1
  36. package/dist/index.js +549 -152
  37. package/dist/init-cloudflare.js +12 -3
  38. package/dist/init-platform.d.ts +10 -1
  39. package/dist/init-platform.js +78 -12
  40. package/dist/init.d.ts +11 -3
  41. package/dist/init.js +129 -31
  42. package/dist/plugins/fonts.js +1 -1
  43. package/dist/plugins/ignore-dynamic-requests.js +1 -1
  44. package/dist/plugins/og-assets.js +2 -1
  45. package/dist/plugins/optimize-imports.js +1 -1
  46. package/dist/plugins/rsc-reference-validation-normalizer.d.ts +12 -0
  47. package/dist/plugins/rsc-reference-validation-normalizer.js +48 -0
  48. package/dist/plugins/sass.d.ts +17 -1
  49. package/dist/plugins/sass.js +74 -1
  50. package/dist/plugins/styled-jsx.d.ts +16 -0
  51. package/dist/plugins/styled-jsx.js +149 -0
  52. package/dist/routing/app-route-graph.js +22 -16
  53. package/dist/routing/file-matcher.d.ts +8 -1
  54. package/dist/routing/file-matcher.js +15 -3
  55. package/dist/server/app-browser-entry.js +44 -14
  56. package/dist/server/app-browser-navigation-controller.js +5 -2
  57. package/dist/server/app-browser-server-action-client.js +5 -3
  58. package/dist/server/app-browser-state.d.ts +1 -0
  59. package/dist/server/app-browser-state.js +1 -1
  60. package/dist/server/app-layout-param-observation.d.ts +1 -1
  61. package/dist/server/app-page-boundary.js +2 -1
  62. package/dist/server/app-page-cache-finalizer.d.ts +1 -0
  63. package/dist/server/app-page-cache-finalizer.js +3 -3
  64. package/dist/server/app-page-cache-render.d.ts +1 -1
  65. package/dist/server/app-page-cache.js +23 -6
  66. package/dist/server/app-page-dispatch.d.ts +2 -1
  67. package/dist/server/app-page-dispatch.js +27 -10
  68. package/dist/server/app-page-element-builder.js +1 -0
  69. package/dist/server/app-page-execution.js +2 -1
  70. package/dist/server/app-page-render-identity.d.ts +1 -0
  71. package/dist/server/app-page-render-identity.js +2 -1
  72. package/dist/server/app-page-render.d.ts +3 -1
  73. package/dist/server/app-page-render.js +67 -16
  74. package/dist/server/app-page-response.d.ts +7 -0
  75. package/dist/server/app-page-response.js +16 -4
  76. package/dist/server/app-page-route-wiring.js +3 -3
  77. package/dist/server/app-page-stream.d.ts +2 -1
  78. package/dist/server/app-page-stream.js +1 -1
  79. package/dist/server/app-route-handler-execution.d.ts +1 -1
  80. package/dist/server/app-route-tree-prefetch.d.ts +43 -0
  81. package/dist/server/app-route-tree-prefetch.js +187 -0
  82. package/dist/server/app-router-entry.js +1 -1
  83. package/dist/server/app-rsc-cache-busting.d.ts +2 -1
  84. package/dist/server/app-rsc-cache-busting.js +9 -5
  85. package/dist/server/app-rsc-handler.d.ts +6 -0
  86. package/dist/server/app-rsc-handler.js +88 -10
  87. package/dist/server/app-rsc-render-mode.d.ts +3 -5
  88. package/dist/server/app-rsc-render-mode.js +5 -12
  89. package/dist/server/app-rsc-request-normalization.d.ts +3 -4
  90. package/dist/server/app-rsc-request-normalization.js +4 -5
  91. package/dist/server/app-server-action-execution.js +6 -5
  92. package/dist/server/app-ssr-entry.js +1 -1
  93. package/dist/server/cache-control.d.ts +3 -1
  94. package/dist/server/cache-control.js +13 -1
  95. package/dist/server/dev-module-runner.js +1 -1
  96. package/dist/server/dev-origin-check.d.ts +2 -2
  97. package/dist/server/dev-origin-check.js +2 -2
  98. package/dist/server/dev-server.d.ts +11 -1
  99. package/dist/server/dev-server.js +82 -24
  100. package/dist/server/headers.d.ts +6 -2
  101. package/dist/server/headers.js +11 -5
  102. package/dist/server/image-optimization.d.ts +12 -1
  103. package/dist/server/image-optimization.js +13 -1
  104. package/dist/server/isr-cache.d.ts +13 -4
  105. package/dist/server/isr-cache.js +8 -4
  106. package/dist/server/pages-data-route.d.ts +4 -2
  107. package/dist/server/pages-data-route.js +18 -4
  108. package/dist/server/pages-dev-module-url.d.ts +2 -1
  109. package/dist/server/pages-dev-module-url.js +6 -3
  110. package/dist/server/pages-node-compat.d.ts +12 -1
  111. package/dist/server/pages-node-compat.js +50 -1
  112. package/dist/server/pages-page-data.d.ts +9 -0
  113. package/dist/server/pages-page-data.js +18 -10
  114. package/dist/server/pages-page-handler.js +19 -7
  115. package/dist/server/pages-page-response.d.ts +1 -0
  116. package/dist/server/pages-page-response.js +5 -4
  117. package/dist/server/pages-request-pipeline.d.ts +8 -6
  118. package/dist/server/pages-request-pipeline.js +31 -7
  119. package/dist/server/pages-router-entry.js +1 -1
  120. package/dist/server/prerender-manifest.d.ts +15 -1
  121. package/dist/server/prerender-manifest.js +29 -1
  122. package/dist/server/prod-server.d.ts +10 -7
  123. package/dist/server/prod-server.js +43 -28
  124. package/dist/server/request-pipeline.js +1 -1
  125. package/dist/server/static-file-cache.js +1 -1
  126. package/dist/shims/cache-handler.js +8 -1
  127. package/dist/shims/cache.d.ts +1 -1
  128. package/dist/shims/cache.js +3 -0
  129. package/dist/shims/cdn-cache.d.ts +2 -7
  130. package/dist/shims/cdn-cache.js +2 -14
  131. package/dist/shims/fetch-cache.d.ts +3 -1
  132. package/dist/shims/fetch-cache.js +77 -52
  133. package/dist/shims/internal/app-prefetch-fetch-queue.d.ts +10 -0
  134. package/dist/shims/internal/app-prefetch-fetch-queue.js +61 -0
  135. package/dist/shims/internal/hybrid-client-route-owner.d.ts +2 -1
  136. package/dist/shims/internal/hybrid-client-route-owner.js +34 -1
  137. package/dist/shims/internal/pages-data-fetch-dedup.d.ts +2 -1
  138. package/dist/shims/internal/pages-data-fetch-dedup.js +13 -2
  139. package/dist/shims/internal/pages-data-target.d.ts +8 -3
  140. package/dist/shims/internal/pages-data-target.js +9 -4
  141. package/dist/shims/link.d.ts +1 -1
  142. package/dist/shims/link.js +179 -42
  143. package/dist/shims/navigation.d.ts +15 -3
  144. package/dist/shims/navigation.js +202 -39
  145. package/dist/shims/request-context.js +18 -0
  146. package/dist/shims/request-state-types.d.ts +2 -2
  147. package/dist/shims/router.js +31 -15
  148. package/dist/shims/unified-request-context.d.ts +1 -1
  149. package/dist/shims/unified-request-context.js +1 -0
  150. package/dist/typegen.js +1 -1
  151. package/dist/utils/middleware-request-headers.js +1 -1
  152. package/dist/utils/project.d.ts +1 -1
  153. package/dist/utils/protocol-headers.d.ts +7 -1
  154. package/dist/utils/protocol-headers.js +7 -1
  155. package/dist/utils/vite-version.d.ts +4 -12
  156. package/dist/utils/vite-version.js +39 -21
  157. package/package.json +31 -13
  158. package/dist/cloudflare/index.d.ts +0 -3
  159. package/dist/cloudflare/index.js +0 -3
  160. package/dist/packages/cloudflare/src/cache/cdn-adapter.runtime.js +0 -102
  161. package/dist/packages/cloudflare/src/cache/kv-data-adapter.runtime.d.ts +0 -126
  162. package/dist/packages/cloudflare/src/cache/kv-data-adapter.runtime.js +0 -435
  163. package/dist/packages/cloudflare/src/deploy-config.js +0 -150
  164. package/dist/packages/cloudflare/src/deploy-help.js +0 -55
  165. package/dist/packages/cloudflare/src/deploy.js +0 -276
  166. package/dist/packages/cloudflare/src/tpr.d.ts +0 -45
  167. package/dist/packages/cloudflare/src/tpr.js +0 -561
  168. package/dist/packages/cloudflare/src/utils/cache-control-metadata.js +0 -20
@@ -4,6 +4,7 @@ import { applyMiddlewareRequestHeaders, isExternalUrl, matchRedirect, matchRewri
4
4
  import { applyConfigHeadersToHeaderRecord, cloneRequestWithUrl, normalizeTrailingSlash } from "./request-pipeline.js";
5
5
  import { mergeRewriteQuery } from "../utils/query.js";
6
6
  import { normalizeDefaultLocalePathname, stripI18nLocaleForApiRoute } from "./pages-i18n.js";
7
+ import { buildMiddlewarePrefetchSkipResponse } from "./pages-data-route.js";
7
8
  import { mergeHeaders } from "./worker-utils.js";
8
9
  //#region src/server/pages-request-pipeline.ts
9
10
  async function fetchWorkerFilesystemRoute(request, requestPathname, phase, fetchAsset) {
@@ -149,6 +150,15 @@ async function runPagesRequest(request, deps) {
149
150
  if (resolvedLocale && i18nConfig.locales.includes(resolvedLocale)) return matchedPathname === "/" ? `/${resolvedLocale}` : `/${resolvedLocale}${matchedPathname}`;
150
151
  return matchResolvedPathname(matchedPathname);
151
152
  };
153
+ const buildMiddlewarePrefetchSkipResult = (match) => {
154
+ if (!match) return null;
155
+ if (match.route.dataKind !== "server" || !isDataRequest || !deps.hasMiddleware || request.headers.get("x-middleware-prefetch") !== "1") return null;
156
+ return {
157
+ type: "response",
158
+ response: mergeHeaders(buildMiddlewarePrefetchSkipResponse(matchedPathnameForRoute(match.route.pattern)), middlewareHeaders, void 0),
159
+ defaultContentType: "application/json"
160
+ };
161
+ };
152
162
  if (configHeaders.length) applyConfigHeadersToHeaderRecord(middlewareHeaders, {
153
163
  configHeaders,
154
164
  pathname: matchPathname,
@@ -178,14 +188,16 @@ async function runPagesRequest(request, deps) {
178
188
  const beforeFilesResult = await serveFilesystemRoute(resolvedPathname, "beforeFiles");
179
189
  if (beforeFilesResult) return beforeFilesResult;
180
190
  }
181
- if (basePath && !hadBasePath && !configRewriteFired) return {
191
+ const isOutsideBasePathUnclaimed = () => basePath && !hadBasePath && !configRewriteFired;
192
+ const outOfBasePathNotFound = () => ({
182
193
  type: "response",
183
194
  response: new Response("This page could not be found", {
184
195
  status: 404,
185
196
  headers: { "Content-Type": "text/html; charset=utf-8" }
186
197
  })
187
- };
198
+ });
188
199
  const handleResolvedApiRoute = async () => {
200
+ if (isOutsideBasePathUnclaimed()) return null;
189
201
  const apiLookupUrl = stripI18nLocaleForApiRoute(resolvedUrl, i18nConfig);
190
202
  const apiLookupPathname = apiLookupUrl.split("?")[0];
191
203
  if (!apiLookupPathname.startsWith("/api/") && apiLookupPathname !== "/api") return null;
@@ -212,7 +224,7 @@ async function runPagesRequest(request, deps) {
212
224
  };
213
225
  const apiResult = await handleResolvedApiRoute();
214
226
  if (apiResult) return apiResult;
215
- let pageMatch = deps.matchPageRoute ? deps.matchPageRoute(resolvedPathname, request) : null;
227
+ let pageMatch = !isOutsideBasePathUnclaimed() && deps.matchPageRoute ? deps.matchPageRoute(resolvedPathname, request) : null;
216
228
  let resolvedPathnameChanged = false;
217
229
  if (!pageMatch || pageMatch.route.isDynamic) for (const rewrite of configRewrites.afterFiles ?? []) {
218
230
  const rewritten = matchRewrite(matchResolvedPathname(resolvedPathname), [rewrite], rewriteRequestContext(), basePathState);
@@ -223,6 +235,7 @@ async function runPagesRequest(request, deps) {
223
235
  };
224
236
  resolvedUrl = mergeRewriteQuery(resolvedUrl, rewritten);
225
237
  resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
238
+ configRewriteFired = true;
226
239
  resolvedPathnameChanged = true;
227
240
  const afterFilesFilesystemResult = await serveFilesystemRoute(resolvedPathname, "afterFiles");
228
241
  if (afterFilesFilesystemResult) return afterFilesFilesystemResult;
@@ -239,7 +252,7 @@ async function runPagesRequest(request, deps) {
239
252
  refreshDataRewriteHeader();
240
253
  if (typeof deps.renderPage === "function") {
241
254
  let renderPageMatch = pageMatch;
242
- if ((isDataReq || isDataRequest) && !renderPageMatch && configRewrites.fallback?.length) for (const rewrite of configRewrites.fallback) {
255
+ if ((isOutsideBasePathUnclaimed() || isDataReq || isDataRequest) && !renderPageMatch && configRewrites.fallback?.length) for (const rewrite of configRewrites.fallback) {
243
256
  const fallbackRewrite = matchRewrite(matchResolvedPathname(resolvedPathname), [rewrite], rewriteRequestContext(), basePathState);
244
257
  if (!fallbackRewrite) continue;
245
258
  if (isExternalUrl(fallbackRewrite)) return {
@@ -248,6 +261,7 @@ async function runPagesRequest(request, deps) {
248
261
  };
249
262
  resolvedUrl = mergeRewriteQuery(resolvedUrl, fallbackRewrite);
250
263
  resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
264
+ configRewriteFired = true;
251
265
  const fallbackFilesystemResult = await serveFilesystemRoute(resolvedPathname, "fallback");
252
266
  if (fallbackFilesystemResult) return fallbackFilesystemResult;
253
267
  const fallbackApiResult = await handleResolvedApiRoute();
@@ -256,6 +270,9 @@ async function runPagesRequest(request, deps) {
256
270
  refreshDataRewriteHeader();
257
271
  if (renderPageMatch) break;
258
272
  }
273
+ const prefetchSkipResult = buildMiddlewarePrefetchSkipResult(renderPageMatch);
274
+ if (prefetchSkipResult) return prefetchSkipResult;
275
+ if (isOutsideBasePathUnclaimed()) return outOfBasePathNotFound();
259
276
  const shouldDeferErrorPageOnMiss = !isDataReq && !isDataRequest && !!deps.matchPageRoute && !renderPageMatch;
260
277
  const initialRenderOptions = shouldDeferErrorPageOnMiss ? { renderErrorPageOnMiss: false } : isDataReq ? { isDataReq: true } : void 0;
261
278
  const stagedHeaders = new Headers();
@@ -272,6 +289,7 @@ async function runPagesRequest(request, deps) {
272
289
  };
273
290
  resolvedUrl = mergeRewriteQuery(resolvedUrl, fallbackRewrite);
274
291
  resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
292
+ configRewriteFired = true;
275
293
  const fallbackFilesystemResult = await serveFilesystemRoute(resolvedPathname, "fallback");
276
294
  if (fallbackFilesystemResult) return fallbackFilesystemResult;
277
295
  const fallbackApiResult = await handleResolvedApiRoute();
@@ -302,10 +320,11 @@ async function runPagesRequest(request, deps) {
302
320
  return {
303
321
  type: "response",
304
322
  response: merged,
305
- defaultContentType: "text/html"
323
+ defaultContentType: "text/html; charset=utf-8"
306
324
  };
307
325
  }
308
- if (!(resolvedPathnameChanged ? deps.matchPageRoute ? deps.matchPageRoute(resolvedPathname, request) : null : pageMatch) && configRewrites.fallback?.length) for (const rewrite of configRewrites.fallback) {
326
+ let devPageMatch = isOutsideBasePathUnclaimed() ? null : resolvedPathnameChanged ? deps.matchPageRoute ? deps.matchPageRoute(resolvedPathname, request) : null : pageMatch;
327
+ if (!devPageMatch && configRewrites.fallback?.length) for (const rewrite of configRewrites.fallback) {
309
328
  const fallbackRewrite = matchRewrite(matchResolvedPathname(resolvedPathname), [rewrite], rewriteRequestContext(), basePathState);
310
329
  if (!fallbackRewrite) continue;
311
330
  if (isExternalUrl(fallbackRewrite)) return {
@@ -314,12 +333,17 @@ async function runPagesRequest(request, deps) {
314
333
  };
315
334
  resolvedUrl = mergeRewriteQuery(resolvedUrl, fallbackRewrite);
316
335
  resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
336
+ configRewriteFired = true;
317
337
  const fallbackFilesystemResult = await serveFilesystemRoute(resolvedPathname, "fallback");
318
338
  if (fallbackFilesystemResult) return fallbackFilesystemResult;
319
339
  const fallbackApiResult = await handleResolvedApiRoute();
320
340
  if (fallbackApiResult) return fallbackApiResult;
321
- if (deps.matchPageRoute?.(resolvedPathname, request)) break;
341
+ devPageMatch = deps.matchPageRoute?.(resolvedPathname, request) ?? null;
342
+ if (devPageMatch) break;
322
343
  }
344
+ const prefetchSkipResult = buildMiddlewarePrefetchSkipResult(devPageMatch);
345
+ if (prefetchSkipResult) return prefetchSkipResult;
346
+ if (isOutsideBasePathUnclaimed()) return outOfBasePathNotFound();
323
347
  refreshDataRewriteHeader();
324
348
  return {
325
349
  type: "render",
@@ -2,8 +2,8 @@ import { hasBasePath, stripBasePath } from "../utils/base-path.js";
2
2
  import { notFoundStaticAssetResponse } from "./http-error-responses.js";
3
3
  import { isOpenRedirectShaped } from "./open-redirect.js";
4
4
  import { cloneRequestWithHeaders, cloneRequestWithUrl, filterInternalHeaders } from "./request-pipeline.js";
5
- import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, handleConfiguredImageOptimization, isImageOptimizationPath } from "./image-optimization.js";
6
5
  import { assetPrefixPathname, isNextStaticPath } from "../utils/asset-prefix.js";
6
+ import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, handleConfiguredImageOptimization, isImageOptimizationPath } from "./image-optimization.js";
7
7
  import { finalizeMissingStaticAssetResponse } from "./worker-utils.js";
8
8
  import { fetchWorkerFilesystemRoute, runPagesRequest, wrapMiddlewareWithBasePath } from "./pages-request-pipeline.js";
9
9
  import { registerConfiguredCacheAdapters } from "virtual:vinext-cache-adapters";
@@ -15,6 +15,10 @@ type PrerenderManifest = {
15
15
  routes?: PrerenderManifestRoute[];
16
16
  pregeneratedConcretePaths?: Array<[string, string[]]>;
17
17
  };
18
+ type PrerenderedPathSelectionOptions = {
19
+ includeFallbackShells?: boolean;
20
+ includeErrorDocuments?: boolean;
21
+ };
18
22
  declare function readPrerenderManifest(manifestPath: string): PrerenderManifest | null;
19
23
  declare function getRenderedAppRoutes(routes: PrerenderManifestRoute[]): PrerenderManifestRoute[];
20
24
  /**
@@ -31,5 +35,15 @@ declare function isFallbackShellArtifactPath(pathname: string, route?: Prerender
31
35
  * rendered App routes or all routes are fallback-shell artifacts.
32
36
  */
33
37
  declare function buildPregeneratedConcretePathTable(manifest: PrerenderManifest): Array<[string, string[]]>;
38
+ /**
39
+ * Select concrete URL paths that were rendered by the prerender engine.
40
+ *
41
+ * This intentionally includes both App Router and Pages Router entries because
42
+ * deploy-time cache warmup should exercise the same URLs the prerender phase
43
+ * proved are statically renderable. PPR fallback-shell placeholder artifacts
44
+ * and known error documents are excluded by default so warmup does not request
45
+ * synthetic bracket paths or treat a healthy 404 response as a failed warmup.
46
+ */
47
+ declare function getPrerenderedConcretePaths(manifest: PrerenderManifest, options?: PrerenderedPathSelectionOptions): string[];
34
48
  //#endregion
35
- export { PrerenderManifest, buildPregeneratedConcretePathTable, getRenderedAppRoutes, isFallbackShellArtifactPath, readPrerenderManifest };
49
+ export { PrerenderManifest, PrerenderManifestRoute, PrerenderedPathSelectionOptions, buildPregeneratedConcretePathTable, getPrerenderedConcretePaths, getRenderedAppRoutes, isFallbackShellArtifactPath, readPrerenderManifest };
@@ -22,6 +22,9 @@ function groupRoutesByPattern(routes) {
22
22
  }
23
23
  return byPattern;
24
24
  }
25
+ function isErrorDocumentRoute(pathname, route) {
26
+ return pathname === "/404" || pathname === "/500" || pathname === "/_error" || route.route === "/404" || route.route === "/500" || route.route === "/_error";
27
+ }
25
28
  /**
26
29
  * Returns true when `pathname` contains bracket-delimited route params,
27
30
  * indicating it is a fallback-shell placeholder (e.g. `/en/blog/[slug]`)
@@ -50,5 +53,30 @@ function buildPregeneratedConcretePathTable(manifest) {
50
53
  });
51
54
  return Array.from(groupRoutesByPattern(concreteRoutes).entries());
52
55
  }
56
+ /**
57
+ * Select concrete URL paths that were rendered by the prerender engine.
58
+ *
59
+ * This intentionally includes both App Router and Pages Router entries because
60
+ * deploy-time cache warmup should exercise the same URLs the prerender phase
61
+ * proved are statically renderable. PPR fallback-shell placeholder artifacts
62
+ * and known error documents are excluded by default so warmup does not request
63
+ * synthetic bracket paths or treat a healthy 404 response as a failed warmup.
64
+ */
65
+ function getPrerenderedConcretePaths(manifest, options) {
66
+ const routes = manifest.routes;
67
+ if (!routes?.length) return [];
68
+ const paths = [];
69
+ const seen = /* @__PURE__ */ new Set();
70
+ for (const route of routes) {
71
+ if (route.status !== "rendered") continue;
72
+ const pathname = route.path ?? route.route;
73
+ if (!options?.includeFallbackShells && isFallbackShellArtifactPath(pathname, route)) continue;
74
+ if (!options?.includeErrorDocuments && isErrorDocumentRoute(pathname, route)) continue;
75
+ if (seen.has(pathname)) continue;
76
+ seen.add(pathname);
77
+ paths.push(pathname);
78
+ }
79
+ return paths;
80
+ }
53
81
  //#endregion
54
- export { buildPregeneratedConcretePathTable, getRenderedAppRoutes, isFallbackShellArtifactPath, readPrerenderManifest };
82
+ export { buildPregeneratedConcretePathTable, getPrerenderedConcretePaths, getRenderedAppRoutes, isFallbackShellArtifactPath, readPrerenderManifest };
@@ -10,10 +10,9 @@ import { IncomingMessage, ServerResponse } from "node:http";
10
10
  *
11
11
  * The first import of a given path uses the plain file:// URL with NO query
12
12
  * string. This is load-bearing: code-split builds emit lazy chunks that
13
- * import the entry back by bare specifier (default Vite builds on both
14
- * supported majors Rollup on Vite 7 and Rolldown on Vite 8 — hoist modules
15
- * shared between the entry's static graph and lazy route chunks into the
16
- * entry chunk, which the chunks then import as e.g. "../../index.js").
13
+ * import the entry back by bare specifier (default Vite/Rolldown builds hoist
14
+ * modules shared between the entry's static graph and lazy route chunks into
15
+ * the entry chunk, which the chunks then import as e.g. "../../index.js").
17
16
  * Node keys its ESM cache on the full URL including the query string, so if
18
17
  * the server imported the entry as `index.js?t=<mtime>`, a chunk's bare
19
18
  * back-import would evaluate the entire server bundle a second time and
@@ -41,20 +40,24 @@ import { IncomingMessage, ServerResponse } from "node:http";
41
40
  * Exported for direct unit testing of the URL choice.
42
41
  */
43
42
  declare function resolveServerEntryImportUrl(entryPath: string): string;
43
+ declare function rememberCurrentServerEntryImportMtime(entryPath: string): void;
44
44
  declare function importServerEntryModule(entryPath: string): Promise<any>;
45
45
  /** Convert a Node.js IncomingMessage into a ReadableStream for Web Request body. */
46
46
  declare function readNodeStream(req: IncomingMessage): ReadableStream<Uint8Array>;
47
47
  type ProdServerOptions = {
48
48
  /** Port to listen on */port?: number; /** Host to bind to */
49
49
  host?: string; /** Path to the build output directory */
50
- outDir?: string; /** Disable compression (default: false) */
50
+ outDir?: string; /** Explicit App Router RSC entry path. Defaults to `<outDir>/server/index.js`. */
51
+ rscEntryPath?: string; /** Explicit Pages Router server entry path. Defaults to `<outDir>/server/entry.js`. */
52
+ serverEntryPath?: string; /** Disable compression (default: false) */
51
53
  noCompression?: boolean;
52
54
  /**
53
55
  * Narrow startup context for callers that need a more precise log line.
54
56
  * Omitted for normal `vinext start` so the existing production-server output
55
57
  * remains stable.
56
58
  */
57
- purpose?: "prerender";
59
+ purpose?: "prerender"; /** Suppress the startup log for internal child-process servers. */
60
+ silent?: boolean;
58
61
  };
59
62
  /** Content types that benefit from compression. */
60
63
  declare const COMPRESSIBLE_TYPES: Set<string>;
@@ -145,4 +148,4 @@ declare function resolveAppRouterPrerenderSeeder(entryModule: unknown): AppRoute
145
148
  */
146
149
  declare function resolveAppRouterAssetPath(pathname: string, assetPathPrefix: string, assetPrefix: string): string | null;
147
150
  //#endregion
148
- export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, ProdServerOptions, importServerEntryModule, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, readNodeStream, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, resolveServerEntryImportUrl, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic };
151
+ export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, ProdServerOptions, importServerEntryModule, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, readNodeStream, rememberCurrentServerEntryImportMtime, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, resolveServerEntryImportUrl, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic };
@@ -1,24 +1,24 @@
1
1
  import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
2
2
  import { hasBasePath, stripBasePath } from "../utils/base-path.js";
3
- import { VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER } from "../utils/protocol-headers.js";
3
+ import { VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER } from "../utils/protocol-headers.js";
4
4
  import { VINEXT_STATIC_FILE_HEADER } from "./headers.js";
5
5
  import { normalizePath } from "./normalize-path.js";
6
6
  import { notFoundResponse } from "./http-error-responses.js";
7
7
  import { isOpenRedirectShaped } from "./open-redirect.js";
8
8
  import { filterInternalHeaders } from "./request-pipeline.js";
9
9
  import { isUnknownRecord } from "../utils/record.js";
10
- import { buildNextDataNotFoundResponse, isNextDataPathname, parseNextDataPathname } from "./pages-data-route.js";
10
+ import { buildNextDataNotFoundResponse, isNextDataPathname, normalizeNextDataPagePathname, parseNextDataPathname } from "./pages-data-route.js";
11
+ import { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix } from "../utils/asset-prefix.js";
12
+ import { setPagesClientAssets } from "./pages-client-assets.js";
11
13
  import { resolveRequestHost, resolveRequestProtocol, trustProxy, trustedHosts } from "./proxy-trust.js";
12
14
  import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, isImageOptimizationPath, isSafeImageContentType, parseImageParams } from "./image-optimization.js";
13
15
  import { installSocketErrorBackstop } from "./socket-error-backstop.js";
14
- import { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix } from "../utils/asset-prefix.js";
15
16
  import { CONTENT_TYPES, StaticFileCache, etagFromFilenameHash } from "./static-file-cache.js";
16
17
  import { collectInlineCssManifest } from "../build/inline-css.js";
17
18
  import { mergeHeaders } from "./worker-utils.js";
18
19
  import { runPagesRequest, wrapMiddlewareWithBasePath } from "./pages-request-pipeline.js";
19
20
  import { computeClientRuntimeMetadata } from "../utils/client-runtime-metadata.js";
20
21
  import { readTrustedPrerenderRouteParamsFromHeaders, serializePrerenderRouteParamsHeader } from "./prerender-route-params.js";
21
- import { setPagesClientAssets } from "./pages-client-assets.js";
22
22
  import { readPrerenderSecret } from "../build/server-manifest.js";
23
23
  import { seedMemoryCacheFromPrerender } from "./seed-cache.js";
24
24
  import { negotiateEncoding, parseAcceptedEncodings, selectContentEncoding } from "./accept-encoding.js";
@@ -56,16 +56,27 @@ import { createServer } from "node:http";
56
56
  * through a cache-busted URL instead.
57
57
  */
58
58
  const bareServerEntryMtimes = /* @__PURE__ */ new Map();
59
+ function resolveCanonicalServerEntry(entryPath) {
60
+ let canonicalEntryPath;
61
+ try {
62
+ canonicalEntryPath = fs.realpathSync.native(entryPath);
63
+ } catch {
64
+ canonicalEntryPath = entryPath;
65
+ }
66
+ return {
67
+ href: pathToFileURL(canonicalEntryPath).href,
68
+ mtime: fs.statSync(canonicalEntryPath).mtimeMs
69
+ };
70
+ }
59
71
  /**
60
72
  * Import a built server entry module (App Router RSC entry or Pages Router
61
73
  * server entry) by absolute file path.
62
74
  *
63
75
  * The first import of a given path uses the plain file:// URL with NO query
64
76
  * string. This is load-bearing: code-split builds emit lazy chunks that
65
- * import the entry back by bare specifier (default Vite builds on both
66
- * supported majors Rollup on Vite 7 and Rolldown on Vite 8 — hoist modules
67
- * shared between the entry's static graph and lazy route chunks into the
68
- * entry chunk, which the chunks then import as e.g. "../../index.js").
77
+ * import the entry back by bare specifier (default Vite/Rolldown builds hoist
78
+ * modules shared between the entry's static graph and lazy route chunks into
79
+ * the entry chunk, which the chunks then import as e.g. "../../index.js").
69
80
  * Node keys its ESM cache on the full URL including the query string, so if
70
81
  * the server imported the entry as `index.js?t=<mtime>`, a chunk's bare
71
82
  * back-import would evaluate the entire server bundle a second time and
@@ -93,14 +104,7 @@ const bareServerEntryMtimes = /* @__PURE__ */ new Map();
93
104
  * Exported for direct unit testing of the URL choice.
94
105
  */
95
106
  function resolveServerEntryImportUrl(entryPath) {
96
- let canonicalEntryPath;
97
- try {
98
- canonicalEntryPath = fs.realpathSync.native(entryPath);
99
- } catch {
100
- canonicalEntryPath = entryPath;
101
- }
102
- const href = pathToFileURL(canonicalEntryPath).href;
103
- const mtime = fs.statSync(canonicalEntryPath).mtimeMs;
107
+ const { href, mtime } = resolveCanonicalServerEntry(entryPath);
104
108
  const bareMtime = bareServerEntryMtimes.get(href);
105
109
  if (bareMtime === void 0 || bareMtime === mtime) {
106
110
  bareServerEntryMtimes.set(href, mtime);
@@ -108,6 +112,10 @@ function resolveServerEntryImportUrl(entryPath) {
108
112
  }
109
113
  return `${href}?t=${mtime}`;
110
114
  }
115
+ function rememberCurrentServerEntryImportMtime(entryPath) {
116
+ const { href, mtime } = resolveCanonicalServerEntry(entryPath);
117
+ bareServerEntryMtimes.set(href, mtime);
118
+ }
111
119
  async function importServerEntryModule(entryPath) {
112
120
  return import(resolveServerEntryImportUrl(entryPath));
113
121
  }
@@ -557,9 +565,11 @@ function nodeToWebRequest(req, urlOverride, prerenderSecret) {
557
565
  const url = new URL(urlOverride ?? req.url ?? "/", origin);
558
566
  const rawHeaders = nodeHeadersToWebHeaders(req.headers);
559
567
  const prerenderRouteParamsPayload = readTrustedPrerenderRouteParamsFromHeaders(rawHeaders, prerenderSecret);
568
+ const isTrustedSpeculativePrerender = process.env.VINEXT_PRERENDER === "1" && prerenderSecret !== void 0 && rawHeaders.get("x-vinext-prerender-secret") === prerenderSecret && rawHeaders.get("x-vinext-prerender-speculative") === "1";
560
569
  const headers = filterInternalHeaders(rawHeaders);
561
570
  const prerenderRouteParamsHeader = serializePrerenderRouteParamsHeader(prerenderRouteParamsPayload);
562
571
  if (prerenderRouteParamsHeader !== null) headers.set(VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, prerenderRouteParamsHeader);
572
+ if (isTrustedSpeculativePrerender) headers.set(VINEXT_PRERENDER_SPECULATIVE_HEADER, "1");
563
573
  const method = req.method ?? "GET";
564
574
  const hasBody = method !== "GET" && method !== "HEAD";
565
575
  const init = {
@@ -622,12 +632,12 @@ async function sendWebResponse(webResponse, req, res, compress) {
622
632
  */
623
633
  async function startProdServer(options = {}) {
624
634
  installSocketErrorBackstop();
625
- const { port = process.env.PORT ? parseInt(process.env.PORT) : 3e3, host = "0.0.0.0", outDir = path.resolve("dist"), noCompression = false, purpose } = options;
635
+ const { port = process.env.PORT ? parseInt(process.env.PORT) : 3e3, host = "0.0.0.0", outDir = path.resolve("dist"), rscEntryPath: explicitRscEntryPath, serverEntryPath: explicitServerEntryPath, noCompression = false, purpose, silent = false } = options;
626
636
  const compress = !noCompression;
627
637
  const resolvedOutDir = path.resolve(outDir);
628
638
  const clientDir = path.join(resolvedOutDir, "client");
629
- const rscEntryPath = path.join(resolvedOutDir, "server", "index.js");
630
- const serverEntryPath = path.join(resolvedOutDir, "server", "entry.js");
639
+ const rscEntryPath = explicitRscEntryPath ? path.resolve(explicitRscEntryPath) : path.join(resolvedOutDir, "server", "index.js");
640
+ const serverEntryPath = explicitServerEntryPath ? path.resolve(explicitServerEntryPath) : path.join(resolvedOutDir, "server", "entry.js");
631
641
  const isAppRouter = fs.existsSync(rscEntryPath);
632
642
  if (!isAppRouter && !fs.existsSync(serverEntryPath)) {
633
643
  console.error(`[vinext] No build output found in ${outDir}`);
@@ -640,7 +650,8 @@ async function startProdServer(options = {}) {
640
650
  clientDir,
641
651
  rscEntryPath,
642
652
  compress,
643
- purpose
653
+ purpose,
654
+ silent
644
655
  });
645
656
  return startPagesRouterServer({
646
657
  port,
@@ -648,7 +659,8 @@ async function startProdServer(options = {}) {
648
659
  clientDir,
649
660
  serverEntryPath,
650
661
  compress,
651
- purpose
662
+ purpose,
663
+ silent
652
664
  });
653
665
  }
654
666
  function createNodeExecutionContext() {
@@ -776,7 +788,7 @@ function installPagesClientAssets(options) {
776
788
  * 4. Stream the Web Response back (with optional compression)
777
789
  */
778
790
  async function startAppRouterServer(options) {
779
- const { port, host, clientDir, rscEntryPath, compress, purpose } = options;
791
+ const { port, host, clientDir, rscEntryPath, compress, purpose, silent } = options;
780
792
  const prerenderSecret = readPrerenderSecret(path.dirname(rscEntryPath));
781
793
  const rscModule = await importServerEntryModule(rscEntryPath);
782
794
  const rscHandler = resolveAppRouterHandler(rscModule.default);
@@ -899,7 +911,8 @@ async function startAppRouterServer(options) {
899
911
  await new Promise((resolve) => {
900
912
  server.listen(port, host, () => {
901
913
  const addr = server.address();
902
- logProdServerStarted(host, typeof addr === "object" && addr ? addr.port : port, purpose);
914
+ const actualPort = typeof addr === "object" && addr ? addr.port : port;
915
+ if (!silent) logProdServerStarted(host, actualPort, purpose);
903
916
  resolve();
904
917
  });
905
918
  });
@@ -930,7 +943,7 @@ function readPagesServerEntryPageRoutes(value) {
930
943
  * - vinextConfig — embedded next.config.js settings
931
944
  */
932
945
  async function startPagesRouterServer(options) {
933
- const { port, host, clientDir, serverEntryPath, compress, purpose } = options;
946
+ const { port, host, clientDir, serverEntryPath, compress, purpose, silent } = options;
934
947
  const serverEntry = await importServerEntryModule(serverEntryPath);
935
948
  const { renderPage, handleApiRoute: handleApi, runMiddleware, vinextConfig, buildId: pagesBuildId } = serverEntry;
936
949
  const matchPageRoute = typeof serverEntry.matchPageRoute === "function" ? serverEntry.matchPageRoute : void 0;
@@ -1062,8 +1075,9 @@ async function startPagesRouterServer(options) {
1062
1075
  }
1063
1076
  isDataReq = true;
1064
1077
  const qs = url.includes("?") ? url.slice(url.indexOf("?")) : "";
1065
- url = dataMatch.pagePathname + qs;
1066
- pathname = dataMatch.pagePathname;
1078
+ const pagePathname = normalizeNextDataPagePathname(dataMatch.pagePathname, hasMiddleware && trailingSlash);
1079
+ url = pagePathname + qs;
1080
+ pathname = pagePathname;
1067
1081
  }
1068
1082
  const protocol = resolveRequestProtocol(req);
1069
1083
  const hostHeader = resolveRequestHost(req, `${host}:${port}`);
@@ -1144,7 +1158,8 @@ async function startPagesRouterServer(options) {
1144
1158
  await new Promise((resolve) => {
1145
1159
  server.listen(port, host, () => {
1146
1160
  const addr = server.address();
1147
- logProdServerStarted(host, typeof addr === "object" && addr ? addr.port : port, purpose);
1161
+ const actualPort = typeof addr === "object" && addr ? addr.port : port;
1162
+ if (!silent) logProdServerStarted(host, actualPort, purpose);
1148
1163
  resolve();
1149
1164
  });
1150
1165
  });
@@ -1155,4 +1170,4 @@ async function startPagesRouterServer(options) {
1155
1170
  };
1156
1171
  }
1157
1172
  //#endregion
1158
- export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, importServerEntryModule, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, readNodeStream, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, resolveServerEntryImportUrl, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic };
1173
+ export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, importServerEntryModule, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, readNodeStream, rememberCurrentServerEntryImportMtime, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, resolveServerEntryImportUrl, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic };
@@ -338,7 +338,7 @@ function isOriginAllowed(origin, allowed) {
338
338
  */
339
339
  function processMiddlewareHeaders(headers) {
340
340
  const keysToDelete = [];
341
- for (const key of headers.keys()) if (key.startsWith("x-middleware-")) keysToDelete.push(key);
341
+ for (const key of headers.keys()) if (key.startsWith("x-middleware-") && key !== "x-middleware-cache") keysToDelete.push(key);
342
342
  for (const key of keysToDelete) headers.delete(key);
343
343
  }
344
344
  const STRIPPED_INTERNAL_HEADERS = new Set([...INTERNAL_HEADERS, ...VINEXT_INTERNAL_HEADERS]);
@@ -21,7 +21,7 @@ const CONTENT_TYPES = {
21
21
  ".js": "application/javascript",
22
22
  ".mjs": "application/javascript",
23
23
  ".css": "text/css",
24
- ".html": "text/html",
24
+ ".html": "text/html; charset=utf-8",
25
25
  ".json": "application/json",
26
26
  ".txt": "text/plain; charset=utf-8",
27
27
  ".png": "image/png",
@@ -42,6 +42,10 @@ function readStringArrayField(ctx, field) {
42
42
  if (!Array.isArray(value)) return [];
43
43
  return value.filter((item) => typeof item === "string");
44
44
  }
45
+ function readPositiveNumberField(ctx, field) {
46
+ const value = ctx?.[field];
47
+ return typeof value === "number" && value > 0 ? value : void 0;
48
+ }
45
49
  var MemoryCacheHandler = class {
46
50
  store = /* @__PURE__ */ new Map();
47
51
  tagRevalidatedAt = /* @__PURE__ */ new Map();
@@ -89,7 +93,10 @@ var MemoryCacheHandler = class {
89
93
  return null;
90
94
  }
91
95
  this.touchEntry(key, entry);
92
- if (entry.revalidateAt !== null && Date.now() > entry.revalidateAt) return {
96
+ const now = Date.now();
97
+ const requestedRevalidate = readPositiveNumberField(ctx, "revalidate");
98
+ const requestedRevalidateAt = requestedRevalidate === void 0 ? null : entry.lastModified + requestedRevalidate * 1e3;
99
+ if (entry.revalidateAt !== null && now > entry.revalidateAt || requestedRevalidateAt !== null && now > requestedRevalidateAt) return {
93
100
  lastModified: entry.lastModified,
94
101
  value: entry.value,
95
102
  cacheState: "stale",
@@ -1,6 +1,6 @@
1
1
  import { ACTION_DID_REVALIDATE_DYNAMIC_ONLY, ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC, ActionRevalidationKind, CacheLifeConfig, CacheState, UnstableCacheObservation, UnstableCacheRevalidationMode, _consumeRequestScopedCacheLife, _initRequestScopedCacheState, _peekRequestScopedCacheLife, _peekUnstableCacheObservations, _registerCacheContextAccessor, _runWithCacheState, _setRequestScopedCacheLife, cacheLifeProfiles, getAndClearActionRevalidationKind, getRegisteredCacheContext, markActionRevalidation, recordUnstableCacheObservation, shouldServeStaleUnstableCacheEntry } from "./cache-request-state.js";
2
- import { CacheControlMetadata, CacheHandler, CacheHandlerContext, CacheHandlerValue, CachedAppPageValue, CachedFetchValue, CachedImageValue, CachedPagesValue, CachedRedirectValue, CachedRouteValue, IncrementalCacheValue, MemoryCacheHandler, NoOpCacheHandler, configureMemoryCacheHandler, getCacheHandler, getDataCacheHandler, setCacheHandler, setDataCacheHandler } from "./cache-handler.js";
3
2
  import { ExecutionContextLike, getRequestExecutionContext, runWithExecutionContext } from "./request-context.js";
3
+ import { CacheControlMetadata, CacheHandler, CacheHandlerContext, CacheHandlerValue, CachedAppPageValue, CachedFetchValue, CachedImageValue, CachedPagesValue, CachedRedirectValue, CachedRouteValue, IncrementalCacheValue, MemoryCacheHandler, NoOpCacheHandler, configureMemoryCacheHandler, getCacheHandler, getDataCacheHandler, setCacheHandler, setDataCacheHandler } from "./cache-handler.js";
4
4
 
5
5
  //#region src/shims/cache.d.ts
6
6
  /**
@@ -8,6 +8,7 @@ import { makeHangingPromise } from "./internal/make-hanging-promise.js";
8
8
  import { getHeadersAccessPhase, isDraftModeEnabled, markDynamicUsage } from "./headers.js";
9
9
  import { ACTION_DID_REVALIDATE_DYNAMIC_ONLY, ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC, _consumeRequestScopedCacheLife, _initRequestScopedCacheState, _peekRequestScopedCacheLife, _peekUnstableCacheObservations, _registerCacheContextAccessor, _runWithCacheState, _setRequestScopedCacheLife, cacheLifeProfiles, getAndClearActionRevalidationKind, getRegisteredCacheContext, markActionRevalidation, recordUnstableCacheObservation, shouldServeStaleUnstableCacheEntry } from "./cache-request-state.js";
10
10
  import { encodeCacheTag, encodeCacheTags } from "../utils/encode-cache-tag.js";
11
+ import { addCollectedRequestTags } from "./fetch-cache.js";
11
12
  import { workUnitAsyncStorage } from "./internal/work-unit-async-storage.js";
12
13
  //#region src/shims/cache.ts
13
14
  /**
@@ -124,6 +125,7 @@ function updateTag(tag) {
124
125
  * It's provided for API compatibility so apps importing it don't break.
125
126
  */
126
127
  function unstable_noStore() {
128
+ if (isInsideUnstableCacheScope()) return;
127
129
  markDynamicUsage();
128
130
  }
129
131
  /**
@@ -373,6 +375,7 @@ function unstable_cache(fn, keyParts, options) {
373
375
  const revalidateSeconds = options?.revalidate;
374
376
  const cachedFn = async (...args) => {
375
377
  const cacheKey = `unstable_cache:${baseKey}:${JSON.stringify(args)}`;
378
+ addCollectedRequestTags(tags);
376
379
  recordUnstableCacheObservation({
377
380
  kind: "unstable_cache",
378
381
  keyHash: fnv1a64(cacheKey),
@@ -112,13 +112,8 @@ declare class DefaultCdnCacheAdapter implements CdnCacheAdapter {
112
112
  */
113
113
  declare function setCdnCacheAdapter(adapter: CdnCacheAdapter): void;
114
114
  /**
115
- * Get the active CDN cache adapter. See the precedence note above:
116
- * explicit (or an already-resolved auto-detected edge adapter) → origin-managed
117
- * {@link DefaultCdnCacheAdapter}.
118
- *
119
- * Auto-detection of the Cloudflare Workers Cache (`ctx.cache`) only runs when
120
- * `VINEXT_CDN_CACHE_AUTO_DETECT === "1"`; otherwise the default adapter is used
121
- * unless an adapter was set explicitly.
115
+ * Get the active CDN cache adapter. An explicitly configured adapter wins;
116
+ * otherwise the origin-managed {@link DefaultCdnCacheAdapter} is used.
122
117
  */
123
118
  declare function getCdnCacheAdapter(): CdnCacheAdapter;
124
119
  //#endregion
@@ -1,6 +1,4 @@
1
- import { getRequestExecutionContext } from "./request-context.js";
2
1
  import { getDataCacheHandler } from "./cache-handler.js";
3
- import { CloudflareCdnCacheAdapter } from "../packages/cloudflare/src/cache/cdn-adapter.runtime.js";
4
2
  //#region src/shims/cdn-cache.ts
5
3
  /**
6
4
  * CDN cache adapter — owns the *page-level ISR serving strategy*.
@@ -78,22 +76,12 @@ function setCdnCacheAdapter(adapter) {
78
76
  _gCdn[_CDN_KEY] = adapter;
79
77
  }
80
78
  /**
81
- * Get the active CDN cache adapter. See the precedence note above:
82
- * explicit (or an already-resolved auto-detected edge adapter) → origin-managed
83
- * {@link DefaultCdnCacheAdapter}.
84
- *
85
- * Auto-detection of the Cloudflare Workers Cache (`ctx.cache`) only runs when
86
- * `VINEXT_CDN_CACHE_AUTO_DETECT === "1"`; otherwise the default adapter is used
87
- * unless an adapter was set explicitly.
79
+ * Get the active CDN cache adapter. An explicitly configured adapter wins;
80
+ * otherwise the origin-managed {@link DefaultCdnCacheAdapter} is used.
88
81
  */
89
82
  function getCdnCacheAdapter() {
90
83
  const active = _gCdn[_CDN_KEY];
91
84
  if (active) return active;
92
- if (process.env.VINEXT_CDN_CACHE_AUTO_DETECT === "1" && getRequestExecutionContext()?.cache) {
93
- const edge = new CloudflareCdnCacheAdapter();
94
- _gCdn[_CDN_KEY] = edge;
95
- return edge;
96
- }
97
85
  return _defaultAdapter ??= new DefaultCdnCacheAdapter();
98
86
  }
99
87
  //#endregion
@@ -42,6 +42,7 @@ type FetchCacheState = {
42
42
  currentFetchCacheMode: FetchCacheMode | null;
43
43
  currentForceDynamicFetchDefault: boolean;
44
44
  dynamicFetchUrls: Set<string>;
45
+ refreshStaleFetchesInForeground: boolean;
45
46
  isFetchDedupeActive: boolean;
46
47
  currentFetchDedupeEntries: Map<string, FetchDedupeEntry[]>;
47
48
  };
@@ -86,6 +87,7 @@ declare function setCurrentFetchSoftTags(tags: string[]): void;
86
87
  declare function getCurrentFetchSoftTags(): string[];
87
88
  declare function setCurrentFetchCacheMode(mode: FetchCacheMode | null): void;
88
89
  declare function setCurrentForceDynamicFetchDefault(enabled: boolean): void;
90
+ declare function setRefreshStaleFetchesInForeground(enabled: boolean): void;
89
91
  /**
90
92
  * Install the patched fetch and reset per-request tag state.
91
93
  * Returns a cleanup function that clears tags.
@@ -133,4 +135,4 @@ declare function ensureFetchPatch(): void;
133
135
  */
134
136
  declare function getOriginalFetch(): typeof globalThis.fetch;
135
137
  //#endregion
136
- export { FetchCacheMode, FetchCacheState, _resetPendingRefetches, addCollectedRequestTags, consumeDynamicFetchObservations, ensureFetchPatch, getCollectedFetchTags, getCurrentFetchSoftTags, getOriginalFetch, peekCacheableFetchObservations, peekDynamicFetchObservations, runWithFetchCache, runWithFetchDedupe, setCurrentFetchCacheMode, setCurrentFetchSoftTags, setCurrentForceDynamicFetchDefault, withFetchCache };
138
+ export { FetchCacheMode, FetchCacheState, _resetPendingRefetches, addCollectedRequestTags, consumeDynamicFetchObservations, ensureFetchPatch, getCollectedFetchTags, getCurrentFetchSoftTags, getOriginalFetch, peekCacheableFetchObservations, peekDynamicFetchObservations, runWithFetchCache, runWithFetchDedupe, setCurrentFetchCacheMode, setCurrentFetchSoftTags, setCurrentForceDynamicFetchDefault, setRefreshStaleFetchesInForeground, withFetchCache };