vinext 1.0.0-beta.2 → 1.0.0-beta.4

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 (174) hide show
  1. package/README.md +6 -9
  2. package/dist/build/client-build-config.js +7 -1
  3. package/dist/build/precompress.js +1 -1
  4. package/dist/build/prerender.d.ts +2 -1
  5. package/dist/build/prerender.js +12 -2
  6. package/dist/check.js +4 -0
  7. package/dist/cli-args.d.ts +2 -1
  8. package/dist/cli-args.js +10 -0
  9. package/dist/cli.js +14 -8
  10. package/dist/client/navigation-runtime.d.ts +6 -0
  11. package/dist/client/navigation-runtime.js +1 -1
  12. package/dist/config/config-matchers.js +4 -1
  13. package/dist/config/dotenv.d.ts +3 -4
  14. package/dist/config/dotenv.js +30 -22
  15. package/dist/config/next-config.d.ts +16 -5
  16. package/dist/config/next-config.js +7 -0
  17. package/dist/entries/app-browser-entry.d.ts +4 -10
  18. package/dist/entries/app-browser-entry.js +20 -4
  19. package/dist/entries/app-rsc-entry.js +21 -3
  20. package/dist/entries/app-rsc-manifest.js +19 -0
  21. package/dist/entries/pages-client-entry.js +0 -1
  22. package/dist/entries/pages-server-entry.js +13 -3
  23. package/dist/index.js +122 -49
  24. package/dist/init-cloudflare.d.ts +3 -1
  25. package/dist/init-cloudflare.js +59 -5
  26. package/dist/plugins/client-reference-dedup.js +7 -5
  27. package/dist/plugins/extensionless-dynamic-import.js +23 -18
  28. package/dist/plugins/ignore-dynamic-requests.js +3 -1
  29. package/dist/plugins/import-meta-url.js +17 -6
  30. package/dist/plugins/require-context.js +20 -15
  31. package/dist/plugins/sass.js +3 -4
  32. package/dist/plugins/transform-cache.d.ts +19 -0
  33. package/dist/plugins/transform-cache.js +36 -0
  34. package/dist/routing/app-route-graph.d.ts +14 -6
  35. package/dist/routing/app-route-graph.js +106 -16
  36. package/dist/server/api-handler.d.ts +2 -0
  37. package/dist/server/api-handler.js +7 -6
  38. package/dist/server/app-bfcache-identity.js +28 -2
  39. package/dist/server/app-browser-entry.js +175 -75
  40. package/dist/server/app-browser-navigation-controller.d.ts +23 -21
  41. package/dist/server/app-browser-navigation-controller.js +30 -5
  42. package/dist/server/app-browser-prefetch-response.d.ts +9 -0
  43. package/dist/server/app-browser-prefetch-response.js +32 -0
  44. package/dist/server/app-browser-state.d.ts +8 -3
  45. package/dist/server/app-browser-state.js +9 -3
  46. package/dist/server/app-page-cache-finalizer.js +1 -1
  47. package/dist/server/app-page-cache.d.ts +1 -1
  48. package/dist/server/app-page-cache.js +16 -2
  49. package/dist/server/app-page-dispatch.d.ts +9 -1
  50. package/dist/server/app-page-dispatch.js +37 -8
  51. package/dist/server/app-page-element-builder.d.ts +4 -1
  52. package/dist/server/app-page-element-builder.js +34 -7
  53. package/dist/server/app-page-head.d.ts +3 -3
  54. package/dist/server/app-page-head.js +58 -13
  55. package/dist/server/app-page-http-access-fallback-metadata.d.ts +2 -2
  56. package/dist/server/app-page-http-access-fallback-metadata.js +9 -1
  57. package/dist/server/app-page-probe.d.ts +4 -0
  58. package/dist/server/app-page-probe.js +6 -2
  59. package/dist/server/app-page-render.d.ts +1 -1
  60. package/dist/server/app-page-render.js +13 -3
  61. package/dist/server/app-page-request.d.ts +3 -1
  62. package/dist/server/app-page-request.js +4 -13
  63. package/dist/server/app-page-response.d.ts +2 -0
  64. package/dist/server/app-page-response.js +7 -2
  65. package/dist/server/app-page-route-wiring.d.ts +10 -2
  66. package/dist/server/app-page-route-wiring.js +269 -59
  67. package/dist/server/app-pages-bridge.d.ts +2 -1
  68. package/dist/server/app-pages-bridge.js +3 -1
  69. package/dist/server/app-route-handler-cache.js +4 -0
  70. package/dist/server/app-route-handler-dispatch.js +9 -3
  71. package/dist/server/app-route-handler-response.js +1 -1
  72. package/dist/server/app-route-module-loader.d.ts +6 -0
  73. package/dist/server/app-route-module-loader.js +3 -0
  74. package/dist/server/app-router-entry.js +6 -3
  75. package/dist/server/app-rsc-cache-busting.d.ts +7 -0
  76. package/dist/server/app-rsc-cache-busting.js +6 -0
  77. package/dist/server/app-rsc-errors.js +10 -0
  78. package/dist/server/app-rsc-handler.d.ts +2 -0
  79. package/dist/server/app-rsc-handler.js +30 -13
  80. package/dist/server/app-rsc-render-mode.d.ts +3 -2
  81. package/dist/server/app-rsc-render-mode.js +4 -1
  82. package/dist/server/app-rsc-request-normalization.js +65 -4
  83. package/dist/server/app-rsc-route-matching.d.ts +9 -0
  84. package/dist/server/app-rsc-route-matching.js +6 -0
  85. package/dist/server/app-server-action-execution.d.ts +1 -1
  86. package/dist/server/app-ssr-entry.js +4 -4
  87. package/dist/server/app-visited-response-cache.d.ts +6 -1
  88. package/dist/server/app-visited-response-cache.js +49 -1
  89. package/dist/server/cache-control.d.ts +2 -2
  90. package/dist/server/cache-control.js +2 -1
  91. package/dist/server/dev-server.d.ts +3 -1
  92. package/dist/server/dev-server.js +204 -348
  93. package/dist/server/edge-api-runtime.d.ts +10 -1
  94. package/dist/server/edge-api-runtime.js +29 -1
  95. package/dist/server/headers.d.ts +4 -2
  96. package/dist/server/headers.js +6 -3
  97. package/dist/server/isr-cache.d.ts +10 -33
  98. package/dist/server/isr-cache.js +33 -43
  99. package/dist/server/isr-decision.d.ts +2 -2
  100. package/dist/server/pages-api-route.d.ts +4 -0
  101. package/dist/server/pages-api-route.js +6 -3
  102. package/dist/server/pages-asset-tags.d.ts +1 -0
  103. package/dist/server/pages-asset-tags.js +6 -4
  104. package/dist/server/pages-dev-hydration.js +0 -1
  105. package/dist/server/pages-document-asset-props.d.ts +26 -0
  106. package/dist/server/pages-document-asset-props.js +69 -0
  107. package/dist/server/pages-get-initial-props.d.ts +1 -1
  108. package/dist/server/pages-get-initial-props.js +1 -4
  109. package/dist/server/pages-i18n.js +64 -16
  110. package/dist/server/pages-node-compat.d.ts +2 -0
  111. package/dist/server/pages-node-compat.js +10 -6
  112. package/dist/server/pages-page-data.d.ts +61 -9
  113. package/dist/server/pages-page-data.js +334 -74
  114. package/dist/server/pages-page-handler.d.ts +6 -1
  115. package/dist/server/pages-page-handler.js +119 -20
  116. package/dist/server/pages-page-response.d.ts +7 -3
  117. package/dist/server/pages-page-response.js +41 -33
  118. package/dist/server/pages-request-pipeline.d.ts +7 -0
  119. package/dist/server/pages-request-pipeline.js +5 -1
  120. package/dist/server/pages-revalidate.d.ts +1 -1
  121. package/dist/server/pages-revalidate.js +54 -8
  122. package/dist/server/pages-router-entry.d.ts +1 -0
  123. package/dist/server/pages-router-entry.js +10 -4
  124. package/dist/server/prerender-manifest.d.ts +1 -0
  125. package/dist/server/prod-server.d.ts +2 -1
  126. package/dist/server/prod-server.js +38 -13
  127. package/dist/server/revalidation-host.d.ts +11 -0
  128. package/dist/server/revalidation-host.js +19 -0
  129. package/dist/server/seed-cache.js +1 -1
  130. package/dist/server/worker-revalidation-context.d.ts +13 -0
  131. package/dist/server/worker-revalidation-context.js +30 -0
  132. package/dist/shims/app-router-scroll-state.d.ts +2 -1
  133. package/dist/shims/app-router-scroll-state.js +7 -2
  134. package/dist/shims/cache-handler.d.ts +1 -1
  135. package/dist/shims/cache-handler.js +10 -7
  136. package/dist/shims/cache-runtime.d.ts +6 -0
  137. package/dist/shims/cache-runtime.js +4 -1
  138. package/dist/shims/cache.js +1 -1
  139. package/dist/shims/document.js +15 -2
  140. package/dist/shims/error.js +1 -1
  141. package/dist/shims/fetch-cache.js +1 -1
  142. package/dist/shims/font-google-base.js +17 -8
  143. package/dist/shims/font-local.js +41 -10
  144. package/dist/shims/hash-scroll.d.ts +1 -1
  145. package/dist/shims/hash-scroll.js +2 -1
  146. package/dist/shims/headers.d.ts +10 -13
  147. package/dist/shims/headers.js +85 -36
  148. package/dist/shims/internal/cookie-serialize.d.ts +10 -9
  149. package/dist/shims/internal/cookie-serialize.js +9 -8
  150. package/dist/shims/internal/interpolate-as.d.ts +15 -1
  151. package/dist/shims/internal/interpolate-as.js +27 -2
  152. package/dist/shims/link.d.ts +2 -0
  153. package/dist/shims/link.js +77 -24
  154. package/dist/shims/metadata.d.ts +18 -34
  155. package/dist/shims/metadata.js +98 -56
  156. package/dist/shims/navigation.d.ts +18 -2
  157. package/dist/shims/navigation.js +70 -21
  158. package/dist/shims/request-context.d.ts +6 -2
  159. package/dist/shims/router.js +18 -7
  160. package/dist/shims/script.js +1 -1
  161. package/dist/shims/server.d.ts +7 -6
  162. package/dist/shims/server.js +156 -57
  163. package/dist/shims/slot.d.ts +2 -1
  164. package/dist/shims/slot.js +6 -2
  165. package/dist/shims/streamed-icons.d.ts +9 -0
  166. package/dist/shims/streamed-icons.js +33 -0
  167. package/dist/shims/unified-request-context.d.ts +21 -2
  168. package/dist/shims/unified-request-context.js +82 -1
  169. package/dist/utils/cache-control-metadata.d.ts +2 -1
  170. package/dist/utils/cache-control-metadata.js +5 -1
  171. package/dist/utils/html-limited-bots.js +1 -1
  172. package/dist/utils/protocol-headers.d.ts +7 -1
  173. package/dist/utils/protocol-headers.js +7 -1
  174. package/package.json +1 -1
@@ -1,7 +1,9 @@
1
1
  import { AppRouterScrollIntent } from "./app-router-scroll-state.js";
2
2
  import { NavigationRuntimeVisibleCommitMode } from "../client/navigation-runtime.js";
3
+ import { AppElements } from "../server/app-elements-wire.js";
3
4
  import { AppRouterInstance, NavigateOptions, PrefetchOptions as PrefetchOptions$1 } from "./internal/app-router-context.js";
4
5
  import { ReadonlyURLSearchParams } from "./readonly-url-search-params.js";
6
+ import { AppRscRenderMode } from "../server/app-rsc-render-mode.js";
5
7
  import { GLOBAL_ACCESSORS_KEY, NavigationContext, NavigationStateAccessors, SegmentMap, ServerInsertedHTMLContext, _registerStateAccessors, clearServerInsertedHTML, flushServerInsertedHTML, getBfcacheIdMapContext, getBfcacheSegmentIdContext, getLayoutSegmentContext, getNavigationContext, renderServerInsertedHTML, setNavigationContext } from "./navigation-context-state.js";
6
8
  import { BailoutToCSRError, DynamicServerError, HTTP_ERROR_FALLBACK_ERROR_CODE, RedirectType, decodeRedirectError, forbidden, getAccessFallbackHTTPStatus, isBailoutToCSRError, isDynamicServerError, isHTTPAccessFallbackError, isNextRouterError, isRedirectError, notFound, permanentRedirect, redirect, unauthorized, unstable_rethrow } from "./navigation-errors.js";
7
9
  import { UnrecognizedActionError, unstable_isUnrecognizedActionError } from "./unrecognized-action-error.js";
@@ -27,6 +29,7 @@ type CachedRscResponse = {
27
29
  expiresAt?: number;
28
30
  mountedSlotsHeader?: string | null;
29
31
  paramsHeader: string | null;
32
+ preparedElements?: AppElements;
30
33
  renderedPathAndSearch: string | null;
31
34
  url: string;
32
35
  };
@@ -46,6 +49,7 @@ type PrefetchCacheEntry = {
46
49
  snapshot?: CachedRscResponse;
47
50
  cacheKeys?: Set<string>;
48
51
  pending?: Promise<void>;
52
+ preparedElements?: AppElements;
49
53
  prefetchKind?: PrefetchCacheKind;
50
54
  searchAgnosticShell?: boolean;
51
55
  size?: number;
@@ -54,6 +58,13 @@ type PrefetchCacheEntry = {
54
58
  declare function getCurrentInterceptionContext(): string | null;
55
59
  declare function getPrefetchInterceptionContext(targetHref: string): string | null;
56
60
  declare function getCurrentNextUrl(): string;
61
+ declare function createAppPrefetchRequestHeaders(options: {
62
+ fetchPriority: "auto" | "high" | "low";
63
+ interceptionContext?: string | null;
64
+ mountedSlotsHeader?: string | null;
65
+ prefetchKind?: "auto" | "full";
66
+ renderMode?: AppRscRenderMode;
67
+ }): Headers;
57
68
  /** Get or create the shared in-memory RSC prefetch cache on window. */
58
69
  declare function getPrefetchCache(): Map<string, PrefetchCacheEntry>;
59
70
  /**
@@ -69,6 +80,7 @@ declare function hasPrefetchCacheEntryForNavigation(rscUrl: string, interception
69
80
  notifyInvalidation?: boolean;
70
81
  }): boolean;
71
82
  declare function hasSearchAgnosticPrefetchShellForRoute(rscUrl: string, interceptionContext?: string | null, mountedSlotsHeader?: string | null): boolean;
83
+ declare function discardLearningOnlyPrefetchCacheEntry(rscUrl: string, interceptionContext?: string | null): boolean;
72
84
  declare function invalidatePrefetchCache(): void;
73
85
  declare function seedPrefetchResponseSnapshot(rscUrl: string, snapshot: CachedRscResponse, interceptionContext?: string | null, mountedSlotsHeader?: string | null, fallbackTtlMs?: number): void;
74
86
  declare function deletePrefetchResponseSnapshot(rscUrl: string, snapshot: CachedRscResponse, interceptionContext?: string | null): void;
@@ -121,8 +133,10 @@ declare function restoreRscResponse(cached: CachedRscResponse, copy?: boolean):
121
133
  declare function prefetchRscResponse(rscUrl: string, fetchPromise: Promise<Response>, interceptionContext?: string | null, mountedSlotsHeader?: string | null, options?: PrefetchOptions, behavior?: {
122
134
  cacheForNavigation?: boolean;
123
135
  fallbackTtlMs?: number;
136
+ minimumTtlMs?: number;
124
137
  optimisticRouteShell?: boolean;
125
138
  prefetchKind?: PrefetchCacheKind;
139
+ prepareSnapshot?: (snapshot: CachedRscResponse) => Promise<AppElements>;
126
140
  searchAgnosticShell?: boolean;
127
141
  }): void;
128
142
  declare function peekPrefetchResponseForNavigation(rscUrl: string, interceptionContext?: string | null, mountedSlotsHeader?: string | null): CachedRscResponse | null;
@@ -131,7 +145,9 @@ declare function peekPrefetchResponseForNavigation(rscUrl: string, interceptionC
131
145
  * Only returns settled (non-pending) snapshots synchronously.
132
146
  * Returns null if the entry is still in flight or doesn't exist.
133
147
  */
134
- declare function consumePrefetchResponse(rscUrl: string, interceptionContext?: string | null, mountedSlotsHeader?: string | null): CachedRscResponse | null;
148
+ declare function consumePrefetchResponse(rscUrl: string, interceptionContext?: string | null, mountedSlotsHeader?: string | null, options?: {
149
+ additionalRscUrls?: readonly string[];
150
+ }): CachedRscResponse | null;
135
151
  /**
136
152
  * Consume a prefetched response for navigation. Unlike the synchronous cache
137
153
  * read above, this waits for an already-started prefetch snapshot before
@@ -303,4 +319,4 @@ declare function useSelectedLayoutSegments(parallelRoutesKey?: string): string[]
303
319
  */
304
320
  declare function useServerInsertedHTML(callback: () => unknown): void;
305
321
  //#endregion
306
- export { BailoutToCSRError, CachedRscResponse, ClientNavigationRenderSnapshot, DYNAMIC_NAVIGATION_CACHE_TTL, DynamicServerError, GLOBAL_ACCESSORS_KEY, HTTP_ERROR_FALLBACK_ERROR_CODE, MAX_PREFETCH_CACHE_SIZE, type NavigationContext, type NavigationStateAccessors, PREFETCH_CACHE_TTL, PrefetchCacheEntry, PrefetchCacheKind, PrefetchOptions, ReadonlyURLSearchParams, RedirectType, type SegmentMap, ServerInsertedHTMLContext, UnrecognizedActionError, __basePath, _registerStateAccessors, activateNavigationSnapshot, appRouterInstance, applyAppRouterScrollFallback, clearPendingPathname, clearServerInsertedHTML, commitClientNavigationState, consumePrefetchResponse, consumePrefetchResponseForNavigation, createCachedRscResponseSnapshot, createClientNavigationRenderSnapshot, createSnapshotPathAndSearch, decodeRedirectError, deletePrefetchResponseSnapshot, flushServerInsertedHTML, forbidden, getAccessFallbackHTTPStatus, getBfcacheIdMapContext, getBfcacheSegmentIdContext, getClientNavigationRenderContext, getClientNavigationState, getClientParams, getCurrentInterceptionContext, getCurrentNextUrl, getLayoutSegmentContext, getMountedSlotsHeader, getNavigationContext, getPrefetchCache, getPrefetchInterceptionContext, getPrefetchedUrls, hasPrefetchCacheEntryForNavigation, hasSearchAgnosticPrefetchShellForRoute, invalidatePrefetchCache, isBailoutToCSRError, isDynamicServerError, isHTTPAccessFallbackError, isNextRouterError, isRedirectError, navigateClientSide, notFound, peekPrefetchResponseForNavigation, permanentRedirect, prefetchRscResponse, preloadHybridClientRouteOwner, pushHistoryStateWithoutNotify, redirect, renderServerInsertedHTML, replaceClientParamsWithoutNotify, replaceHistoryStateWithoutNotify, resolveCachedRscResponseExpiresAt, resolveCachedRscResponseTtlMs, resolveLoadedHybridClientRewriteHref, resolvePrefetchCacheEntryMountedSlotsHeader, restoreRscResponse, saveScrollPosition, seedPrefetchResponseSnapshot, setClientParams, setMountedSlotsHeader, setNavigationContext, setPendingPathname, snapshotRscResponse, storePrefetchResponse, unauthorized, unstable_isUnrecognizedActionError, unstable_rethrow, useClientNavigationRenderSnapshot, useParams, usePathname, useRouter, useSearchParams, useSelectedLayoutSegment, useSelectedLayoutSegments, useServerInsertedHTML };
322
+ export { BailoutToCSRError, CachedRscResponse, ClientNavigationRenderSnapshot, DYNAMIC_NAVIGATION_CACHE_TTL, DynamicServerError, GLOBAL_ACCESSORS_KEY, HTTP_ERROR_FALLBACK_ERROR_CODE, MAX_PREFETCH_CACHE_SIZE, type NavigationContext, type NavigationStateAccessors, PREFETCH_CACHE_TTL, PrefetchCacheEntry, PrefetchCacheKind, PrefetchOptions, ReadonlyURLSearchParams, RedirectType, type SegmentMap, ServerInsertedHTMLContext, UnrecognizedActionError, __basePath, _registerStateAccessors, activateNavigationSnapshot, appRouterInstance, applyAppRouterScrollFallback, clearPendingPathname, clearServerInsertedHTML, commitClientNavigationState, consumePrefetchResponse, consumePrefetchResponseForNavigation, createAppPrefetchRequestHeaders, createCachedRscResponseSnapshot, createClientNavigationRenderSnapshot, createSnapshotPathAndSearch, decodeRedirectError, deletePrefetchResponseSnapshot, discardLearningOnlyPrefetchCacheEntry, flushServerInsertedHTML, forbidden, getAccessFallbackHTTPStatus, getBfcacheIdMapContext, getBfcacheSegmentIdContext, getClientNavigationRenderContext, getClientNavigationState, getClientParams, getCurrentInterceptionContext, getCurrentNextUrl, getLayoutSegmentContext, getMountedSlotsHeader, getNavigationContext, getPrefetchCache, getPrefetchInterceptionContext, getPrefetchedUrls, hasPrefetchCacheEntryForNavigation, hasSearchAgnosticPrefetchShellForRoute, invalidatePrefetchCache, isBailoutToCSRError, isDynamicServerError, isHTTPAccessFallbackError, isNextRouterError, isRedirectError, navigateClientSide, notFound, peekPrefetchResponseForNavigation, permanentRedirect, prefetchRscResponse, preloadHybridClientRouteOwner, pushHistoryStateWithoutNotify, redirect, renderServerInsertedHTML, replaceClientParamsWithoutNotify, replaceHistoryStateWithoutNotify, resolveCachedRscResponseExpiresAt, resolveCachedRscResponseTtlMs, resolveLoadedHybridClientRewriteHref, resolvePrefetchCacheEntryMountedSlotsHeader, restoreRscResponse, saveScrollPosition, seedPrefetchResponseSnapshot, setClientParams, setMountedSlotsHeader, setNavigationContext, setPendingPathname, snapshotRscResponse, storePrefetchResponse, unauthorized, unstable_isUnrecognizedActionError, unstable_rethrow, useClientNavigationRenderSnapshot, useParams, usePathname, useRouter, useSearchParams, useSelectedLayoutSegment, useSelectedLayoutSegments, useServerInsertedHTML };
@@ -8,12 +8,12 @@ import { markPprFallbackShellDynamicBoundary } from "./ppr-fallback-shell.js";
8
8
  import { AppRouterContext } from "./internal/app-router-context.js";
9
9
  import { resolveDirectHybridClientRouteOwner } from "./internal/hybrid-client-route-owner-direct.js";
10
10
  import { toBrowserNavigationHref, toSameOriginAppPath, withBasePath } from "./url-utils.js";
11
- import { retryScrollTo, scrollToHashTarget } from "./hash-scroll.js";
11
+ import { retryScrollTo, scrollToHashTarget, scrollToHashTargetOnNextFrame } from "./hash-scroll.js";
12
+ import { isBotUserAgent } from "../utils/html-limited-bots.js";
12
13
  import { GLOBAL_ACCESSORS_KEY, ServerInsertedHTMLContext, _registerStateAccessors, clearClientHydrationContext, clearServerInsertedHTML, flushServerInsertedHTML, getBfcacheIdMapContext, getBfcacheSegmentIdContext, getLayoutSegmentContext, getNavigationContext, registerServerInsertedHTMLCallback, renderServerInsertedHTML, setNavigationContext } from "./navigation-context-state.js";
13
14
  import { BailoutToCSRError, DynamicServerError, HTTP_ERROR_FALLBACK_ERROR_CODE, RedirectType, decodeRedirectError, forbidden, getAccessFallbackHTTPStatus, isBailoutToCSRError, isDynamicServerError, isHTTPAccessFallbackError, isNextRouterError, isRedirectError, notFound, permanentRedirect, redirect, unauthorized, unstable_rethrow } from "./navigation-errors.js";
14
- import { isBotUserAgent } from "../utils/html-limited-bots.js";
15
15
  import { clearAppNavigationFailureTarget, stageAppNavigationFailureTarget } from "../client/app-nav-failure-handler.js";
16
- import { beginAppRouterScrollIntent, clearAppRouterScrollIntent, consumeAppRouterScrollIntent, getPendingAppRouterScrollIntent } from "./app-router-scroll-state.js";
16
+ import { beginAppRouterScrollIntent, clearAppRouterScrollIntent, consumeAppRouterScrollIntent, getPendingAppRouterScrollIntent, isLatestAppRouterScrollIntent } from "./app-router-scroll-state.js";
17
17
  import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, createRscRequestHeaders, createRscRequestUrl, stripRscCacheBustingSearchParam, stripRscSuffix } from "../server/app-rsc-cache-busting.js";
18
18
  import { getNavigationRuntime, hasAppNavigationRuntime } from "../client/navigation-runtime.js";
19
19
  import { notifyAppRouterTransitionStart } from "../client/instrumentation-client-state.js";
@@ -121,6 +121,15 @@ function getCurrentNextUrl() {
121
121
  if (isServer) return "/";
122
122
  return window.location.pathname + window.location.search;
123
123
  }
124
+ function createAppPrefetchRequestHeaders(options) {
125
+ const prefetchRouterState = getNavigationRuntime()?.functions.getPrefetchRouterState?.() ?? null;
126
+ return createRscRequestHeaders({
127
+ ...options,
128
+ nextUrl: getCurrentNextUrl(),
129
+ includePrefetchHeader: options.prefetchKind !== "full",
130
+ prefetchRouterState
131
+ });
132
+ }
124
133
  /** Get or create the shared in-memory RSC prefetch cache on window. */
125
134
  function getPrefetchCache() {
126
135
  if (isServer) return /* @__PURE__ */ new Map();
@@ -156,11 +165,11 @@ function resolveCachedRscResponseExpiresAt(timestamp, cached, fallbackTtlMs) {
156
165
  if (isCacheExpiresAt(cached.expiresAt)) return cached.expiresAt;
157
166
  return timestamp + resolveCachedRscResponseTtlMs(cached, fallbackTtlMs);
158
167
  }
159
- function resolvePrefetchedRscResponseExpiresAt(timestamp, cached, fallbackTtlMs) {
168
+ function resolvePrefetchedRscResponseExpiresAt(timestamp, cached, fallbackTtlMs, minimumTtlMs = MIN_PREFETCH_STALE_TIME_MS) {
160
169
  if (isCacheExpiresAt(cached.expiresAt)) return cached.expiresAt;
161
170
  const seconds = cached.dynamicStaleTimeSeconds;
162
- if (!isDynamicStaleTimeSeconds(seconds)) return timestamp + Math.max(fallbackTtlMs, MIN_PREFETCH_STALE_TIME_MS);
163
- return timestamp + Math.max(seconds * 1e3, MIN_PREFETCH_STALE_TIME_MS);
171
+ if (!isDynamicStaleTimeSeconds(seconds)) return timestamp + Math.max(fallbackTtlMs, minimumTtlMs);
172
+ return timestamp + Math.max(seconds * 1e3, minimumTtlMs);
164
173
  }
165
174
  function resolvePrefetchCacheEntryExpiresAt(entry) {
166
175
  if (entry.expiresAt !== void 0) return entry.expiresAt;
@@ -347,12 +356,15 @@ function notifyPrefetchInvalidated(entry) {
347
356
  }
348
357
  }
349
358
  function deletePrefetchCacheEntry(cache, prefetched, cacheKey, entry, notify) {
350
- adjustPrefetchCacheByteSize(cache, -getPrefetchCacheEntrySize(entry));
351
359
  const cacheKeys = entry.cacheKeys ?? /* @__PURE__ */ new Set([cacheKey]);
352
- for (const key of cacheKeys) {
353
- if (cache.get(key) === entry) cache.delete(key);
360
+ let removedOwnedKey = false;
361
+ for (const key of cacheKeys) if (cache.get(key) === entry) {
362
+ cache.delete(key);
354
363
  prefetched.delete(key);
364
+ removedOwnedKey = true;
355
365
  }
366
+ if (!removedOwnedKey) return;
367
+ adjustPrefetchCacheByteSize(cache, -getPrefetchCacheEntrySize(entry));
356
368
  entry.cacheKeys = void 0;
357
369
  if (notify) notifyPrefetchInvalidated(entry);
358
370
  else {
@@ -360,6 +372,22 @@ function deletePrefetchCacheEntry(cache, prefetched, cacheKey, entry, notify) {
360
372
  entry.onInvalidateCallbacks = void 0;
361
373
  }
362
374
  }
375
+ function discardLearningOnlyPrefetchCacheEntry(rscUrl, interceptionContext = null) {
376
+ const cache = getPrefetchCache();
377
+ const prefetched = getPrefetchedUrls();
378
+ const normalizedTarget = normalizeRscCacheLookupUrl(rscUrl);
379
+ if (normalizedTarget === null) return false;
380
+ let discarded = false;
381
+ for (const [cacheKey, entry] of cache) {
382
+ if (entry.cacheForNavigation !== false || entry.prefetchKind !== "navigation") continue;
383
+ const source = parsePrefetchCacheKey(cacheKey);
384
+ if (source.interceptionContext !== interceptionContext) continue;
385
+ if (normalizeRscCacheLookupUrl(source.rscUrl) !== normalizedTarget) continue;
386
+ deletePrefetchCacheEntry(cache, prefetched, cacheKey, entry, false);
387
+ discarded = true;
388
+ }
389
+ return discarded;
390
+ }
363
391
  function invalidatePrefetchCacheEntry(cacheKey) {
364
392
  const cache = getPrefetchCache();
365
393
  const entry = cache.get(cacheKey);
@@ -565,7 +593,12 @@ function prefetchRscResponse(rscUrl, fetchPromise, interceptionContext = null, m
565
593
  entry.snapshot = snapshot;
566
594
  entry.size = snapshot.buffer.byteLength;
567
595
  adjustPrefetchCacheByteSize(cache, entry.size - previousSize);
568
- entry.expiresAt = resolvePrefetchedRscResponseExpiresAt(entry.timestamp, entry.snapshot, behavior.fallbackTtlMs ?? PREFETCH_CACHE_TTL);
596
+ entry.expiresAt = resolvePrefetchedRscResponseExpiresAt(entry.timestamp, entry.snapshot, behavior.fallbackTtlMs ?? PREFETCH_CACHE_TTL, behavior.minimumTtlMs);
597
+ if (behavior.prepareSnapshot) try {
598
+ const preparedElements = await behavior.prepareSnapshot(snapshot);
599
+ if (cache.get(cacheKey) !== entry) return;
600
+ entry.preparedElements = preparedElements;
601
+ } catch {}
569
602
  addRenderedPathAndSearchPrefetchAlias(cache, prefetched, cacheKey, entry);
570
603
  evictPrefetchCacheIfNeeded();
571
604
  } else {
@@ -620,28 +653,37 @@ function peekPrefetchResponseForNavigation(rscUrl, interceptionContext = null, m
620
653
  * Only returns settled (non-pending) snapshots synchronously.
621
654
  * Returns null if the entry is still in flight or doesn't exist.
622
655
  */
623
- function consumePrefetchResponse(rscUrl, interceptionContext = null, mountedSlotsHeader = null) {
656
+ function consumePrefetchResponse(rscUrl, interceptionContext = null, mountedSlotsHeader = null, options) {
624
657
  const cache = getPrefetchCache();
625
658
  const exactCacheKey = AppElementsWire.encodeCacheKey(rscUrl, interceptionContext);
626
659
  const exactEntry = cache.get(exactCacheKey);
627
660
  if (exactEntry && exactEntry.cacheForNavigation !== false && !isPrefetchCacheEntryCompatibleWithMountedSlots(exactEntry, mountedSlotsHeader)) deletePrefetchCacheEntry(cache, getPrefetchedUrls(), exactCacheKey, exactEntry, false);
628
- const match = findPrefetchCacheEntryForNavigation(rscUrl, interceptionContext, mountedSlotsHeader);
661
+ const match = findPrefetchCacheEntryForNavigation(rscUrl, interceptionContext, mountedSlotsHeader, options?.additionalRscUrls);
629
662
  if (!match) return null;
630
663
  const { cacheKey, entry } = match;
631
664
  return consumeMatchedPrefetchResponse(cacheKey, entry, mountedSlotsHeader);
632
665
  }
633
666
  function consumeMatchedPrefetchResponse(cacheKey, entry, mountedSlotsHeader) {
667
+ const cache = getPrefetchCache();
634
668
  if (entry.pending || entry.outcome !== "cache-seeded") return null;
635
669
  if (entry.cacheForNavigation === false) return null;
636
- deletePrefetchCacheEntry(getPrefetchCache(), getPrefetchedUrls(), cacheKey, entry, false);
637
670
  if (entry.snapshot) {
638
671
  if (!isPrefetchCacheEntryCompatibleWithMountedSlots(entry, mountedSlotsHeader)) return null;
639
- if (resolvePrefetchCacheEntryExpiresAt(entry) <= Date.now()) return null;
672
+ if (resolvePrefetchCacheEntryExpiresAt(entry) <= Date.now()) {
673
+ deletePrefetchCacheEntry(cache, getPrefetchedUrls(), cacheKey, entry, false);
674
+ return null;
675
+ }
676
+ deletePrefetchCacheEntry(cache, getPrefetchedUrls(), cacheKey, entry, false);
677
+ const snapshot = entry.snapshot;
640
678
  if (entry.expiresAt !== void 0 || entry.snapshot.expiresAt !== void 0) return {
641
- ...entry.snapshot,
642
- expiresAt: resolvePrefetchCacheEntryExpiresAt(entry)
679
+ ...snapshot,
680
+ expiresAt: resolvePrefetchCacheEntryExpiresAt(entry),
681
+ ...entry.preparedElements ? { preparedElements: entry.preparedElements } : {}
643
682
  };
644
- return entry.snapshot;
683
+ return entry.preparedElements ? {
684
+ ...snapshot,
685
+ preparedElements: entry.preparedElements
686
+ } : snapshot;
645
687
  }
646
688
  return null;
647
689
  }
@@ -1036,7 +1078,7 @@ function commitHashOnlyHistoryState(href, mode, scroll) {
1036
1078
  function applyAppRouterScrollFallback(intent) {
1037
1079
  if (typeof document === "undefined" || typeof window === "undefined") return;
1038
1080
  if (intent.hash !== null) {
1039
- scrollToHashTarget(intent.hash);
1081
+ scrollToHashTargetOnNextFrame(intent.hash, () => isLatestAppRouterScrollIntent(intent));
1040
1082
  return;
1041
1083
  }
1042
1084
  if (intent.targetHoistedInHead) return;
@@ -1259,7 +1301,10 @@ const _appRouter = {
1259
1301
  const fullHref = toBrowserNavigationHref(prefetchHref, window.location.href, __basePath);
1260
1302
  const interceptionContext = getPrefetchInterceptionContext(fullHref);
1261
1303
  const mountedSlotsHeader = getMountedSlotsHeader();
1262
- const headers = createRscRequestHeaders({ interceptionContext });
1304
+ const headers = createAppPrefetchRequestHeaders({
1305
+ fetchPriority: "low",
1306
+ interceptionContext
1307
+ });
1263
1308
  if (mountedSlotsHeader) headers.set(VINEXT_MOUNTED_SLOTS_HEADER, mountedSlotsHeader);
1264
1309
  const rscUrl = await createRscRequestUrl(fullHref, headers);
1265
1310
  const cacheKey = AppElementsWire.encodeCacheKey(rscUrl, interceptionContext);
@@ -1273,7 +1318,11 @@ const _appRouter = {
1273
1318
  headers,
1274
1319
  credentials: "include",
1275
1320
  priority: "low"
1276
- }), "low"), interceptionContext, mountedSlotsHeader, options);
1321
+ }), "low"), interceptionContext, mountedSlotsHeader, options, {
1322
+ cacheForNavigation: false,
1323
+ optimisticRouteShell: true,
1324
+ prefetchKind: "navigation"
1325
+ });
1277
1326
  })().catch((error) => {
1278
1327
  console.error("[vinext] RSC prefetch setup error:", error);
1279
1328
  });
@@ -1412,4 +1461,4 @@ if (!isServer) {
1412
1461
  }
1413
1462
  }
1414
1463
  //#endregion
1415
- export { BailoutToCSRError, DYNAMIC_NAVIGATION_CACHE_TTL, DynamicServerError, GLOBAL_ACCESSORS_KEY, HTTP_ERROR_FALLBACK_ERROR_CODE, MAX_PREFETCH_CACHE_SIZE, PREFETCH_CACHE_TTL, ReadonlyURLSearchParams, RedirectType, ServerInsertedHTMLContext, UnrecognizedActionError, __basePath, _registerStateAccessors, activateNavigationSnapshot, appRouterInstance, applyAppRouterScrollFallback, clearPendingPathname, clearServerInsertedHTML, commitClientNavigationState, consumePrefetchResponse, consumePrefetchResponseForNavigation, createCachedRscResponseSnapshot, createClientNavigationRenderSnapshot, createSnapshotPathAndSearch, decodeRedirectError, deletePrefetchResponseSnapshot, flushServerInsertedHTML, forbidden, getAccessFallbackHTTPStatus, getBfcacheIdMapContext, getBfcacheSegmentIdContext, getClientNavigationRenderContext, getClientNavigationState, getClientParams, getCurrentInterceptionContext, getCurrentNextUrl, getLayoutSegmentContext, getMountedSlotsHeader, getNavigationContext, getPrefetchCache, getPrefetchInterceptionContext, getPrefetchedUrls, hasPrefetchCacheEntryForNavigation, hasSearchAgnosticPrefetchShellForRoute, invalidatePrefetchCache, isBailoutToCSRError, isDynamicServerError, isHTTPAccessFallbackError, isNextRouterError, isRedirectError, navigateClientSide, notFound, peekPrefetchResponseForNavigation, permanentRedirect, prefetchRscResponse, preloadHybridClientRouteOwner, pushHistoryStateWithoutNotify, redirect, renderServerInsertedHTML, replaceClientParamsWithoutNotify, replaceHistoryStateWithoutNotify, resolveCachedRscResponseExpiresAt, resolveCachedRscResponseTtlMs, resolveLoadedHybridClientRewriteHref, resolvePrefetchCacheEntryMountedSlotsHeader, restoreRscResponse, saveScrollPosition, seedPrefetchResponseSnapshot, setClientParams, setMountedSlotsHeader, setNavigationContext, setPendingPathname, snapshotRscResponse, storePrefetchResponse, unauthorized, unstable_isUnrecognizedActionError, unstable_rethrow, useClientNavigationRenderSnapshot, useParams, usePathname, useRouter, useSearchParams, useSelectedLayoutSegment, useSelectedLayoutSegments, useServerInsertedHTML };
1464
+ export { BailoutToCSRError, DYNAMIC_NAVIGATION_CACHE_TTL, DynamicServerError, GLOBAL_ACCESSORS_KEY, HTTP_ERROR_FALLBACK_ERROR_CODE, MAX_PREFETCH_CACHE_SIZE, PREFETCH_CACHE_TTL, ReadonlyURLSearchParams, RedirectType, ServerInsertedHTMLContext, UnrecognizedActionError, __basePath, _registerStateAccessors, activateNavigationSnapshot, appRouterInstance, applyAppRouterScrollFallback, clearPendingPathname, clearServerInsertedHTML, commitClientNavigationState, consumePrefetchResponse, consumePrefetchResponseForNavigation, createAppPrefetchRequestHeaders, createCachedRscResponseSnapshot, createClientNavigationRenderSnapshot, createSnapshotPathAndSearch, decodeRedirectError, deletePrefetchResponseSnapshot, discardLearningOnlyPrefetchCacheEntry, flushServerInsertedHTML, forbidden, getAccessFallbackHTTPStatus, getBfcacheIdMapContext, getBfcacheSegmentIdContext, getClientNavigationRenderContext, getClientNavigationState, getClientParams, getCurrentInterceptionContext, getCurrentNextUrl, getLayoutSegmentContext, getMountedSlotsHeader, getNavigationContext, getPrefetchCache, getPrefetchInterceptionContext, getPrefetchedUrls, hasPrefetchCacheEntryForNavigation, hasSearchAgnosticPrefetchShellForRoute, invalidatePrefetchCache, isBailoutToCSRError, isDynamicServerError, isHTTPAccessFallbackError, isNextRouterError, isRedirectError, navigateClientSide, notFound, peekPrefetchResponseForNavigation, permanentRedirect, prefetchRscResponse, preloadHybridClientRouteOwner, pushHistoryStateWithoutNotify, redirect, renderServerInsertedHTML, replaceClientParamsWithoutNotify, replaceHistoryStateWithoutNotify, resolveCachedRscResponseExpiresAt, resolveCachedRscResponseTtlMs, resolveLoadedHybridClientRewriteHref, resolvePrefetchCacheEntryMountedSlotsHeader, restoreRscResponse, saveScrollPosition, seedPrefetchResponseSnapshot, setClientParams, setMountedSlotsHeader, setNavigationContext, setPendingPathname, snapshotRscResponse, storePrefetchResponse, unauthorized, unstable_isUnrecognizedActionError, unstable_rethrow, useClientNavigationRenderSnapshot, useParams, usePathname, useRouter, useSearchParams, useSelectedLayoutSegment, useSelectedLayoutSegments, useServerInsertedHTML };
@@ -27,13 +27,17 @@
27
27
  */
28
28
  type ExecutionContextLike = {
29
29
  waitUntil(promise: Promise<unknown>): void;
30
- passThroughOnException?(): void;
30
+ passThroughOnException?(): void; /** Host runtime executing the current request. */
31
+ hostRuntime?: "node" | "worker";
31
32
  /**
32
33
  * Optional host-provided cache handle that some runtimes expose on the
33
34
  * execution context. Typed as `unknown` to keep this module runtime-agnostic;
34
35
  * CDN cache adapters that know the concrete shape narrow it themselves.
35
36
  */
36
- cache?: unknown;
37
+ cache?: unknown; /** Server-owned origin for credential-bearing Pages revalidation loopbacks. */
38
+ trustedRevalidateOrigin?: string; /** Worker-owned in-process dispatcher for authenticated Pages revalidation. */
39
+ dispatchPagesRevalidate?: (request: Request) => Promise<Response>; /** Marks a request currently executing through the internal revalidation dispatcher. */
40
+ isInternalPagesRevalidation?: boolean;
37
41
  };
38
42
  /**
39
43
  * Run `fn` with the given `ExecutionContext` available via
@@ -1159,13 +1159,12 @@ async function renderPagesNavigationTarget(url, target, props, options, assertSt
1159
1159
  assertStillCurrent();
1160
1160
  const React = (await import("react")).default;
1161
1161
  assertStillCurrent();
1162
- const rawPageProps = props.pageProps;
1163
- const pageProps = isUnknownRecord(rawPageProps) ? rawPageProps : {};
1162
+ const pageProps = Object.assign({}, props.pageProps);
1163
+ props.pageProps = pageProps;
1164
1164
  let element;
1165
1165
  if (AppComponent) element = React.createElement(AppComponent, {
1166
1166
  ...props,
1167
1167
  Component: PageComponent,
1168
- pageProps: rawPageProps,
1169
1168
  router: singletonRouter
1170
1169
  });
1171
1170
  else element = React.createElement(PageComponent, pageProps);
@@ -1312,8 +1311,8 @@ async function navigateClientHtml(url, fetchUrl, controller, navId, assertStillC
1312
1311
  if (!nextDataJson) scheduleHardNavigationAndThrow(url, "Navigation failed: missing __NEXT_DATA__ in response");
1313
1312
  const nextData = parseVinextNextDataJson(nextDataJson);
1314
1313
  const props = nextData.props && typeof nextData.props === "object" ? nextData.props : {};
1315
- const rawPageProps = props.pageProps;
1316
- const pageProps = isUnknownRecord(rawPageProps) ? rawPageProps : {};
1314
+ const pageProps = Object.assign({}, props.pageProps);
1315
+ props.pageProps = pageProps;
1317
1316
  let pageModuleUrl = nextData.__vinext?.pageModuleUrl;
1318
1317
  if (!pageModuleUrl) {
1319
1318
  const moduleMatch = html.match(/import\("([^"]+)"\);\s*\n\s*const PageComponent/);
@@ -1355,7 +1354,6 @@ async function navigateClientHtml(url, fetchUrl, controller, navId, assertStillC
1355
1354
  if (AppComponent) element = React.createElement(AppComponent, {
1356
1355
  ...props,
1357
1356
  Component: PageComponent,
1358
- pageProps: rawPageProps,
1359
1357
  router: singletonRouter
1360
1358
  });
1361
1359
  else element = React.createElement(PageComponent, pageProps);
@@ -1647,7 +1645,20 @@ async function performNavigation(url, as, options, mode, onStateUpdate) {
1647
1645
  const hashStr = extractHash(resolved);
1648
1646
  resolved = normalizePathTrailingSlash(`${projection.href.split(/[?#]/, 1)[0]}${searchStr ? `?${searchStr}` : ""}${hashStr}`, __trailingSlash);
1649
1647
  }
1650
- } else interpolatedRoute = resolved;
1648
+ } else {
1649
+ const missingParams = projection ? routePatternParts(projection.routePathname).filter((part) => part.startsWith(":") && !part.endsWith("*")).map((part) => part.slice(1, part.endsWith("+") ? -1 : void 0)).filter((paramName) => {
1650
+ const value = projection.query[paramName];
1651
+ return value === void 0 || value === "" || Array.isArray(value) && value.length === 0;
1652
+ }) : [];
1653
+ const hasExplicitHrefPathname = typeof url === "string" || url.pathname !== void 0;
1654
+ const isMiddlewareMatch = options?.shallow !== true && getPagesMiddlewareDataHref(resolved, __basePath) !== null;
1655
+ if (missingParams.length > 0 && hasExplicitHrefPathname && !isMiddlewareMatch) {
1656
+ const asPathname = stripHash(resolved).split("?", 1)[0];
1657
+ const routePathname = projection?.routePathname ?? stripHash(resolvedRoute).split("?", 1)[0];
1658
+ throw new HrefInterpolationError(asPathname === routePathname ? `The provided \`href\` (${resolvedRoute}) value is missing query values (${missingParams.join(", ")}) to be interpolated properly. Read more: https://nextjs.org/docs/messages/href-interpolation-failed` : `The provided \`as\` value (${asPathname}) is incompatible with the \`href\` value (${routePathname}). Read more: https://nextjs.org/docs/messages/incompatible-href-as`);
1659
+ }
1660
+ interpolatedRoute = resolved;
1661
+ }
1651
1662
  }
1652
1663
  const full = normalizePathTrailingSlash(toBrowserNavigationHref(resolved, window.location.href, __basePath), __trailingSlash);
1653
1664
  const fullRouteUrl = interpolatedRoute !== resolved ? normalizePathTrailingSlash(toBrowserNavigationHref(interpolatedRoute, window.location.href, __basePath), __trailingSlash) : full;
@@ -1,8 +1,8 @@
1
1
  "use client";
2
- import { escapeInlineContent } from "./head.js";
3
2
  import { useScriptNonce } from "./script-nonce-context.js";
4
3
  import { hasAppNavigationRuntimeBootstrap } from "../client/navigation-runtime.js";
5
4
  import { useBeforeInteractiveRegister } from "./before-interactive-context.js";
5
+ import { escapeInlineContent } from "./head.js";
6
6
  import React, { useEffect, useRef } from "react";
7
7
  import * as ReactDOM from "react-dom";
8
8
  //#region src/shims/script.tsx
@@ -230,15 +230,16 @@ declare class ResponseCookies {
230
230
  }]): this;
231
231
  get(...args: [name: string] | [options: {
232
232
  name: string;
233
- }]): CookieEntry | undefined;
233
+ }]): ResponseCookieEntry | undefined;
234
234
  has(name: string): boolean;
235
235
  getAll(...args: [name: string] | [options: {
236
236
  name: string;
237
- }] | []): CookieEntry[];
237
+ }] | []): ResponseCookieEntry[];
238
238
  delete(...args: [name: string] | [options: Omit<CookieOptions & {
239
239
  name: string;
240
240
  }, "maxAge" | "expires">]): this;
241
- [Symbol.iterator](): MapIterator<[string, CookieEntry]>;
241
+ [Symbol.iterator](): MapIterator<[string, ResponseCookieEntry]>;
242
+ toString(): string;
242
243
  /** Delete all Set-Cookie headers and re-append from the internal map. */
243
244
  private _syncHeaders;
244
245
  }
@@ -249,10 +250,11 @@ type CookieOptions = {
249
250
  expires?: Date | number;
250
251
  httpOnly?: boolean;
251
252
  secure?: boolean;
252
- sameSite?: true | false | "strict" | "lax" | "none" | "Strict" | "Lax" | "None";
253
+ sameSite?: true | false | "strict" | "lax" | "none";
253
254
  partitioned?: boolean;
254
255
  priority?: "low" | "medium" | "high";
255
256
  };
257
+ type ResponseCookieEntry = CookieEntry & CookieOptions;
256
258
  type MiddlewareResponseInit = {
257
259
  request?: {
258
260
  headers?: Headers;
@@ -323,8 +325,7 @@ type UserAgent = {
323
325
  *
324
326
  * Uses the platform's `waitUntil` (via the per-request ExecutionContext) when
325
327
  * available so the task survives past the response on Cloudflare Workers.
326
- * Falls back to a fire-and-forget microtask on runtimes without an execution
327
- * context (e.g. Node.js dev server).
328
+ * Node.js dev drains callbacks from its response finish/close lifecycle.
328
329
  *
329
330
  * Throws when called inside a cached scope — request-specific
330
331
  * side-effects must not leak into cached results.