vinext 1.1.0 → 1.2.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 (234) hide show
  1. package/README.md +21 -9
  2. package/dist/build/client-build-config.d.ts +10 -0
  3. package/dist/build/client-build-config.js +18 -2
  4. package/dist/build/pages-client-assets-module.d.ts +8 -0
  5. package/dist/build/pages-client-assets-module.js +21 -1
  6. package/dist/build/prerender-paths.js +5 -3
  7. package/dist/build/run-prerender.js +1 -1
  8. package/dist/build/ssr-manifest.d.ts +2 -0
  9. package/dist/build/ssr-manifest.js +6 -2
  10. package/dist/check.js +7 -6
  11. package/dist/cli.js +2 -1
  12. package/dist/client/client-redirect-matcher.d.ts +14 -0
  13. package/dist/client/client-redirect-matcher.js +37 -0
  14. package/dist/client/client-redirects.d.ts +27 -0
  15. package/dist/client/client-redirects.js +28 -0
  16. package/dist/client/client-rewrites.d.ts +2 -1
  17. package/dist/client/client-rewrites.js +1 -1
  18. package/dist/config/config-matchers.d.ts +26 -11
  19. package/dist/config/config-matchers.js +78 -81
  20. package/dist/config/destination-params.d.ts +47 -0
  21. package/dist/config/destination-params.js +116 -0
  22. package/dist/config/next-config.d.ts +7 -0
  23. package/dist/config/next-config.js +3 -0
  24. package/dist/entries/app-rsc-entry.d.ts +4 -0
  25. package/dist/entries/app-rsc-entry.js +20 -6
  26. package/dist/entries/pages-client-entry.js +3 -1
  27. package/dist/entries/pages-server-entry.js +13 -37
  28. package/dist/index.d.ts +3 -1
  29. package/dist/index.js +134 -43
  30. package/dist/init-cloudflare.d.ts +9 -0
  31. package/dist/init-cloudflare.js +107 -31
  32. package/dist/init-platform.d.ts +4 -0
  33. package/dist/init-platform.js +27 -2
  34. package/dist/init.d.ts +1 -0
  35. package/dist/init.js +67 -3
  36. package/dist/plugins/ast-scope.d.ts +7 -0
  37. package/dist/plugins/ast-scope.js +59 -3
  38. package/dist/plugins/cache-flight-codec.d.ts +1 -1
  39. package/dist/plugins/dynamic-preload-metadata.d.ts +1 -1
  40. package/dist/plugins/dynamic-preload-metadata.js +9 -8
  41. package/dist/plugins/ignore-dynamic-requests.d.ts +1 -1
  42. package/dist/plugins/ignore-dynamic-requests.js +3 -4
  43. package/dist/plugins/middleware-server-only.d.ts +5 -4
  44. package/dist/plugins/middleware-server-only.js +8 -5
  45. package/dist/plugins/pages-node-externals.js +1 -1
  46. package/dist/plugins/rsc-scan-glob-imports.d.ts +19 -0
  47. package/dist/plugins/rsc-scan-glob-imports.js +50 -0
  48. package/dist/plugins/strip-server-exports.js +262 -98
  49. package/dist/plugins/transform-result.d.ts +9 -5
  50. package/dist/plugins/transitive-externals.js +1 -1
  51. package/dist/plugins/typeof-window.d.ts +2 -2
  52. package/dist/plugins/use-cache-callable.js +8 -2
  53. package/dist/plugins/worker-server-only-imports.d.ts +20 -0
  54. package/dist/plugins/worker-server-only-imports.js +31 -0
  55. package/dist/plugins/worker-use-server.d.ts +25 -0
  56. package/dist/plugins/worker-use-server.js +129 -0
  57. package/dist/server/api-handler.d.ts +1 -0
  58. package/dist/server/api-handler.js +2 -1
  59. package/dist/server/app-browser-action-result.d.ts +1 -1
  60. package/dist/server/app-browser-action-result.js +9 -2
  61. package/dist/server/app-browser-navigation-controller.js +1 -1
  62. package/dist/server/app-browser-server-action-client.js +1 -1
  63. package/dist/server/app-middleware.d.ts +11 -0
  64. package/dist/server/app-middleware.js +17 -2
  65. package/dist/server/app-page-cache.d.ts +1 -1
  66. package/dist/server/app-page-cache.js +1 -1
  67. package/dist/server/app-page-dispatch.d.ts +3 -1
  68. package/dist/server/app-page-dispatch.js +4 -3
  69. package/dist/server/app-page-ppr-runtime.js +1 -1
  70. package/dist/server/app-page-render-observation.js +1 -1
  71. package/dist/server/app-page-request.js +53 -15
  72. package/dist/server/app-page-route-wiring.js +2 -2
  73. package/dist/server/app-pages-bridge.d.ts +1 -1
  74. package/dist/server/app-pages-bridge.js +2 -3
  75. package/dist/server/app-prerender-endpoints.js +9 -6
  76. package/dist/server/app-request-stage-dispatch.js +6 -2
  77. package/dist/server/app-request-stage-independent-entry.js +25 -5
  78. package/dist/server/app-response-stage-entry.js +10 -2
  79. package/dist/server/app-route-handler-cache.d.ts +1 -0
  80. package/dist/server/app-route-handler-cache.js +1 -0
  81. package/dist/server/app-route-handler-dispatch.d.ts +1 -0
  82. package/dist/server/app-route-handler-dispatch.js +5 -2
  83. package/dist/server/app-route-handler-execution.d.ts +1 -0
  84. package/dist/server/app-route-handler-execution.js +1 -0
  85. package/dist/server/app-route-handler-runtime.d.ts +4 -1
  86. package/dist/server/app-route-handler-runtime.js +45 -24
  87. package/dist/server/app-router-entry.js +24 -5
  88. package/dist/server/app-rsc-cache-busting.js +1 -1
  89. package/dist/server/app-rsc-errors.js +20 -16
  90. package/dist/server/app-rsc-handler.d.ts +7 -0
  91. package/dist/server/app-rsc-handler.js +85 -35
  92. package/dist/server/app-rsc-response-stage.js +4 -2
  93. package/dist/server/app-segment-config.d.ts +2 -1
  94. package/dist/server/app-server-action-execution.d.ts +2 -1
  95. package/dist/server/app-server-action-execution.js +61 -22
  96. package/dist/server/app-ssr-entry.js +2 -1
  97. package/dist/server/app-worker-stages.d.ts +5 -0
  98. package/dist/server/app-worker-stages.js +1 -1
  99. package/dist/server/cacheability-request.js +2 -2
  100. package/dist/server/client-build-files.d.ts +16 -0
  101. package/dist/server/client-build-files.js +36 -0
  102. package/dist/server/client-reuse-manifest.js +1 -1
  103. package/dist/server/dev-server.js +38 -27
  104. package/dist/server/dev-stack-sourcemap.js +18 -23
  105. package/dist/server/document-initial-head.d.ts +0 -11
  106. package/dist/server/document-initial-head.js +1 -1
  107. package/dist/server/forwarded-middleware-context.d.ts +5 -0
  108. package/dist/server/forwarded-middleware-context.js +22 -0
  109. package/dist/server/headers.d.ts +4 -1
  110. package/dist/server/headers.js +4 -1
  111. package/dist/server/http-conditional.d.ts +11 -0
  112. package/dist/server/http-conditional.js +10 -1
  113. package/dist/server/http-error-responses.d.ts +1 -1
  114. package/dist/server/image-optimization.d.ts +38 -0
  115. package/dist/server/image-optimization.js +93 -4
  116. package/dist/server/isr-cache.d.ts +10 -0
  117. package/dist/server/isr-cache.js +41 -6
  118. package/dist/server/metadata-route-response.js +9 -8
  119. package/dist/server/middleware-matcher.d.ts +2 -0
  120. package/dist/server/middleware-matcher.js +23 -2
  121. package/dist/server/middleware-runtime.d.ts +13 -0
  122. package/dist/server/middleware-runtime.js +33 -20
  123. package/dist/server/middleware.d.ts +1 -1
  124. package/dist/server/middleware.js +3 -1
  125. package/dist/server/multi-stage.d.ts +11 -0
  126. package/dist/server/normalize-path.d.ts +0 -13
  127. package/dist/server/normalize-path.js +1 -34
  128. package/dist/server/pages-api-route.d.ts +3 -2
  129. package/dist/server/pages-api-route.js +4 -3
  130. package/dist/server/pages-cache-tag.d.ts +12 -0
  131. package/dist/server/pages-cache-tag.js +19 -0
  132. package/dist/server/pages-client-assets.d.ts +2 -0
  133. package/dist/server/pages-data-route.d.ts +10 -0
  134. package/dist/server/pages-data-route.js +19 -2
  135. package/dist/server/pages-document-asset-props.d.ts +5 -1
  136. package/dist/server/pages-document-asset-props.js +13 -5
  137. package/dist/server/pages-document-splice.d.ts +19 -0
  138. package/dist/server/pages-document-splice.js +55 -0
  139. package/dist/server/pages-i18n.d.ts +12 -1
  140. package/dist/server/pages-i18n.js +18 -4
  141. package/dist/server/pages-node-compat.d.ts +16 -5
  142. package/dist/server/pages-node-compat.js +20 -12
  143. package/dist/server/pages-page-data.d.ts +32 -24
  144. package/dist/server/pages-page-data.js +50 -120
  145. package/dist/server/pages-page-handler.d.ts +0 -6
  146. package/dist/server/pages-page-handler.js +188 -126
  147. package/dist/server/pages-page-response.d.ts +12 -6
  148. package/dist/server/pages-page-response.js +41 -34
  149. package/dist/server/pages-request-pipeline.d.ts +10 -2
  150. package/dist/server/pages-request-pipeline.js +37 -18
  151. package/dist/server/pages-request-stage-entry.d.ts +6 -0
  152. package/dist/server/pages-request-stage-entry.js +71 -12
  153. package/dist/server/pages-response-stage-entry.js +4 -0
  154. package/dist/server/pages-revalidate.js +1 -1
  155. package/dist/server/pages-router-entry.js +4 -3
  156. package/dist/server/prerender-manifest.d.ts +3 -3
  157. package/dist/server/prerender-manifest.js +4 -3
  158. package/dist/server/prerender-work-unit-setup.js +4 -0
  159. package/dist/server/prod-server.d.ts +12 -0
  160. package/dist/server/prod-server.js +199 -69
  161. package/dist/server/proper-error.d.ts +4 -0
  162. package/dist/server/proper-error.js +31 -0
  163. package/dist/server/request-pipeline.d.ts +4 -1
  164. package/dist/server/request-pipeline.js +3 -2
  165. package/dist/server/revalidation-request.d.ts +1 -0
  166. package/dist/server/revalidation-request.js +1 -1
  167. package/dist/server/rsc-stream-hints.js +31 -2
  168. package/dist/server/seed-cache.d.ts +1 -0
  169. package/dist/server/seed-cache.js +5 -4
  170. package/dist/server/server-reference-loader.d.ts +4 -0
  171. package/dist/server/server-reference-loader.js +29 -0
  172. package/dist/server/socket-error-backstop.d.ts +9 -1
  173. package/dist/server/socket-error-backstop.js +46 -17
  174. package/dist/server/static-file-signal.d.ts +15 -4
  175. package/dist/server/static-file-signal.js +46 -12
  176. package/dist/server/worker-image-optimization.d.ts +34 -0
  177. package/dist/server/worker-image-optimization.js +86 -0
  178. package/dist/server/worker-stages.d.ts +1 -1
  179. package/dist/server/worker-stages.js +2 -2
  180. package/dist/server/worker-utils.d.ts +8 -3
  181. package/dist/server/worker-utils.js +12 -7
  182. package/dist/shims/app-router-scroll.js +4 -1
  183. package/dist/shims/app-router-tree-context.d.ts +5 -0
  184. package/dist/shims/app-router-tree-context.js +27 -0
  185. package/dist/shims/cache-callable-runtime.d.ts +4 -2
  186. package/dist/shims/cache-callable-runtime.js +21 -12
  187. package/dist/shims/cache-handler.js +1 -9
  188. package/dist/shims/cache-runtime.d.ts +6 -1
  189. package/dist/shims/cache-runtime.js +2 -1
  190. package/dist/shims/cache.d.ts +2 -2
  191. package/dist/shims/cache.js +40 -13
  192. package/dist/shims/cdn-cache.d.ts +8 -0
  193. package/dist/shims/cdn-cache.js +17 -1
  194. package/dist/shims/disable-smooth-scroll.d.ts +3 -0
  195. package/dist/shims/disable-smooth-scroll.js +36 -0
  196. package/dist/shims/document-placeholders.d.ts +19 -0
  197. package/dist/shims/document-placeholders.js +45 -0
  198. package/dist/shims/document.js +21 -5
  199. package/dist/shims/dynamic-preload-chunks.d.ts +1 -0
  200. package/dist/shims/dynamic-preload-chunks.js +13 -9
  201. package/dist/shims/dynamic.d.ts +1 -1
  202. package/dist/shims/dynamic.js +60 -8
  203. package/dist/shims/fetch-cache-state.d.ts +66 -0
  204. package/dist/shims/fetch-cache-state.js +92 -0
  205. package/dist/shims/fetch-cache.d.ts +3 -67
  206. package/dist/shims/fetch-cache.js +160 -125
  207. package/dist/shims/head.d.ts +10 -6
  208. package/dist/shims/head.js +15 -13
  209. package/dist/shims/image.js +2 -1
  210. package/dist/shims/internal/work-store-scope.d.ts +5 -0
  211. package/dist/shims/internal/work-store-scope.js +24 -0
  212. package/dist/shims/navigation.js +31 -5
  213. package/dist/shims/request-context.d.ts +2 -0
  214. package/dist/shims/request-state-types.d.ts +2 -1
  215. package/dist/shims/router.js +12 -20
  216. package/dist/shims/server.d.ts +16 -0
  217. package/dist/shims/server.js +24 -4
  218. package/dist/shims/unified-request-context.d.ts +1 -1
  219. package/dist/shims/url-utils.d.ts +2 -0
  220. package/dist/shims/url-utils.js +3 -1
  221. package/dist/utils/client-runtime-metadata.js +1 -1
  222. package/dist/utils/hash.d.ts +7 -0
  223. package/dist/utils/hash.js +11 -1
  224. package/dist/utils/package-name.d.ts +6 -0
  225. package/dist/utils/package-name.js +12 -1
  226. package/dist/utils/plugin-rsc-version.d.ts +4 -0
  227. package/dist/utils/plugin-rsc-version.js +15 -0
  228. package/dist/utils/prerender-output-paths.js +15 -4
  229. package/dist/utils/public-file-path.d.ts +10 -2
  230. package/dist/utils/public-file-path.js +13 -9
  231. package/dist/utils/public-routes.js +2 -2
  232. package/dist/utils/react-version.d.ts +3 -0
  233. package/dist/utils/react-version.js +1 -1
  234. package/package.json +7 -3
@@ -1,19 +1,22 @@
1
+ import { AppRouterTreeContext } from "./app-router-tree-context.js";
1
2
  import { DynamicPreloadChunks } from "./dynamic-preload-chunks.js";
2
3
  import React from "react";
3
4
  //#region src/shims/dynamic.ts
4
5
  /**
5
6
  * next/dynamic shim
6
7
  *
7
- * SSR-safe dynamic imports. On the server, uses React.lazy + Suspense so that
8
+ * SSR-safe dynamic imports. On the server, uses React.lazy so that
8
9
  * renderToReadableStream suspends until the dynamically-imported component is
9
- * available. On the client, also uses React.lazy for code splitting.
10
+ * available. On the client, also uses React.lazy for code splitting. As in
11
+ * the Next.js App Router, a Suspense boundary is added only for `ssr: false`
12
+ * or an explicit `loading` component. Other trees (Pages Router) keep it.
10
13
  *
11
14
  * Works in RSC, SSR, and client environments:
12
- * - RSC: Uses React.lazy + Suspense (available in React 19.x react-server).
15
+ * - RSC: Uses React.lazy (available in React 19.x react-server).
13
16
  * Falls back to async component pattern if a future React version
14
17
  * strips lazy from react-server.
15
- * - SSR: React.lazy + Suspense (renderToReadableStream suspends)
16
- * - Client: React.lazy + Suspense (standard code splitting)
18
+ * - SSR: React.lazy (renderToReadableStream suspends)
19
+ * - Client: React.lazy (standard code splitting)
17
20
  *
18
21
  * Supports:
19
22
  * - dynamic(import('./Component'))
@@ -115,8 +118,55 @@ function getDynamicErrorBoundary() {
115
118
  }
116
119
  const isServer = typeof window === "undefined";
117
120
  /**
121
+ * Whether this dynamic() renders in an App Router tree. RSC renders the App
122
+ * Router only, and react-server has no context support. On the server, the
123
+ * App Router SSR entry marks its tree; in the browser, App Router documents
124
+ * set `window.next.appDir` before anything renders, which also covers
125
+ * separate client roots (e.g. a modal rendered with createRoot). Everything
126
+ * else (Pages Router pages, `pages/_document`, ad-hoc react-dom/server
127
+ * renders) keeps the boundary, since Pages next/dynamic is react-loadable,
128
+ * which never suspends.
129
+ */
130
+ function useIsAppRouterTree() {
131
+ if (!isServer) return window.next?.appDir === true;
132
+ return AppRouterTreeContext ? React.use(AppRouterTreeContext) : true;
133
+ }
134
+ /**
135
+ * The element tree around an SSR-enabled dynamic() component. The client
136
+ * renders empty slots where the server renders the preload chunks, so both
137
+ * sides render the same shape and useId values inside the dynamic component
138
+ * match on hydration.
139
+ *
140
+ * Match Next.js App Router Loadable: the component only gets a Suspense
141
+ * boundary when it has a `loading` component. Without one, the lazy component
142
+ * suspends up to the nearest parent boundary, so the server shell waits for
143
+ * the import instead of flushing an empty boundary and streaming the
144
+ * component in after first paint. Other trees always get the boundary.
145
+ *
146
+ * React hoists the preload links out of place, but a hoisted <link> right
147
+ * after a text node leaves a `<!-- -->` separator behind. Next.js avoids it by
148
+ * hinting scripts with ReactDOM.preload(), which renders nothing. vinext
149
+ * renders real modulepreload links (preloadModule() drops the nonce and
150
+ * fetchPriority), so they go where text rarely precedes them: first inside the
151
+ * boundary, or after the content when there is no boundary (a component whose
152
+ * output ends in text still gets a separator after it). Stylesheets stay
153
+ * before the boundary: a precedence stylesheet inside it makes React outline
154
+ * the boundary even when its content is already resolved, and rendering them
155
+ * ahead of the content keeps their nonce when the content links the same CSS.
156
+ * A dynamic component with CSS right after text therefore still gets the
157
+ * separator.
158
+ */
159
+ function createDynamicBoundary(fallback, content, preloadModuleIds, hasBoundary) {
160
+ const preloadChunks = (assets) => isServer ? React.createElement(DynamicPreloadChunks, {
161
+ moduleIds: preloadModuleIds,
162
+ assets
163
+ }) : null;
164
+ if (!hasBoundary) return React.createElement(React.Fragment, null, preloadChunks("styles"), content, preloadChunks("scripts"));
165
+ return React.createElement(React.Fragment, null, preloadChunks("styles"), React.createElement(React.Suspense, { fallback }, preloadChunks("scripts"), content));
166
+ }
167
+ /**
118
168
  * Retained for the Pages Router render pipeline, which calls this before
119
- * rendering. Dynamic imports now use React.lazy + Suspense, so there is no
169
+ * rendering. Dynamic imports now use React.lazy, so there is no
120
170
  * separate preload work to await.
121
171
  */
122
172
  function flushPreloads() {
@@ -170,6 +220,7 @@ function dynamic(dynamicInput, options) {
170
220
  }
171
221
  const LazyServer = createLazyComponent(loader);
172
222
  const ServerDynamic = (props) => {
223
+ const isAppRouterTree = useIsAppRouterTree();
173
224
  const fallback = LoadingComponent ? React.createElement(LoadingComponent, createDynamicLoadingProps()) : null;
174
225
  const lazyElement = createElementWithProps(LazyServer, props);
175
226
  let content = lazyElement;
@@ -181,7 +232,7 @@ function dynamic(dynamicInput, options) {
181
232
  resetKey: 0
182
233
  }, lazyElement);
183
234
  }
184
- return React.createElement(React.Fragment, null, React.createElement(DynamicPreloadChunks, { moduleIds: preloadModuleIds }), React.createElement(React.Suspense, { fallback }, content));
235
+ return createDynamicBoundary(fallback, content, preloadModuleIds, LoadingComponent != null || !isAppRouterTree);
185
236
  };
186
237
  ServerDynamic.displayName = "DynamicServer";
187
238
  return ServerDynamic;
@@ -189,6 +240,7 @@ function dynamic(dynamicInput, options) {
189
240
  const InitialLazyComponent = createLazyComponent(loader);
190
241
  const ClientDynamic = (props) => {
191
242
  const { LazyComponent, retry, retryKey } = useRetryableLazyComponent(loader, InitialLazyComponent);
243
+ const isAppRouterTree = useIsAppRouterTree();
192
244
  const fallback = LoadingComponent ? React.createElement(LoadingComponent, createDynamicLoadingProps({ retry })) : null;
193
245
  const lazyElement = createElementWithProps(LazyComponent, props);
194
246
  let content = lazyElement;
@@ -200,7 +252,7 @@ function dynamic(dynamicInput, options) {
200
252
  resetKey: retryKey
201
253
  }, lazyElement);
202
254
  }
203
- return React.createElement(React.Suspense, { fallback }, content);
255
+ return createDynamicBoundary(fallback, content, preloadModuleIds, LoadingComponent != null || !isAppRouterTree);
204
256
  };
205
257
  ClientDynamic.displayName = "DynamicClient";
206
258
  return ClientDynamic;
@@ -0,0 +1,66 @@
1
+ //#region src/shims/fetch-cache-state.d.ts
2
+ /**
3
+ * Request-scoped fetch cache state, kept apart from the fetch patching in
4
+ * fetch-cache.ts so modules that only read or update it, like next/cache,
5
+ * don't pull the patched fetch and its Node.js builtins into client graphs
6
+ * that import them.
7
+ */
8
+ export type FetchDedupeEntry = {
9
+ key: string;
10
+ promise: Promise<Response>;
11
+ response: Response | null;
12
+ };
13
+ export type FetchCacheState = {
14
+ cacheableFetchUrls: Set<string>;
15
+ currentRequestTags: string[];
16
+ currentFetchSoftTags: string[];
17
+ currentFetchCacheMode: FetchCacheMode | null;
18
+ currentFetchRevalidate: number | null;
19
+ currentForceDynamicFetchDefault: boolean;
20
+ dynamicFetchUrls: Set<string>;
21
+ refreshStaleFetchesInForeground: boolean;
22
+ isFetchDedupeActive: boolean;
23
+ currentFetchDedupeEntries: Map<string, FetchDedupeEntry[]>;
24
+ nextFetchId: number;
25
+ };
26
+ export type FetchCacheMode = "auto" | "default-cache" | "default-no-store" | "force-cache" | "force-no-store" | "only-cache" | "only-no-store";
27
+ export declare const fetchCacheAls: import("node:async_hooks").AsyncLocalStorage<FetchCacheState>;
28
+ /**
29
+ * Process-wide state used only while the deprecated `withFetchCache()` is
30
+ * active, which marks it with `isFetchDedupeActive`.
31
+ */
32
+ export declare const fallbackFetchCacheState: FetchCacheState;
33
+ export declare function getFetchCacheState(): FetchCacheState;
34
+ /**
35
+ * Reset the fallback state for a new request. Used by `withFetchCache()`
36
+ * in single-threaded contexts where ALS.run() isn't used.
37
+ */
38
+ export declare function resetFallbackFetchCacheState(isFetchDedupeActive: boolean): void;
39
+ /**
40
+ * Append cache tags to the current request's collected tags.
41
+ *
42
+ * Mirrors Next.js's `propagateCacheLifeAndTagsToRevalidateStore`: tags declared
43
+ * inside a `"use cache"` function (via `cacheTag()`, persisted on the data cache
44
+ * entry) must also bubble up to the surrounding page / route-handler ISR entry
45
+ * so `revalidateTag()` / `revalidatePath()` can evict the rendered output, not
46
+ * just the inner data cache entry. Without this, a cached `"use cache"` result
47
+ * keeps being served from a stale page/route entry after its tag is revalidated
48
+ * (issue #1453). Tags are already encoded by the caller; deduped to match the
49
+ * tagged-fetch path. A no-op for empty input.
50
+ */
51
+ export declare function addCollectedRequestTags(tags: readonly string[]): void;
52
+ /**
53
+ * Read the path-derived soft tags for the current render.
54
+ *
55
+ * Used by the "use cache" runtime to pass soft tags to the cache handler
56
+ * so that `revalidatePath()` invalidates "use cache" entries during the
57
+ * affected route's next request, even when the entry carries no hard tags.
58
+ */
59
+ export declare function getCurrentFetchSoftTags(): string[];
60
+ /**
61
+ * Run `fn` with the request's fetch settings but its own record of the tags and
62
+ * URLs its fetches touch, so work done on the request's behalf after the fact
63
+ * (a background regeneration) leaves the request's response untouched.
64
+ */
65
+ export declare function runWithDetachedFetchObservations<T>(fn: () => Promise<T>): Promise<T>;
66
+ //#endregion
@@ -0,0 +1,92 @@
1
+ import { getOrCreateAls } from "./internal/als-registry.js";
2
+ import { getRequestContext, isInsideUnifiedScope, runWithUnifiedStateMutation } from "./unified-request-context.js";
3
+ //#region src/shims/fetch-cache-state.ts
4
+ /**
5
+ * Request-scoped fetch cache state, kept apart from the fetch patching in
6
+ * fetch-cache.ts so modules that only read or update it, like next/cache,
7
+ * don't pull the patched fetch and its Node.js builtins into client graphs
8
+ * that import them.
9
+ */
10
+ const _FALLBACK_KEY = Symbol.for("vinext.fetchCache.fallback");
11
+ const fetchCacheAls = getOrCreateAls("vinext.fetchCache.als");
12
+ function createFetchCacheState() {
13
+ return {
14
+ cacheableFetchUrls: /* @__PURE__ */ new Set(),
15
+ currentRequestTags: [],
16
+ currentFetchSoftTags: [],
17
+ currentFetchCacheMode: null,
18
+ currentFetchRevalidate: null,
19
+ currentForceDynamicFetchDefault: false,
20
+ dynamicFetchUrls: /* @__PURE__ */ new Set(),
21
+ refreshStaleFetchesInForeground: false,
22
+ isFetchDedupeActive: false,
23
+ currentFetchDedupeEntries: /* @__PURE__ */ new Map(),
24
+ nextFetchId: 1
25
+ };
26
+ }
27
+ /**
28
+ * Process-wide state used only while the deprecated `withFetchCache()` is
29
+ * active, which marks it with `isFetchDedupeActive`.
30
+ */
31
+ const fallbackFetchCacheState = globalThis[_FALLBACK_KEY] ??= createFetchCacheState();
32
+ function getFetchCacheState() {
33
+ if (isInsideUnifiedScope()) return getRequestContext();
34
+ const store = fetchCacheAls.getStore();
35
+ if (store) return store;
36
+ if (fallbackFetchCacheState.isFetchDedupeActive) return fallbackFetchCacheState;
37
+ return createFetchCacheState();
38
+ }
39
+ /**
40
+ * Reset the fallback state for a new request. Used by `withFetchCache()`
41
+ * in single-threaded contexts where ALS.run() isn't used.
42
+ */
43
+ function resetFallbackFetchCacheState(isFetchDedupeActive) {
44
+ Object.assign(fallbackFetchCacheState, createFetchCacheState(), { isFetchDedupeActive });
45
+ }
46
+ /**
47
+ * Append cache tags to the current request's collected tags.
48
+ *
49
+ * Mirrors Next.js's `propagateCacheLifeAndTagsToRevalidateStore`: tags declared
50
+ * inside a `"use cache"` function (via `cacheTag()`, persisted on the data cache
51
+ * entry) must also bubble up to the surrounding page / route-handler ISR entry
52
+ * so `revalidateTag()` / `revalidatePath()` can evict the rendered output, not
53
+ * just the inner data cache entry. Without this, a cached `"use cache"` result
54
+ * keeps being served from a stale page/route entry after its tag is revalidated
55
+ * (issue #1453). Tags are already encoded by the caller; deduped to match the
56
+ * tagged-fetch path. A no-op for empty input.
57
+ */
58
+ function addCollectedRequestTags(tags) {
59
+ if (tags.length === 0) return;
60
+ const reqTags = getFetchCacheState().currentRequestTags;
61
+ for (const tag of tags) if (!reqTags.includes(tag)) reqTags.push(tag);
62
+ }
63
+ /**
64
+ * Read the path-derived soft tags for the current render.
65
+ *
66
+ * Used by the "use cache" runtime to pass soft tags to the cache handler
67
+ * so that `revalidatePath()` invalidates "use cache" entries during the
68
+ * affected route's next request, even when the entry carries no hard tags.
69
+ */
70
+ function getCurrentFetchSoftTags() {
71
+ return getFetchCacheState().currentFetchSoftTags;
72
+ }
73
+ /**
74
+ * Run `fn` with the request's fetch settings but its own record of the tags and
75
+ * URLs its fetches touch, so work done on the request's behalf after the fact
76
+ * (a background regeneration) leaves the request's response untouched.
77
+ */
78
+ function runWithDetachedFetchObservations(fn) {
79
+ if (isInsideUnifiedScope()) return runWithUnifiedStateMutation((uCtx) => {
80
+ uCtx.cacheableFetchUrls = /* @__PURE__ */ new Set();
81
+ uCtx.currentRequestTags = [];
82
+ uCtx.dynamicFetchUrls = /* @__PURE__ */ new Set();
83
+ }, fn);
84
+ return fetchCacheAls.run({
85
+ ...getFetchCacheState(),
86
+ cacheableFetchUrls: /* @__PURE__ */ new Set(),
87
+ currentRequestTags: [],
88
+ dynamicFetchUrls: /* @__PURE__ */ new Set()
89
+ }, fn);
90
+ }
91
+ //#endregion
92
+ export { addCollectedRequestTags, fallbackFetchCacheState, fetchCacheAls, getCurrentFetchSoftTags, getFetchCacheState, resetFallbackFetchCacheState, runWithDetachedFetchObservations };
@@ -1,33 +1,9 @@
1
+ import { FetchCacheMode, FetchCacheState, addCollectedRequestTags, getCurrentFetchSoftTags, runWithDetachedFetchObservations } from "./fetch-cache-state.js";
1
2
  //#region src/shims/fetch-cache.d.ts
2
- /**
3
- * Extended fetch() with Next.js caching semantics.
4
- *
5
- * Patches `globalThis.fetch` during server rendering to support:
6
- *
7
- * fetch(url, { next: { revalidate: 60, tags: ['posts'] } })
8
- * fetch(url, { cache: 'force-cache' })
9
- * fetch(url, { cache: 'no-store' })
10
- *
11
- * Cached responses are stored via the pluggable CacheHandler, so
12
- * revalidateTag() and revalidatePath() invalidate fetch-level caches.
13
- *
14
- * Usage (in server entry):
15
- * import { withFetchCache, cleanupFetchCache } from './fetch-cache';
16
- * const cleanup = withFetchCache();
17
- * try { ... render ... } finally { cleanup(); }
18
- *
19
- * Or use the async helper:
20
- * await runWithFetchCache(async () => { ... render ... });
21
- */
22
3
  type NextFetchOptions = {
23
4
  revalidate?: number | false;
24
5
  tags?: string[];
25
6
  };
26
- type FetchDedupeEntry = {
27
- key: string;
28
- promise: Promise<Response>;
29
- response: Response | null;
30
- };
31
7
  declare global {
32
8
  interface RequestInit {
33
9
  next?: NextFetchOptions;
@@ -35,20 +11,6 @@ declare global {
35
11
  }
36
12
  /** @internal Reset dedup state — exposed for test isolation only. */
37
13
  export declare function _resetPendingRefetches(): void;
38
- export type FetchCacheState = {
39
- cacheableFetchUrls: Set<string>;
40
- currentRequestTags: string[];
41
- currentFetchSoftTags: string[];
42
- currentFetchCacheMode: FetchCacheMode | null;
43
- currentFetchRevalidate: number | null;
44
- currentForceDynamicFetchDefault: boolean;
45
- dynamicFetchUrls: Set<string>;
46
- refreshStaleFetchesInForeground: boolean;
47
- isFetchDedupeActive: boolean;
48
- currentFetchDedupeEntries: Map<string, FetchDedupeEntry[]>;
49
- nextFetchId: number;
50
- };
51
- export type FetchCacheMode = "auto" | "default-cache" | "default-no-store" | "force-cache" | "force-no-store" | "only-cache" | "only-no-store";
52
14
  export declare function peekCacheableFetchObservations(): string[];
53
15
  export declare function peekDynamicFetchObservations(): string[];
54
16
  export declare function consumeDynamicFetchObservations(): string[];
@@ -58,19 +20,6 @@ export declare function consumeDynamicFetchObservations(): string[];
58
20
  * fetch tags used during rendering.
59
21
  */
60
22
  export declare function getCollectedFetchTags(): string[];
61
- /**
62
- * Append cache tags to the current request's collected tags.
63
- *
64
- * Mirrors Next.js's `propagateCacheLifeAndTagsToRevalidateStore`: tags declared
65
- * inside a `"use cache"` function (via `cacheTag()`, persisted on the data cache
66
- * entry) must also bubble up to the surrounding page / route-handler ISR entry
67
- * so `revalidateTag()` / `revalidatePath()` can evict the rendered output, not
68
- * just the inner data cache entry. Without this, a cached `"use cache"` result
69
- * keeps being served from a stale page/route entry after its tag is revalidated
70
- * (issue #1453). Tags are already encoded by the caller; deduped to match the
71
- * tagged-fetch path. A no-op for empty input.
72
- */
73
- export declare function addCollectedRequestTags(tags: readonly string[]): void;
74
23
  /**
75
24
  * Set path-derived implicit tags for fetch cache reads in the current render.
76
25
  *
@@ -79,14 +28,6 @@ export declare function addCollectedRequestTags(tags: readonly string[]): void;
79
28
  * affected route, without permanently coupling a shared fetch entry to one path.
80
29
  */
81
30
  export declare function setCurrentFetchSoftTags(tags: string[]): void;
82
- /**
83
- * Read the path-derived soft tags for the current render.
84
- *
85
- * Used by the "use cache" runtime to pass soft tags to the cache handler
86
- * so that `revalidatePath()` invalidates "use cache" entries during the
87
- * affected route's next request, even when the entry carries no hard tags.
88
- */
89
- export declare function getCurrentFetchSoftTags(): string[];
90
31
  export declare function setCurrentFetchCacheMode(mode: FetchCacheMode | null): void;
91
32
  export declare function setCurrentFetchRevalidate(revalidate: number | null): void;
92
33
  export declare function setCurrentForceDynamicFetchDefault(enabled: boolean): void;
@@ -103,12 +44,6 @@ export declare function setRefreshStaleFetchesInForeground(enabled: boolean): vo
103
44
  * try { await render(); } finally { cleanup(); }
104
45
  */
105
46
  export declare function withFetchCache(): () => void;
106
- /**
107
- * Run `fn` with the request's fetch settings but its own record of the tags and
108
- * URLs its fetches touch, so work done on the request's behalf after the fact
109
- * (a background regeneration) leaves the request's response untouched.
110
- */
111
- export declare function runWithDetachedFetchObservations<T>(fn: () => Promise<T>): Promise<T>;
112
47
  /**
113
48
  * Run an async function with patched fetch caching enabled.
114
49
  * Uses `AsyncLocalStorage.run()` for proper per-request isolation
@@ -143,4 +78,5 @@ export declare function ensureFetchPatch(): void;
143
78
  * Useful for internal code that should bypass caching.
144
79
  */
145
80
  export declare function getOriginalFetch(): typeof globalThis.fetch;
146
- //#endregion
81
+ //#endregion
82
+ export { type FetchCacheMode, type FetchCacheState, addCollectedRequestTags, getCurrentFetchSoftTags, runWithDetachedFetchObservations };