vinext 0.1.8 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/README.md +51 -30
  2. package/dist/build/inject-pregenerated-paths.d.ts +4 -0
  3. package/dist/build/inject-pregenerated-paths.js +18 -0
  4. package/dist/build/layout-classification.js +1 -1
  5. package/dist/build/nitro-route-rules.js +1 -1
  6. package/dist/build/pages-client-assets-module.d.ts +11 -0
  7. package/dist/build/pages-client-assets-module.js +27 -0
  8. package/dist/build/prerender.d.ts +2 -1
  9. package/dist/build/prerender.js +13 -6
  10. package/dist/build/report.d.ts +13 -2
  11. package/dist/build/report.js +99 -2
  12. package/dist/build/run-prerender.d.ts +7 -0
  13. package/dist/build/run-prerender.js +10 -1
  14. package/dist/build/standalone.js +2 -0
  15. package/dist/cache/cache-adapters-virtual.js +3 -3
  16. package/dist/check.js +40 -2
  17. package/dist/cli-args.d.ts +1 -2
  18. package/dist/cli-args.js +1 -1
  19. package/dist/cli-dev-config.d.ts +12 -0
  20. package/dist/cli-dev-config.js +23 -0
  21. package/dist/cli.js +111 -95
  22. package/dist/{server → client}/dev-error-overlay-store.d.ts +1 -1
  23. package/dist/{server → client}/dev-error-overlay-store.js +1 -1
  24. package/dist/{server → client}/dev-error-overlay.d.ts +1 -1
  25. package/dist/{server → client}/dev-error-overlay.js +2 -2
  26. package/dist/client/navigation-runtime.d.ts +2 -0
  27. package/dist/client/navigation-runtime.js +3 -1
  28. package/dist/client/window-next.d.ts +2 -1
  29. package/dist/cloudflare/index.d.ts +2 -2
  30. package/dist/cloudflare/index.js +2 -2
  31. package/dist/config/config-matchers.js +14 -10
  32. package/dist/config/next-config.d.ts +5 -3
  33. package/dist/config/next-config.js +44 -1
  34. package/dist/config/prerender.d.ts +34 -0
  35. package/dist/config/prerender.js +55 -0
  36. package/dist/entries/app-rsc-entry.d.ts +2 -1
  37. package/dist/entries/app-rsc-entry.js +40 -10
  38. package/dist/entries/app-rsc-manifest.js +2 -0
  39. package/dist/entries/pages-client-entry.d.ts +3 -0
  40. package/dist/entries/pages-client-entry.js +15 -1
  41. package/dist/entries/pages-server-entry.js +5 -2
  42. package/dist/image/image-adapters-virtual.d.ts +59 -0
  43. package/dist/image/image-adapters-virtual.js +59 -0
  44. package/dist/index.d.ts +33 -0
  45. package/dist/index.js +167 -100
  46. package/dist/init-cloudflare.d.ts +43 -0
  47. package/dist/init-cloudflare.js +839 -0
  48. package/dist/init-platform.d.ts +40 -0
  49. package/dist/init-platform.js +195 -0
  50. package/dist/init.d.ts +16 -38
  51. package/dist/init.js +209 -99
  52. package/dist/node_modules/.pnpm/am-i-vibing@0.5.0/node_modules/am-i-vibing/dist/detector-1yx2Hoe0.js +294 -0
  53. package/dist/node_modules/.pnpm/process-ancestry@0.1.0/node_modules/process-ancestry/dist/index.js +94 -0
  54. package/dist/{cloudflare → packages/cloudflare}/src/cache/cdn-adapter.runtime.js +1 -1
  55. package/dist/{cloudflare → packages/cloudflare}/src/cache/kv-data-adapter.runtime.d.ts +2 -2
  56. package/dist/{cloudflare → packages/cloudflare}/src/cache/kv-data-adapter.runtime.js +1 -1
  57. package/dist/packages/cloudflare/src/deploy-config.js +150 -0
  58. package/dist/packages/cloudflare/src/deploy-help.js +55 -0
  59. package/dist/packages/cloudflare/src/deploy.js +276 -0
  60. package/dist/{cloudflare → packages/cloudflare/src}/tpr.d.ts +4 -41
  61. package/dist/{cloudflare → packages/cloudflare/src}/tpr.js +18 -15
  62. package/dist/plugins/ast-scope.d.ts +16 -0
  63. package/dist/plugins/ast-scope.js +62 -0
  64. package/dist/plugins/ast-utils.d.ts +27 -1
  65. package/dist/plugins/ast-utils.js +32 -1
  66. package/dist/plugins/css-module-imports.d.ts +14 -0
  67. package/dist/plugins/css-module-imports.js +59 -0
  68. package/dist/plugins/extensionless-dynamic-import.js +2 -2
  69. package/dist/plugins/ignore-dynamic-requests.d.ts +11 -0
  70. package/dist/plugins/ignore-dynamic-requests.js +530 -0
  71. package/dist/plugins/typeof-window.d.ts +1 -1
  72. package/dist/plugins/typeof-window.js +28 -56
  73. package/dist/routing/app-route-graph.d.ts +23 -2
  74. package/dist/routing/app-route-graph.js +126 -32
  75. package/dist/routing/file-matcher.d.ts +3 -0
  76. package/dist/routing/file-matcher.js +3 -0
  77. package/dist/routing/pages-router.js +2 -2
  78. package/dist/server/api-handler.js +9 -14
  79. package/dist/server/app-browser-action-result.d.ts +4 -1
  80. package/dist/server/app-browser-action-result.js +8 -1
  81. package/dist/server/app-browser-entry.js +161 -52
  82. package/dist/server/app-browser-interception-context.d.ts +1 -0
  83. package/dist/server/app-browser-interception-context.js +4 -2
  84. package/dist/server/app-browser-navigation-controller.d.ts +2 -0
  85. package/dist/server/app-browser-navigation-controller.js +60 -31
  86. package/dist/server/app-browser-server-action-client.d.ts +1 -1
  87. package/dist/server/app-browser-server-action-client.js +3 -4
  88. package/dist/server/app-browser-state.d.ts +11 -1
  89. package/dist/server/app-browser-state.js +25 -8
  90. package/dist/server/app-browser-visible-commit.d.ts +6 -1
  91. package/dist/server/app-browser-visible-commit.js +43 -25
  92. package/dist/server/app-elements-wire.d.ts +9 -2
  93. package/dist/server/app-elements-wire.js +8 -1
  94. package/dist/server/app-elements.d.ts +2 -2
  95. package/dist/server/app-elements.js +4 -3
  96. package/dist/server/app-fallback-renderer.d.ts +2 -1
  97. package/dist/server/app-fallback-renderer.js +10 -6
  98. package/dist/server/app-hydration-cache-publication.d.ts +11 -0
  99. package/dist/server/app-hydration-cache-publication.js +43 -0
  100. package/dist/server/app-middleware.d.ts +4 -3
  101. package/dist/server/app-middleware.js +8 -3
  102. package/dist/server/app-optimistic-routing.js +11 -4
  103. package/dist/server/app-page-cache-finalizer.js +2 -1
  104. package/dist/server/app-page-cache-render.d.ts +1 -0
  105. package/dist/server/app-page-cache-render.js +8 -4
  106. package/dist/server/app-page-cache.d.ts +1 -0
  107. package/dist/server/app-page-cache.js +5 -2
  108. package/dist/server/app-page-dispatch.js +1 -0
  109. package/dist/server/app-page-element-builder.d.ts +2 -1
  110. package/dist/server/app-page-element-builder.js +58 -4
  111. package/dist/server/app-page-probe.js +5 -0
  112. package/dist/server/app-page-render-identity.d.ts +1 -0
  113. package/dist/server/app-page-render-identity.js +1 -1
  114. package/dist/server/app-page-render.js +18 -0
  115. package/dist/server/app-page-request.d.ts +14 -0
  116. package/dist/server/app-page-request.js +69 -4
  117. package/dist/server/app-page-route-wiring.d.ts +8 -1
  118. package/dist/server/app-page-route-wiring.js +32 -18
  119. package/dist/server/app-page-segment-state.d.ts +2 -1
  120. package/dist/server/app-page-segment-state.js +3 -1
  121. package/dist/server/app-page-stream.d.ts +5 -0
  122. package/dist/server/app-page-stream.js +3 -1
  123. package/dist/server/app-route-handler-execution.js +5 -1
  124. package/dist/server/app-route-handler-response.js +1 -0
  125. package/dist/server/app-route-handler-runtime.js +1 -1
  126. package/dist/server/app-router-entry.js +9 -2
  127. package/dist/server/app-rsc-handler.d.ts +1 -0
  128. package/dist/server/app-rsc-handler.js +62 -31
  129. package/dist/server/app-rsc-request-normalization.d.ts +3 -2
  130. package/dist/server/app-rsc-request-normalization.js +6 -3
  131. package/dist/server/app-segment-config.d.ts +11 -0
  132. package/dist/server/app-segment-config.js +62 -6
  133. package/dist/server/app-server-action-execution.d.ts +1 -0
  134. package/dist/server/app-server-action-execution.js +58 -28
  135. package/dist/server/app-ssr-entry.d.ts +3 -0
  136. package/dist/server/app-ssr-entry.js +15 -7
  137. package/dist/server/app-ssr-stream.d.ts +7 -3
  138. package/dist/server/app-ssr-stream.js +10 -6
  139. package/dist/server/app-visited-response-cache.d.ts +6 -0
  140. package/dist/server/app-visited-response-cache.js +3 -1
  141. package/dist/server/client-trace-metadata.js +26 -0
  142. package/dist/server/default-global-not-found-module.d.ts +14 -0
  143. package/dist/server/default-global-not-found-module.js +14 -0
  144. package/dist/server/dev-route-files.js +4 -0
  145. package/dist/server/dev-server.js +19 -18
  146. package/dist/server/dev-stack-sourcemap.d.ts +1 -1
  147. package/dist/server/dev-stack-sourcemap.js +1 -1
  148. package/dist/server/fetch-handler.d.ts +2 -0
  149. package/dist/server/fetch-handler.js +18 -0
  150. package/dist/server/headers.d.ts +3 -15
  151. package/dist/server/headers.js +2 -15
  152. package/dist/server/image-optimization.d.ts +51 -1
  153. package/dist/server/image-optimization.js +52 -2
  154. package/dist/server/implicit-tags.js +1 -1
  155. package/dist/server/isr-cache.d.ts +1 -1
  156. package/dist/server/isr-cache.js +2 -2
  157. package/dist/server/metadata-routes.d.ts +3 -1
  158. package/dist/server/metadata-routes.js +12 -2
  159. package/dist/server/middleware-runtime.js +6 -1
  160. package/dist/server/navigation-planner.d.ts +1 -0
  161. package/dist/server/navigation-planner.js +14 -3
  162. package/dist/server/pages-api-route.js +2 -2
  163. package/dist/server/pages-asset-tags.d.ts +4 -6
  164. package/dist/server/pages-asset-tags.js +12 -12
  165. package/dist/server/pages-client-assets.d.ts +12 -0
  166. package/dist/server/pages-client-assets.js +10 -0
  167. package/dist/server/pages-node-compat.d.ts +8 -1
  168. package/dist/server/pages-node-compat.js +32 -4
  169. package/dist/server/pages-page-data.d.ts +24 -1
  170. package/dist/server/pages-page-data.js +45 -24
  171. package/dist/server/pages-page-handler.d.ts +2 -1
  172. package/dist/server/pages-page-handler.js +19 -12
  173. package/dist/server/pages-page-response.d.ts +7 -0
  174. package/dist/server/pages-page-response.js +4 -1
  175. package/dist/server/pages-request-pipeline.d.ts +2 -0
  176. package/dist/server/pages-request-pipeline.js +25 -1
  177. package/dist/server/pages-router-entry.d.ts +30 -0
  178. package/dist/server/pages-router-entry.js +108 -0
  179. package/dist/server/prerender-manifest.d.ts +3 -1
  180. package/dist/server/prerender-route-params.js +1 -1
  181. package/dist/server/prod-server.d.ts +1 -1
  182. package/dist/server/prod-server.js +33 -20
  183. package/dist/server/request-pipeline.js +1 -0
  184. package/dist/server/seed-cache.js +4 -4
  185. package/dist/shims/app-router-scroll-state.d.ts +1 -0
  186. package/dist/shims/app-router-scroll-state.js +1 -0
  187. package/dist/shims/app-router-scroll.js +2 -1
  188. package/dist/shims/cache.js +19 -15
  189. package/dist/shims/cdn-cache.js +1 -1
  190. package/dist/shims/dynamic-preload-chunks.js +2 -1
  191. package/dist/shims/form.d.ts +3 -1
  192. package/dist/shims/form.js +37 -43
  193. package/dist/shims/headers.d.ts +7 -1
  194. package/dist/shims/headers.js +17 -3
  195. package/dist/shims/image.js +75 -14
  196. package/dist/shims/internal/app-route-detection.d.ts +2 -17
  197. package/dist/shims/internal/app-route-detection.js +4 -17
  198. package/dist/shims/internal/hybrid-client-route-owner-direct.d.ts +23 -0
  199. package/dist/shims/internal/hybrid-client-route-owner-direct.js +51 -0
  200. package/dist/shims/internal/hybrid-client-route-owner.d.ts +2 -15
  201. package/dist/shims/internal/hybrid-client-route-owner.js +9 -60
  202. package/dist/shims/internal/pages-data-fetch-dedup.d.ts +9 -5
  203. package/dist/shims/internal/pages-data-fetch-dedup.js +73 -14
  204. package/dist/shims/internal/pages-data-target.d.ts +10 -9
  205. package/dist/shims/internal/pages-data-target.js +91 -12
  206. package/dist/shims/internal/pages-router-components.d.ts +7 -0
  207. package/dist/shims/internal/pages-router-components.js +13 -0
  208. package/dist/shims/layout-segment-context.d.ts +4 -1
  209. package/dist/shims/layout-segment-context.js +18 -4
  210. package/dist/shims/link.js +55 -26
  211. package/dist/shims/navigation.d.ts +5 -1
  212. package/dist/shims/navigation.js +45 -10
  213. package/dist/shims/navigation.react-server.js +1 -1
  214. package/dist/shims/root-params.d.ts +15 -1
  215. package/dist/shims/root-params.js +21 -1
  216. package/dist/shims/router.d.ts +5 -7
  217. package/dist/shims/router.js +362 -93
  218. package/dist/shims/server.js +3 -2
  219. package/dist/shims/slot.js +3 -6
  220. package/dist/utils/client-runtime-metadata.d.ts +2 -18
  221. package/dist/utils/client-runtime-metadata.js +31 -22
  222. package/dist/utils/dev-stack-sourcemap-endpoint.d.ts +4 -0
  223. package/dist/{server → utils}/dev-stack-sourcemap-endpoint.js +1 -1
  224. package/dist/utils/domain-locale.d.ts +6 -3
  225. package/dist/{server → utils}/middleware-request-headers.d.ts +1 -1
  226. package/dist/{server → utils}/middleware-request-headers.js +2 -2
  227. package/dist/utils/project.d.ts +48 -2
  228. package/dist/utils/project.js +225 -4
  229. package/dist/utils/protocol-headers.d.ts +17 -0
  230. package/dist/utils/protocol-headers.js +17 -0
  231. package/dist/utils/react-version.d.ts +4 -0
  232. package/dist/utils/react-version.js +44 -0
  233. package/package.json +47 -2
  234. package/dist/deploy.d.ts +0 -197
  235. package/dist/deploy.js +0 -1040
  236. package/dist/server/dev-stack-sourcemap-endpoint.d.ts +0 -4
  237. /package/dist/{cloudflare → packages/cloudflare}/src/utils/cache-control-metadata.js +0 -0
@@ -1,8 +1,8 @@
1
1
  import { stripBasePath } from "../utils/base-path.js";
2
2
  import { clearAppNavigationFailureTarget, getAppNavigationFailureTarget } from "../client/app-nav-failure-handler.js";
3
3
  import { claimAppRouterScrollIntentForCommit, consumeAppRouterScrollIntent } from "../shims/app-router-scroll-state.js";
4
+ import { resolveServerActionOperationLane, shouldScheduleRefreshForDiscardedServerAction } from "./app-browser-action-result.js";
4
5
  import { activateNavigationSnapshot, clearPendingPathname, commitClientNavigationState, createSnapshotPathAndSearch } from "../shims/navigation.js";
5
- import { shouldScheduleRefreshForDiscardedServerAction } from "./app-browser-action-result.js";
6
6
  import { FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN, createPendingNavigationCommit } from "./app-browser-state.js";
7
7
  import { applyApprovedVisibleCommit, approveHmrVisibleCommit, approvePendingNavigationCommit, resolveAndClassifyNavigationCommit } from "./app-browser-visible-commit.js";
8
8
  import { startTransition, useInsertionEffect, useLayoutEffect } from "react";
@@ -73,6 +73,9 @@ function createAppBrowserNavigationController(deps = {}) {
73
73
  const syncHistoryStatePreviousNextUrl = deps.syncHistoryStatePreviousNextUrl ?? (() => {});
74
74
  let nextNavigationRenderId = 0;
75
75
  let activeNavigationId = 0;
76
+ let pendingUserNavigationId = null;
77
+ let pendingUserNavigationLane = null;
78
+ let latestHmrUpdateId = 0;
76
79
  const pendingNavigationCommits = /* @__PURE__ */ new Map();
77
80
  const pendingNavigationFailureTargets = /* @__PURE__ */ new Map();
78
81
  const pendingNavigationPrePaintEffects = /* @__PURE__ */ new Map();
@@ -105,7 +108,10 @@ function createAppBrowserNavigationController(deps = {}) {
105
108
  resolveReady?.();
106
109
  }
107
110
  function beginNavigation() {
111
+ latestHmrUpdateId += 1;
108
112
  activeNavigationId += 1;
113
+ pendingUserNavigationId = activeNavigationId;
114
+ pendingUserNavigationLane = null;
109
115
  return activeNavigationId;
110
116
  }
111
117
  function getActiveNavigationId() {
@@ -149,13 +155,11 @@ function createAppBrowserNavigationController(deps = {}) {
149
155
  }
150
156
  function finalizeNavigation(navId, pending) {
151
157
  settlePendingBrowserRouterState(pending);
152
- if (isCurrentNavigation(navId)) clearPendingPathname(navId);
153
- }
154
- function resolvePendingBrowserRouterState(pending, commit) {
155
- if (!pending || pending.settled) return;
156
- pending.settled = true;
157
- pending.resolve(applyApprovedVisibleCommit(getBrowserRouterState(), commit));
158
- if (activePendingBrowserRouterState === pending) activePendingBrowserRouterState = null;
158
+ if (isCurrentNavigation(navId)) {
159
+ pendingUserNavigationId = null;
160
+ pendingUserNavigationLane = null;
161
+ clearPendingPathname(navId);
162
+ }
159
163
  }
160
164
  function queuePrePaintNavigationEffect(renderId, effect) {
161
165
  if (!effect) return;
@@ -181,16 +185,17 @@ function createAppBrowserNavigationController(deps = {}) {
181
185
  }
182
186
  }
183
187
  /**
184
- * Resolve all pending navigation commits with renderId <= the committed renderId.
185
- * Note: Map iteration handles concurrent deletion safely entries are visited in
186
- * insertion order and deletion doesn't affect the iterator's view of remaining entries.
187
- * This pattern is also used in drainPrePaintEffects with the same semantics.
188
+ * Settle all pending navigation renders through the supplied renderId. Only
189
+ * the exact render whose layout effect ran is a successful commit; older
190
+ * superseded renders and cleanup-only settlements resolve as no-commit.
188
191
  */
189
- function resolveCommittedNavigations(renderId) {
190
- for (const [pendingId, resolve] of pendingNavigationCommits) {
192
+ function settleNavigationCommits(renderId, committed) {
193
+ for (const [pendingId, pendingCommit] of pendingNavigationCommits) {
191
194
  if (pendingId > renderId) continue;
192
195
  pendingNavigationCommits.delete(pendingId);
193
- resolve();
196
+ const didCommit = committed && pendingId === renderId;
197
+ if (didCommit && pendingCommit.committedState !== null) pendingCommit.onCommittedState?.(pendingCommit.committedState);
198
+ pendingCommit.resolve(didCommit);
194
199
  }
195
200
  }
196
201
  function clearCommittedNavigationFailureTargets(renderId) {
@@ -201,6 +206,8 @@ function createAppBrowserNavigationController(deps = {}) {
201
206
  }
202
207
  }
203
208
  async function hmrReplaceTree(nextElements, navigationSnapshot) {
209
+ const hmrUpdateId = ++latestHmrUpdateId;
210
+ const startedDuringUserNavigation = pendingUserNavigationLane === "navigation";
204
211
  if (!hasBrowserRouterState()) return;
205
212
  const pending = await createPendingNavigationCommit({
206
213
  currentState: getBrowserRouterState(),
@@ -211,8 +218,16 @@ function createAppBrowserNavigationController(deps = {}) {
211
218
  renderId: allocateRenderId(),
212
219
  type: "replace"
213
220
  });
221
+ if (hmrUpdateId !== latestHmrUpdateId || startedDuringUserNavigation) return;
214
222
  if (!hasBrowserRouterState()) return;
215
- dispatchSynchronousVisibleCommit(approveHmrVisibleCommit(pending));
223
+ const approval = approveHmrVisibleCommit({
224
+ currentState: getBrowserRouterState(),
225
+ pending,
226
+ routeManifest: deps.getRouteManifest?.() ?? null,
227
+ targetHref: createSnapshotPathAndSearch(navigationSnapshot)
228
+ });
229
+ if (approval.approvedCommit) dispatchSynchronousVisibleCommit(approval.approvedCommit);
230
+ else if (approval.decision.disposition === "hard-navigate") performHardNavigation(createSnapshotPathAndSearch(navigationSnapshot));
216
231
  }
217
232
  function NavigationCommitSignal({ renderId, children }) {
218
233
  useInsertionEffect(() => {
@@ -220,30 +235,36 @@ function createAppBrowserNavigationController(deps = {}) {
220
235
  }, [renderId]);
221
236
  useLayoutEffect(() => {
222
237
  drainPrePaintEffects(renderId);
223
- const frame = requestAnimationFrame(() => {
224
- resolveCommittedNavigations(renderId);
225
- });
238
+ settleNavigationCommits(renderId, true);
226
239
  return () => {
227
- cancelAnimationFrame(frame);
228
- resolveCommittedNavigations(renderId);
240
+ settleNavigationCommits(renderId, false);
229
241
  };
230
242
  }, [renderId]);
231
243
  return children;
232
244
  }
233
- function dispatchApprovedVisibleCommit(commit, pendingRouterState, visibleCommitMode) {
245
+ function dispatchApprovedVisibleCommit(renderId, commit, pendingRouterState, visibleCommitMode) {
234
246
  const setter = getBrowserRouterStateSetter();
247
+ const pendingCommit = pendingNavigationCommits.get(renderId);
248
+ const captureCandidateState = (state) => {
249
+ if (pendingCommit) pendingCommit.committedState = state;
250
+ return state;
251
+ };
235
252
  if (pendingRouterState) {
236
- resolvePendingBrowserRouterState(pendingRouterState, commit);
253
+ if (pendingRouterState.settled) return;
254
+ const committedState = captureCandidateState(applyApprovedVisibleCommit(getBrowserRouterState(), commit));
255
+ pendingRouterState.settled = true;
256
+ pendingRouterState.resolve(committedState);
257
+ if (activePendingBrowserRouterState === pendingRouterState) activePendingBrowserRouterState = null;
237
258
  return;
238
259
  }
239
260
  if (visibleCommitMode === "synchronous") {
240
261
  flushSync(() => {
241
- setter(applyApprovedVisibleCommit(getBrowserRouterState(), commit));
262
+ setter(captureCandidateState(applyApprovedVisibleCommit(getBrowserRouterState(), commit)));
242
263
  });
243
264
  return;
244
265
  }
245
266
  startTransition(() => {
246
- setter(applyApprovedVisibleCommit(getBrowserRouterState(), commit));
267
+ setter(captureCandidateState(applyApprovedVisibleCommit(getBrowserRouterState(), commit)));
247
268
  });
248
269
  }
249
270
  function dispatchSynchronousVisibleCommit(commit) {
@@ -280,6 +301,7 @@ function createAppBrowserNavigationController(deps = {}) {
280
301
  previousNextUrl: options.restoredState.previousNextUrl,
281
302
  rootLayoutTreePath: options.restoredState.rootLayoutTreePath,
282
303
  routeId: options.restoredState.routeId,
304
+ restoredHistorySnapshot: true,
283
305
  skippedLayoutIds: []
284
306
  };
285
307
  }
@@ -309,18 +331,25 @@ function createAppBrowserNavigationController(deps = {}) {
309
331
  lifecycleOptions?.onDiscardedRevalidation?.();
310
332
  }
311
333
  async function renderNavigationPayload(options) {
334
+ if (options.navId === pendingUserNavigationId) pendingUserNavigationLane = options.operationLane;
312
335
  const renderId = allocateRenderId();
313
336
  const failureTarget = getAppNavigationFailureTarget(options.targetHref);
314
337
  if (failureTarget) pendingNavigationFailureTargets.set(renderId, failureTarget);
315
338
  let resolveCommitted;
316
339
  const committed = new Promise((resolve) => {
317
340
  resolveCommitted = resolve;
318
- pendingNavigationCommits.set(renderId, resolve);
341
+ pendingNavigationCommits.set(renderId, {
342
+ committedState: null,
343
+ onCommittedState: options.onCommittedState,
344
+ resolve
345
+ });
319
346
  });
320
347
  let snapshotActivated = false;
321
348
  try {
322
349
  const pending = await createPendingNavigationCommit({
323
350
  currentState: getBrowserRouterState(),
351
+ navigationCommitKind: options.navigationCommitKind,
352
+ navigationId: options.navId,
324
353
  nextElements: options.nextElements,
325
354
  navigationSnapshot: options.navigationSnapshot,
326
355
  operationLane: options.operationLane,
@@ -344,7 +373,7 @@ function createAppBrowserNavigationController(deps = {}) {
344
373
  pendingNavigationFailureTargets.delete(renderId);
345
374
  if (failureTarget) clearAppNavigationFailureTarget(failureTarget);
346
375
  pendingNavigationCommits.delete(renderId);
347
- resolveCommitted?.();
376
+ resolveCommitted?.(false);
348
377
  consumeAppRouterScrollIntent(options.scrollIntent ?? null);
349
378
  return "no-commit";
350
379
  }
@@ -371,17 +400,17 @@ function createAppBrowserNavigationController(deps = {}) {
371
400
  claimAppRouterScrollIntentForCommit(options.scrollIntent, renderId);
372
401
  activateNavigationSnapshot();
373
402
  snapshotActivated = true;
374
- dispatchApprovedVisibleCommit(approvedCommit, options.pendingRouterState, options.visibleCommitMode ?? "transition");
403
+ dispatchApprovedVisibleCommit(renderId, approvedCommit, options.pendingRouterState, options.visibleCommitMode ?? "transition");
375
404
  } catch (error) {
376
405
  pendingNavigationFailureTargets.delete(renderId);
377
406
  pendingNavigationPrePaintEffects.delete(renderId);
378
407
  pendingNavigationCommits.delete(renderId);
379
408
  if (snapshotActivated) commitClientNavigationStateImpl(options.navId);
380
409
  settlePendingBrowserRouterState(options.pendingRouterState);
381
- resolveCommitted?.();
410
+ resolveCommitted?.(false);
382
411
  throw error;
383
412
  }
384
- return committed.then(() => "committed");
413
+ return committed.then((didCommit) => didCommit ? "committed" : "no-commit");
385
414
  }
386
415
  async function commitSameUrlNavigatePayload(nextElements, navigationSnapshot, returnValue, actionInitiationState, lifecycleOptions) {
387
416
  const currentState = actionInitiationState ?? getBrowserRouterState();
@@ -395,7 +424,7 @@ function createAppBrowserNavigationController(deps = {}) {
395
424
  navigationSnapshot,
396
425
  nextElements,
397
426
  renderId: allocateRenderId(),
398
- operationLane: "server-action",
427
+ operationLane: resolveServerActionOperationLane(lifecycleOptions?.revalidation ?? "none"),
399
428
  payloadOrigin: FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN,
400
429
  startedNavigationId,
401
430
  routeManifest: getRouteManifest(),
@@ -23,7 +23,7 @@ type ClientServerActionDeps = {
23
23
  commitSameUrlNavigatePayload(elements: Promise<AppElements>, actionInitiation: ClientServerActionInitiation, returnValue: ServerActionResult["returnValue"] | undefined, revalidation: ServerActionRevalidationKind): Promise<unknown>;
24
24
  navigationPlanner: typeof navigationPlanner;
25
25
  performHardNavigation(url: string, historyMode?: "assign" | "replace"): void;
26
- renderRedirectPayload(elements: AppElements, target: ActionRedirectTarget, actionInitiation: ClientServerActionInitiation): void;
26
+ renderRedirectPayload(elements: AppElements, target: ActionRedirectTarget, actionInitiation: ClientServerActionInitiation, revalidation: ServerActionRevalidationKind): void;
27
27
  syncCurrentHistoryState(previousNextUrl: string | null, bfcacheIds: Readonly<Record<string, string>>): void;
28
28
  syncServerActionHttpFallbackHead(status: number | null): void;
29
29
  };
@@ -3,7 +3,7 @@ import { DANGEROUS_URL_BLOCK_MESSAGE, isDangerousScheme } from "../shims/url-saf
3
3
  import { AppElementsWire } from "./app-elements-wire.js";
4
4
  import "./app-elements.js";
5
5
  import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, createServerActionRequestUrl } from "./app-rsc-cache-busting.js";
6
- import { createServerActionResultFacts, isServerActionResult, normalizeServerActionThrownValue, parseServerActionRevalidationHeader, readInvalidServerActionResponseError, shouldClearClientNavigationCachesForServerActionResult } from "./app-browser-action-result.js";
6
+ import { createServerActionResultFacts, isServerActionResult, normalizeServerActionThrownValue, parseServerActionRevalidationHeader, readInvalidServerActionResponseError, shouldClearClientNavigationCachesForServerActionResult, shouldSyncServerActionHttpFallbackHead } from "./app-browser-action-result.js";
7
7
  import { resolveServerActionRequestState } from "./app-browser-state.js";
8
8
  import { applyServerActionResultDecision } from "./app-browser-server-action-navigation.js";
9
9
  import { throwOnServerActionNotFound } from "./server-action-not-found.js";
@@ -100,14 +100,13 @@ async function invokeClientServerAction(id, args, actionInitiation, deps) {
100
100
  if (revalidation === "none" && shouldClearClientNavigationCachesForServerActionResult(result, revalidation)) deps.clearClientNavigationCaches();
101
101
  if (actionRedirectTarget) {
102
102
  if (isServerActionResult(result) && result.root !== void 0) {
103
- deps.renderRedirectPayload(AppElementsWire.decode(result.root), actionRedirectTarget, actionInitiation);
103
+ deps.renderRedirectPayload(AppElementsWire.decode(result.root), actionRedirectTarget, actionInitiation, revalidation);
104
104
  throw new ServerActionRedirectError(actionRedirectTarget);
105
105
  }
106
106
  deps.performHardNavigation(actionRedirectTarget.href);
107
107
  return;
108
108
  }
109
- const hasSameUrlRerenderPayload = isServerActionResult(result) && result.root !== void 0;
110
- deps.syncServerActionHttpFallbackHead(hasSameUrlRerenderPayload ? null : fetchResponse.status);
109
+ deps.syncServerActionHttpFallbackHead(shouldSyncServerActionHttpFallbackHead(result) ? fetchResponse.status : null);
111
110
  if (isServerActionResult(result)) {
112
111
  if (result.root !== void 0) {
113
112
  const returnValue = result.returnValue && !result.returnValue.ok ? {
@@ -11,6 +11,8 @@ import { ClientNavigationRenderSnapshot } from "../shims/navigation.js";
11
11
  type OperationRecordBase = {
12
12
  id: number;
13
13
  lane: OperationLane;
14
+ navigationCommitKind?: "authoritative" | "detached";
15
+ navigationId?: number;
14
16
  startedVisibleCommitVersion: number;
15
17
  };
16
18
  type PendingOperationRecord = OperationRecordBase & {
@@ -64,18 +66,23 @@ type PendingNavigationCommit = {
64
66
  previousNextUrl: string | null;
65
67
  rootLayoutTreePath: string | null;
66
68
  routeId: string;
69
+ restoredHistorySnapshot?: boolean;
67
70
  skippedLayoutIds: readonly string[];
68
71
  };
69
72
  type AppNavigationPayloadOrigin = Readonly<{
73
+ origin: "committed-cache";
74
+ } | {
70
75
  origin: "fresh";
71
76
  } | {
72
77
  origin: "visited-cache";
73
78
  }>;
79
+ declare const COMMITTED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN: AppNavigationPayloadOrigin;
74
80
  declare const FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN: AppNavigationPayloadOrigin;
75
81
  declare const VISITED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN: AppNavigationPayloadOrigin;
76
82
  type PendingNavigationCommitDisposition = "dispatch" | "hard-navigate" | "skip";
77
83
  type CacheRestorableAppPayloadMetadata = Readonly<{
78
84
  cacheEntryReuseProof?: CacheEntryReuseProof;
85
+ dynamicStaleTimeSeconds?: number;
79
86
  skippedLayoutIds: readonly string[];
80
87
  }>;
81
88
  type DispatchPendingNavigationCommitDispositionDecision = {
@@ -91,6 +98,7 @@ type NonDispatchPendingNavigationCommitDispositionDecision = {
91
98
  trace: NavigationTrace;
92
99
  };
93
100
  type PendingNavigationCommitDispositionDecision = DispatchPendingNavigationCommitDispositionDecision | NonDispatchPendingNavigationCommitDispositionDecision;
101
+ declare function isCompleteAppPayloadMetadata(metadata: CacheRestorableAppPayloadMetadata): boolean;
94
102
  declare function isCacheRestorableAppPayloadMetadata(metadata: CacheRestorableAppPayloadMetadata): metadata is CacheRestorableAppPayloadMetadata & {
95
103
  cacheEntryReuseProof: CacheEntryReuseProof;
96
104
  };
@@ -126,6 +134,8 @@ declare function resolvePendingNavigationCommitDispositionDecision(options: {
126
134
  }): PendingNavigationCommitDispositionDecision;
127
135
  declare function createPendingNavigationCommit(options: {
128
136
  currentState: AppRouterState;
137
+ navigationCommitKind?: "authoritative" | "detached";
138
+ navigationId?: number;
129
139
  nextElements: Promise<AppElements>;
130
140
  navigationSnapshot: ClientNavigationRenderSnapshot;
131
141
  operationLane: OperationLane;
@@ -137,4 +147,4 @@ declare function createPendingNavigationCommit(options: {
137
147
  type: "navigate" | "replace" | "traverse";
138
148
  }): Promise<PendingNavigationCommit>;
139
149
  //#endregion
140
- export { AppNavigationPayloadOrigin, AppRouterAction, AppRouterState, type BfcacheIdMap, CommittedOperationRecord, FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN, type HistoryTraversalIntent, type OperationLane, PendingNavigationCommit, PendingOperationRecord, VISITED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN, createBfcacheSegmentStateKeyMap, createHistoryStateWithNavigationMetadata, createHistoryStateWithPreviousNextUrl, createInitialBfcacheIdMap, createNextBfcacheIdMap, createPendingNavigationCommit, isCacheRestorableAppPayloadMetadata, isHistoryStateBfcacheVersionCurrent, preserveBfcacheIdsForMergedElements, readHistoryStateBfcacheIds, readHistoryStateBfcacheVersion, readHistoryStatePreviousNextUrl, readHistoryStateTraversalIndex, resolveHistoryTraversalIntent, resolveInterceptionContextFromPreviousNextUrl, resolvePendingNavigationCommitDispositionDecision, resolveServerActionRequestState };
150
+ export { AppNavigationPayloadOrigin, AppRouterAction, AppRouterState, type BfcacheIdMap, COMMITTED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN, CommittedOperationRecord, FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN, type HistoryTraversalIntent, type OperationLane, PendingNavigationCommit, PendingOperationRecord, VISITED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN, createBfcacheSegmentStateKeyMap, createHistoryStateWithNavigationMetadata, createHistoryStateWithPreviousNextUrl, createInitialBfcacheIdMap, createNextBfcacheIdMap, createPendingNavigationCommit, isCacheRestorableAppPayloadMetadata, isCompleteAppPayloadMetadata, isHistoryStateBfcacheVersionCurrent, preserveBfcacheIdsForMergedElements, readHistoryStateBfcacheIds, readHistoryStateBfcacheVersion, readHistoryStatePreviousNextUrl, readHistoryStateTraversalIndex, resolveHistoryTraversalIntent, resolveInterceptionContextFromPreviousNextUrl, resolvePendingNavigationCommitDispositionDecision, resolveServerActionRequestState };
@@ -6,28 +6,35 @@ import { AppElementsWire } from "./app-elements-wire.js";
6
6
  import { getMountedSlotIds, getMountedSlotIdsHeader } from "./app-elements.js";
7
7
  import { createRscRequestHeaders } from "./app-rsc-cache-busting.js";
8
8
  import { createCacheEntryReuseProof } from "./cache-proof.js";
9
+ import { createBfcacheSegmentStateKeyMap, createInitialBfcacheIdMap, createNextBfcacheIdMap, preserveBfcacheIdsForMergedElements } from "./app-bfcache-identity.js";
9
10
  import { createHistoryStateWithNavigationMetadata, createHistoryStateWithPreviousNextUrl, isHistoryStateBfcacheVersionCurrent, readHistoryStateBfcacheIds, readHistoryStateBfcacheVersion, readHistoryStatePreviousNextUrl, readHistoryStateTraversalIndex, resolveHistoryTraversalIntent } from "./app-history-state.js";
10
11
  import { NavigationTraceReasonCodes, createNavigationLifecycleTraceFields, createNavigationTrace } from "./navigation-trace.js";
11
12
  import { verifyOperationTokenForCommit } from "./operation-token.js";
12
13
  import { navigationPlanner, resolveDefaultOrUnmatchedSlotPersistenceForLayouts } from "./navigation-planner.js";
13
14
  import { createSnapshotPathAndSearch } from "../shims/navigation.js";
14
- import { createBfcacheSegmentStateKeyMap, createInitialBfcacheIdMap, createNextBfcacheIdMap, preserveBfcacheIdsForMergedElements } from "./app-bfcache-identity.js";
15
15
  //#region src/server/app-browser-state.ts
16
+ const COMMITTED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN = { origin: "committed-cache" };
16
17
  const FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN = { origin: "fresh" };
17
18
  const VISITED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN = { origin: "visited-cache" };
18
19
  function createOperationRecord(options) {
19
20
  return {
20
21
  id: options.id,
21
22
  lane: options.lane,
23
+ ...options.navigationCommitKind !== void 0 ? { navigationCommitKind: options.navigationCommitKind } : {},
24
+ ...options.navigationId !== void 0 ? { navigationId: options.navigationId } : {},
22
25
  startedVisibleCommitVersion: options.startedVisibleCommitVersion,
23
26
  state: "pending"
24
27
  };
25
28
  }
29
+ function isCompleteAppPayloadMetadata(metadata) {
30
+ return metadata.skippedLayoutIds.length === 0;
31
+ }
26
32
  function isCacheRestorableAppPayloadMetadata(metadata) {
27
- return metadata.cacheEntryReuseProof !== void 0 && metadata.skippedLayoutIds.length === 0;
33
+ return metadata.cacheEntryReuseProof !== void 0 && isCompleteAppPayloadMetadata(metadata);
28
34
  }
29
35
  function requiresCacheEntryReuseProof(origin) {
30
36
  switch (origin.origin) {
37
+ case "committed-cache":
31
38
  case "fresh": return false;
32
39
  case "visited-cache": return true;
33
40
  default: throw new Error("[vinext] Unknown App Router payload origin: " + String(origin));
@@ -70,14 +77,21 @@ function resolveServerActionRequestState(options) {
70
77
  function resolvePendingNavigationCommitDispositionDecision(options) {
71
78
  const traceFields = createPendingNavigationTraceFields(options);
72
79
  const targetSnapshot = createPendingRouteSnapshot(options.pending);
73
- const verdict = verifyOperationTokenForCommit(createPendingNavigationOperationToken({
80
+ const token = createPendingNavigationOperationToken({
74
81
  pending: options.pending,
75
82
  routeManifest: options.routeManifest ?? null,
76
83
  startedNavigationId: options.startedNavigationId,
77
84
  targetSnapshot
78
- }), {
85
+ });
86
+ if (options.pending.action.operation.navigationCommitKind === "detached" && options.currentState.activeOperation?.navigationId === options.startedNavigationId && options.currentState.activeOperation.navigationCommitKind === "authoritative") return {
87
+ disposition: "skip",
88
+ preserveElementIds: [],
89
+ trace: createNavigationTrace(NavigationTraceReasonCodes.staleOperation, traceFields)
90
+ };
91
+ const visibleCommitVersion = options.currentState.activeOperation?.navigationId === options.startedNavigationId && options.currentState.activeOperation.navigationCommitKind === "detached" && options.pending.action.operation.navigationCommitKind === "authoritative" ? options.pending.action.operation.startedVisibleCommitVersion : options.currentState.visibleCommitVersion;
92
+ const verdict = verifyOperationTokenForCommit(token, {
79
93
  activeNavigationId: options.activeNavigationId,
80
- visibleCommitVersion: options.currentState.visibleCommitVersion
94
+ visibleCommitVersion
81
95
  });
82
96
  if (!verdict.authorized) return {
83
97
  disposition: "skip",
@@ -126,7 +140,7 @@ function createMountedParallelSlotSnapshots(elements) {
126
140
  }
127
141
  function createVisibleRouteSnapshot(state) {
128
142
  const displayUrl = createSnapshotPathAndSearch(state.navigationSnapshot);
129
- const matchedUrl = normalizeNavigationSnapshotMatchedUrl(state.navigationSnapshot.pathname);
143
+ const matchedUrl = state.interception?.targetMatchedUrl ?? normalizeNavigationSnapshotMatchedUrl(state.navigationSnapshot.pathname);
130
144
  return {
131
145
  displayUrl,
132
146
  interception: state.interception,
@@ -144,7 +158,7 @@ function createVisibleRouteSnapshot(state) {
144
158
  }
145
159
  function createPendingRouteSnapshot(pending) {
146
160
  const displayUrl = createSnapshotPathAndSearch(pending.action.navigationSnapshot);
147
- const matchedUrl = normalizeNavigationSnapshotMatchedUrl(pending.action.navigationSnapshot.pathname);
161
+ const matchedUrl = pending.action.interception?.targetMatchedUrl ?? normalizeNavigationSnapshotMatchedUrl(pending.action.navigationSnapshot.pathname);
148
162
  return {
149
163
  displayUrl,
150
164
  interception: pending.action.interception,
@@ -188,6 +202,7 @@ function planPendingRootBoundaryFlightResponse(options) {
188
202
  kind: "flightResponseArrived",
189
203
  result: {
190
204
  ...cacheEntryReuseProof ? { cacheEntryReuseProof } : {},
205
+ ...options.pending.restoredHistorySnapshot ? { restoredHistorySnapshot: true } : {},
191
206
  href: options.targetHref ?? options.targetSnapshot.displayUrl,
192
207
  targetSnapshot: options.targetSnapshot
193
208
  },
@@ -292,6 +307,8 @@ async function createPendingNavigationCommit(options) {
292
307
  operation: createOperationRecord({
293
308
  id: options.renderId,
294
309
  lane: options.operationLane,
310
+ navigationCommitKind: options.navigationCommitKind,
311
+ navigationId: options.navigationId,
295
312
  startedVisibleCommitVersion: options.currentState.visibleCommitVersion
296
313
  }),
297
314
  previousNextUrl,
@@ -312,4 +329,4 @@ async function createPendingNavigationCommit(options) {
312
329
  };
313
330
  }
314
331
  //#endregion
315
- export { FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN, VISITED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN, createBfcacheSegmentStateKeyMap, createHistoryStateWithNavigationMetadata, createHistoryStateWithPreviousNextUrl, createInitialBfcacheIdMap, createNextBfcacheIdMap, createPendingNavigationCommit, isCacheRestorableAppPayloadMetadata, isHistoryStateBfcacheVersionCurrent, preserveBfcacheIdsForMergedElements, readHistoryStateBfcacheIds, readHistoryStateBfcacheVersion, readHistoryStatePreviousNextUrl, readHistoryStateTraversalIndex, resolveHistoryTraversalIntent, resolveInterceptionContextFromPreviousNextUrl, resolvePendingNavigationCommitDispositionDecision, resolveServerActionRequestState };
332
+ export { COMMITTED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN, FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN, VISITED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN, createBfcacheSegmentStateKeyMap, createHistoryStateWithNavigationMetadata, createHistoryStateWithPreviousNextUrl, createInitialBfcacheIdMap, createNextBfcacheIdMap, createPendingNavigationCommit, isCacheRestorableAppPayloadMetadata, isCompleteAppPayloadMetadata, isHistoryStateBfcacheVersionCurrent, preserveBfcacheIdsForMergedElements, readHistoryStateBfcacheIds, readHistoryStateBfcacheVersion, readHistoryStatePreviousNextUrl, readHistoryStateTraversalIndex, resolveHistoryTraversalIntent, resolveInterceptionContextFromPreviousNextUrl, resolvePendingNavigationCommitDispositionDecision, resolveServerActionRequestState };
@@ -49,7 +49,12 @@ type ClassifiedPendingNavigationCommit = {
49
49
  trace: NavigationTrace;
50
50
  };
51
51
  declare function applyApprovedVisibleCommit(state: AppRouterState, commit: ApprovedVisibleCommit): AppRouterState;
52
- declare function approveHmrVisibleCommit(pending: PendingNavigationCommit): ApprovedVisibleCommit;
52
+ declare function approveHmrVisibleCommit(options: {
53
+ currentState: AppRouterState;
54
+ pending: PendingNavigationCommit;
55
+ routeManifest?: RouteManifest | null;
56
+ targetHref: string;
57
+ }): CommitApproval;
53
58
  declare function approvePendingNavigationCommit(options: {
54
59
  activeNavigationId: number;
55
60
  currentState: AppRouterState;
@@ -1,8 +1,8 @@
1
1
  import { normalizeAppElementsSlotBindings } from "./app-elements-wire.js";
2
2
  import "./app-elements.js";
3
3
  import { mergeElements } from "../shims/slot.js";
4
- import { NavigationTraceReasonCodes, NavigationTraceTransactionCodes, createNavigationTrace, prependNavigationTraceEntry } from "./navigation-trace.js";
5
4
  import { preserveBfcacheIdsForMergedElements } from "./app-bfcache-identity.js";
5
+ import { NavigationTraceReasonCodes, NavigationTraceTransactionCodes, createNavigationTrace, prependNavigationTraceEntry } from "./navigation-trace.js";
6
6
  import { createPendingNavigationCommit, resolvePendingNavigationCommitDispositionDecision } from "./app-browser-state.js";
7
7
  //#region src/server/app-browser-visible-commit.ts
8
8
  const approvedVisibleCommitBrand = Symbol("ApprovedVisibleCommit");
@@ -17,6 +17,8 @@ function commitOperationRecord(operation, visibleCommitVersion) {
17
17
  return {
18
18
  id: operation.id,
19
19
  lane: operation.lane,
20
+ ...operation.navigationCommitKind !== void 0 ? { navigationCommitKind: operation.navigationCommitKind } : {},
21
+ ...operation.navigationId !== void 0 ? { navigationId: operation.navigationId } : {},
20
22
  startedVisibleCommitVersion: operation.startedVisibleCommitVersion,
21
23
  state: "committed",
22
24
  visibleCommitVersion
@@ -56,9 +58,23 @@ function reduceApprovedVisibleCommitState(state, commit) {
56
58
  const { action } = commit;
57
59
  switch (action.type) {
58
60
  case "traverse":
59
- case "navigate": {
60
- const preserveElementIds = action.reuseCurrentBfcacheIds ? commit.decision.preserveElementIds : [];
61
- const preservePreviousSlotIds = action.reuseCurrentBfcacheIds ? commit.decision.preservePreviousSlotIds : [];
61
+ case "navigate":
62
+ case "replace": {
63
+ const bfcacheCompatiblePreserveElementIds = action.reuseCurrentBfcacheIds && action.operation.lane !== "refresh" ? commit.decision.preserveElementIds.filter((id) => {
64
+ const previousBfcacheId = state.bfcacheIds[id];
65
+ return previousBfcacheId !== void 0 && action.bfcacheIds[id] === previousBfcacheId;
66
+ }) : [];
67
+ const preservedSlotOwnerElementIdSet = new Set(bfcacheCompatiblePreserveElementIds);
68
+ const preservePreviousSlotIds = action.reuseCurrentBfcacheIds ? commit.decision.preservePreviousSlotIds.filter((slotId) => {
69
+ const targetBinding = action.slotBindings.find((binding) => binding.slotId === slotId);
70
+ return targetBinding?.ownerLayoutId !== null && targetBinding?.ownerLayoutId !== void 0 && preservedSlotOwnerElementIdSet.has(targetBinding.ownerLayoutId);
71
+ }) : [];
72
+ const hmrPreservedSlotOwnerLayoutIds = action.operation.lane === "hmr" ? bfcacheCompatiblePreserveElementIds.filter((id) => preservePreviousSlotIds.some((slotId) => {
73
+ return action.slotBindings.find((binding) => binding.slotId === slotId)?.ownerLayoutId === id;
74
+ })) : [];
75
+ const hmrPreserveElementIds = action.operation.lane === "hmr" && state.routeId === action.routeId && Object.hasOwn(state.elements, state.routeId) ? [state.routeId, ...hmrPreservedSlotOwnerLayoutIds] : hmrPreservedSlotOwnerLayoutIds;
76
+ const hmrUniquePreserveElementIds = hmrPreserveElementIds.length > 1 ? [...new Set(hmrPreserveElementIds)] : hmrPreserveElementIds;
77
+ const preserveElementIds = action.operation.lane === "hmr" ? hmrUniquePreserveElementIds : bfcacheCompatiblePreserveElementIds;
62
78
  const mergedElements = mergeElements(state.elements, action.elements, {
63
79
  clearAbsentSlots: action.type === "traverse" || !action.reuseCurrentBfcacheIds,
64
80
  preserveAbsentSlots: action.reuseCurrentBfcacheIds && commit.decision.preserveAbsentSlots,
@@ -84,20 +100,6 @@ function reduceApprovedVisibleCommitState(state, commit) {
84
100
  slotBindings: mergeSlotBindings(state.slotBindings, action.slotBindings, action.layoutIds, preservePreviousSlotIds)
85
101
  }, action.operation);
86
102
  }
87
- case "replace": return commitVisibleRouterState(state, {
88
- bfcacheIds: action.bfcacheIds,
89
- elements: action.elements,
90
- interception: action.interception,
91
- interceptionContext: action.interceptionContext,
92
- layoutFlags: action.layoutFlags,
93
- layoutIds: action.layoutIds,
94
- navigationSnapshot: action.navigationSnapshot,
95
- previousNextUrl: action.previousNextUrl,
96
- renderId: action.renderId,
97
- rootLayoutTreePath: action.rootLayoutTreePath,
98
- routeId: action.routeId,
99
- slotBindings: action.slotBindings
100
- }, action.operation);
101
103
  default: {
102
104
  const _exhaustive = action.type;
103
105
  throw new Error("[vinext] Unknown router action: " + String(_exhaustive));
@@ -176,14 +178,28 @@ function addCommitTransactionTrace(decision, pending) {
176
178
  default: throw new Error("[vinext] Unknown commit decision: " + String(decision));
177
179
  }
178
180
  }
179
- function approveHmrVisibleCommit(pending) {
181
+ function approveHmrVisibleCommit(options) {
182
+ const { currentState, pending } = options;
180
183
  if (pending.action.operation.lane !== "hmr") throw new Error("[vinext] HMR visible commit approval requires an HMR pending operation");
181
- const decision = addCommitTransactionTrace(createVisibleCommitDecision(), pending);
182
- if (decision.disposition !== "commit") throw new Error("[vinext] HMR visible commit approval did not produce a commit decision");
183
- return createApprovedVisibleCommit({
184
- decision,
185
- pending
186
- });
184
+ const tracedDecision = addCommitTransactionTrace(resolvePendingNavigationCommitDecision({
185
+ activeNavigationId: pending.action.operation.id,
186
+ currentState,
187
+ pending,
188
+ routeManifest: options.routeManifest,
189
+ startedNavigationId: pending.action.operation.id,
190
+ targetHref: options.targetHref
191
+ }), pending);
192
+ if (tracedDecision.disposition === "commit") return {
193
+ approvedCommit: createApprovedVisibleCommit({
194
+ decision: tracedDecision,
195
+ pending
196
+ }),
197
+ decision: tracedDecision
198
+ };
199
+ return {
200
+ approvedCommit: null,
201
+ decision: tracedDecision
202
+ };
187
203
  }
188
204
  function approvePendingNavigationCommit(options) {
189
205
  const decision = addCommitTransactionTrace(resolvePendingNavigationCommitDecision({
@@ -213,6 +229,8 @@ function approvePendingNavigationCommit(options) {
213
229
  async function resolveAndClassifyNavigationCommit(options) {
214
230
  const pending = await createPendingNavigationCommit({
215
231
  currentState: options.currentState,
232
+ navigationCommitKind: void 0,
233
+ navigationId: options.startedNavigationId,
216
234
  nextElements: options.nextElements,
217
235
  navigationSnapshot: options.navigationSnapshot,
218
236
  operationLane: options.operationLane,
@@ -6,6 +6,7 @@ import { ReactNode } from "react";
6
6
  //#region src/server/app-elements-wire.d.ts
7
7
  declare const APP_ARTIFACT_COMPATIBILITY_KEY = "__artifactCompatibility";
8
8
  declare const APP_CACHE_ENTRY_REUSE_PROOF_KEY = "__cacheEntryReuseProof";
9
+ declare const APP_DYNAMIC_STALE_TIME_KEY = "__dynamicStaleTime";
9
10
  declare const APP_INTERCEPTION_KEY = "__interception";
10
11
  declare const APP_INTERCEPTION_CONTEXT_KEY = "__interceptionContext";
11
12
  declare const APP_LAYOUT_IDS_KEY = "__layoutIds";
@@ -70,6 +71,7 @@ type LayoutFlags = Readonly<Record<string, "s" | "d">>;
70
71
  type AppElementsMetadata = {
71
72
  artifactCompatibility: ArtifactCompatibilityEnvelope;
72
73
  cacheEntryReuseProof?: CacheEntryReuseProof;
74
+ dynamicStaleTimeSeconds?: number;
73
75
  interception: AppElementsInterception | null;
74
76
  interceptionContext: string | null;
75
77
  layoutIds: readonly string[];
@@ -100,6 +102,7 @@ type AppElementsWireElementKey = {
100
102
  treePath: string;
101
103
  };
102
104
  type AppElementsWireMetadataInput = {
105
+ dynamicStaleTimeSeconds?: number;
103
106
  interception?: AppElementsInterception | null;
104
107
  interceptionContext: string | null;
105
108
  layoutIds?: readonly string[];
@@ -109,6 +112,7 @@ type AppElementsWireMetadataInput = {
109
112
  sourcePage?: string | null;
110
113
  };
111
114
  type AppElementsWireMetadataEntries = Readonly<{
115
+ [APP_DYNAMIC_STALE_TIME_KEY]?: number;
112
116
  [APP_ROUTE_KEY]: string;
113
117
  [APP_INTERCEPTION_KEY]?: AppElementsInterception;
114
118
  [APP_INTERCEPTION_CONTEXT_KEY]: string | null;
@@ -123,10 +127,11 @@ type AppElementsWireMetadataEntries = Readonly<{
123
127
  * known keys (e.g. __layoutFlags). Distinct from AppElements / AppWireElements
124
128
  * which only carry render-time values.
125
129
  */
126
- type AppOutgoingElements = Readonly<Record<string, ReactNode | LayoutFlags | ArtifactCompatibilityEnvelope | CacheEntryReuseProof | AppElementsInterception | RenderObservation | readonly string[] | readonly AppElementsSlotBinding[]>>;
130
+ type AppOutgoingElements = Readonly<Record<string, ReactNode | LayoutFlags | ArtifactCompatibilityEnvelope | CacheEntryReuseProof | AppElementsInterception | RenderObservation | number | readonly string[] | readonly AppElementsSlotBinding[]>>;
127
131
  type AppElementsWireKeys = {
128
132
  readonly artifactCompatibility: typeof APP_ARTIFACT_COMPATIBILITY_KEY;
129
133
  readonly cacheEntryReuseProof: typeof APP_CACHE_ENTRY_REUSE_PROOF_KEY;
134
+ readonly dynamicStaleTime: typeof APP_DYNAMIC_STALE_TIME_KEY;
130
135
  readonly interception: typeof APP_INTERCEPTION_KEY;
131
136
  readonly interceptionContext: typeof APP_INTERCEPTION_CONTEXT_KEY;
132
137
  readonly layoutIds: typeof APP_LAYOUT_IDS_KEY;
@@ -149,6 +154,7 @@ type AppElementsWireCodec = {
149
154
  element: ReactNode | AppElements;
150
155
  artifactCompatibility?: ArtifactCompatibilityEnvelope;
151
156
  cacheEntryReuseProof?: CacheEntryReuseProof;
157
+ dynamicStaleTimeSeconds?: number;
152
158
  layoutFlags: LayoutFlags;
153
159
  renderObservation?: RenderObservation;
154
160
  skipDisposition?: ClientReuseManifestSkipDisposition;
@@ -182,6 +188,7 @@ declare function buildOutgoingAppPayload(input: {
182
188
  element: ReactNode | AppElements;
183
189
  artifactCompatibility?: ArtifactCompatibilityEnvelope;
184
190
  cacheEntryReuseProof?: CacheEntryReuseProof;
191
+ dynamicStaleTimeSeconds?: number;
185
192
  layoutFlags: LayoutFlags;
186
193
  renderObservation?: RenderObservation;
187
194
  skipDisposition?: ClientReuseManifestSkipDisposition;
@@ -189,4 +196,4 @@ declare function buildOutgoingAppPayload(input: {
189
196
  declare function readAppElementsMetadata(elements: Readonly<Record<string, unknown>>): AppElementsMetadata;
190
197
  declare const AppElementsWire: AppElementsWireCodec;
191
198
  //#endregion
192
- export { APP_ARTIFACT_COMPATIBILITY_KEY, APP_CACHE_ENTRY_REUSE_PROOF_KEY, APP_INTERCEPTION_CONTEXT_KEY, APP_INTERCEPTION_KEY, APP_LAYOUT_FLAGS_KEY, APP_LAYOUT_IDS_KEY, APP_RENDER_OBSERVATION_KEY, APP_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_KEY, APP_STATIC_SIBLINGS_KEY, APP_UNMATCHED_SLOT_WIRE_VALUE, AppElementValue, AppElements, AppElementsInterception, AppElementsSlotBinding, AppElementsWire, AppOutgoingElements, AppWireElements, LayoutFlags, UNMATCHED_SLOT, buildOutgoingAppPayload, compareAppElementsSlotIds, isAppElementsRecord, normalizeAppElements, normalizeAppElementsSlotBindings, readAppElementsMetadata, withLayoutFlags };
199
+ export { APP_ARTIFACT_COMPATIBILITY_KEY, APP_CACHE_ENTRY_REUSE_PROOF_KEY, APP_DYNAMIC_STALE_TIME_KEY, APP_INTERCEPTION_CONTEXT_KEY, APP_INTERCEPTION_KEY, APP_LAYOUT_FLAGS_KEY, APP_LAYOUT_IDS_KEY, APP_RENDER_OBSERVATION_KEY, APP_ROOT_LAYOUT_KEY, APP_ROUTE_KEY, APP_SKIPPED_LAYOUT_IDS_KEY, APP_SLOT_BINDINGS_KEY, APP_SOURCE_PAGE_KEY, APP_STATIC_SIBLINGS_KEY, APP_UNMATCHED_SLOT_WIRE_VALUE, AppElementValue, AppElements, AppElementsInterception, AppElementsSlotBinding, AppElementsWire, AppOutgoingElements, AppWireElements, LayoutFlags, UNMATCHED_SLOT, buildOutgoingAppPayload, compareAppElementsSlotIds, isAppElementsRecord, normalizeAppElements, normalizeAppElementsSlotBindings, readAppElementsMetadata, withLayoutFlags };