vinext 1.0.0-beta.3 → 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 (90) hide show
  1. package/dist/build/precompress.js +1 -1
  2. package/dist/build/prerender.d.ts +2 -1
  3. package/dist/build/prerender.js +12 -2
  4. package/dist/cli-args.d.ts +2 -1
  5. package/dist/cli-args.js +10 -0
  6. package/dist/cli.js +14 -8
  7. package/dist/client/navigation-runtime.d.ts +6 -0
  8. package/dist/client/navigation-runtime.js +1 -1
  9. package/dist/config/dotenv.d.ts +3 -4
  10. package/dist/config/dotenv.js +30 -22
  11. package/dist/config/next-config.d.ts +4 -2
  12. package/dist/config/next-config.js +3 -0
  13. package/dist/entries/app-browser-entry.js +1 -1
  14. package/dist/entries/app-rsc-entry.js +18 -3
  15. package/dist/entries/pages-server-entry.js +4 -1
  16. package/dist/index.js +11 -7
  17. package/dist/plugins/client-reference-dedup.js +7 -5
  18. package/dist/plugins/sass.js +3 -4
  19. package/dist/server/api-handler.js +4 -3
  20. package/dist/server/app-bfcache-identity.js +28 -2
  21. package/dist/server/app-browser-entry.js +73 -45
  22. package/dist/server/app-browser-navigation-controller.d.ts +1 -1
  23. package/dist/server/app-browser-navigation-controller.js +29 -4
  24. package/dist/server/app-browser-prefetch-response.d.ts +9 -0
  25. package/dist/server/app-browser-prefetch-response.js +32 -0
  26. package/dist/server/app-browser-state.d.ts +8 -3
  27. package/dist/server/app-browser-state.js +9 -3
  28. package/dist/server/app-page-dispatch.d.ts +1 -1
  29. package/dist/server/app-page-dispatch.js +6 -2
  30. package/dist/server/app-page-element-builder.d.ts +2 -1
  31. package/dist/server/app-page-element-builder.js +1 -0
  32. package/dist/server/app-page-render.d.ts +1 -1
  33. package/dist/server/app-page-render.js +13 -3
  34. package/dist/server/app-page-request.d.ts +0 -1
  35. package/dist/server/app-page-request.js +3 -12
  36. package/dist/server/app-page-response.d.ts +2 -0
  37. package/dist/server/app-page-response.js +6 -1
  38. package/dist/server/app-page-route-wiring.d.ts +3 -2
  39. package/dist/server/app-page-route-wiring.js +61 -13
  40. package/dist/server/app-pages-bridge.d.ts +2 -1
  41. package/dist/server/app-pages-bridge.js +2 -1
  42. package/dist/server/app-rsc-cache-busting.d.ts +7 -0
  43. package/dist/server/app-rsc-cache-busting.js +6 -0
  44. package/dist/server/app-rsc-handler.d.ts +2 -0
  45. package/dist/server/app-rsc-handler.js +9 -4
  46. package/dist/server/app-rsc-render-mode.d.ts +3 -2
  47. package/dist/server/app-rsc-render-mode.js +4 -1
  48. package/dist/server/app-rsc-request-normalization.js +65 -4
  49. package/dist/server/app-server-action-execution.d.ts +1 -1
  50. package/dist/server/app-visited-response-cache.d.ts +6 -1
  51. package/dist/server/app-visited-response-cache.js +49 -1
  52. package/dist/server/dev-server.d.ts +1 -1
  53. package/dist/server/dev-server.js +61 -38
  54. package/dist/server/edge-api-runtime.d.ts +10 -1
  55. package/dist/server/edge-api-runtime.js +29 -1
  56. package/dist/server/headers.d.ts +3 -1
  57. package/dist/server/headers.js +3 -1
  58. package/dist/server/pages-api-route.d.ts +2 -0
  59. package/dist/server/pages-api-route.js +2 -2
  60. package/dist/server/pages-asset-tags.d.ts +1 -0
  61. package/dist/server/pages-asset-tags.js +6 -4
  62. package/dist/server/pages-document-asset-props.d.ts +26 -0
  63. package/dist/server/pages-document-asset-props.js +69 -0
  64. package/dist/server/pages-node-compat.js +3 -3
  65. package/dist/server/pages-page-handler.d.ts +1 -0
  66. package/dist/server/pages-page-handler.js +4 -1
  67. package/dist/server/pages-page-response.d.ts +2 -0
  68. package/dist/server/pages-page-response.js +19 -8
  69. package/dist/server/pages-router-entry.js +1 -1
  70. package/dist/server/prerender-manifest.d.ts +1 -0
  71. package/dist/server/prod-server.js +2 -1
  72. package/dist/server/seed-cache.js +1 -1
  73. package/dist/server/worker-revalidation-context.js +1 -0
  74. package/dist/shims/app-router-scroll-state.d.ts +2 -1
  75. package/dist/shims/app-router-scroll-state.js +7 -2
  76. package/dist/shims/document.js +15 -2
  77. package/dist/shims/hash-scroll.d.ts +1 -1
  78. package/dist/shims/hash-scroll.js +2 -1
  79. package/dist/shims/link.d.ts +2 -0
  80. package/dist/shims/link.js +59 -18
  81. package/dist/shims/metadata.d.ts +11 -17
  82. package/dist/shims/metadata.js +24 -32
  83. package/dist/shims/navigation.d.ts +18 -2
  84. package/dist/shims/navigation.js +69 -20
  85. package/dist/shims/request-context.d.ts +2 -1
  86. package/dist/shims/slot.d.ts +2 -1
  87. package/dist/shims/slot.js +6 -2
  88. package/dist/shims/streamed-icons.d.ts +9 -0
  89. package/dist/shims/streamed-icons.js +33 -0
  90. 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
12
  import { isBotUserAgent } from "../utils/html-limited-bots.js";
13
13
  import { GLOBAL_ACCESSORS_KEY, ServerInsertedHTMLContext, _registerStateAccessors, clearClientHydrationContext, clearServerInsertedHTML, flushServerInsertedHTML, getBfcacheIdMapContext, getBfcacheSegmentIdContext, getLayoutSegmentContext, getNavigationContext, registerServerInsertedHTMLCallback, renderServerInsertedHTML, setNavigationContext } from "./navigation-context-state.js";
14
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";
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,7 +27,8 @@
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;
@@ -25,6 +25,7 @@ type MergeElementsOptions = {
25
25
  preservePreviousSlotIds?: readonly string[];
26
26
  };
27
27
  declare function updateBfcacheSlotEntryOrder(previousOrder: readonly string[], activeStateKey: string, maxEntries?: number): string[];
28
+ declare function getNonCacheComponentsSegmentKey(id: string, activeStateKey: string): string | undefined;
28
29
  declare function mergeElements(prev: AppElements, next: AppElements, options?: MergeElementsOptions | boolean): AppElements;
29
30
  declare function Slot({
30
31
  id,
@@ -42,4 +43,4 @@ declare function ParallelSlot({
42
43
  name: string;
43
44
  }): string | number | bigint | boolean | Iterable<React$1.ReactNode> | Promise<string | number | bigint | boolean | Iterable<React$1.ReactNode> | React$1.ReactElement<unknown, string | React$1.JSXElementConstructor<any>> | React$1.ReactPortal | null | undefined> | React$1.ReactElement<unknown, string | React$1.JSXElementConstructor<any>> | null;
44
45
  //#endregion
45
- export { BfcacheSlotEntry, BfcacheStateKeyMapContext, Children, ChildrenContext, ElementsContext, ParallelSlot, ParallelSlotsContext, Slot, UNMATCHED_SLOT, mergeElements, updateBfcacheSlotEntryOrder };
46
+ export { BfcacheSlotEntry, BfcacheStateKeyMapContext, Children, ChildrenContext, ElementsContext, ParallelSlot, ParallelSlotsContext, Slot, UNMATCHED_SLOT, getNonCacheComponentsSegmentKey, mergeElements, updateBfcacheSlotEntryOrder };
@@ -137,6 +137,10 @@ function BfcacheActivitySlotBoundary({ activeStateKey, content, elements, id, Se
137
137
  })
138
138
  }, entry.stateKey)) });
139
139
  }
140
+ function getNonCacheComponentsSegmentKey(id, activeStateKey) {
141
+ const parsed = AppElementsWire.parseElementKey(id);
142
+ return parsed?.kind === "page" || parsed?.kind === "slot" && parsed.name === "children" ? activeStateKey : void 0;
143
+ }
140
144
  function BfcacheSlotBoundary({ content, id }) {
141
145
  const SegmentContext = BfcacheSegmentIdContext;
142
146
  const elements = React$1.useContext(ElementsContext);
@@ -150,7 +154,7 @@ function BfcacheSlotBoundary({ content, id }) {
150
154
  if (!isCacheComponentsEnabled()) return /* @__PURE__ */ jsx(SegmentContext.Provider, {
151
155
  value: id,
152
156
  children: content
153
- });
157
+ }, getNonCacheComponentsSegmentKey(id, activeStateKey));
154
158
  return /* @__PURE__ */ jsx(BfcacheActivitySlotBoundary, {
155
159
  activeStateKey,
156
160
  content,
@@ -224,4 +228,4 @@ function ParallelSlot({ name }) {
224
228
  return React$1.useContext(ParallelSlotsContext)?.[name] ?? null;
225
229
  }
226
230
  //#endregion
227
- export { BfcacheStateKeyMapContext, Children, ChildrenContext, ElementsContext, ParallelSlot, ParallelSlotsContext, Slot, UNMATCHED_SLOT, mergeElements, updateBfcacheSlotEntryOrder };
231
+ export { BfcacheStateKeyMapContext, Children, ChildrenContext, ElementsContext, ParallelSlot, ParallelSlotsContext, Slot, UNMATCHED_SLOT, getNonCacheComponentsSegmentKey, mergeElements, updateBfcacheSlotEntryOrder };
@@ -0,0 +1,9 @@
1
+ //#region src/shims/streamed-icons.d.ts
2
+ declare function reconcileStreamedIcons(metadataKey: string): void;
3
+ declare function StreamedIconsInsertion({
4
+ metadataKey
5
+ }: {
6
+ metadataKey: string;
7
+ }): null;
8
+ //#endregion
9
+ export { StreamedIconsInsertion, reconcileStreamedIcons };
@@ -0,0 +1,33 @@
1
+ "use client";
2
+ import { useLayoutEffect } from "react";
3
+ //#region src/shims/streamed-icons.tsx
4
+ const STREAMED_ICON_ATTRIBUTE = "data-vinext-streamed-icon";
5
+ function getStreamedIconOrder(icon, metadataKey) {
6
+ const marker = icon.getAttribute(STREAMED_ICON_ATTRIBUTE);
7
+ const prefix = `${metadataKey}:`;
8
+ if (!marker?.startsWith(prefix)) return null;
9
+ const order = Number(marker.slice(prefix.length));
10
+ return Number.isInteger(order) && order >= 0 ? order : null;
11
+ }
12
+ function reconcileStreamedIcons(metadataKey) {
13
+ document.querySelectorAll(`body link[${STREAMED_ICON_ATTRIBUTE}]`).forEach((icon) => document.head.appendChild(icon));
14
+ const ownedIcons = [...document.querySelectorAll(`head link[${STREAMED_ICON_ATTRIBUTE}]`)];
15
+ const retainedIcons = /* @__PURE__ */ new Map();
16
+ for (const icon of ownedIcons) {
17
+ const order = getStreamedIconOrder(icon, metadataKey);
18
+ if (order === null) {
19
+ icon.remove();
20
+ continue;
21
+ }
22
+ const previousIcon = retainedIcons.get(order);
23
+ if (previousIcon) previousIcon.remove();
24
+ retainedIcons.set(order, icon);
25
+ }
26
+ for (const [, icon] of [...retainedIcons].sort(([leftOrder], [rightOrder]) => leftOrder - rightOrder)) document.head.appendChild(icon);
27
+ }
28
+ function StreamedIconsInsertion({ metadataKey }) {
29
+ useLayoutEffect(() => reconcileStreamedIcons(metadataKey), [metadataKey]);
30
+ return null;
31
+ }
32
+ //#endregion
33
+ export { StreamedIconsInsertion, reconcileStreamedIcons };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vinext",
3
- "version": "1.0.0-beta.3",
3
+ "version": "1.0.0-beta.4",
4
4
  "description": "Run Next.js apps on Vite. Drop-in replacement for the next CLI.",
5
5
  "license": "MIT",
6
6
  "repository": {