vinext 1.0.0-beta.0 → 1.0.0-beta.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 (305) hide show
  1. package/README.md +21 -2
  2. package/dist/build/assets-ignore.js +1 -1
  3. package/dist/build/clean-output.js +1 -1
  4. package/dist/build/client-build-config.js +7 -4
  5. package/dist/build/css-url-assets.js +3 -3
  6. package/dist/build/google-fonts/fallback-metrics.js +1 -1
  7. package/dist/build/google-fonts/font-metadata.js +1 -1
  8. package/dist/build/inject-pregenerated-paths.js +1 -1
  9. package/dist/build/inline-css.js +2 -5
  10. package/dist/build/module-dependency-cache.d.ts +4 -0
  11. package/dist/build/module-dependency-cache.js +13 -0
  12. package/dist/build/next-client-runtime-manifests.js +1 -1
  13. package/dist/build/pages-client-assets-module.js +1 -1
  14. package/dist/build/precompress.js +7 -7
  15. package/dist/build/prerender-paths.d.ts +2 -2
  16. package/dist/build/prerender-paths.js +10 -15
  17. package/dist/build/prerender-server-pool.js +2 -2
  18. package/dist/build/prerender.js +1 -1
  19. package/dist/build/preview-credentials.d.ts +11 -0
  20. package/dist/build/preview-credentials.js +19 -0
  21. package/dist/build/report.d.ts +8 -4
  22. package/dist/build/report.js +14 -6
  23. package/dist/build/run-prerender.d.ts +2 -11
  24. package/dist/build/run-prerender.js +2 -10
  25. package/dist/build/server-manifest.js +1 -1
  26. package/dist/build/ssr-manifest.d.ts +4 -0
  27. package/dist/build/ssr-manifest.js +10 -6
  28. package/dist/build/standalone.js +37 -37
  29. package/dist/cache/cache-adapters-virtual.d.ts +1 -17
  30. package/dist/cache/cache-adapters-virtual.js +20 -11
  31. package/dist/check.d.ts +0 -16
  32. package/dist/check.js +27 -36
  33. package/dist/cli.js +61 -55
  34. package/dist/client/instrumentation-client-inject.js +1 -1
  35. package/dist/client/pages-router-link-navigation.js +1 -1
  36. package/dist/config/config-matchers.d.ts +13 -37
  37. package/dist/config/config-matchers.js +33 -123
  38. package/dist/config/dotenv.js +1 -1
  39. package/dist/config/next-config.d.ts +7 -3
  40. package/dist/config/next-config.js +84 -15
  41. package/dist/config/prerender.d.ts +2 -2
  42. package/dist/config/prerender.js +7 -15
  43. package/dist/config/request-context.d.ts +14 -0
  44. package/dist/config/request-context.js +26 -0
  45. package/dist/config/server-external-packages.js +1 -1
  46. package/dist/config/tsconfig-paths.js +2 -2
  47. package/dist/{node_modules/.pnpm/am-i-vibing@0.5.0/node_modules → deps/.pnpm/am-i-vibing@0.5.0/deps}/am-i-vibing/dist/detector-1yx2Hoe0.js +2 -2
  48. package/dist/deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js +50 -0
  49. package/dist/entries/app-rsc-entry.js +39 -14
  50. package/dist/entries/app-rsc-manifest.js +21 -4
  51. package/dist/entries/pages-client-entry.js +6 -5
  52. package/dist/entries/pages-server-entry.js +18 -28
  53. package/dist/entries/runtime-entry-module.js +2 -2
  54. package/dist/index.js +314 -230
  55. package/dist/init-cloudflare.js +1 -1
  56. package/dist/init-platform.js +1 -1
  57. package/dist/init.d.ts +6 -4
  58. package/dist/init.js +25 -27
  59. package/dist/plugins/ast-utils.js +1 -1
  60. package/dist/plugins/dynamic-preload-metadata.js +7 -7
  61. package/dist/plugins/extensionless-dynamic-import.js +1 -1
  62. package/dist/plugins/fonts.js +3 -3
  63. package/dist/plugins/ignore-dynamic-requests.js +13 -8
  64. package/dist/plugins/import-meta-url.js +16 -17
  65. package/dist/plugins/middleware-export-validation.js +1 -1
  66. package/dist/plugins/middleware-server-only.js +2 -2
  67. package/dist/plugins/og-asset-ownership.js +7 -6
  68. package/dist/plugins/og-assets.js +5 -6
  69. package/dist/plugins/optimize-imports.js +12 -13
  70. package/dist/plugins/postcss.js +1 -1
  71. package/dist/plugins/remove-console.js +1 -1
  72. package/dist/plugins/require-context.js +1 -1
  73. package/dist/plugins/sass.js +2 -2
  74. package/dist/plugins/server-externals-manifest.js +1 -1
  75. package/dist/plugins/strip-server-exports.js +1 -1
  76. package/dist/plugins/styled-jsx.js +3 -2
  77. package/dist/plugins/typeof-window.js +1 -1
  78. package/dist/routing/app-route-graph.d.ts +16 -15
  79. package/dist/routing/app-route-graph.js +86 -88
  80. package/dist/routing/app-router.d.ts +0 -5
  81. package/dist/routing/app-router.js +12 -11
  82. package/dist/routing/file-matcher.d.ts +13 -28
  83. package/dist/routing/file-matcher.js +31 -44
  84. package/dist/routing/pages-router.js +26 -32
  85. package/dist/routing/route-matching.d.ts +14 -1
  86. package/dist/routing/route-matching.js +13 -1
  87. package/dist/routing/route-pattern.d.ts +2 -1
  88. package/dist/routing/route-pattern.js +7 -4
  89. package/dist/routing/route-trie.d.ts +5 -1
  90. package/dist/routing/route-trie.js +5 -2
  91. package/dist/server/api-handler.js +41 -33
  92. package/dist/server/app-browser-entry.js +18 -11
  93. package/dist/server/app-browser-history-controller.d.ts +1 -1
  94. package/dist/server/app-browser-history-controller.js +3 -3
  95. package/dist/server/app-browser-navigation-controller.js +4 -2
  96. package/dist/server/app-browser-server-action-client.js +1 -1
  97. package/dist/server/app-elements-wire.d.ts +2 -1
  98. package/dist/server/app-fallback-renderer.d.ts +11 -0
  99. package/dist/server/app-fallback-renderer.js +16 -2
  100. package/dist/server/app-middleware.js +2 -1
  101. package/dist/server/app-page-boundary-render.d.ts +26 -2
  102. package/dist/server/app-page-boundary-render.js +190 -55
  103. package/dist/server/app-page-cache-finalizer.d.ts +1 -1
  104. package/dist/server/app-page-cache.d.ts +1 -1
  105. package/dist/server/app-page-dispatch.d.ts +12 -2
  106. package/dist/server/app-page-dispatch.js +37 -51
  107. package/dist/server/app-page-element-builder.d.ts +8 -2
  108. package/dist/server/app-page-element-builder.js +112 -27
  109. package/dist/server/app-page-execution.d.ts +3 -1
  110. package/dist/server/app-page-execution.js +31 -39
  111. package/dist/server/app-page-head.d.ts +54 -2
  112. package/dist/server/app-page-head.js +164 -78
  113. package/dist/server/app-page-http-access-fallback-metadata.d.ts +29 -0
  114. package/dist/server/app-page-http-access-fallback-metadata.js +106 -0
  115. package/dist/server/app-page-render.d.ts +2 -2
  116. package/dist/server/app-page-render.js +1 -1
  117. package/dist/server/app-page-request.d.ts +12 -0
  118. package/dist/server/app-page-request.js +134 -14
  119. package/dist/server/app-page-route-wiring.d.ts +9 -0
  120. package/dist/server/app-page-route-wiring.js +44 -4
  121. package/dist/server/app-post-middleware-context.d.ts +1 -1
  122. package/dist/server/app-post-middleware-context.js +1 -1
  123. package/dist/server/app-route-handler-dispatch.js +31 -17
  124. package/dist/server/app-route-handler-execution.d.ts +6 -1
  125. package/dist/server/app-route-handler-execution.js +41 -15
  126. package/dist/server/app-route-handler-policy.d.ts +2 -0
  127. package/dist/server/app-route-handler-policy.js +3 -3
  128. package/dist/server/app-route-module-loader.d.ts +3 -1
  129. package/dist/server/app-route-module-loader.js +1 -0
  130. package/dist/server/app-route-tree-prefetch.js +1 -1
  131. package/dist/server/app-rsc-cache-busting.d.ts +1 -1
  132. package/dist/server/app-rsc-cache-busting.js +1 -1
  133. package/dist/server/app-rsc-errors.d.ts +1 -1
  134. package/dist/server/app-rsc-errors.js +12 -2
  135. package/dist/server/app-rsc-handler.d.ts +10 -5
  136. package/dist/server/app-rsc-handler.js +51 -23
  137. package/dist/server/app-rsc-redirect-flight.d.ts +66 -0
  138. package/dist/server/app-rsc-redirect-flight.js +71 -0
  139. package/dist/server/app-rsc-request-normalization.d.ts +3 -2
  140. package/dist/server/app-rsc-request-normalization.js +8 -2
  141. package/dist/server/app-rsc-response-finalizer.d.ts +2 -2
  142. package/dist/server/app-rsc-response-finalizer.js +5 -11
  143. package/dist/server/app-rsc-route-matching.d.ts +20 -0
  144. package/dist/server/app-rsc-route-matching.js +87 -11
  145. package/dist/server/app-segment-config.js +15 -5
  146. package/dist/server/app-server-action-execution.d.ts +3 -1
  147. package/dist/server/app-server-action-execution.js +8 -6
  148. package/dist/server/app-ssr-entry.js +4 -1
  149. package/dist/server/app-ssr-stream.d.ts +2 -7
  150. package/dist/server/app-ssr-stream.js +5 -10
  151. package/dist/server/cache-proof.js +1 -1
  152. package/dist/server/config-headers.d.ts +24 -0
  153. package/dist/server/config-headers.js +52 -0
  154. package/dist/server/default-global-error-module.d.ts +3 -1
  155. package/dist/server/default-not-found-module.d.ts +3 -1
  156. package/dist/server/dev-initial-server-error.d.ts +1 -1
  157. package/dist/server/dev-lockfile.js +2 -3
  158. package/dist/server/dev-route-files.js +1 -1
  159. package/dist/server/dev-server.js +281 -173
  160. package/dist/server/dev-stack-sourcemap.js +4 -3
  161. package/dist/server/headers.d.ts +3 -1
  162. package/dist/server/headers.js +3 -1
  163. package/dist/server/image-optimization.js +2 -2
  164. package/dist/server/implicit-tags.js +2 -1
  165. package/dist/server/instrumentation.js +2 -2
  166. package/dist/server/isr-cache.d.ts +1 -1
  167. package/dist/server/isr-cache.js +1 -1
  168. package/dist/server/metadata-routes.js +4 -4
  169. package/dist/server/middleware-matcher-pattern.d.ts +22 -0
  170. package/dist/server/middleware-matcher-pattern.js +219 -0
  171. package/dist/server/middleware-matcher.d.ts +2 -7
  172. package/dist/server/middleware-matcher.js +13 -60
  173. package/dist/server/middleware-path-to-regexp.d.ts +14 -0
  174. package/dist/server/middleware-path-to-regexp.js +228 -0
  175. package/dist/server/middleware-response-headers.js +1 -1
  176. package/dist/server/middleware-runtime.js +5 -4
  177. package/dist/server/middleware.d.ts +2 -1
  178. package/dist/server/middleware.js +3 -2
  179. package/dist/server/navigation-planner.d.ts +2 -0
  180. package/dist/server/navigation-planner.js +8 -2
  181. package/dist/server/next-error-digest.d.ts +9 -24
  182. package/dist/server/next-error-digest.js +23 -15
  183. package/dist/server/operation-token.js +1 -1
  184. package/dist/server/pages-body-parser-config.js +2 -1
  185. package/dist/server/pages-data-route.d.ts +11 -1
  186. package/dist/server/pages-data-route.js +16 -1
  187. package/dist/server/pages-dev-hydration.d.ts +14 -0
  188. package/dist/server/pages-dev-hydration.js +83 -0
  189. package/dist/server/pages-dev-module-url.js +2 -2
  190. package/dist/server/pages-get-initial-props.d.ts +15 -2
  191. package/dist/server/pages-get-initial-props.js +16 -6
  192. package/dist/server/pages-node-compat.d.ts +13 -6
  193. package/dist/server/pages-node-compat.js +44 -45
  194. package/dist/server/pages-page-data.d.ts +4 -2
  195. package/dist/server/pages-page-data.js +20 -10
  196. package/dist/server/pages-page-handler.d.ts +4 -2
  197. package/dist/server/pages-page-handler.js +74 -43
  198. package/dist/server/pages-page-response.d.ts +2 -2
  199. package/dist/server/pages-page-response.js +1 -1
  200. package/dist/server/pages-preview.d.ts +26 -0
  201. package/dist/server/pages-preview.js +165 -0
  202. package/dist/server/pages-readiness.d.ts +5 -4
  203. package/dist/server/pages-readiness.js +4 -2
  204. package/dist/server/pages-request-pipeline.d.ts +1 -0
  205. package/dist/server/pages-request-pipeline.js +25 -11
  206. package/dist/server/pages-router-entry.js +6 -0
  207. package/dist/server/prerender-route-params.js +2 -2
  208. package/dist/server/prod-server.d.ts +1 -4
  209. package/dist/server/prod-server.js +44 -30
  210. package/dist/server/request-pipeline.d.ts +15 -29
  211. package/dist/server/request-pipeline.js +25 -60
  212. package/dist/server/rsc-stream-hints.js +122 -18
  213. package/dist/server/seed-cache.js +1 -1
  214. package/dist/server/socket-error-backstop.js +1 -1
  215. package/dist/server/static-file-cache.js +7 -8
  216. package/dist/server/worker-utils.js +1 -1
  217. package/dist/shims/app.d.ts +1 -1
  218. package/dist/shims/cache-request-state.d.ts +27 -1
  219. package/dist/shims/cache-request-state.js +60 -1
  220. package/dist/shims/cache-runtime.d.ts +12 -1
  221. package/dist/shims/cache-runtime.js +22 -7
  222. package/dist/shims/cache.d.ts +5 -5
  223. package/dist/shims/cache.js +28 -11
  224. package/dist/shims/constants.js +1 -1
  225. package/dist/shims/document.d.ts +42 -83
  226. package/dist/shims/document.js +62 -53
  227. package/dist/shims/dynamic.d.ts +5 -25
  228. package/dist/shims/dynamic.js +29 -8
  229. package/dist/shims/error-boundary.d.ts +4 -4
  230. package/dist/shims/error.d.ts +22 -6
  231. package/dist/shims/error.js +53 -15
  232. package/dist/shims/fetch-cache.js +13 -4
  233. package/dist/shims/font-google-base.d.ts +11 -4
  234. package/dist/shims/font-local.d.ts +10 -3
  235. package/dist/shims/font-utils.js +1 -1
  236. package/dist/shims/form.d.ts +1 -1
  237. package/dist/shims/head.d.ts +13 -2
  238. package/dist/shims/head.js +6 -6
  239. package/dist/shims/headers.d.ts +28 -31
  240. package/dist/shims/headers.js +115 -53
  241. package/dist/shims/image.d.ts +27 -37
  242. package/dist/shims/image.js +21 -14
  243. package/dist/shims/internal/cookie-serialize.d.ts +3 -1
  244. package/dist/shims/internal/cookie-serialize.js +3 -1
  245. package/dist/shims/internal/hybrid-client-route-owner.js +3 -1
  246. package/dist/shims/internal/pages-data-fetch-dedup.js +5 -3
  247. package/dist/shims/internal/utils.d.ts +3 -3
  248. package/dist/shims/layout-segment-context.d.ts +1 -1
  249. package/dist/shims/legacy-image.d.ts +3 -37
  250. package/dist/shims/legacy-image.js +6 -16
  251. package/dist/shims/link.d.ts +16 -21
  252. package/dist/shims/link.js +25 -13
  253. package/dist/shims/navigation-context-state.d.ts +1 -1
  254. package/dist/shims/navigation-errors.d.ts +2 -1
  255. package/dist/shims/navigation-errors.js +9 -13
  256. package/dist/shims/navigation.d.ts +8 -4
  257. package/dist/shims/navigation.js +29 -18
  258. package/dist/shims/next-shims-augmentations.d.ts +1 -0
  259. package/dist/shims/next-shims-public.d.ts +1 -0
  260. package/dist/shims/public-shim-map.json.js +103 -0
  261. package/dist/shims/readonly-url-search-params.d.ts +3 -3
  262. package/dist/shims/readonly-url-search-params.js +3 -3
  263. package/dist/shims/request-state-types.d.ts +1 -1
  264. package/dist/shims/router-state.d.ts +1 -0
  265. package/dist/shims/router.d.ts +57 -22
  266. package/dist/shims/router.js +178 -46
  267. package/dist/shims/script.d.ts +6 -10
  268. package/dist/shims/script.js +7 -4
  269. package/dist/shims/server.d.ts +45 -22
  270. package/dist/shims/server.js +46 -7
  271. package/dist/shims/slot.d.ts +1 -1
  272. package/dist/shims/thenable-params.js +1 -1
  273. package/dist/shims/unified-request-context.d.ts +1 -1
  274. package/dist/shims/unified-request-context.js +2 -0
  275. package/dist/shims/url-safety.d.ts +1 -1
  276. package/dist/shims/url-safety.js +2 -2
  277. package/dist/typegen.d.ts +8 -2
  278. package/dist/typegen.js +84 -25
  279. package/dist/utils/client-build-manifest.js +2 -2
  280. package/dist/utils/client-entry-manifest.js +1 -1
  281. package/dist/utils/client-runtime-metadata.js +1 -1
  282. package/dist/utils/commonjs-loader.js +3 -3
  283. package/dist/utils/domain-locale.d.ts +2 -2
  284. package/dist/utils/external-url.d.ts +5 -0
  285. package/dist/utils/external-url.js +7 -0
  286. package/dist/utils/mdx-scan.js +1 -1
  287. package/dist/utils/path.d.ts +1 -13
  288. package/dist/utils/path.js +1 -15
  289. package/dist/utils/plugin-options.d.ts +4 -0
  290. package/dist/utils/plugin-options.js +8 -0
  291. package/dist/utils/project.d.ts +0 -6
  292. package/dist/utils/project.js +2 -8
  293. package/dist/utils/public-routes.js +2 -2
  294. package/dist/utils/react-version.js +1 -1
  295. package/dist/utils/redirect-digest.d.ts +9 -0
  296. package/dist/utils/redirect-digest.js +25 -0
  297. package/dist/utils/regex-safety.d.ts +8 -0
  298. package/dist/utils/regex-safety.js +737 -0
  299. package/dist/utils/vinext-root.js +1 -1
  300. package/dist/utils/vite-version.d.ts +5 -2
  301. package/dist/utils/vite-version.js +43 -17
  302. package/package.json +16 -4
  303. /package/dist/build/google-fonts/{fallback-metrics-data.js → fallback-metrics-data.json.js} +0 -0
  304. /package/dist/build/google-fonts/{font-data.js → font-data.json.js} +0 -0
  305. /package/dist/{node_modules/.pnpm/process-ancestry@0.1.0/node_modules → deps/.pnpm/process-ancestry@0.1.0/deps}/process-ancestry/dist/index.js +0 -0
@@ -1,10 +1,21 @@
1
- import { NextHeader } from "../config/next-config.js";
2
- import { BasePathMatchState, RequestContext } from "../config/config-matchers.js";
3
1
  import { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS } from "./headers.js";
4
- import { isOpenRedirectShaped } from "./open-redirect.js";
5
2
  import { hasBasePath, stripBasePath } from "../utils/base-path.js";
3
+ import { isOpenRedirectShaped } from "./open-redirect.js";
6
4
 
7
5
  //#region src/server/request-pipeline.d.ts
6
+ /**
7
+ * Apply the URL Standard's pathname canonicalization without decoding and
8
+ * re-encoding ordinary percent escapes.
9
+ *
10
+ * In particular, WHATWG URLs remove literal and percent-encoded dot segments
11
+ * (`/%2e/about` becomes `/about`) while preserving unrelated spellings such
12
+ * as `/%61bout`, `%2F`, `%5C`, and `%252F` byte-for-byte. Node request adapters
13
+ * must do this before comparing raw route/config/basePath identity so those
14
+ * comparisons agree with the `Request` that userland eventually receives.
15
+ */
16
+ declare function canonicalizeRequestPathname(pathname: string): string;
17
+ /** Canonicalize only the pathname portion while preserving the raw query. */
18
+ declare function canonicalizeRequestUrlPathname(url: string): string;
8
19
  /**
9
20
  * Shared request pipeline utilities.
10
21
  *
@@ -44,18 +55,6 @@ import { hasBasePath, stripBasePath } from "../utils/base-path.js";
44
55
  */
45
56
  declare function guardProtocolRelativeUrl(rawPathname: string): Response | null;
46
57
  type HeaderRecord = Record<string, string | string[]>;
47
- type ApplyConfigHeadersOptions = {
48
- configHeaders: NextHeader[];
49
- pathname: string;
50
- requestContext: RequestContext;
51
- /**
52
- * basePath gating state. When omitted, every rule is treated as a default
53
- * (basePath: true) rule for backward compatibility — callers that need to
54
- * support `basePath: false` headers must pass this in.
55
- */
56
- basePathState?: BasePathMatchState; /** Existing framework-generated headers that matching config rules may replace. */
57
- overwriteExisting?: ReadonlySet<string>;
58
- };
59
58
  type StaticFileSignalContext = {
60
59
  headers: Headers | null;
61
60
  status: number | null;
@@ -67,19 +66,6 @@ type ResolvePublicFileRouteOptions = {
67
66
  publicFiles: ReadonlySet<string>;
68
67
  request: Request;
69
68
  };
70
- /**
71
- * Apply matched next.config.js headers to a Web Headers object.
72
- *
73
- * Next.js evaluates config header match conditions against the original
74
- * request snapshot. Middleware response headers still win for the same
75
- * response key, while multi-value headers are additive.
76
- */
77
- declare function applyConfigHeadersToResponse(responseHeaders: Headers, options: ApplyConfigHeadersOptions): void;
78
- /**
79
- * Apply matched next.config.js headers to the early response header record used
80
- * by Node and Worker Pages Router pipelines before a concrete response exists.
81
- */
82
- declare function applyConfigHeadersToHeaderRecord(headers: HeaderRecord, options: ApplyConfigHeadersOptions): void;
83
69
  declare function createStaticFileSignal(pathname: string, context: StaticFileSignalContext): Response;
84
70
  /**
85
71
  * Resolve the public/ filesystem-route slot in the Next.js routing order.
@@ -184,4 +170,4 @@ declare function cloneRequestWithHeaders(request: Request, headers: Headers): Re
184
170
  */
185
171
  declare function cloneRequestWithUrl(request: Request, url: string): Request;
186
172
  //#endregion
187
- export { HeaderRecord, INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, applyConfigHeadersToHeaderRecord, applyConfigHeadersToResponse, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, stripBasePath, validateCsrfOrigin, validateServerActionPayload };
173
+ export { HeaderRecord, INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, canonicalizeRequestPathname, canonicalizeRequestUrlPathname, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, stripBasePath, validateCsrfOrigin, validateServerActionPayload };
@@ -1,10 +1,32 @@
1
1
  import { hasBasePath, removeTrailingSlash, stripBasePath } from "../utils/base-path.js";
2
2
  import "../utils/protocol-headers.js";
3
3
  import { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, VINEXT_STATIC_FILE_HEADER } from "./headers.js";
4
- import { matchHeaders } from "../config/config-matchers.js";
5
4
  import { forbiddenResponse, notFoundResponse } from "./http-error-responses.js";
6
5
  import { isOpenRedirectShaped } from "./open-redirect.js";
7
6
  //#region src/server/request-pipeline.ts
7
+ const PATHNAME_CANONICALIZATION_BASE = new URL("http://vinext.invalid/");
8
+ /**
9
+ * Apply the URL Standard's pathname canonicalization without decoding and
10
+ * re-encoding ordinary percent escapes.
11
+ *
12
+ * In particular, WHATWG URLs remove literal and percent-encoded dot segments
13
+ * (`/%2e/about` becomes `/about`) while preserving unrelated spellings such
14
+ * as `/%61bout`, `%2F`, `%5C`, and `%252F` byte-for-byte. Node request adapters
15
+ * must do this before comparing raw route/config/basePath identity so those
16
+ * comparisons agree with the `Request` that userland eventually receives.
17
+ */
18
+ function canonicalizeRequestPathname(pathname) {
19
+ const url = new URL(PATHNAME_CANONICALIZATION_BASE);
20
+ url.pathname = pathname;
21
+ return url.pathname;
22
+ }
23
+ /** Canonicalize only the pathname portion while preserving the raw query. */
24
+ function canonicalizeRequestUrlPathname(url) {
25
+ const queryIndex = url.indexOf("?");
26
+ const pathname = queryIndex === -1 ? url : url.slice(0, queryIndex);
27
+ const search = queryIndex === -1 ? "" : url.slice(queryIndex);
28
+ return canonicalizeRequestPathname(pathname) + search;
29
+ }
8
30
  /**
9
31
  * Shared request pipeline utilities.
10
32
  *
@@ -50,63 +72,6 @@ const FILE_LIKE_PATHNAME_RE = /\.[^/]+\/?$/;
50
72
  function isWellKnownPathname(pathname) {
51
73
  return pathname === "/.well-known" || pathname.startsWith("/.well-known/");
52
74
  }
53
- function findHeaderRecordKey(headers, lowerName) {
54
- for (const key of Object.keys(headers)) if (key.toLowerCase() === lowerName) return key;
55
- }
56
- function appendHeaderRecord(headers, lowerName, value) {
57
- const key = findHeaderRecordKey(headers, lowerName) ?? lowerName;
58
- const existing = headers[key];
59
- if (existing === void 0) {
60
- headers[key] = value;
61
- return;
62
- }
63
- if (Array.isArray(existing)) {
64
- existing.push(value);
65
- return;
66
- }
67
- headers[key] = [existing, value];
68
- }
69
- function appendVaryHeaderRecord(headers, value) {
70
- const key = findHeaderRecordKey(headers, "vary") ?? "vary";
71
- const existing = headers[key];
72
- if (existing === void 0) {
73
- headers[key] = value;
74
- return;
75
- }
76
- if (Array.isArray(existing)) {
77
- existing.push(value);
78
- return;
79
- }
80
- headers[key] = existing + ", " + value;
81
- }
82
- /**
83
- * Apply matched next.config.js headers to a Web Headers object.
84
- *
85
- * Next.js evaluates config header match conditions against the original
86
- * request snapshot. Middleware response headers still win for the same
87
- * response key, while multi-value headers are additive.
88
- */
89
- function applyConfigHeadersToResponse(responseHeaders, options) {
90
- const matched = matchHeaders(options.pathname, options.configHeaders, options.requestContext, options.basePathState);
91
- for (const header of matched) {
92
- const lowerName = header.key.toLowerCase();
93
- if (lowerName === "vary" || lowerName === "set-cookie") responseHeaders.append(header.key, header.value);
94
- else if (options.overwriteExisting?.has(lowerName) || !responseHeaders.has(lowerName)) responseHeaders.set(header.key, header.value);
95
- }
96
- }
97
- /**
98
- * Apply matched next.config.js headers to the early response header record used
99
- * by Node and Worker Pages Router pipelines before a concrete response exists.
100
- */
101
- function applyConfigHeadersToHeaderRecord(headers, options) {
102
- const matched = matchHeaders(options.pathname, options.configHeaders, options.requestContext, options.basePathState);
103
- for (const header of matched) {
104
- const lowerName = header.key.toLowerCase();
105
- if (lowerName === "set-cookie") appendHeaderRecord(headers, lowerName, header.value);
106
- else if (lowerName === "vary") appendVaryHeaderRecord(headers, header.value);
107
- else if (findHeaderRecordKey(headers, lowerName) === void 0) headers[lowerName] = header.value;
108
- }
109
- }
110
75
  function createStaticFileSignal(pathname, context) {
111
76
  const headers = new Headers({ [VINEXT_STATIC_FILE_HEADER]: encodeURIComponent(pathname) });
112
77
  if (context.headers) for (const [key, value] of context.headers) headers.append(key, value);
@@ -341,7 +306,7 @@ function processMiddlewareHeaders(headers) {
341
306
  for (const key of headers.keys()) if (key.startsWith("x-middleware-") && key !== "x-middleware-cache") keysToDelete.push(key);
342
307
  for (const key of keysToDelete) headers.delete(key);
343
308
  }
344
- const STRIPPED_INTERNAL_HEADERS = new Set([...INTERNAL_HEADERS, ...VINEXT_INTERNAL_HEADERS]);
309
+ const STRIPPED_INTERNAL_HEADERS = /* @__PURE__ */ new Set([...INTERNAL_HEADERS, ...VINEXT_INTERNAL_HEADERS]);
345
310
  /**
346
311
  * Strip internal headers from an inbound request so they cannot be forged by
347
312
  * an external attacker to influence routing or impersonate internal state.
@@ -445,4 +410,4 @@ function cloneRequestWithUrl(request, url) {
445
410
  return cloned;
446
411
  }
447
412
  //#endregion
448
- export { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, applyConfigHeadersToHeaderRecord, applyConfigHeadersToResponse, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, stripBasePath, validateCsrfOrigin, validateServerActionPayload };
413
+ export { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, canonicalizeRequestPathname, canonicalizeRequestUrlPathname, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, stripBasePath, validateCsrfOrigin, validateServerActionPayload };
@@ -1,31 +1,135 @@
1
1
  //#region src/server/rsc-stream-hints.ts
2
- const REACT_FLIGHT_STYLESHEET_PRELOAD_HINT = /(\d*:HL\[.*?),"stylesheet"(\]|,)/g;
3
- /**
4
- * React Flight emits HL hints with "stylesheet" for CSS preloads, but the
5
- * HTML spec requires "style" for <link rel="preload">. Rewrite each complete
6
- * Flight line so SSR embeds, navigation, and server actions see valid hints.
7
- */
2
+ const REACT_FLIGHT_STYLESHEET_PRELOAD_HINT = /^([0-9a-f]*:HL\[.*?),"stylesheet"(\]|,)/;
3
+ const STYLESHEET_TO_STYLE_JSON_PADDING = " ".repeat(5);
4
+ const LENGTH_PREFIXED_ROW_TAGS = /* @__PURE__ */ new Set([
5
+ "T",
6
+ "A",
7
+ "O",
8
+ "o",
9
+ "b",
10
+ "U",
11
+ "S",
12
+ "s",
13
+ "L",
14
+ "l",
15
+ "G",
16
+ "g",
17
+ "M",
18
+ "m",
19
+ "V"
20
+ ]);
21
+ const NEWLINE_PREFIXED_ROW_TAGS = /* @__PURE__ */ new Set([
22
+ "I",
23
+ "H",
24
+ "E",
25
+ "N",
26
+ "D",
27
+ "J",
28
+ "W",
29
+ "R",
30
+ "r",
31
+ "X",
32
+ "x",
33
+ "C",
34
+ "P",
35
+ "#"
36
+ ]);
37
+ const decoder = new TextDecoder();
38
+ const encoder = new TextEncoder();
39
+ /** Rewrite only a complete React Flight stylesheet hint row. */
8
40
  function normalizeReactFlightHintLine(line) {
9
- return line.replace(REACT_FLIGHT_STYLESHEET_PRELOAD_HINT, "$1,\"style\"$2");
41
+ const text = decoder.decode(line);
42
+ const normalized = text.replace(REACT_FLIGHT_STYLESHEET_PRELOAD_HINT, `$1,"style"${STYLESHEET_TO_STYLE_JSON_PADDING}$2`);
43
+ if (normalized === text) return line;
44
+ const normalizedBytes = encoder.encode(normalized);
45
+ return normalizedBytes.byteLength === line.byteLength ? normalizedBytes : line;
46
+ }
47
+ function concatBytes(first, second) {
48
+ if (first.byteLength === 0) return second;
49
+ const combined = new Uint8Array(first.byteLength + second.byteLength);
50
+ combined.set(first);
51
+ combined.set(second, first.byteLength);
52
+ return combined;
53
+ }
54
+ function indexOfByte(bytes, byte, from = 0) {
55
+ for (let index = from; index < bytes.byteLength; index++) if (bytes[index] === byte) return index;
56
+ return -1;
57
+ }
58
+ function parseHexBytes(bytes, start, end) {
59
+ if (start === end) return null;
60
+ let value = 0;
61
+ for (let index = start; index < end; index++) {
62
+ const byte = bytes[index];
63
+ const digit = byte >= 48 && byte <= 57 ? byte - 48 : byte >= 97 && byte <= 102 ? byte - 87 : -1;
64
+ if (digit === -1) return null;
65
+ value = value * 16 + digit;
66
+ if (!Number.isSafeInteger(value)) return null;
67
+ }
68
+ return value;
69
+ }
70
+ function isUntaggedJsonRowStart(byte) {
71
+ return byte === 34 || byte === 45 || byte >= 48 && byte <= 57 || byte === 91 || byte === 102 || byte === 110 || byte === 116 || byte === 123;
10
72
  }
11
73
  function normalizeReactFlightPreloadHints(stream) {
12
- const decoder = new TextDecoder();
13
- const encoder = new TextEncoder();
14
- let carry = "";
74
+ let carry = /* @__PURE__ */ new Uint8Array();
75
+ let rawBytesRemaining = 0;
76
+ let passThrough = false;
15
77
  return stream.pipeThrough(new TransformStream({
16
78
  transform(chunk, controller) {
17
- const text = carry + decoder.decode(chunk, { stream: true });
18
- const lastNewline = text.lastIndexOf("\n");
19
- if (lastNewline === -1) {
20
- carry = text;
79
+ if (passThrough) {
80
+ controller.enqueue(chunk);
21
81
  return;
22
82
  }
23
- carry = text.slice(lastNewline + 1);
24
- controller.enqueue(encoder.encode(normalizeReactFlightHintLine(text.slice(0, lastNewline + 1))));
83
+ let bytes = concatBytes(carry, chunk);
84
+ carry = /* @__PURE__ */ new Uint8Array();
85
+ while (bytes.byteLength > 0) {
86
+ if (rawBytesRemaining > 0) {
87
+ const length = Math.min(rawBytesRemaining, bytes.byteLength);
88
+ controller.enqueue(bytes.slice(0, length));
89
+ rawBytesRemaining -= length;
90
+ bytes = bytes.subarray(length);
91
+ continue;
92
+ }
93
+ const colon = indexOfByte(bytes, 58);
94
+ if (colon === -1 || colon + 1 === bytes.byteLength) {
95
+ carry = bytes.slice();
96
+ return;
97
+ }
98
+ const tag = String.fromCharCode(bytes[colon + 1]);
99
+ if (LENGTH_PREFIXED_ROW_TAGS.has(tag)) {
100
+ const comma = indexOfByte(bytes, 44, colon + 2);
101
+ if (comma === -1) {
102
+ carry = bytes.slice();
103
+ return;
104
+ }
105
+ const length = parseHexBytes(bytes, colon + 2, comma);
106
+ if (length != null) {
107
+ controller.enqueue(bytes.slice(0, comma + 1));
108
+ rawBytesRemaining = length;
109
+ bytes = bytes.subarray(comma + 1);
110
+ continue;
111
+ }
112
+ passThrough = true;
113
+ controller.enqueue(bytes);
114
+ return;
115
+ }
116
+ const tagByte = bytes[colon + 1];
117
+ if (!NEWLINE_PREFIXED_ROW_TAGS.has(tag) && !isUntaggedJsonRowStart(tagByte)) {
118
+ passThrough = true;
119
+ controller.enqueue(bytes);
120
+ return;
121
+ }
122
+ const newline = indexOfByte(bytes, 10);
123
+ if (newline === -1) {
124
+ carry = bytes.slice();
125
+ return;
126
+ }
127
+ controller.enqueue(normalizeReactFlightHintLine(bytes.slice(0, newline + 1)));
128
+ bytes = bytes.subarray(newline + 1);
129
+ }
25
130
  },
26
131
  flush(controller) {
27
- const text = carry + decoder.decode();
28
- if (text) controller.enqueue(encoder.encode(normalizeReactFlightHintLine(text)));
132
+ if (carry.byteLength > 0) controller.enqueue(rawBytesRemaining > 0 ? carry : normalizeReactFlightHintLine(carry));
29
133
  }
30
134
  }));
31
135
  }
@@ -1,10 +1,10 @@
1
+ import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
1
2
  import { isrCacheKey, isrSetPrerenderedAppPage } from "./isr-cache.js";
2
3
  import { getRenderedAppRoutes, isFallbackShellArtifactPath, readPrerenderManifest } from "./prerender-manifest.js";
3
4
  import { buildAppPageCacheTags } from "./app-page-cache.js";
4
5
  import { getOutputPath, getRscOutputPath } from "../utils/prerender-output-paths.js";
5
6
  import { addPregeneratedConcretePath, clearPregeneratedConcretePaths, normalizePregeneratedPathname } from "./pregenerated-concrete-paths.js";
6
7
  import fs from "node:fs";
7
- import path from "node:path";
8
8
  //#region src/server/seed-cache.ts
9
9
  /**
10
10
  * Seed the memory cache from pre-rendered build output.
@@ -90,7 +90,7 @@ function peerDisconnectCode(err) {
90
90
  const code = err?.code;
91
91
  return code === "ECONNRESET" || code === "EPIPE" || code === "ECONNABORTED" ? code : void 0;
92
92
  }
93
- const STATIC_ASSET_IMPORT_EXTENSIONS = new Set([
93
+ const STATIC_ASSET_IMPORT_EXTENSIONS = /* @__PURE__ */ new Set([
94
94
  ".css",
95
95
  ".scss",
96
96
  ".sass",
@@ -1,7 +1,6 @@
1
- import { normalizePathSeparators } from "../utils/path.js";
1
+ import path, { toSlash } from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
2
  import { ASSET_PREFIX_URL_DIR } from "../utils/asset-prefix.js";
3
- import path from "node:path";
4
- import fs from "node:fs/promises";
3
+ import fsp from "node:fs/promises";
5
4
  //#region src/server/static-file-cache.ts
6
5
  /**
7
6
  * Startup metadata cache for static file serving.
@@ -137,7 +136,7 @@ var StaticFileCache = class StaticFileCache {
137
136
  }
138
137
  }
139
138
  for (let i = 0; i < toBuffer.length; i += 64) await Promise.all(toBuffer.slice(i, i + 64).map(async (v) => {
140
- v.buffer = await fs.readFile(v.path);
139
+ v.buffer = await fsp.readFile(v.path);
141
140
  }));
142
141
  return new StaticFileCache(entries);
143
142
  }
@@ -170,7 +169,7 @@ function etagFromFilenameHash(relativePath, ext) {
170
169
  const suffix = basename.slice(lastDash + 1);
171
170
  if (suffix.length >= 6 && suffix.length <= 12 && /^[A-Za-z0-9_-]+$/.test(suffix)) return `W/"${suffix}"`;
172
171
  }
173
- const normalizedPath = normalizePathSeparators(relativePath);
172
+ const normalizedPath = toSlash(relativePath);
174
173
  const managedMediaSegment = `${ASSET_PREFIX_URL_DIR}/media/`;
175
174
  if (normalizedPath.startsWith(managedMediaSegment) || normalizedPath.includes(`/${managedMediaSegment}`)) {
176
175
  const lastDot = basename.lastIndexOf(".");
@@ -204,7 +203,7 @@ const STAT_BATCH_SIZE = 64;
204
203
  async function* walkFilesWithStats(dir, base = dir) {
205
204
  let entries;
206
205
  try {
207
- entries = await fs.readdir(dir, { withFileTypes: true });
206
+ entries = await fsp.readdir(dir, { withFileTypes: true });
208
207
  } catch {
209
208
  return;
210
209
  }
@@ -216,9 +215,9 @@ async function* walkFilesWithStats(dir, base = dir) {
216
215
  }
217
216
  for (let i = 0; i < files.length; i += STAT_BATCH_SIZE) {
218
217
  const batch = files.slice(i, i + STAT_BATCH_SIZE);
219
- const stats = await Promise.all(batch.map((f) => fs.stat(f)));
218
+ const stats = await Promise.all(batch.map((f) => fsp.stat(f)));
220
219
  for (let j = 0; j < batch.length; j++) yield {
221
- relativePath: normalizePathSeparators(path.relative(base, batch[j])),
220
+ relativePath: path.relative(base, batch[j]),
222
221
  fullPath: batch[j],
223
222
  stat: {
224
223
  size: stats[j].size,
@@ -14,7 +14,7 @@ import { notFoundStaticAssetResponse } from "./http-error-responses.js";
14
14
  * are preserved). Uses getSetCookie() to preserve multiple Set-Cookie values.
15
15
  * Keep this in sync with prod-server.ts.
16
16
  */
17
- const NO_BODY_RESPONSE_STATUSES = new Set([
17
+ const NO_BODY_RESPONSE_STATUSES = /* @__PURE__ */ new Set([
18
18
  204,
19
19
  205,
20
20
  304
@@ -36,7 +36,7 @@ declare function appGetInitialProps({
36
36
  declare class App<P = any, CP = any, S = any> extends React.Component<P & AppProps<CP>, S> {
37
37
  static origGetInitialProps: typeof appGetInitialProps;
38
38
  static getInitialProps: typeof appGetInitialProps;
39
- render(): React.ReactNode;
39
+ render(): React.ReactElement;
40
40
  }
41
41
  //#endregion
42
42
  export { AppContext, AppInitialProps, AppProps, App as default };
@@ -26,6 +26,8 @@ type UnstableCacheObservation = Readonly<{
26
26
  }>;
27
27
  type CacheState = {
28
28
  actionRevalidationKind: ActionRevalidationKind;
29
+ pendingRevalidatedTags: Set<string>;
30
+ pendingRevalidations: Set<Promise<void>>;
29
31
  requestScopedCacheLife: CacheLifeConfig | null;
30
32
  unstableCacheObservations: Map<string, UnstableCacheObservation>;
31
33
  unstableCacheRevalidation: UnstableCacheRevalidationMode;
@@ -37,6 +39,30 @@ declare function _runWithCacheState<T>(fn: () => T | Promise<T>): T | Promise<T>
37
39
  declare function _initRequestScopedCacheState(): void;
38
40
  declare function markActionRevalidation(kind: ActionRevalidationKind): void;
39
41
  declare function getAndClearActionRevalidationKind(): ActionRevalidationKind;
42
+ /** @internal */
43
+ declare function _markPendingRevalidatedTag(tag: string): void;
44
+ /** @internal */
45
+ declare function _hasPendingRevalidatedTag(tags: readonly string[]): boolean;
46
+ /**
47
+ * Record a cache invalidation that must finish before the current action or
48
+ * route-handler request is finalized. The public revalidation APIs remain
49
+ * synchronous, matching Next.js, while the request boundary owns the await.
50
+ *
51
+ * Returns false outside a request-like phase so standalone calls can retain
52
+ * their historical background-work behavior without accumulating promises in
53
+ * the process-global fallback state.
54
+ *
55
+ * @internal
56
+ */
57
+ declare function _queuePendingRevalidation(promise: Promise<void>): boolean;
58
+ /**
59
+ * Await and clear every cache invalidation queued in the current request.
60
+ * Clearing before awaiting also lets a later drain observe work enqueued by
61
+ * an async continuation while this batch is settling.
62
+ *
63
+ * @internal
64
+ */
65
+ declare function _drainPendingRevalidations(): Promise<void>;
40
66
  declare function _setRequestScopedCacheLife(config: CacheLifeConfig): void;
41
67
  declare function _peekRequestScopedCacheLife(): CacheLifeConfig | null;
42
68
  declare function _consumeRequestScopedCacheLife(): CacheLifeConfig | null;
@@ -44,4 +70,4 @@ declare function recordUnstableCacheObservation(observation: UnstableCacheObserv
44
70
  declare function _peekUnstableCacheObservations(): UnstableCacheObservation[];
45
71
  declare function shouldServeStaleUnstableCacheEntry(): boolean;
46
72
  //#endregion
47
- export { 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 };
73
+ export { ACTION_DID_REVALIDATE_DYNAMIC_ONLY, ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC, ActionRevalidationKind, CacheLifeConfig, CacheState, UnstableCacheObservation, UnstableCacheRevalidationMode, _consumeRequestScopedCacheLife, _drainPendingRevalidations, _hasPendingRevalidatedTag, _initRequestScopedCacheState, _markPendingRevalidatedTag, _peekRequestScopedCacheLife, _peekUnstableCacheObservations, _queuePendingRevalidation, _registerCacheContextAccessor, _runWithCacheState, _setRequestScopedCacheLife, cacheLifeProfiles, getAndClearActionRevalidationKind, getRegisteredCacheContext, markActionRevalidation, recordUnstableCacheObservation, shouldServeStaleUnstableCacheEntry };
@@ -53,6 +53,8 @@ const ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC = 1;
53
53
  const ACTION_DID_REVALIDATE_DYNAMIC_ONLY = 2;
54
54
  const fallbackState = globalState[FALLBACK_KEY] ??= {
55
55
  actionRevalidationKind: ACTION_DID_NOT_REVALIDATE,
56
+ pendingRevalidatedTags: /* @__PURE__ */ new Set(),
57
+ pendingRevalidations: /* @__PURE__ */ new Set(),
56
58
  requestScopedCacheLife: null,
57
59
  unstableCacheObservations: /* @__PURE__ */ new Map(),
58
60
  unstableCacheRevalidation: "foreground"
@@ -70,6 +72,8 @@ function _runWithCacheState(fn) {
70
72
  }, fn);
71
73
  const state = {
72
74
  actionRevalidationKind: ACTION_DID_NOT_REVALIDATE,
75
+ pendingRevalidatedTags: /* @__PURE__ */ new Set(),
76
+ pendingRevalidations: /* @__PURE__ */ new Set(),
73
77
  requestScopedCacheLife: null,
74
78
  unstableCacheObservations: /* @__PURE__ */ new Map(),
75
79
  unstableCacheRevalidation: "foreground"
@@ -93,6 +97,61 @@ function getAndClearActionRevalidationKind() {
93
97
  state.actionRevalidationKind = ACTION_DID_NOT_REVALIDATE;
94
98
  return kind;
95
99
  }
100
+ function hasRequestScopedCacheState() {
101
+ if (isInsideUnifiedScope() || cacheAls.getStore() !== void 0) return true;
102
+ const phase = getHeadersAccessPhase();
103
+ return phase === "action" || phase === "route-handler";
104
+ }
105
+ /** @internal */
106
+ function _markPendingRevalidatedTag(tag) {
107
+ if (!hasRequestScopedCacheState()) return;
108
+ getCacheState().pendingRevalidatedTags.add(tag);
109
+ }
110
+ /** @internal */
111
+ function _hasPendingRevalidatedTag(tags) {
112
+ if (!hasRequestScopedCacheState()) return false;
113
+ const pendingTags = getCacheState().pendingRevalidatedTags;
114
+ return tags.some((tag) => pendingTags.has(tag));
115
+ }
116
+ /**
117
+ * Record a cache invalidation that must finish before the current action or
118
+ * route-handler request is finalized. The public revalidation APIs remain
119
+ * synchronous, matching Next.js, while the request boundary owns the await.
120
+ *
121
+ * Returns false outside a request-like phase so standalone calls can retain
122
+ * their historical background-work behavior without accumulating promises in
123
+ * the process-global fallback state.
124
+ *
125
+ * @internal
126
+ */
127
+ function _queuePendingRevalidation(promise) {
128
+ if (!hasRequestScopedCacheState()) return false;
129
+ getCacheState().pendingRevalidations.add(promise);
130
+ promise.catch(() => {});
131
+ return true;
132
+ }
133
+ /**
134
+ * Await and clear every cache invalidation queued in the current request.
135
+ * Clearing before awaiting also lets a later drain observe work enqueued by
136
+ * an async continuation while this batch is settling.
137
+ *
138
+ * @internal
139
+ */
140
+ async function _drainPendingRevalidations() {
141
+ const state = getCacheState();
142
+ let didReject = false;
143
+ let firstRejection;
144
+ while (state.pendingRevalidations.size > 0) {
145
+ const pending = [...state.pendingRevalidations];
146
+ state.pendingRevalidations.clear();
147
+ const results = await Promise.allSettled(pending);
148
+ for (const result of results) if (result.status === "rejected" && !didReject) {
149
+ didReject = true;
150
+ firstRejection = result.reason;
151
+ }
152
+ }
153
+ if (didReject) throw firstRejection;
154
+ }
96
155
  function _setRequestScopedCacheLife(config) {
97
156
  const state = getCacheState();
98
157
  if (state.requestScopedCacheLife === null) {
@@ -123,4 +182,4 @@ function shouldServeStaleUnstableCacheEntry() {
123
182
  return getCacheState().unstableCacheRevalidation === "background";
124
183
  }
125
184
  //#endregion
126
- export { ACTION_DID_REVALIDATE_DYNAMIC_ONLY, ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC, _consumeRequestScopedCacheLife, _initRequestScopedCacheState, _peekRequestScopedCacheLife, _peekUnstableCacheObservations, _registerCacheContextAccessor, _runWithCacheState, _setRequestScopedCacheLife, cacheLifeProfiles, getAndClearActionRevalidationKind, getRegisteredCacheContext, markActionRevalidation, recordUnstableCacheObservation, shouldServeStaleUnstableCacheEntry };
185
+ export { ACTION_DID_REVALIDATE_DYNAMIC_ONLY, ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC, _consumeRequestScopedCacheLife, _drainPendingRevalidations, _hasPendingRevalidatedTag, _initRequestScopedCacheState, _markPendingRevalidatedTag, _peekRequestScopedCacheLife, _peekUnstableCacheObservations, _queuePendingRevalidation, _registerCacheContextAccessor, _runWithCacheState, _setRequestScopedCacheLife, cacheLifeProfiles, getAndClearActionRevalidationKind, getRegisteredCacheContext, markActionRevalidation, recordUnstableCacheObservation, shouldServeStaleUnstableCacheEntry };
@@ -35,6 +35,17 @@ declare const cacheContextStorage: import("node:async_hooks").AsyncLocalStorage<
35
35
  * Get the current cache context. Returns null if not inside a "use cache" function.
36
36
  */
37
37
  declare function getCacheContext(): CacheContext | null;
38
+ /**
39
+ * Build the shared-cache key for a "use cache" function from its build-scoped
40
+ * identity and serialized arguments.
41
+ *
42
+ * This is a logical handler key, not a storage key. Backend-specific adapters
43
+ * are responsible for mapping it to their physical key constraints after
44
+ * applying any storage prefixes.
45
+ *
46
+ * Exported for testing.
47
+ */
48
+ declare function buildUseCacheKey(id: string, keySeed: string | undefined, argsKey?: string): string;
38
49
  /**
39
50
  * Convert an encodeReply result (string | FormData) to a cache key string.
40
51
  * For FormData (binary args), produces a deterministic SHA-256 hash over
@@ -80,4 +91,4 @@ type RegisterCachedFunctionOptions = {
80
91
  */
81
92
  declare function registerCachedFunction<TArgs extends unknown[], TResult>(fn: (...args: TArgs) => Promise<TResult>, id: string, variant?: string, options?: RegisterCachedFunctionOptions): (...args: TArgs) => Promise<TResult>;
82
93
  //#endregion
83
- export { CacheContext, NestedDynamicUseCacheError, PrivateCacheState, cacheContextStorage, clearPrivateCache, getCacheContext, markAppPagePropsForUseCache, registerCachedFunction, replyToCacheKey, runWithPrivateCache };
94
+ export { CacheContext, NestedDynamicUseCacheError, PrivateCacheState, buildUseCacheKey, cacheContextStorage, clearPrivateCache, getCacheContext, markAppPagePropsForUseCache, registerCachedFunction, replyToCacheKey, runWithPrivateCache };
@@ -4,7 +4,7 @@ import { VINEXT_RSC_MARKER_HEADER } from "../server/headers.js";
4
4
  import { getDataCacheHandler } from "./cache-handler.js";
5
5
  import { trackPprFallbackShellCacheTask } from "./ppr-fallback-shell.js";
6
6
  import { markDynamicUsage } from "./headers.js";
7
- import { _registerCacheContextAccessor, _setRequestScopedCacheLife, cacheLifeProfiles } from "./cache-request-state.js";
7
+ import { _hasPendingRevalidatedTag, _registerCacheContextAccessor, _setRequestScopedCacheLife, cacheLifeProfiles } from "./cache-request-state.js";
8
8
  import { addCollectedRequestTags, getCurrentFetchSoftTags } from "./fetch-cache.js";
9
9
  import { isMarkedAppPagePropsObject, markAppPagePropsForUseCache } from "./internal/app-page-props-cache-key.js";
10
10
  //#region src/shims/cache-runtime.ts
@@ -102,6 +102,16 @@ function getUseCacheBuildIdDefine() {
102
102
  function getUseCacheKeySeed() {
103
103
  return getUseCacheDeploymentIdDefine() || getUseCacheBuildIdDefine();
104
104
  }
105
+ /**
106
+ * Build the shared-cache key for a "use cache" function from its build-scoped
107
+ * identity and serialized arguments.
108
+ *
109
+ * This is a logical handler key, not a storage key. Backend-specific adapters
110
+ * are responsible for mapping it to their physical key constraints after
111
+ * applying any storage prefixes.
112
+ *
113
+ * Exported for testing.
114
+ */
105
115
  function buildUseCacheKey(id, keySeed, argsKey) {
106
116
  const scopedId = keySeed ? `build:${encodeURIComponent(keySeed)}:${id}` : id;
107
117
  return argsKey === void 0 ? `use-cache:${scopedId}` : `use-cache:${scopedId}:${argsKey}`;
@@ -263,11 +273,16 @@ function registerCachedFunction(fn, id, variant, options = {}) {
263
273
  if (isDev) return executeWithContext(fn, args, cacheVariant);
264
274
  const handler = getDataCacheHandler();
265
275
  const softTags = getCurrentFetchSoftTags();
266
- const existing = await handler.get(cacheKey, {
267
- kind: "FETCH",
268
- softTags
269
- });
270
- if (existing?.value && existing.value.kind === "FETCH" && existing.cacheState !== "stale") try {
276
+ let existing = null;
277
+ if (!_hasPendingRevalidatedTag(softTags)) try {
278
+ existing = await handler.get(cacheKey, {
279
+ kind: "FETCH",
280
+ softTags
281
+ });
282
+ } catch (error) {
283
+ console.error("[vinext] use cache: handler.get failed; treating as a cache miss:", error);
284
+ }
285
+ if (existing?.value && existing.value.kind === "FETCH" && existing.cacheState !== "stale" && !_hasPendingRevalidatedTag([...existing.value.tags ?? [], ...softTags])) try {
271
286
  propagateCacheTagsToRequest(existing.value.tags);
272
287
  if (rsc && existing.value.data.headers["x-vinext-rsc"] === "1") {
273
288
  const stream = uint8ToStream(base64ToUint8(existing.value.data.body));
@@ -449,4 +464,4 @@ function stableStringify(value, seen) {
449
464
  return JSON.stringify(value);
450
465
  }
451
466
  //#endregion
452
- export { NestedDynamicUseCacheError, cacheContextStorage, clearPrivateCache, getCacheContext, markAppPagePropsForUseCache, registerCachedFunction, replyToCacheKey, runWithPrivateCache };
467
+ export { NestedDynamicUseCacheError, buildUseCacheKey, cacheContextStorage, clearPrivateCache, getCacheContext, markAppPagePropsForUseCache, registerCachedFunction, replyToCacheKey, runWithPrivateCache };