vinext 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/README.md +2 -5
  2. package/dist/build/client-build-config.d.ts +7 -1
  3. package/dist/build/client-build-config.js +9 -1
  4. package/dist/check.js +4 -3
  5. package/dist/client/navigation-runtime.d.ts +3 -2
  6. package/dist/client/window-next.d.ts +6 -4
  7. package/dist/config/config-matchers.d.ts +11 -4
  8. package/dist/config/config-matchers.js +15 -2
  9. package/dist/config/next-config.d.ts +13 -0
  10. package/dist/config/next-config.js +2 -0
  11. package/dist/deploy.js +9 -2
  12. package/dist/entries/app-rsc-entry.js +7 -1
  13. package/dist/entries/pages-client-entry.js +1 -1
  14. package/dist/entries/pages-server-entry.js +7 -6
  15. package/dist/index.d.ts +0 -2
  16. package/dist/index.js +86 -78
  17. package/dist/plugins/dynamic-preload-metadata.d.ts +13 -0
  18. package/dist/plugins/dynamic-preload-metadata.js +415 -0
  19. package/dist/plugins/og-assets.js +2 -2
  20. package/dist/plugins/optimize-imports.d.ts +8 -4
  21. package/dist/plugins/optimize-imports.js +16 -12
  22. package/dist/plugins/sass.d.ts +53 -24
  23. package/dist/plugins/sass.js +249 -1
  24. package/dist/plugins/wasm-module-import.d.ts +15 -0
  25. package/dist/plugins/wasm-module-import.js +50 -0
  26. package/dist/routing/app-route-graph.d.ts +23 -1
  27. package/dist/routing/app-route-graph.js +47 -8
  28. package/dist/routing/file-matcher.js +1 -1
  29. package/dist/server/app-browser-entry.js +108 -213
  30. package/dist/server/app-browser-error.d.ts +4 -1
  31. package/dist/server/app-browser-error.js +7 -1
  32. package/dist/server/app-browser-history-controller.d.ts +104 -0
  33. package/dist/server/app-browser-history-controller.js +210 -0
  34. package/dist/server/app-browser-navigation-controller.d.ts +3 -2
  35. package/dist/server/app-browser-navigation-controller.js +10 -7
  36. package/dist/server/app-browser-rsc-redirect.d.ts +11 -2
  37. package/dist/server/app-browser-rsc-redirect.js +30 -8
  38. package/dist/server/app-browser-state.js +4 -7
  39. package/dist/server/app-browser-visible-commit.js +1 -1
  40. package/dist/server/app-fallback-renderer.d.ts +2 -1
  41. package/dist/server/app-fallback-renderer.js +3 -1
  42. package/dist/server/app-middleware.js +1 -0
  43. package/dist/server/app-optimistic-routing.js +22 -1
  44. package/dist/server/app-page-boundary-render.d.ts +2 -1
  45. package/dist/server/app-page-boundary-render.js +4 -2
  46. package/dist/server/app-page-cache.js +9 -7
  47. package/dist/server/app-page-dispatch.d.ts +8 -0
  48. package/dist/server/app-page-dispatch.js +18 -5
  49. package/dist/server/app-page-element-builder.d.ts +22 -2
  50. package/dist/server/app-page-element-builder.js +37 -8
  51. package/dist/server/app-page-execution.d.ts +1 -1
  52. package/dist/server/app-page-execution.js +32 -17
  53. package/dist/server/app-page-render.d.ts +1 -1
  54. package/dist/server/app-page-render.js +7 -14
  55. package/dist/server/app-page-request.d.ts +1 -0
  56. package/dist/server/app-page-request.js +3 -2
  57. package/dist/server/app-page-response.js +1 -1
  58. package/dist/server/app-page-route-wiring.d.ts +3 -1
  59. package/dist/server/app-page-route-wiring.js +8 -7
  60. package/dist/server/app-page-stream.d.ts +1 -6
  61. package/dist/server/app-page-stream.js +1 -4
  62. package/dist/server/app-route-handler-response.js +11 -10
  63. package/dist/server/app-route-handler-runtime.js +12 -1
  64. package/dist/server/app-rsc-handler.js +1 -1
  65. package/dist/server/app-rsc-response-finalizer.js +1 -1
  66. package/dist/server/app-server-action-execution.d.ts +11 -0
  67. package/dist/server/app-server-action-execution.js +5 -2
  68. package/dist/server/app-ssr-entry.js +2 -2
  69. package/dist/server/app-ssr-stream.js +9 -1
  70. package/dist/server/dev-lockfile.js +2 -1
  71. package/dist/server/dev-server.js +43 -12
  72. package/dist/server/headers.d.ts +8 -1
  73. package/dist/server/headers.js +8 -1
  74. package/dist/server/instrumentation-runtime.d.ts +6 -0
  75. package/dist/server/instrumentation-runtime.js +8 -0
  76. package/dist/server/isr-decision.d.ts +79 -0
  77. package/dist/server/isr-decision.js +70 -0
  78. package/dist/server/metadata-route-response.js +5 -3
  79. package/dist/server/middleware-runtime.d.ts +13 -0
  80. package/dist/server/middleware-runtime.js +11 -7
  81. package/dist/server/middleware.js +1 -0
  82. package/dist/server/navigation-planner.d.ts +62 -1
  83. package/dist/server/navigation-planner.js +188 -0
  84. package/dist/server/navigation-trace.d.ts +11 -1
  85. package/dist/server/navigation-trace.js +11 -1
  86. package/dist/server/normalize-path.d.ts +0 -8
  87. package/dist/server/normalize-path.js +3 -1
  88. package/dist/server/otel-tracer-extension.d.ts +45 -0
  89. package/dist/server/otel-tracer-extension.js +89 -0
  90. package/dist/server/pages-api-route.d.ts +14 -3
  91. package/dist/server/pages-api-route.js +6 -1
  92. package/dist/server/pages-asset-tags.d.ts +15 -4
  93. package/dist/server/pages-asset-tags.js +18 -12
  94. package/dist/server/pages-data-route.js +5 -1
  95. package/dist/server/pages-node-compat.d.ts +3 -11
  96. package/dist/server/pages-node-compat.js +174 -121
  97. package/dist/server/pages-page-data.d.ts +28 -0
  98. package/dist/server/pages-page-data.js +61 -17
  99. package/dist/server/pages-page-handler.d.ts +1 -0
  100. package/dist/server/pages-page-handler.js +22 -6
  101. package/dist/server/pages-page-response.d.ts +45 -1
  102. package/dist/server/pages-page-response.js +66 -5
  103. package/dist/server/pages-readiness.d.ts +1 -1
  104. package/dist/server/pages-request-pipeline.d.ts +15 -1
  105. package/dist/server/pages-request-pipeline.js +23 -2
  106. package/dist/server/prod-server.d.ts +39 -1
  107. package/dist/server/prod-server.js +98 -34
  108. package/dist/shims/cache-runtime.js +9 -2
  109. package/dist/shims/dynamic-preload-chunks.d.ts +8 -0
  110. package/dist/shims/dynamic-preload-chunks.js +77 -0
  111. package/dist/shims/dynamic.d.ts +4 -0
  112. package/dist/shims/dynamic.js +4 -2
  113. package/dist/shims/error-boundary.d.ts +4 -4
  114. package/dist/shims/error.js +37 -11
  115. package/dist/shims/fetch-cache.d.ts +9 -1
  116. package/dist/shims/fetch-cache.js +11 -1
  117. package/dist/shims/head.js +6 -1
  118. package/dist/shims/headers.d.ts +16 -2
  119. package/dist/shims/headers.js +37 -1
  120. package/dist/shims/image-config.js +7 -1
  121. package/dist/shims/internal/app-route-detection.d.ts +6 -3
  122. package/dist/shims/internal/app-route-detection.js +10 -6
  123. package/dist/shims/internal/app-router-context.d.ts +5 -0
  124. package/dist/shims/metadata.d.ts +6 -2
  125. package/dist/shims/metadata.js +32 -14
  126. package/dist/shims/navigation.d.ts +7 -16
  127. package/dist/shims/navigation.js +33 -16
  128. package/dist/shims/router.js +28 -1
  129. package/dist/shims/script-nonce-context.d.ts +1 -1
  130. package/dist/shims/script-nonce-context.js +11 -3
  131. package/dist/shims/server.d.ts +17 -1
  132. package/dist/shims/server.js +31 -6
  133. package/dist/shims/slot.js +1 -1
  134. package/dist/shims/unified-request-context.js +1 -0
  135. package/dist/typegen.js +1 -0
  136. package/dist/utils/client-build-manifest.js +15 -5
  137. package/dist/utils/client-runtime-metadata.d.ts +45 -0
  138. package/dist/utils/client-runtime-metadata.js +63 -0
  139. package/dist/utils/hash.d.ts +17 -1
  140. package/dist/utils/hash.js +36 -1
  141. package/dist/utils/lazy-chunks.d.ts +27 -1
  142. package/dist/utils/lazy-chunks.js +65 -1
  143. package/dist/utils/manifest-paths.d.ts +20 -2
  144. package/dist/utils/manifest-paths.js +38 -3
  145. package/dist/utils/path.d.ts +2 -1
  146. package/dist/utils/path.js +5 -1
  147. package/package.json +2 -2
@@ -1,7 +1,10 @@
1
1
  import { createRequestContext, runWithRequestContext } from "../shims/unified-request-context.js";
2
2
  import { patternToNextFormat } from "../routing/route-validation.js";
3
3
  import { getRequestExecutionContext } from "../shims/request-context.js";
4
+ import { NEXTJS_DEPLOYMENT_ID_HEADER } from "./headers.js";
4
5
  import { reportRequestError } from "./instrumentation.js";
6
+ import { NEVER_CACHE_CONTROL } from "./cache-control.js";
7
+ import "./isr-decision.js";
5
8
  import { PRERENDER_REVALIDATE_HEADER, isOnDemandRevalidateRequest, isrCacheKey, isrGet, isrSet, triggerBackgroundRegeneration } from "./isr-cache.js";
6
9
  import { ensureFetchPatch } from "../shims/fetch-cache.js";
7
10
  import { mergeRouteParamsIntoQuery, parseQueryString } from "../utils/query.js";
@@ -140,8 +143,8 @@ function createPagesPageHandler(opts) {
140
143
  });
141
144
  if (methodResponse) return methodResponse;
142
145
  }
143
- const pageModuleUrl = resolveClientModuleUrl(manifest, route.filePath);
144
- const appModuleUrl = resolveClientModuleUrl(manifest, appAssetPath);
146
+ const pageModuleUrl = resolveClientModuleUrl(manifest, route.filePath, vinextConfig.basePath, vinextConfig.assetPrefix);
147
+ const appModuleUrl = resolveClientModuleUrl(manifest, appAssetPath, vinextConfig.basePath, vinextConfig.assetPrefix);
145
148
  const serializedPagesNextData = {
146
149
  ...pagesNextData,
147
150
  __vinext: {
@@ -163,6 +166,7 @@ function createPagesPageHandler(opts) {
163
166
  err,
164
167
  applyRequestContexts: applySSRContext,
165
168
  buildId,
169
+ deploymentId: process.env.__VINEXT_DEPLOYMENT_ID || process.env.NEXT_DEPLOYMENT_ID,
166
170
  createGsspReqRes() {
167
171
  return createPagesReqRes({
168
172
  body: void 0,
@@ -203,7 +207,10 @@ function createPagesPageHandler(opts) {
203
207
  statusCode: renderStatusCode,
204
208
  triggerBackgroundRegeneration,
205
209
  vinext: serializedPagesNextData.__vinext,
206
- nextData: serializedPagesNextData
210
+ nextData: serializedPagesNextData,
211
+ userAgent: request.headers.get("user-agent") ?? void 0,
212
+ ifNoneMatch: request.headers.get("if-none-match") ?? void 0,
213
+ requestCacheControl: request.headers.get("cache-control") ?? void 0
207
214
  });
208
215
  if (pageDataResult.kind === "notFound") {
209
216
  const notFoundRoute = findNotFoundRoute();
@@ -251,7 +258,11 @@ function createPagesPageHandler(opts) {
251
258
  hasUserCacheControl = true;
252
259
  break;
253
260
  }
254
- if (!hasUserCacheControl) init.headers["Cache-Control"] = "private, no-cache, no-store, max-age=0, must-revalidate";
261
+ if (!hasUserCacheControl) init.headers["Cache-Control"] = NEVER_CACHE_CONTROL;
262
+ }
263
+ if (routePattern !== "/_error" && routePattern !== "/500") {
264
+ const deploymentId = process.env.__VINEXT_DEPLOYMENT_ID || process.env.NEXT_DEPLOYMENT_ID;
265
+ if (deploymentId) init.headers[NEXTJS_DEPLOYMENT_ID_HEADER] = deploymentId;
255
266
  }
256
267
  return buildNextDataJsonResponse(pageProps, safeJsonStringify, init);
257
268
  }
@@ -263,7 +274,9 @@ function createPagesPageHandler(opts) {
263
274
  manifest,
264
275
  moduleIds: pageModuleIds,
265
276
  scriptNonce,
266
- disableOptimizedLoading: vinextConfig.disableOptimizedLoading
277
+ disableOptimizedLoading: vinextConfig.disableOptimizedLoading,
278
+ basePath: vinextConfig.basePath,
279
+ assetPrefix: vinextConfig.assetPrefix
267
280
  }),
268
281
  buildId,
269
282
  clearSsrContext() {
@@ -305,7 +318,10 @@ function createPagesPageHandler(opts) {
305
318
  safeJsonStringify,
306
319
  scriptNonce,
307
320
  statusCode: renderStatusCode,
308
- nextData: serializedPagesNextData
321
+ nextData: serializedPagesNextData,
322
+ userAgent: request.headers.get("user-agent") ?? void 0,
323
+ ifNoneMatch: request.headers.get("if-none-match") ?? void 0,
324
+ requestCacheControl: request.headers.get("cache-control") ?? void 0
309
325
  });
310
326
  } catch (e) {
311
327
  console.error("[vinext] SSR error:", e);
@@ -4,6 +4,28 @@ import { RenderPageEnhancers } from "./pages-document-initial-props.js";
4
4
  import { ComponentType, ReactNode } from "react";
5
5
 
6
6
  //#region src/server/pages-page-response.d.ts
7
+ /**
8
+ * Returns true when the User-Agent belongs to a bot or crawler that cannot
9
+ * reliably consume a streamed HTML response.
10
+ */
11
+ declare function isPagesStreamingBot(userAgent: string): boolean;
12
+ declare function generatePagesETag(payload: string): string;
13
+ /**
14
+ * Mirrors Next.js `sendEtagResponse` semantics (weak/strong comparison).
15
+ *
16
+ * A weak ETag `W/"..."` matches both `W/"..."` and `"..."` in `If-None-Match`.
17
+ * A strong ETag `"..."` only matches the same strong token.
18
+ * `*` always matches.
19
+ */
20
+ declare function etagMatches(etag: string, ifNoneMatch: string): boolean;
21
+ /**
22
+ * Returns true when a request `Cache-Control` header asks to bypass the 304
23
+ * short-circuit. Mirrors the `fresh` package's check used by Next.js's
24
+ * `sendEtagResponse` (`/(?:^|,)\s*?no-cache\s*?(?:,|$)/`). Shared by the
25
+ * fresh-MISS bot path here and the ISR HIT/STALE paths in
26
+ * `pages-page-data.ts` so the two cannot drift.
27
+ */
28
+ declare function requestsNoCache(cacheControl: string | undefined): boolean;
7
29
  type PagesFontPreload = {
8
30
  href: string;
9
31
  type: string;
@@ -82,10 +104,32 @@ type RenderPagesPageResponseOptions = {
82
104
  statusCode?: number;
83
105
  vinext?: VinextNextData["__vinext"];
84
106
  nextData?: PagesNextDataExtras;
107
+ /**
108
+ * The request's User-Agent string (from `request.headers.get('user-agent')`).
109
+ * When this matches a known crawler / bot pattern, the response is fully
110
+ * buffered before sending so bots receive a single complete HTML chunk with
111
+ * an ETag header. Omitting this field disables bot-detection (streaming as
112
+ * normal), which is the correct behaviour for non-HTML requests and tests.
113
+ */
114
+ userAgent?: string;
115
+ /**
116
+ * The incoming request's `If-None-Match` header value. When set and the
117
+ * computed ETag matches (weak-ETag semantics, mirroring Next.js's
118
+ * `sendEtagResponse`), a `304 Not Modified` is returned with an empty body.
119
+ * Only evaluated on bot/buffered responses that carry an ETag.
120
+ */
121
+ ifNoneMatch?: string;
122
+ /**
123
+ * The incoming request's `Cache-Control` header value. When the value
124
+ * contains `no-cache`, the 304 short-circuit is skipped and a full 200
125
+ * response is always returned — mirroring the `fresh` package used by
126
+ * Next.js's `sendEtagResponse`.
127
+ */
128
+ requestCacheControl?: string;
85
129
  };
86
130
  declare function buildPagesNextDataScript(options: Pick<RenderPagesPageResponseOptions, "buildId" | "i18n" | "isFallback" | "pageProps" | "params" | "routePattern" | "safeJsonStringify" | "scriptNonce" | "nextData"> & {
87
131
  vinext?: VinextNextData["__vinext"];
88
132
  }): string;
89
133
  declare function renderPagesPageResponse(options: RenderPagesPageResponseOptions): Promise<Response>;
90
134
  //#endregion
91
- export { PagesGsspResponse, PagesI18nRenderContext, PagesNextDataExtras, buildPagesNextDataScript, renderPagesPageResponse };
135
+ export { PagesGsspResponse, PagesI18nRenderContext, PagesNextDataExtras, buildPagesNextDataScript, etagMatches, generatePagesETag, isPagesStreamingBot, renderPagesPageResponse, requestsNoCache };
@@ -1,16 +1,64 @@
1
1
  import { getRequestExecutionContext } from "../shims/request-context.js";
2
2
  import { reportRequestError } from "./instrumentation.js";
3
3
  import { setCacheStateHeaders } from "./cache-headers.js";
4
+ import { fnv1a52 } from "../utils/hash.js";
4
5
  import { encodeCacheTag } from "../utils/encode-cache-tag.js";
6
+ import { NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL, applyCdnResponseHeaders } from "./cache-control.js";
7
+ import { buildMissIsrCacheControl } from "./isr-decision.js";
5
8
  import { withScriptNonce } from "../shims/script-nonce-context.js";
6
9
  import { createInlineScriptTag, createNonceAttribute, escapeHtmlAttr } from "./html.js";
7
10
  import { getClientTraceMetadataHTML } from "./client-trace-metadata.js";
8
11
  import { readStreamAsText } from "../utils/text-stream.js";
9
12
  import { loadUserDocumentInitialProps, runDocumentRenderPage } from "./pages-document-initial-props.js";
10
13
  import { callDocumentGetInitialProps } from "./document-initial-head.js";
11
- import { applyCdnResponseHeaders, buildRevalidateCacheControl } from "./cache-control.js";
12
14
  import React from "react";
13
15
  //#region src/server/pages-page-response.ts
16
+ /**
17
+ * Crawlers that cannot handle streamed HTML: they read metadata only from
18
+ * the first network chunk, so streaming would give them an incomplete <head>.
19
+ * Pattern sourced from Next.js html-bots.ts (updated to match the canary).
20
+ */
21
+ const HTML_LIMITED_BOT_UA_RE = /[\w-]+-Google|Google-[\w-]+|Chrome-Lighthouse|Slurp|DuckDuckBot|baiduspider|yandex|sogou|bitlybot|tumblr|vkShare|quora link preview|redditbot|ia_archiver|Bingbot|BingPreview|applebot|facebookexternalhit|facebookcatalog|Twitterbot|LinkedInBot|Slackbot|Discordbot|WhatsApp|SkypeUriPreview|Yeti|googleweblight/i;
22
+ /**
23
+ * Googlebot (the main search crawler) executes JavaScript via a headless
24
+ * browser, so it too cannot safely handle mid-stream HTML mutations.
25
+ * Matches "Googlebot" but NOT suffixed variants like "Googlebot-Image".
26
+ */
27
+ const HEADLESS_BROWSER_BOT_UA_RE = /Googlebot(?!-)|Googlebot$/i;
28
+ /**
29
+ * Returns true when the User-Agent belongs to a bot or crawler that cannot
30
+ * reliably consume a streamed HTML response.
31
+ */
32
+ function isPagesStreamingBot(userAgent) {
33
+ return HEADLESS_BROWSER_BOT_UA_RE.test(userAgent) || HTML_LIMITED_BOT_UA_RE.test(userAgent);
34
+ }
35
+ function generatePagesETag(payload) {
36
+ return "\"" + fnv1a52(payload).toString(36) + payload.length.toString(36) + "\"";
37
+ }
38
+ /**
39
+ * Mirrors Next.js `sendEtagResponse` semantics (weak/strong comparison).
40
+ *
41
+ * A weak ETag `W/"..."` matches both `W/"..."` and `"..."` in `If-None-Match`.
42
+ * A strong ETag `"..."` only matches the same strong token.
43
+ * `*` always matches.
44
+ */
45
+ function etagMatches(etag, ifNoneMatch) {
46
+ if (ifNoneMatch === "*") return true;
47
+ const normalize = (t) => t.replace(/^W\//, "");
48
+ const etagNorm = normalize(etag.trim());
49
+ for (const token of ifNoneMatch.split(",")) if (normalize(token.trim()) === etagNorm) return true;
50
+ return false;
51
+ }
52
+ /**
53
+ * Returns true when a request `Cache-Control` header asks to bypass the 304
54
+ * short-circuit. Mirrors the `fresh` package's check used by Next.js's
55
+ * `sendEtagResponse` (`/(?:^|,)\s*?no-cache\s*?(?:,|$)/`). Shared by the
56
+ * fresh-MISS bot path here and the ISR HIT/STALE paths in
57
+ * `pages-page-data.ts` so the two cannot drift.
58
+ */
59
+ function requestsNoCache(cacheControl) {
60
+ return /(?:^|,)\s*no-cache\s*(?:,|$)/.test(cacheControl ?? "");
61
+ }
14
62
  function buildPagesFontHeadHtml(fontLinks, fontPreloads, fontStyles, scriptNonce) {
15
63
  let html = "";
16
64
  const nonceAttr = createNonceAttribute(scriptNonce);
@@ -201,21 +249,34 @@ async function renderPagesPageResponse(options) {
201
249
  }
202
250
  const compositeStream = await buildPagesCompositeStream(responseBodyStream, shellPrefix, shellSuffix);
203
251
  const userSetCacheControl = responseHeaders.has("Cache-Control");
204
- if (options.scriptNonce) responseHeaders.set("Cache-Control", "no-store, must-revalidate");
252
+ if (options.scriptNonce) responseHeaders.set("Cache-Control", NO_STORE_CACHE_CONTROL);
205
253
  else if (options.isrRevalidateSeconds) {
206
254
  const isrPathname = options.routeUrl.split("?")[0];
207
255
  const stem = isrPathname.endsWith("/") ? isrPathname.slice(0, -1) : isrPathname;
208
256
  applyCdnResponseHeaders(responseHeaders, {
209
- cacheControl: buildRevalidateCacheControl(options.isrRevalidateSeconds, options.expireSeconds),
257
+ cacheControl: buildMissIsrCacheControl(options.isrRevalidateSeconds, options.expireSeconds),
210
258
  tags: [encodeCacheTag(`_N_T_${stem || "/"}`)]
211
259
  });
212
260
  setCacheStateHeaders(responseHeaders, "MISS");
213
- } else if (options.gsspRes && !userSetCacheControl) responseHeaders.set("Cache-Control", "private, no-cache, no-store, max-age=0, must-revalidate");
261
+ } else if (options.gsspRes && !userSetCacheControl) responseHeaders.set("Cache-Control", NEVER_CACHE_CONTROL);
214
262
  if (options.fontLinkHeader) responseHeaders.set("Link", options.fontLinkHeader);
263
+ if (options.userAgent && isPagesStreamingBot(options.userAgent)) {
264
+ const fullHtml = await readStreamAsText(compositeStream);
265
+ const etag = generatePagesETag(fullHtml);
266
+ responseHeaders.set("ETag", etag);
267
+ if (!requestsNoCache(options.requestCacheControl) && options.ifNoneMatch && etagMatches(etag, options.ifNoneMatch)) return new Response(null, {
268
+ status: 304,
269
+ headers: responseHeaders
270
+ });
271
+ return new Response(fullHtml, {
272
+ status: finalStatus,
273
+ headers: responseHeaders
274
+ });
275
+ }
215
276
  return Object.assign(new Response(compositeStream, {
216
277
  status: finalStatus,
217
278
  headers: responseHeaders
218
279
  }), { __vinextStreamedHtmlResponse: true });
219
280
  }
220
281
  //#endregion
221
- export { buildPagesNextDataScript, renderPagesPageResponse };
282
+ export { buildPagesNextDataScript, etagMatches, generatePagesETag, isPagesStreamingBot, renderPagesPageResponse, requestsNoCache };
@@ -33,4 +33,4 @@ declare function buildPagesReadinessNextData(options: {
33
33
  hasRewrites: boolean;
34
34
  }): PagesReadinessNextData;
35
35
  //#endregion
36
- export { PagesReadinessNextData, buildPagesReadinessNextData };
36
+ export { buildPagesReadinessNextData };
@@ -62,6 +62,20 @@ type PagesPipelineDeps = {
62
62
  */
63
63
  serveStaticFile?: ((requestPathname: string, stagedHeaders: HeaderRecord) => Promise<boolean>) | null;
64
64
  };
65
+ /**
66
+ * Wrap an adapter's `runMiddleware` callback so middleware receives the original
67
+ * (pre-basePath-stripping) URL. Adapters strip the basePath before handing the
68
+ * request to `runPagesRequest`, but Next.js passes the un-stripped URL to the
69
+ * middleware adapter so `request.nextUrl.basePath` reflects whether the URL
70
+ * actually had the basePath prefix. Requests outside the basePath
71
+ * (`hadBasePath === false`) are passed through untouched so middleware sees
72
+ * `nextUrl.basePath === ""` and can redirect them into the basePath
73
+ * (see the middleware-base-path e2e test / #1830).
74
+ *
75
+ * Shared by the Node prod server (prod-server.ts) and the generated Pages
76
+ * Router worker entry (deploy.ts) to keep the two adapters in sync.
77
+ */
78
+ declare function wrapMiddlewareWithBasePath(runMiddleware: NonNullable<PagesPipelineDeps["runMiddleware"]>, basePath: string, hadBasePath: boolean): NonNullable<PagesPipelineDeps["runMiddleware"]>;
65
79
  type PagesPipelineResult = {
66
80
  type: "response";
67
81
  response: Response;
@@ -96,4 +110,4 @@ type PagesPipelineResult = {
96
110
  */
97
111
  declare function runPagesRequest(request: Request, deps: PagesPipelineDeps): Promise<PagesPipelineResult>;
98
112
  //#endregion
99
- export { MiddlewareResult, PagesPipelineDeps, PagesPipelineResult, PagesRenderOptions, runPagesRequest };
113
+ export { MiddlewareResult, PagesPipelineDeps, PagesPipelineResult, PagesRenderOptions, runPagesRequest, wrapMiddlewareWithBasePath };
@@ -1,4 +1,4 @@
1
- import { hasBasePath } from "../utils/base-path.js";
1
+ import { addBasePathToPathname, hasBasePath } from "../utils/base-path.js";
2
2
  import { applyMiddlewareRequestHeaders, isExternalUrl, matchRedirect, matchRewrite, preserveRedirectDestinationQuery, proxyExternalRequest, requestContextFromRequest, sanitizeDestination } from "../config/config-matchers.js";
3
3
  import { applyConfigHeadersToHeaderRecord, normalizeTrailingSlash } from "./request-pipeline.js";
4
4
  import { mergeRewriteQuery } from "../utils/query.js";
@@ -6,6 +6,27 @@ import { normalizeDefaultLocalePathname, stripI18nLocaleForApiRoute } from "./pa
6
6
  import { mergeHeaders } from "./worker-utils.js";
7
7
  //#region src/server/pages-request-pipeline.ts
8
8
  /**
9
+ * Wrap an adapter's `runMiddleware` callback so middleware receives the original
10
+ * (pre-basePath-stripping) URL. Adapters strip the basePath before handing the
11
+ * request to `runPagesRequest`, but Next.js passes the un-stripped URL to the
12
+ * middleware adapter so `request.nextUrl.basePath` reflects whether the URL
13
+ * actually had the basePath prefix. Requests outside the basePath
14
+ * (`hadBasePath === false`) are passed through untouched so middleware sees
15
+ * `nextUrl.basePath === ""` and can redirect them into the basePath
16
+ * (see the middleware-base-path e2e test / #1830).
17
+ *
18
+ * Shared by the Node prod server (prod-server.ts) and the generated Pages
19
+ * Router worker entry (deploy.ts) to keep the two adapters in sync.
20
+ */
21
+ function wrapMiddlewareWithBasePath(runMiddleware, basePath, hadBasePath) {
22
+ if (!hadBasePath || !basePath) return runMiddleware;
23
+ return (request, ctx, opts) => {
24
+ const mwUrl = new URL(request.url);
25
+ mwUrl.pathname = addBasePathToPathname(mwUrl.pathname, basePath);
26
+ return runMiddleware(new Request(mwUrl, request), ctx, opts);
27
+ };
28
+ }
29
+ /**
9
30
  * Run the Pages Router request pipeline.
10
31
  *
11
32
  * ASSUMPTION: request already has internal headers filtered and basePath stripped.
@@ -206,4 +227,4 @@ async function runPagesRequest(request, deps) {
206
227
  };
207
228
  }
208
229
  //#endregion
209
- export { runPagesRequest };
230
+ export { runPagesRequest, wrapMiddlewareWithBasePath };
@@ -3,6 +3,44 @@ import { resolveRequestHost, trustProxy, trustedHosts } from "./proxy-trust.js";
3
3
  import { IncomingMessage, ServerResponse } from "node:http";
4
4
 
5
5
  //#region src/server/prod-server.d.ts
6
+ /**
7
+ * Import a built server entry module (App Router RSC entry or Pages Router
8
+ * server entry) by absolute file path.
9
+ *
10
+ * The first import of a given path uses the plain file:// URL with NO query
11
+ * string. This is load-bearing: code-split builds emit lazy chunks that
12
+ * import the entry back by bare specifier (default Vite builds on both
13
+ * supported majors — Rollup on Vite 7 and Rolldown on Vite 8 — hoist modules
14
+ * shared between the entry's static graph and lazy route chunks into the
15
+ * entry chunk, which the chunks then import as e.g. "../../index.js").
16
+ * Node keys its ESM cache on the full URL including the query string, so if
17
+ * the server imported the entry as `index.js?t=<mtime>`, a chunk's bare
18
+ * back-import would evaluate the entire server bundle a second time and
19
+ * module-level singletons (db pools, service registries) would silently
20
+ * diverge between the two copies. See
21
+ * https://github.com/cloudflare/vinext/issues/1923.
22
+ *
23
+ * A `?t=<mtime>` query string is appended only when the same path is
24
+ * imported again after a rebuild (different mtime) — e.g. test suites that
25
+ * rebuild a fixture to the same output path within one process — where the
26
+ * bare URL's cache entry would return the stale previous build. Note this
27
+ * rebuild branch trades the single-instance guarantee back: chunks that
28
+ * import the entry by bare path still resolve to the FIRST build's cache
29
+ * entry, so freshness and single-instance only hold together on the first
30
+ * import of a path. Production processes import each entry path exactly
31
+ * once and always get both.
32
+ *
33
+ * The entry is imported via its canonical real path: the bundler
34
+ * canonicalizes module ids with fs.realpathSync.native, so chunks evaluate
35
+ * under realpath-based URLs and their relative imports resolve to realpath
36
+ * URLs too. Importing the entry through a symlinked path (macOS /var/...
37
+ * tmpdirs, symlinked deploy directories) would otherwise create a second
38
+ * instance keyed on the symlinked URL.
39
+ *
40
+ * Exported for direct unit testing of the URL choice.
41
+ */
42
+ declare function resolveServerEntryImportUrl(entryPath: string): string;
43
+ declare function importServerEntryModule(entryPath: string): Promise<any>;
6
44
  type ProdServerOptions = {
7
45
  /** Port to listen on */port?: number; /** Host to bind to */
8
46
  host?: string; /** Path to the build output directory */
@@ -105,4 +143,4 @@ declare function resolveAppRouterPrerenderSeeder(entryModule: unknown): AppRoute
105
143
  */
106
144
  declare function resolveAppRouterAssetPath(pathname: string, assetPathPrefix: string, assetPrefix: string): string | null;
107
145
  //#endregion
108
- export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, ProdServerOptions, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic };
146
+ export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, ProdServerOptions, importServerEntryModule, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, resolveServerEntryImportUrl, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic };
@@ -8,17 +8,14 @@ import { isUnknownRecord } from "../utils/record.js";
8
8
  import { resolveRequestHost, resolveRequestProtocol, trustProxy, trustedHosts } from "./proxy-trust.js";
9
9
  import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, isImageOptimizationPath, isSafeImageContentType, parseImageParams } from "./image-optimization.js";
10
10
  import { installSocketErrorBackstop } from "./socket-error-backstop.js";
11
- import { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix, resolveAssetsDir } from "../utils/asset-prefix.js";
11
+ import { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix } from "../utils/asset-prefix.js";
12
12
  import { CONTENT_TYPES, StaticFileCache, etagFromFilenameHash } from "./static-file-cache.js";
13
13
  import { buildNextDataNotFoundResponse, isNextDataPathname, parseNextDataPathname } from "./pages-data-route.js";
14
14
  import { collectInlineCssManifest } from "../build/inline-css.js";
15
15
  import { mergeHeaders } from "./worker-utils.js";
16
- import { runPagesRequest } from "./pages-request-pipeline.js";
17
- import { manifestFileWithBase } from "../utils/manifest-paths.js";
16
+ import { runPagesRequest, wrapMiddlewareWithBasePath } from "./pages-request-pipeline.js";
18
17
  import { readTrustedPrerenderRouteParamsFromHeaders, serializePrerenderRouteParamsHeader } from "./prerender-route-params.js";
19
- import { computeLazyChunks } from "../utils/lazy-chunks.js";
20
- import { findClientEntryFile, findPagesClientEntryFile, readClientBuildManifest } from "../utils/client-build-manifest.js";
21
- import { findClientEntryFileFromVinextManifest, findPagesClientEntryFileFromVinextManifest, readClientEntryManifest } from "../utils/client-entry-manifest.js";
18
+ import { computeClientRuntimeMetadata } from "../utils/client-runtime-metadata.js";
22
19
  import { readPrerenderSecret } from "../build/server-manifest.js";
23
20
  import { seedMemoryCacheFromPrerender } from "./seed-cache.js";
24
21
  import fs from "node:fs";
@@ -48,6 +45,68 @@ import { Readable, pipeline } from "node:stream";
48
45
  * - dist/server/index.js — RSC entry (default export: handler(Request) → Response)
49
46
  * - dist/server/ssr/index.js — SSR entry (imported by RSC entry at runtime)
50
47
  */
48
+ /**
49
+ * mtime of the build each bare (query-less) server-entry URL was first
50
+ * imported from in this process. Node's ESM cache pins a bare URL to that
51
+ * build forever, so rebuilds to the same path must be detected and loaded
52
+ * through a cache-busted URL instead.
53
+ */
54
+ const bareServerEntryMtimes = /* @__PURE__ */ new Map();
55
+ /**
56
+ * Import a built server entry module (App Router RSC entry or Pages Router
57
+ * server entry) by absolute file path.
58
+ *
59
+ * The first import of a given path uses the plain file:// URL with NO query
60
+ * string. This is load-bearing: code-split builds emit lazy chunks that
61
+ * import the entry back by bare specifier (default Vite builds on both
62
+ * supported majors — Rollup on Vite 7 and Rolldown on Vite 8 — hoist modules
63
+ * shared between the entry's static graph and lazy route chunks into the
64
+ * entry chunk, which the chunks then import as e.g. "../../index.js").
65
+ * Node keys its ESM cache on the full URL including the query string, so if
66
+ * the server imported the entry as `index.js?t=<mtime>`, a chunk's bare
67
+ * back-import would evaluate the entire server bundle a second time and
68
+ * module-level singletons (db pools, service registries) would silently
69
+ * diverge between the two copies. See
70
+ * https://github.com/cloudflare/vinext/issues/1923.
71
+ *
72
+ * A `?t=<mtime>` query string is appended only when the same path is
73
+ * imported again after a rebuild (different mtime) — e.g. test suites that
74
+ * rebuild a fixture to the same output path within one process — where the
75
+ * bare URL's cache entry would return the stale previous build. Note this
76
+ * rebuild branch trades the single-instance guarantee back: chunks that
77
+ * import the entry by bare path still resolve to the FIRST build's cache
78
+ * entry, so freshness and single-instance only hold together on the first
79
+ * import of a path. Production processes import each entry path exactly
80
+ * once and always get both.
81
+ *
82
+ * The entry is imported via its canonical real path: the bundler
83
+ * canonicalizes module ids with fs.realpathSync.native, so chunks evaluate
84
+ * under realpath-based URLs and their relative imports resolve to realpath
85
+ * URLs too. Importing the entry through a symlinked path (macOS /var/...
86
+ * tmpdirs, symlinked deploy directories) would otherwise create a second
87
+ * instance keyed on the symlinked URL.
88
+ *
89
+ * Exported for direct unit testing of the URL choice.
90
+ */
91
+ function resolveServerEntryImportUrl(entryPath) {
92
+ let canonicalEntryPath;
93
+ try {
94
+ canonicalEntryPath = fs.realpathSync.native(entryPath);
95
+ } catch {
96
+ canonicalEntryPath = entryPath;
97
+ }
98
+ const href = pathToFileURL(canonicalEntryPath).href;
99
+ const mtime = fs.statSync(canonicalEntryPath).mtimeMs;
100
+ const bareMtime = bareServerEntryMtimes.get(href);
101
+ if (bareMtime === void 0 || bareMtime === mtime) {
102
+ bareServerEntryMtimes.set(href, mtime);
103
+ return href;
104
+ }
105
+ return `${href}?t=${mtime}`;
106
+ }
107
+ async function importServerEntryModule(entryPath) {
108
+ return import(resolveServerEntryImportUrl(entryPath));
109
+ }
51
110
  /** Convert a Node.js IncomingMessage into a ReadableStream for Web Request body. */
52
111
  function readNodeStream(req) {
53
112
  return new ReadableStream({ start(controller) {
@@ -158,8 +217,14 @@ const NO_BODY_RESPONSE_STATUSES = new Set([
158
217
  205,
159
218
  304
160
219
  ]);
161
- function omitHeadersCaseInsensitive(headersRecord, names) {
162
- const targets = new Set(names.map((name) => name.toLowerCase()));
220
+ const OMIT_BODY_HEADERS = new Set(["content-length", "content-type"]);
221
+ const OMIT_STATIC_RESPONSE_HEADERS = new Set([
222
+ VINEXT_STATIC_FILE_HEADER,
223
+ "content-encoding",
224
+ "content-length",
225
+ "content-type"
226
+ ]);
227
+ function omitHeadersCaseInsensitive(headersRecord, targets) {
163
228
  const filtered = {};
164
229
  for (const [key, value] of Object.entries(headersRecord)) {
165
230
  if (targets.has(key.toLowerCase())) continue;
@@ -172,6 +237,15 @@ function matchesIfNoneMatchHeader(ifNoneMatch, etag) {
172
237
  if (ifNoneMatch === "*") return true;
173
238
  return ifNoneMatch.split(",").map((value) => value.trim()).some((value) => value === etag);
174
239
  }
240
+ function installClientBuildManifestGlobals(clientDir, assetBase, assetPrefix) {
241
+ const metadata = computeClientRuntimeMetadata({
242
+ clientDir,
243
+ assetBase,
244
+ assetPrefix
245
+ });
246
+ globalThis.__VINEXT_LAZY_CHUNKS__ = metadata.lazyChunks;
247
+ globalThis.__VINEXT_DYNAMIC_PRELOADS__ = metadata.dynamicPreloads;
248
+ }
175
249
  function isNoBodyResponseStatus(status) {
176
250
  return NO_BODY_RESPONSE_STATUSES.has(status);
177
251
  }
@@ -212,7 +286,7 @@ function sendCompressed(req, res, body, contentType, statusCode, extraHeaders =
212
286
  const buf = typeof body === "string" ? Buffer.from(body) : body;
213
287
  const baseType = contentType.split(";")[0].trim();
214
288
  const encoding = compress ? negotiateEncoding(req) : null;
215
- const headersWithoutBodyHeaders = omitHeadersCaseInsensitive(extraHeaders, ["content-length", "content-type"]);
289
+ const headersWithoutBodyHeaders = omitHeadersCaseInsensitive(extraHeaders, OMIT_BODY_HEADERS);
216
290
  const writeHead = (headers) => {
217
291
  if (statusText) res.writeHead(statusCode, statusText, headers);
218
292
  else res.writeHead(statusCode, headers);
@@ -619,19 +693,15 @@ function readSsrManifest(clientDir) {
619
693
  function installPagesClientAssetGlobals(options) {
620
694
  const ssrManifest = readSsrManifest(options.clientDir);
621
695
  globalThis.__VINEXT_SSR_MANIFEST__ = Object.keys(ssrManifest).length > 0 ? ssrManifest : void 0;
622
- const buildManifest = readClientBuildManifest(path.join(options.clientDir, ".vite", "manifest.json"));
623
- const clientEntryManifest = readClientEntryManifest(options.clientDir);
624
- const entryOptions = {
696
+ const metadata = computeClientRuntimeMetadata({
625
697
  clientDir: options.clientDir,
626
- assetsSubdir: options.assetsSubdir,
627
698
  assetBase: options.assetBase,
628
- ...buildManifest ? { buildManifest } : {}
629
- };
630
- globalThis.__VINEXT_CLIENT_ENTRY__ = options.clientEntryLookup === "pages-client-entry" ? findPagesClientEntryFileFromVinextManifest(clientEntryManifest, options.assetBase) ?? findPagesClientEntryFile(entryOptions) : findClientEntryFileFromVinextManifest(clientEntryManifest, options.assetBase) ?? findClientEntryFile(entryOptions);
631
- if (buildManifest) {
632
- const lazyChunks = computeLazyChunks(buildManifest).map((file) => manifestFileWithBase(file, options.assetBase));
633
- globalThis.__VINEXT_LAZY_CHUNKS__ = lazyChunks.length > 0 ? lazyChunks : void 0;
634
- } else globalThis.__VINEXT_LAZY_CHUNKS__ = void 0;
699
+ assetPrefix: options.assetPrefix,
700
+ includeClientEntry: options.clientEntryLookup === "pages-client-entry" ? "pages-client-entry" : true
701
+ });
702
+ globalThis.__VINEXT_CLIENT_ENTRY__ = metadata.clientEntryFile;
703
+ globalThis.__VINEXT_LAZY_CHUNKS__ = metadata.lazyChunks;
704
+ globalThis.__VINEXT_DYNAMIC_PRELOADS__ = metadata.dynamicPreloads;
635
705
  return ssrManifest;
636
706
  }
637
707
  /**
@@ -659,8 +729,7 @@ async function startAppRouterServer(options) {
659
729
  imageConfig = JSON.parse(fs.readFileSync(imageConfigPath, "utf-8"));
660
730
  } catch {}
661
731
  const prerenderSecret = readPrerenderSecret(path.dirname(rscEntryPath));
662
- const rscMtime = fs.statSync(rscEntryPath).mtimeMs;
663
- const rscModule = await import(`${pathToFileURL(rscEntryPath).href}?t=${rscMtime}`);
732
+ const rscModule = await importServerEntryModule(rscEntryPath);
664
733
  const rscHandler = resolveAppRouterHandler(rscModule.default);
665
734
  const appRouterAssetPrefix = typeof rscModule.__assetPrefix === "string" ? rscModule.__assetPrefix : "";
666
735
  const appRouterBasePath = typeof rscModule.__basePath === "string" ? rscModule.__basePath : "";
@@ -671,10 +740,11 @@ async function startAppRouterServer(options) {
671
740
  const appAssetBase = appRouterBasePath ? `${appRouterBasePath}/` : "/";
672
741
  if (appRouterHasPagesDir) installPagesClientAssetGlobals({
673
742
  clientDir,
674
- assetsSubdir: resolveAssetsDir(appRouterAssetPrefix),
743
+ assetPrefix: appRouterAssetPrefix,
675
744
  assetBase: appAssetBase,
676
745
  clientEntryLookup: "pages-client-entry"
677
746
  });
747
+ else installClientBuildManifestGlobals(clientDir, appAssetBase, appRouterAssetPrefix);
678
748
  const seededRoutes = await resolveAppRouterPrerenderSeeder(rscModule)(path.dirname(rscEntryPath));
679
749
  if (seededRoutes > 0) console.log(`[vinext] Seeded ${seededRoutes} pre-rendered route${seededRoutes !== 1 ? "s" : ""} into memory cache`);
680
750
  const staticCache = await StaticFileCache.create(clientDir);
@@ -745,12 +815,7 @@ async function startAppRouterServer(options) {
745
815
  } catch {
746
816
  staticFilePath = staticFileSignal;
747
817
  }
748
- const staticResponseHeaders = omitHeadersCaseInsensitive(mergeResponseHeaders({}, response), [
749
- VINEXT_STATIC_FILE_HEADER,
750
- "content-encoding",
751
- "content-length",
752
- "content-type"
753
- ]);
818
+ const staticResponseHeaders = omitHeadersCaseInsensitive(mergeResponseHeaders({}, response), OMIT_STATIC_RESPONSE_HEADERS);
754
819
  const served = await tryServeStatic(req, res, clientDir, staticFilePath, compress, staticCache, staticResponseHeaders, response.status);
755
820
  cancelResponseBody(response);
756
821
  if (served) return;
@@ -804,8 +869,7 @@ function readPagesServerEntryPageRoutes(value) {
804
869
  */
805
870
  async function startPagesRouterServer(options) {
806
871
  const { port, host, clientDir, serverEntryPath, compress, purpose } = options;
807
- const serverMtime = fs.statSync(serverEntryPath).mtimeMs;
808
- const serverEntry = await import(`${pathToFileURL(serverEntryPath).href}?t=${serverMtime}`);
872
+ const serverEntry = await importServerEntryModule(serverEntryPath);
809
873
  const { renderPage, handleApiRoute: handleApi, runMiddleware, vinextConfig, buildId: pagesBuildId } = serverEntry;
810
874
  const matchPageRoute = typeof serverEntry.matchPageRoute === "function" ? serverEntry.matchPageRoute : void 0;
811
875
  const pageRoutes = readPagesServerEntryPageRoutes(serverEntry.pageRoutes);
@@ -832,7 +896,7 @@ async function startPagesRouterServer(options) {
832
896
  } : void 0;
833
897
  const ssrManifest = installPagesClientAssetGlobals({
834
898
  clientDir,
835
- assetsSubdir: resolveAssetsDir(assetPrefix),
899
+ assetPrefix,
836
900
  assetBase,
837
901
  clientEntryLookup: "any-client-entry"
838
902
  });
@@ -963,7 +1027,7 @@ async function startPagesRouterServer(options) {
963
1027
  ctx: void 0,
964
1028
  rawSearch: rawQs,
965
1029
  matchPageRoute: matchPageRoute ?? null,
966
- runMiddleware: typeof runMiddleware === "function" ? runMiddleware : null,
1030
+ runMiddleware: typeof runMiddleware === "function" ? wrapMiddlewareWithBasePath(runMiddleware, basePath, hadBasePath) : null,
967
1031
  renderPage: typeof renderPage === "function" ? (request, resolvedUrl, options, stagedHeaders) => renderPage(request, resolvedUrl, ssrManifest, void 0, stagedHeaders, options) : null,
968
1032
  handleApi: typeof handleApi === "function" ? (request, apiUrl) => handleApi(request, apiUrl, createNodeExecutionContext()) : null,
969
1033
  serveStaticFile: async (requestPathname, stagedHeaders) => {
@@ -1018,4 +1082,4 @@ async function startPagesRouterServer(options) {
1018
1082
  };
1019
1083
  }
1020
1084
  //#endregion
1021
- export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic };
1085
+ export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, importServerEntryModule, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, resolveServerEntryImportUrl, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic };
@@ -3,7 +3,7 @@ import { getRequestContext, isInsideUnifiedScope, runWithUnifiedStateMutation }
3
3
  import { VINEXT_RSC_MARKER_HEADER } from "../server/headers.js";
4
4
  import { markDynamicUsage } from "./headers.js";
5
5
  import { _registerCacheContextAccessor, _setRequestScopedCacheLife, cacheLifeProfiles, getDataCacheHandler } from "./cache.js";
6
- import { addCollectedRequestTags } from "./fetch-cache.js";
6
+ import { addCollectedRequestTags, getCurrentFetchSoftTags } from "./fetch-cache.js";
7
7
  //#region src/shims/cache-runtime.ts
8
8
  /**
9
9
  * "use cache" runtime
@@ -269,7 +269,11 @@ function registerCachedFunction(fn, id, variant, options = {}) {
269
269
  }
270
270
  if (isDev) return executeWithContext(fn, args, cacheVariant);
271
271
  const handler = getDataCacheHandler();
272
- const existing = await handler.get(cacheKey, { kind: "FETCH" });
272
+ const softTags = getCurrentFetchSoftTags();
273
+ const existing = await handler.get(cacheKey, {
274
+ kind: "FETCH",
275
+ softTags
276
+ });
273
277
  if (existing?.value && existing.value.kind === "FETCH" && existing.cacheState !== "stale") try {
274
278
  propagateCacheTagsToRequest(existing.value.tags);
275
279
  if (rsc && existing.value.data.headers["x-vinext-rsc"] === "1") {
@@ -321,8 +325,11 @@ function registerCachedFunction(fn, id, variant, options = {}) {
321
325
  value: fn.length,
322
326
  configurable: true
323
327
  });
328
+ cachedFn[USE_CACHE_FUNCTION_SYMBOL] = true;
324
329
  return cachedFn;
325
330
  }
331
+ /** @internal Symbol used to identify "use cache" wrapper functions. */
332
+ const USE_CACHE_FUNCTION_SYMBOL = Symbol.for("vinext.useCacheFunction");
326
333
  function throwPrivateUseCacheInsidePublicUseCacheError() {
327
334
  const error = /* @__PURE__ */ new Error("\"use cache: private\" must not be used within \"use cache\". It can only be nested inside of another \"use cache: private\".");
328
335
  const ctx = getRequestContext();
@@ -0,0 +1,8 @@
1
+ import React from "react";
2
+
3
+ //#region src/shims/dynamic-preload-chunks.d.ts
4
+ declare function DynamicPreloadChunks(props: {
5
+ moduleIds?: readonly string[];
6
+ }): React.FunctionComponentElement<React.FragmentProps> | null;
7
+ //#endregion
8
+ export { DynamicPreloadChunks };